diff options
author | Steven Laver <lavers@google.com> | 2020-02-04 22:57:22 -0800 |
---|---|---|
committer | Steven Laver <lavers@google.com> | 2020-02-06 14:27:42 -0800 |
commit | 89ec9480cf4430b6728b16396a408c1aa26f19a9 (patch) | |
tree | f9a888e63588b25c457d8871f6fbb6b673044f1a | |
parent | 4e64db5a184f0734778df5e1c12d32f702220b35 (diff) | |
parent | 146a5a28331209aa145c0af2f883b7e023ce4800 (diff) |
Merge RP1A.200204.001
Change-Id: Icdc452e8cf70e432759c112a5b5b12b61e40f0bc
249 files changed, 11107 insertions, 2225 deletions
diff --git a/audio/common/all-versions/copyHAL.sh b/audio/common/all-versions/copyHAL.sh index d07012fe04..56559d13e3 100755 --- a/audio/common/all-versions/copyHAL.sh +++ b/audio/common/all-versions/copyHAL.sh @@ -26,6 +26,7 @@ readonly IMPL_FACTORYHAL=$IMPL_DIRECTORY/include/libaudiohal/FactoryHalHidl.h readonly VTS_DIRECTORY=test/vts-testcase/hal/audio readonly VTS_LIST=test/vts/tools/build/tasks/list/vts_test_lib_hidl_package_list.mk readonly WATCHDOG=frameworks/base/services/core/java/com/android/server/Watchdog.cpp +readonly DUMP_UTILS=frameworks/native/libs/dumputils/dump_utils.cpp readonly GSI_CURRENT=build/make/target/product/gsi/current.txt readonly BASE_VERSION=${1:-$(ls $ANDROID_BUILD_TOP/$HAL_DIRECTORY | grep -E '[0-9]+\.[0-9]+' | @@ -171,6 +172,9 @@ runIfNeeded $(dirname $VTS_LIST) updateAudioVersion -v original_before=1 $(basen echo "Now update watchdog" runIfNeeded $(dirname $WATCHDOG) updateAudioVersion -v original_before=1 $(basename $WATCHDOG) +echo "Now update dumputils" +runIfNeeded $(dirname $DUMP_UTILS) updateAudioVersion -v original_before=1 $(basename $DUMP_UTILS) + echo "Now update GSI current.txt" runIfNeeded $(dirname $GSI_CURRENT) update-vndk-list.sh diff --git a/audio/common/all-versions/default/service/Android.bp b/audio/common/all-versions/default/service/Android.bp index 6737ba6428..e5d47706c3 100644 --- a/audio/common/all-versions/default/service/Android.bp +++ b/audio/common/all-versions/default/service/Android.bp @@ -25,25 +25,6 @@ cc_binary { "liblog", "libutils", "libhardware", - "android.hardware.audio@2.0", - "android.hardware.audio@4.0", - "android.hardware.audio@5.0", - "android.hardware.audio@6.0", - "android.hardware.audio.common@2.0", - "android.hardware.audio.common@4.0", - "android.hardware.audio.common@5.0", - "android.hardware.audio.common@6.0", - "android.hardware.audio.effect@2.0", - "android.hardware.audio.effect@4.0", - "android.hardware.audio.effect@5.0", - "android.hardware.audio.effect@6.0", - "android.hardware.bluetooth.a2dp@1.0", - "android.hardware.bluetooth.audio@2.0", - "android.hardware.soundtrigger@2.0", - "android.hardware.soundtrigger@2.1", - "android.hardware.soundtrigger@2.2", - "android.hardware.soundtrigger@2.3", - "com.qualcomm.qti.bluetooth_audio@1.0", ], arch : { arm : { diff --git a/audio/common/all-versions/default/service/service.cpp b/audio/common/all-versions/default/service/service.cpp index d53443356a..2801769863 100644 --- a/audio/common/all-versions/default/service/service.cpp +++ b/audio/common/all-versions/default/service/service.cpp @@ -16,20 +16,9 @@ #define LOG_TAG "audiohalservice" -#include <android/hardware/audio/2.0/IDevicesFactory.h> -#include <android/hardware/audio/4.0/IDevicesFactory.h> -#include <android/hardware/audio/5.0/IDevicesFactory.h> -#include <android/hardware/audio/6.0/IDevicesFactory.h> -#include <android/hardware/audio/effect/2.0/IEffectsFactory.h> -#include <android/hardware/audio/effect/4.0/IEffectsFactory.h> -#include <android/hardware/audio/effect/5.0/IEffectsFactory.h> -#include <android/hardware/audio/effect/6.0/IEffectsFactory.h> -#include <android/hardware/bluetooth/a2dp/1.0/IBluetoothAudioOffload.h> -#include <android/hardware/bluetooth/audio/2.0/IBluetoothAudioProvidersFactory.h> -#include <android/hardware/soundtrigger/2.0/ISoundTriggerHw.h> -#include <android/hardware/soundtrigger/2.1/ISoundTriggerHw.h> -#include <android/hardware/soundtrigger/2.2/ISoundTriggerHw.h> -#include <android/hardware/soundtrigger/2.3/ISoundTriggerHw.h> +#include <string> +#include <vector> + #include <binder/ProcessState.h> #include <cutils/properties.h> #include <hidl/HidlTransportSupport.h> @@ -39,6 +28,8 @@ using namespace android::hardware; using android::OK; +using InterfacesList = std::vector<std::string>; + #ifdef ARCH_ARM_32 //default h/w binder memsize is 1 MB #define DEFAULT_HW_BINDER_MEM_SIZE_KB 1024 @@ -55,9 +46,14 @@ size_t getHWBinderMmapSize(){ * If any registers successfully, do not register any other and return true. * If all fail, return false. */ -template <class... Factories> -bool registerPassthroughServiceImplementations() { - return ((registerPassthroughServiceImplementation<Factories>() != OK) && ...); +template <class Iter> +static bool registerPassthroughServiceImplementations(Iter first, Iter last) { + for (; first != last; ++first) { + if (registerPassthroughServiceImplementation(*first) == OK) { + return true; + } + } + return false; } int main(int /* argc */, char* /* argv */ []) { @@ -77,36 +73,58 @@ int main(int /* argc */, char* /* argv */ []) { } configureRpcThreadpool(16, true /*callerWillJoin*/); - // Keep versions on a separate line for easier parsing + // Automatic formatting tries to compact the lines, making them less readable // clang-format off - LOG_ALWAYS_FATAL_IF((registerPassthroughServiceImplementations< - audio::V6_0::IDevicesFactory, - audio::V5_0::IDevicesFactory, - audio::V4_0::IDevicesFactory, - audio::V2_0::IDevicesFactory>()), - "Could not register audio core API"); + const std::vector<InterfacesList> mandatoryInterfaces = { + { + "Audio Core API", + "android.hardware.audio@6.0::IDevicesFactory", + "android.hardware.audio@5.0::IDevicesFactory", + "android.hardware.audio@4.0::IDevicesFactory", + "android.hardware.audio@2.0::IDevicesFactory" + }, + { + "Audio Effect API", + "android.hardware.audio.effect@6.0::IEffectsFactory", + "android.hardware.audio.effect@5.0::IEffectsFactory", + "android.hardware.audio.effect@4.0::IEffectsFactory", + "android.hardware.audio.effect@2.0::IEffectsFactory", + } + }; - LOG_ALWAYS_FATAL_IF((registerPassthroughServiceImplementations< - audio::effect::V6_0::IEffectsFactory, - audio::effect::V5_0::IEffectsFactory, - audio::effect::V4_0::IEffectsFactory, - audio::effect::V2_0::IEffectsFactory>()), - "Could not register audio effect API"); + const std::vector<InterfacesList> optionalInterfaces = { + { + "Soundtrigger API", + "android.hardware.soundtrigger@2.3::ISoundTriggerHw", + "android.hardware.soundtrigger@2.2::ISoundTriggerHw", + "android.hardware.soundtrigger@2.1::ISoundTriggerHw", + "android.hardware.soundtrigger@2.0::ISoundTriggerHw", + }, + { + "Bluetooth Audio API", + "android.hardware.bluetooth.audio@2.0::IBluetoothAudioProvidersFactory" + }, + // remove the old HIDL when Bluetooth Audio Hal V2 has offloading supported + { + "Bluetooth Audio Offload API", + "android.hardware.bluetooth.a2dp@1.0::IBluetoothAudioOffload" + } + }; // clang-format on - ALOGW_IF((registerPassthroughServiceImplementations< - soundtrigger::V2_3::ISoundTriggerHw, soundtrigger::V2_2::ISoundTriggerHw, - soundtrigger::V2_1::ISoundTriggerHw, soundtrigger::V2_0::ISoundTriggerHw>()), - "Could not register soundtrigger API"); - - ALOGW_IF(registerPassthroughServiceImplementations< - bluetooth::audio::V2_0::IBluetoothAudioProvidersFactory>(), - "Could not register Bluetooth audio API"); + for (const auto& listIter : mandatoryInterfaces) { + auto iter = listIter.begin(); + const std::string& interfaceFamilyName = *iter++; + LOG_ALWAYS_FATAL_IF(!registerPassthroughServiceImplementations(iter, listIter.end()), + "Could not register %s", interfaceFamilyName.c_str()); + } - // remove the old HIDL when Bluetooth Audio Hal V2 has offloading supported - ALOGW_IF(registerPassthroughServiceImplementations< - bluetooth::a2dp::V1_0::IBluetoothAudioOffload>(), - "Could not register Bluetooth audio offload API"); + for (const auto& listIter : optionalInterfaces) { + auto iter = listIter.begin(); + const std::string& interfaceFamilyName = *iter++; + ALOGW_IF(!registerPassthroughServiceImplementations(iter, listIter.end()), + "Could not register %s", interfaceFamilyName.c_str()); + } joinRpcThreadpool(); } diff --git a/audio/core/all-versions/default/PrimaryDevice.cpp b/audio/core/all-versions/default/PrimaryDevice.cpp index 679f85dad3..11c1c5a4df 100644 --- a/audio/core/all-versions/default/PrimaryDevice.cpp +++ b/audio/core/all-versions/default/PrimaryDevice.cpp @@ -203,6 +203,9 @@ Return<Result> PrimaryDevice::setMode(AudioMode mode) { case AudioMode::RINGTONE: case AudioMode::IN_CALL: case AudioMode::IN_COMMUNICATION: +#if MAJOR_VERSION >= 6 + case AudioMode::CALL_SCREEN: +#endif break; // Valid values default: return Result::INVALID_ARGUMENTS; diff --git a/audio/core/all-versions/vts/functional/4.0/AudioPrimaryHidlHalTest.cpp b/audio/core/all-versions/vts/functional/4.0/AudioPrimaryHidlHalTest.cpp index 709b7cd369..b0eb2e0cfb 100644 --- a/audio/core/all-versions/vts/functional/4.0/AudioPrimaryHidlHalTest.cpp +++ b/audio/core/all-versions/vts/functional/4.0/AudioPrimaryHidlHalTest.cpp @@ -244,7 +244,13 @@ TEST_P(OutputStreamTest, updateSourceMetadata) { TEST_P(AudioPrimaryHidlTest, setMode) { doc::test("Make sure setMode always succeeds if mode is valid and fails otherwise"); // Test Invalid values - for (int mode : {-2, -1, int(AudioMode::IN_COMMUNICATION) + 1}) { +#if MAJOR_VERSION >= 6 + int maxMode = int(AudioMode::CALL_SCREEN); +#else + int maxMode = int(AudioMode::IN_COMMUNICATION); +#endif + + for (int mode : {-2, -1, maxMode + 1}) { ASSERT_RESULT(Result::INVALID_ARGUMENTS, getDevice()->setMode(AudioMode(mode))) << "mode=" << mode; } @@ -253,6 +259,10 @@ TEST_P(AudioPrimaryHidlTest, setMode) { AudioMode::NORMAL /* Make sure to leave the test in normal mode */}) { ASSERT_OK(getDevice()->setMode(mode)) << "mode=" << toString(mode); } + // AudioMode::CALL_SCREEN as support is optional +#if MAJOR_VERSION >= 6 + ASSERT_RESULT(okOrNotSupportedOrInvalidArgs, getDevice()->setMode(AudioMode::CALL_SCREEN)); +#endif } TEST_P(AudioPrimaryHidlTest, setBtHfpSampleRate) { diff --git a/audio/core/all-versions/vts/functional/Android.bp b/audio/core/all-versions/vts/functional/Android.bp index 1818e36112..715f3763b2 100644 --- a/audio/core/all-versions/vts/functional/Android.bp +++ b/audio/core/all-versions/vts/functional/Android.bp @@ -48,7 +48,7 @@ cc_test { "-DMAJOR_VERSION=2", "-DMINOR_VERSION=0", "-include common/all-versions/VersionMacro.h", - ] + ], } cc_test { @@ -65,7 +65,7 @@ cc_test { "-DMAJOR_VERSION=4", "-DMINOR_VERSION=0", "-include common/all-versions/VersionMacro.h", - ] + ], } cc_test { @@ -82,7 +82,7 @@ cc_test { "-DMAJOR_VERSION=5", "-DMINOR_VERSION=0", "-include common/all-versions/VersionMacro.h", - ] + ], } cc_test { @@ -99,5 +99,15 @@ cc_test { "-DMAJOR_VERSION=6", "-DMINOR_VERSION=0", "-include common/all-versions/VersionMacro.h", - ] + ], + // Use test_config for vts-core suite. + // TODO(b/146104851): Add auto-gen rules and remove it. + test_config: "VtsHalAudioV6_0TargetTest.xml", + data: [ + ":audio_policy_configuration_V6_0", + ], + test_suites: [ + "general-tests", + "vts-core", + ], } diff --git a/audio/core/all-versions/vts/functional/VtsHalAudioV6_0TargetTest.xml b/audio/core/all-versions/vts/functional/VtsHalAudioV6_0TargetTest.xml new file mode 100644 index 0000000000..05edc0dc7d --- /dev/null +++ b/audio/core/all-versions/vts/functional/VtsHalAudioV6_0TargetTest.xml @@ -0,0 +1,40 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- Copyright (C) 2020 The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +--> +<configuration description="Runs VtsHalAudioV6_0TargetTest."> + <option name="test-suite-tag" value="apct" /> + <option name="test-suite-tag" value="apct-native" /> + + <target_preparer class="com.android.tradefed.targetprep.RootTargetPreparer"> + </target_preparer> + + <target_preparer class="com.android.tradefed.targetprep.RunCommandTargetPreparer"> + <option name="run-command" value="stop"/> + <option name="run-command" value="setprop vts.native_server.on 1"/> + <option name="teardown-command" value="start"/> + <option name="teardown-command" value="setprop vts.native_server.on 0"/> + </target_preparer> + + <target_preparer class="com.android.tradefed.targetprep.PushFilePreparer"> + <option name="cleanup" value="true" /> + <option name="push" value="VtsHalAudioV6_0TargetTest->/data/local/tmp/VtsHalAudioV6_0TargetTest" /> + <option name="push" value="audio_policy_configuration_V6_0.xsd->/data/local/tmp/audio_policy_configuration_V6_0.xsd" /> + </target_preparer> + + <test class="com.android.tradefed.testtype.GTest" > + <option name="native-test-device-path" value="/data/local/tmp" /> + <option name="module-name" value="VtsHalAudioV6_0TargetTest" /> + </test> +</configuration> diff --git a/audio/effect/6.0/xml/api/current.txt b/audio/effect/6.0/xml/api/current.txt index 2dfcb9b348..072cf8f9b8 100644 --- a/audio/effect/6.0/xml/api/current.txt +++ b/audio/effect/6.0/xml/api/current.txt @@ -3,11 +3,13 @@ package audio.effects.V6_0 { public class AudioEffectsConf { ctor public AudioEffectsConf(); + method public audio.effects.V6_0.AudioEffectsConf.DeviceEffects getDeviceEffects(); method public audio.effects.V6_0.EffectsType getEffects(); method public audio.effects.V6_0.LibrariesType getLibraries(); method public audio.effects.V6_0.AudioEffectsConf.Postprocess getPostprocess(); method public audio.effects.V6_0.AudioEffectsConf.Preprocess getPreprocess(); method public audio.effects.V6_0.VersionType getVersion(); + method public void setDeviceEffects(audio.effects.V6_0.AudioEffectsConf.DeviceEffects); method public void setEffects(audio.effects.V6_0.EffectsType); method public void setLibraries(audio.effects.V6_0.LibrariesType); method public void setPostprocess(audio.effects.V6_0.AudioEffectsConf.Postprocess); @@ -15,6 +17,11 @@ package audio.effects.V6_0 { method public void setVersion(audio.effects.V6_0.VersionType); } + public static class AudioEffectsConf.DeviceEffects { + ctor public AudioEffectsConf.DeviceEffects(); + method public java.util.List<audio.effects.V6_0.DeviceProcessType> getDevicePort(); + } + public static class AudioEffectsConf.Postprocess { ctor public AudioEffectsConf.Postprocess(); method public java.util.List<audio.effects.V6_0.StreamPostprocessType> getStream(); @@ -25,6 +32,68 @@ package audio.effects.V6_0 { method public java.util.List<audio.effects.V6_0.StreamPreprocessType> getStream(); } + public class DeviceProcessType extends audio.effects.V6_0.StreamProcessingType { + ctor public DeviceProcessType(); + } + + public enum DeviceType { + method public String getRawName(); + enum_constant public static final audio.effects.V6_0.DeviceType AUDIO_DEVICE_IN_AUX_DIGITAL; + enum_constant public static final audio.effects.V6_0.DeviceType AUDIO_DEVICE_IN_BACK_MIC; + enum_constant public static final audio.effects.V6_0.DeviceType AUDIO_DEVICE_IN_BLUETOOTH_A2DP; + enum_constant public static final audio.effects.V6_0.DeviceType AUDIO_DEVICE_IN_BLUETOOTH_BLE; + enum_constant public static final audio.effects.V6_0.DeviceType AUDIO_DEVICE_IN_BLUETOOTH_SCO_HEADSET; + enum_constant public static final audio.effects.V6_0.DeviceType AUDIO_DEVICE_IN_BUILTIN_MIC; + enum_constant public static final audio.effects.V6_0.DeviceType AUDIO_DEVICE_IN_BUS; + enum_constant public static final audio.effects.V6_0.DeviceType AUDIO_DEVICE_IN_COMMUNICATION; + enum_constant public static final audio.effects.V6_0.DeviceType AUDIO_DEVICE_IN_DGTL_DOCK_HEADSET; + enum_constant public static final audio.effects.V6_0.DeviceType AUDIO_DEVICE_IN_ECHO_REFERENCE; + enum_constant public static final audio.effects.V6_0.DeviceType AUDIO_DEVICE_IN_FM_TUNER; + enum_constant public static final audio.effects.V6_0.DeviceType AUDIO_DEVICE_IN_HDMI; + enum_constant public static final audio.effects.V6_0.DeviceType AUDIO_DEVICE_IN_HDMI_ARC; + enum_constant public static final audio.effects.V6_0.DeviceType AUDIO_DEVICE_IN_IP; + enum_constant public static final audio.effects.V6_0.DeviceType AUDIO_DEVICE_IN_LINE; + enum_constant public static final audio.effects.V6_0.DeviceType AUDIO_DEVICE_IN_LOOPBACK; + enum_constant public static final audio.effects.V6_0.DeviceType AUDIO_DEVICE_IN_PROXY; + enum_constant public static final audio.effects.V6_0.DeviceType AUDIO_DEVICE_IN_REMOTE_SUBMIX; + enum_constant public static final audio.effects.V6_0.DeviceType AUDIO_DEVICE_IN_SPDIF; + enum_constant public static final audio.effects.V6_0.DeviceType AUDIO_DEVICE_IN_TELEPHONY_RX; + enum_constant public static final audio.effects.V6_0.DeviceType AUDIO_DEVICE_IN_TV_TUNER; + enum_constant public static final audio.effects.V6_0.DeviceType AUDIO_DEVICE_IN_USB_ACCESSORY; + enum_constant public static final audio.effects.V6_0.DeviceType AUDIO_DEVICE_IN_USB_DEVICE; + enum_constant public static final audio.effects.V6_0.DeviceType AUDIO_DEVICE_IN_USB_HEADSET; + enum_constant public static final audio.effects.V6_0.DeviceType AUDIO_DEVICE_IN_VOICE_CALL; + enum_constant public static final audio.effects.V6_0.DeviceType AUDIO_DEVICE_IN_WIRED_HEADSET; + enum_constant public static final audio.effects.V6_0.DeviceType AUDIO_DEVICE_OUT_AUX_DIGITAL; + enum_constant public static final audio.effects.V6_0.DeviceType AUDIO_DEVICE_OUT_AUX_LINE; + enum_constant public static final audio.effects.V6_0.DeviceType AUDIO_DEVICE_OUT_BLUETOOTH_A2DP; + enum_constant public static final audio.effects.V6_0.DeviceType AUDIO_DEVICE_OUT_BLUETOOTH_A2DP_HEADPHONES; + enum_constant public static final audio.effects.V6_0.DeviceType AUDIO_DEVICE_OUT_BLUETOOTH_A2DP_SPEAKER; + enum_constant public static final audio.effects.V6_0.DeviceType AUDIO_DEVICE_OUT_BLUETOOTH_SCO; + enum_constant public static final audio.effects.V6_0.DeviceType AUDIO_DEVICE_OUT_BLUETOOTH_SCO_CARKIT; + enum_constant public static final audio.effects.V6_0.DeviceType AUDIO_DEVICE_OUT_BLUETOOTH_SCO_HEADSET; + enum_constant public static final audio.effects.V6_0.DeviceType AUDIO_DEVICE_OUT_BUS; + enum_constant public static final audio.effects.V6_0.DeviceType AUDIO_DEVICE_OUT_EARPIECE; + enum_constant public static final audio.effects.V6_0.DeviceType AUDIO_DEVICE_OUT_ECHO_CANCELLER; + enum_constant public static final audio.effects.V6_0.DeviceType AUDIO_DEVICE_OUT_FM; + enum_constant public static final audio.effects.V6_0.DeviceType AUDIO_DEVICE_OUT_HDMI; + enum_constant public static final audio.effects.V6_0.DeviceType AUDIO_DEVICE_OUT_HDMI_ARC; + enum_constant public static final audio.effects.V6_0.DeviceType AUDIO_DEVICE_OUT_HEARING_AID; + enum_constant public static final audio.effects.V6_0.DeviceType AUDIO_DEVICE_OUT_IP; + enum_constant public static final audio.effects.V6_0.DeviceType AUDIO_DEVICE_OUT_LINE; + enum_constant public static final audio.effects.V6_0.DeviceType AUDIO_DEVICE_OUT_PROXY; + enum_constant public static final audio.effects.V6_0.DeviceType AUDIO_DEVICE_OUT_REMOTE_SUBMIX; + enum_constant public static final audio.effects.V6_0.DeviceType AUDIO_DEVICE_OUT_SPDIF; + enum_constant public static final audio.effects.V6_0.DeviceType AUDIO_DEVICE_OUT_SPEAKER; + enum_constant public static final audio.effects.V6_0.DeviceType AUDIO_DEVICE_OUT_SPEAKER_SAFE; + enum_constant public static final audio.effects.V6_0.DeviceType AUDIO_DEVICE_OUT_TELEPHONY_TX; + enum_constant public static final audio.effects.V6_0.DeviceType AUDIO_DEVICE_OUT_USB_ACCESSORY; + enum_constant public static final audio.effects.V6_0.DeviceType AUDIO_DEVICE_OUT_USB_DEVICE; + enum_constant public static final audio.effects.V6_0.DeviceType AUDIO_DEVICE_OUT_USB_HEADSET; + enum_constant public static final audio.effects.V6_0.DeviceType AUDIO_DEVICE_OUT_WIRED_HEADPHONE; + enum_constant public static final audio.effects.V6_0.DeviceType AUDIO_DEVICE_OUT_WIRED_HEADSET; + } + public class EffectImplType { ctor public EffectImplType(); method public String getLibrary(); @@ -69,6 +138,8 @@ package audio.effects.V6_0 { public enum StreamInputType { method public String getRawName(); enum_constant public static final audio.effects.V6_0.StreamInputType camcorder; + enum_constant public static final audio.effects.V6_0.StreamInputType echo_reference; + enum_constant public static final audio.effects.V6_0.StreamInputType fm_tuner; enum_constant public static final audio.effects.V6_0.StreamInputType mic; enum_constant public static final audio.effects.V6_0.StreamInputType unprocessed; enum_constant public static final audio.effects.V6_0.StreamInputType voice_call; diff --git a/audio/effect/6.0/xml/audio_effects_conf.xsd b/audio/effect/6.0/xml/audio_effects_conf.xsd index a7ff20bb1c..fcfecda821 100644 --- a/audio/effect/6.0/xml/audio_effects_conf.xsd +++ b/audio/effect/6.0/xml/audio_effects_conf.xsd @@ -36,6 +36,8 @@ <xs:enumeration value="voice_communication"/> <xs:enumeration value="unprocessed"/> <xs:enumeration value="voice_performance"/> + <xs:enumeration value="echo_reference"/> + <xs:enumeration value="fm_tuner"/> </xs:restriction> </xs:simpleType> <xs:simpleType name="streamOutputType"> @@ -58,6 +60,65 @@ <xs:pattern value="[^/].*"/> </xs:restriction> </xs:simpleType> + <xs:simpleType name="deviceType"> + <xs:restriction base="xs:string"> + <xs:enumeration value="AUDIO_DEVICE_OUT_EARPIECE"/> + <xs:enumeration value="AUDIO_DEVICE_OUT_SPEAKER"/> + <xs:enumeration value="AUDIO_DEVICE_OUT_WIRED_HEADSET"/> + <xs:enumeration value="AUDIO_DEVICE_OUT_WIRED_HEADPHONE"/> + <xs:enumeration value="AUDIO_DEVICE_OUT_BLUETOOTH_SCO"/> + <xs:enumeration value="AUDIO_DEVICE_OUT_BLUETOOTH_SCO_HEADSET"/> + <xs:enumeration value="AUDIO_DEVICE_OUT_BLUETOOTH_SCO_CARKIT"/> + <xs:enumeration value="AUDIO_DEVICE_OUT_BLUETOOTH_A2DP"/> + <xs:enumeration value="AUDIO_DEVICE_OUT_BLUETOOTH_A2DP_HEADPHONES"/> + <xs:enumeration value="AUDIO_DEVICE_OUT_BLUETOOTH_A2DP_SPEAKER"/> + <xs:enumeration value="AUDIO_DEVICE_OUT_AUX_DIGITAL"/> + <xs:enumeration value="AUDIO_DEVICE_OUT_HDMI"/> + <xs:enumeration value="AUDIO_DEVICE_OUT_USB_ACCESSORY"/> + <xs:enumeration value="AUDIO_DEVICE_OUT_USB_DEVICE"/> + <xs:enumeration value="AUDIO_DEVICE_OUT_REMOTE_SUBMIX"/> + <xs:enumeration value="AUDIO_DEVICE_OUT_TELEPHONY_TX"/> + <xs:enumeration value="AUDIO_DEVICE_OUT_LINE"/> + <xs:enumeration value="AUDIO_DEVICE_OUT_HDMI_ARC"/> + <xs:enumeration value="AUDIO_DEVICE_OUT_SPDIF"/> + <xs:enumeration value="AUDIO_DEVICE_OUT_FM"/> + <xs:enumeration value="AUDIO_DEVICE_OUT_AUX_LINE"/> + <xs:enumeration value="AUDIO_DEVICE_OUT_SPEAKER_SAFE"/> + <xs:enumeration value="AUDIO_DEVICE_OUT_IP"/> + <xs:enumeration value="AUDIO_DEVICE_OUT_BUS"/> + <xs:enumeration value="AUDIO_DEVICE_OUT_PROXY"/> + <xs:enumeration value="AUDIO_DEVICE_OUT_USB_HEADSET"/> + <xs:enumeration value="AUDIO_DEVICE_OUT_HEARING_AID"/> + <xs:enumeration value="AUDIO_DEVICE_OUT_ECHO_CANCELLER"/> + <!-- Due to the xml format, IN types can not be a separated from OUT types --> + <xs:enumeration value="AUDIO_DEVICE_IN_COMMUNICATION"/> + <xs:enumeration value="AUDIO_DEVICE_IN_BUILTIN_MIC"/> + <xs:enumeration value="AUDIO_DEVICE_IN_BLUETOOTH_SCO_HEADSET"/> + <xs:enumeration value="AUDIO_DEVICE_IN_WIRED_HEADSET"/> + <xs:enumeration value="AUDIO_DEVICE_IN_AUX_DIGITAL"/> + <xs:enumeration value="AUDIO_DEVICE_IN_HDMI"/> + <xs:enumeration value="AUDIO_DEVICE_IN_VOICE_CALL"/> + <xs:enumeration value="AUDIO_DEVICE_IN_TELEPHONY_RX"/> + <xs:enumeration value="AUDIO_DEVICE_IN_BACK_MIC"/> + <xs:enumeration value="AUDIO_DEVICE_IN_REMOTE_SUBMIX"/> + <xs:enumeration value="AUDIO_DEVICE_IN_DGTL_DOCK_HEADSET"/> + <xs:enumeration value="AUDIO_DEVICE_IN_USB_ACCESSORY"/> + <xs:enumeration value="AUDIO_DEVICE_IN_USB_DEVICE"/> + <xs:enumeration value="AUDIO_DEVICE_IN_FM_TUNER"/> + <xs:enumeration value="AUDIO_DEVICE_IN_TV_TUNER"/> + <xs:enumeration value="AUDIO_DEVICE_IN_LINE"/> + <xs:enumeration value="AUDIO_DEVICE_IN_SPDIF"/> + <xs:enumeration value="AUDIO_DEVICE_IN_BLUETOOTH_A2DP"/> + <xs:enumeration value="AUDIO_DEVICE_IN_LOOPBACK"/> + <xs:enumeration value="AUDIO_DEVICE_IN_IP"/> + <xs:enumeration value="AUDIO_DEVICE_IN_BUS"/> + <xs:enumeration value="AUDIO_DEVICE_IN_PROXY"/> + <xs:enumeration value="AUDIO_DEVICE_IN_USB_HEADSET"/> + <xs:enumeration value="AUDIO_DEVICE_IN_BLUETOOTH_BLE"/> + <xs:enumeration value="AUDIO_DEVICE_IN_HDMI_ARC"/> + <xs:enumeration value="AUDIO_DEVICE_IN_ECHO_REFERENCE"/> + </xs:restriction> + </xs:simpleType> <!-- Complex types --> <xs:complexType name="librariesType"> <xs:annotation> @@ -174,6 +235,33 @@ </xs:extension> </xs:complexContent> </xs:complexType> + <xs:complexType name="deviceProcessType"> + <xs:annotation> + <xs:documentation xml:lang="en"> + Audio Device Effects configuration. The processing configuration consists + of a list of effects to be automatically added on a device Port when involved in an audio + patch. + Valid device type are listed in "deviceType" and shall be aligned. + Each stream element contains a list of "apply" elements. The value of the + "effect" attr must correspond to the name of an "effect" element. + Note that if the device is involved in a hardware patch, the effect must be hardware + accelerated. + Example: + <devicePort address="BUS00_USAGE_MAIN" type="AUDIO_DEVICE_OUT_BUS"> + <apply effect="equalizer"/> + <apply effect="effect2"/> + </devicePort> + </xs:documentation> + </xs:annotation> + <xs:complexContent> + <xs:extension base="aec:streamProcessingType"> + <xs:complexType> + <xs:attribute name="address" type="xs:string" use="required"/> + <xs:attribute name="type" type="aec:deviceType" use="required"/> + </xs:complexType> + </xs:extension> + </xs:complexContent> + </xs:complexType> <!-- Root element --> <xs:element name="audio_effects_conf"> <xs:complexType> @@ -194,6 +282,13 @@ </xs:sequence> </xs:complexType> </xs:element> + <xs:element name="deviceEffects" minOccurs="0" maxOccurs="1"> + <xs:complexType> + <xs:sequence> + <xs:element name="devicePort" type="aec:deviceProcessType" minOccurs="0" maxOccurs="unbounded"/> + </xs:sequence> + </xs:complexType> + </xs:element> </xs:sequence> <xs:attribute name="version" type="aec:versionType" use="required"/> </xs:complexType> diff --git a/automotive/audiocontrol/1.0/default/Android.bp b/automotive/audiocontrol/1.0/default/Android.bp index 3d04c890bc..ae4b8057ae 100644 --- a/automotive/audiocontrol/1.0/default/Android.bp +++ b/automotive/audiocontrol/1.0/default/Android.bp @@ -30,9 +30,4 @@ cc_binary { "libutils", ], vintf_fragments: ["audiocontrol_manifest.xml"], - cflags: [ - "-DLOG_TAG=\"AudCntrlDrv\"", - "-O0", - "-g", - ], } diff --git a/automotive/audiocontrol/2.0/Android.bp b/automotive/audiocontrol/2.0/Android.bp new file mode 100644 index 0000000000..2a9f8499e3 --- /dev/null +++ b/automotive/audiocontrol/2.0/Android.bp @@ -0,0 +1,20 @@ +// This file is autogenerated by hidl-gen -Landroidbp. + +hidl_interface { + name: "android.hardware.automotive.audiocontrol@2.0", + root: "android.hardware", + vndk: { + enabled: true, + }, + srcs: [ + "types.hal", + "IAudioControl.hal", + "ICloseHandle.hal", + "IFocusListener.hal", + ], + interfaces: [ + "android.hidl.base@1.0", + "android.hardware.audio.common@6.0", + ], + gen_java: true, +} diff --git a/automotive/audiocontrol/2.0/IAudioControl.hal b/automotive/audiocontrol/2.0/IAudioControl.hal new file mode 100644 index 0000000000..1073498f61 --- /dev/null +++ b/automotive/audiocontrol/2.0/IAudioControl.hal @@ -0,0 +1,78 @@ +/* + * Copyright (C) 2020 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.hardware.automotive.audiocontrol@2.0; + +import ICloseHandle; +import IFocusListener; +import android.hardware.audio.common@6.0::AudioUsage; + +/** + * Interacts with the car's audio subsystem to manage audio sources and volumes + */ +interface IAudioControl { + /** + * Registers focus listener to be used by HAL for requesting and abandoning audio focus. + * + * It is expected that there will only ever be a single focus listener registered. If the + * observer dies, the HAL implementation must unregister observer automatically. If called when + * a listener is already registered, the existing one should be unregistered and replaced with + * the new listener. + * + * @param listener the listener interface + * @return closeHandle A handle to unregister observer. + */ + registerFocusListener(IFocusListener listener) generates (ICloseHandle closeHandle); + + /** + * Notifies HAL of changes in audio focus status for focuses requested or abandoned by the HAL. + * + * This will be called in response to IFocusListener's requestAudioFocus and + * abandonAudioFocus, as well as part of any change in focus being held by the HAL due focus + * request from other activities or services. + * + * The HAL is not required to wait for an callback of AUDIOFOCUS_GAIN before playing audio, nor + * is it required to stop playing audio in the event of a AUDIOFOCUS_LOSS callback is received. + * + * @param usage The audio usage associated with the focus change {@code AttributeUsage} + * @param zoneId The identifier for the audio zone that the HAL is playing the stream in + * @param focusChange the AudioFocusChange that has occurred + */ + oneway onAudioFocusChange(bitfield<AudioUsage> usage, int32_t zoneId, + bitfield<AudioFocusChange> focusChange); + + /** + * Control the right/left balance setting of the car speakers. + * + * This is intended to shift the speaker volume toward the right (+) or left (-) side of + * the car. 0.0 means "centered". +1.0 means fully right. -1.0 means fully left. + * + * A value outside the range -1 to 1 must be clamped by the implementation to the -1 to 1 + * range. + */ + oneway setBalanceTowardRight(float value); + + /** + * Control the fore/aft fade setting of the car speakers. + * + * This is intended to shift the speaker volume toward the front (+) or back (-) of the car. + * 0.0 means "centered". +1.0 means fully forward. -1.0 means fully rearward. + * + * A value outside the range -1 to 1 must be clamped by the implementation to the -1 to 1 + * range. + */ + oneway setFadeTowardFront(float value); +}; diff --git a/automotive/audiocontrol/2.0/ICloseHandle.hal b/automotive/audiocontrol/2.0/ICloseHandle.hal new file mode 100644 index 0000000000..537af6d635 --- /dev/null +++ b/automotive/audiocontrol/2.0/ICloseHandle.hal @@ -0,0 +1,34 @@ +/* + * Copyright (C) 2020 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.hardware.automotive.audiocontrol@2.0; + +/** + * Represents a generic close handle to remove a callback that doesn't need + * active interface. + * + * When close() is called OR when the interface is released, the underlying + * resources must be freed. + */ +interface ICloseHandle { + /** + * Closes the handle. + * + * The call must not fail and must be issued by the client at most once. + * Otherwise, the server must ignore subsequent calls. + */ + close(); +}; diff --git a/automotive/audiocontrol/2.0/IFocusListener.hal b/automotive/audiocontrol/2.0/IFocusListener.hal new file mode 100644 index 0000000000..4fd5ef0425 --- /dev/null +++ b/automotive/audiocontrol/2.0/IFocusListener.hal @@ -0,0 +1,55 @@ +/* + * Copyright (C) 2020 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.hardware.automotive.audiocontrol@2.0; + +import android.hardware.audio.common@6.0::AudioUsage; + +/** + * Callback interface for audio focus listener. + * + * For typical configuration, the listener the car audio service. + */ +interface IFocusListener { + /** + * Called whenever HAL is requesting focus as it is starting to play audio of a given usage in a + * specified zone. + * + * In response, IAudioControl#onAudioFocusChange will be called with focusChange status. This + * interaction is oneway to avoid blocking HAL so that it is not required to wait for a response + * before playing audio. + * + * @param usage The audio usage associated with the focus request {@code AttributeUsage} + * @param zoneId The identifier for the audio zone where the HAL is requesting focus + * @param focusGain The AudioFocusChange associated with this request. Should be one of the + * following: GAIN, GAIN_TRANSIENT, GAIN_TRANSIENT_MAY_DUCK, GAIN_TRANSIENT_EXCLUSIVE. + */ + oneway requestAudioFocus(bitfield<AudioUsage> usage, int32_t zoneId, + bitfield<AudioFocusChange> focusGain); + + /** + * Called whenever HAL is abandoning focus as it is finished playing audio of a given usage in a + * specific zone. + * + * In response, IAudioControl#onAudioFocusChange will be called with focusChange status. This + * interaction is oneway to avoid blocking HAL so that it is not required to wait for a response + * before stopping audio playback. + * + * @param usage The audio usage for which the HAL is abandoning focus {@code AttributeUsage} + * @param zoneId The identifier for the audio zone that the HAL abandoning focus + */ + oneway abandonAudioFocus(bitfield<AudioUsage> usage, int32_t zoneId); +}; diff --git a/automotive/audiocontrol/2.0/default/Android.bp b/automotive/audiocontrol/2.0/default/Android.bp new file mode 100644 index 0000000000..44ad0281fe --- /dev/null +++ b/automotive/audiocontrol/2.0/default/Android.bp @@ -0,0 +1,39 @@ +// Copyright (C) 2020 The Android Open Source Project +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +cc_binary { + name: "android.hardware.automotive.audiocontrol@2.0-service", + defaults: ["hidl_defaults"], + vendor: true, + relative_install_path: "hw", + srcs: [ + "AudioControl.cpp", + "service.cpp", + "CloseHandle.cpp", + ], + init_rc: ["android.hardware.automotive.audiocontrol@2.0-service.rc"], + + shared_libs: [ + "android.hardware.automotive.audiocontrol@2.0", + "libbase", + "libhidlbase", + "liblog", + "libutils", + ], + vintf_fragments: ["audiocontrol2_manifest.xml"], + cflags: [ + "-O0", + "-g", + ], +} diff --git a/automotive/audiocontrol/2.0/default/AudioControl.cpp b/automotive/audiocontrol/2.0/default/AudioControl.cpp new file mode 100644 index 0000000000..6505e34765 --- /dev/null +++ b/automotive/audiocontrol/2.0/default/AudioControl.cpp @@ -0,0 +1,60 @@ +#include "AudioControl.h" + +#include <android-base/logging.h> +#include <hidl/HidlTransportSupport.h> + +#include "CloseHandle.h" + +namespace android::hardware::automotive::audiocontrol::V2_0::implementation { + +AudioControl::AudioControl() {} + +Return<sp<ICloseHandle>> AudioControl::registerFocusListener(const sp<IFocusListener>& listener) { + LOG(DEBUG) << "registering focus listener"; + sp<ICloseHandle> closeHandle(nullptr); + + if (listener) { + mFocusListener = listener; + + closeHandle = new CloseHandle([this, listener]() { + if (mFocusListener == listener) { + mFocusListener = nullptr; + } + }); + } else { + LOG(ERROR) << "Unexpected nullptr for listener resulting in no-op."; + } + + return closeHandle; +} + +Return<void> AudioControl::setBalanceTowardRight(float value) { + // For completeness, lets bounds check the input... + if (isValidValue(value)) { + LOG(ERROR) << "Balance value out of range -1 to 1 at " << value; + } else { + // Just log in this default mock implementation + LOG(INFO) << "Balance set to " << value; + } + return Void(); +} + +Return<void> AudioControl::setFadeTowardFront(float value) { + // For completeness, lets bounds check the input... + if (isValidValue(value)) { + LOG(ERROR) << "Fader value out of range -1 to 1 at " << value; + } else { + // Just log in this default mock implementation + LOG(INFO) << "Fader set to " << value; + } + return Void(); +} + +Return<void> AudioControl::onAudioFocusChange(hidl_bitfield<AudioUsage> usage, int zoneId, + hidl_bitfield<AudioFocusChange> focusChange) { + LOG(INFO) << "Focus changed: " << static_cast<int>(focusChange) << " for usage " + << static_cast<int>(usage) << " in zone " << zoneId; + return Void(); +} + +} // namespace android::hardware::automotive::audiocontrol::V2_0::implementation diff --git a/automotive/audiocontrol/2.0/default/AudioControl.h b/automotive/audiocontrol/2.0/default/AudioControl.h new file mode 100644 index 0000000000..475a693cb7 --- /dev/null +++ b/automotive/audiocontrol/2.0/default/AudioControl.h @@ -0,0 +1,49 @@ +/* + * Copyright (C) 2020 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef ANDROID_HARDWARE_AUTOMOTIVE_AUDIOCONTROL_V2_0_AUDIOCONTROL_H +#define ANDROID_HARDWARE_AUTOMOTIVE_AUDIOCONTROL_V2_0_AUDIOCONTROL_H + +#include <android/hardware/audio/common/6.0/types.h> +#include <android/hardware/automotive/audiocontrol/2.0/IAudioControl.h> +#include <android/hardware/automotive/audiocontrol/2.0/ICloseHandle.h> +#include <hidl/MQDescriptor.h> +#include <hidl/Status.h> + +using android::hardware::audio::common::V6_0::AudioUsage; + +namespace android::hardware::automotive::audiocontrol::V2_0::implementation { + +class AudioControl : public IAudioControl { + public: + // Methods from ::android::hardware::automotive::audiocontrol::V2_0::IAudioControl follow. + Return<sp<ICloseHandle>> registerFocusListener(const sp<IFocusListener>& listener); + Return<void> onAudioFocusChange(hidl_bitfield<AudioUsage> usage, int zoneId, + hidl_bitfield<AudioFocusChange> focusChange); + Return<void> setBalanceTowardRight(float value) override; + Return<void> setFadeTowardFront(float value) override; + + // Implementation details + AudioControl(); + + private: + sp<IFocusListener> mFocusListener; + static bool isValidValue(float value) { return (value > 1.0f) || (value < -1.0f); } +}; + +} // namespace android::hardware::automotive::audiocontrol::V2_0::implementation + +#endif // ANDROID_HARDWARE_AUTOMOTIVE_AUDIOCONTROL_V2_0_AUDIOCONTROL_H diff --git a/automotive/audiocontrol/2.0/default/CloseHandle.cpp b/automotive/audiocontrol/2.0/default/CloseHandle.cpp new file mode 100644 index 0000000000..bc47931535 --- /dev/null +++ b/automotive/audiocontrol/2.0/default/CloseHandle.cpp @@ -0,0 +1,35 @@ +/* + * Copyright (C) 2020 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "CloseHandle.h" + +namespace android::hardware::automotive::audiocontrol::V2_0::implementation { + +CloseHandle::CloseHandle(Callback callback) : mCallback(callback) {} + +CloseHandle::~CloseHandle() { + close(); +} + +Return<void> CloseHandle::close() { + const auto wasClosed = mIsClosed.exchange(true); + if (wasClosed) return {}; + + if (mCallback) mCallback(); + return {}; +} + +} // namespace android::hardware::automotive::audiocontrol::V2_0::implementation diff --git a/automotive/audiocontrol/2.0/default/CloseHandle.h b/automotive/audiocontrol/2.0/default/CloseHandle.h new file mode 100644 index 0000000000..6caf0bfa7e --- /dev/null +++ b/automotive/audiocontrol/2.0/default/CloseHandle.h @@ -0,0 +1,48 @@ +/* + * Copyright (C) 2020 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#pragma once + +#include <android-base/macros.h> +#include <android/hardware/automotive/audiocontrol/2.0/ICloseHandle.h> + +namespace android::hardware::automotive::audiocontrol::V2_0::implementation { + +/** Generic ICloseHandle implementation ignoring double-close events. */ +class CloseHandle : public ICloseHandle { + public: + using Callback = std::function<void()>; + + /** + * Create a handle with a callback. + * + * The callback is guaranteed to be called exactly once. + * + * \param callback Called on the first close() call, or on destruction of the handle + */ + CloseHandle(Callback callback = nullptr); + virtual ~CloseHandle(); + + Return<void> close() override; + + private: + const Callback mCallback; + std::atomic<bool> mIsClosed = false; + + DISALLOW_COPY_AND_ASSIGN(CloseHandle); +}; + +} // namespace android::hardware::automotive::audiocontrol::V2_0::implementation diff --git a/automotive/audiocontrol/2.0/default/android.hardware.automotive.audiocontrol@2.0-service.rc b/automotive/audiocontrol/2.0/default/android.hardware.automotive.audiocontrol@2.0-service.rc new file mode 100644 index 0000000000..81c9be4460 --- /dev/null +++ b/automotive/audiocontrol/2.0/default/android.hardware.automotive.audiocontrol@2.0-service.rc @@ -0,0 +1,4 @@ +service vendor.audiocontrol-hal-2.0 /vendor/bin/hw/android.hardware.automotive.audiocontrol@2.0-service + class hal + user audioserver + group system diff --git a/automotive/audiocontrol/2.0/default/audiocontrol2_manifest.xml b/automotive/audiocontrol/2.0/default/audiocontrol2_manifest.xml new file mode 100644 index 0000000000..42d23ed62e --- /dev/null +++ b/automotive/audiocontrol/2.0/default/audiocontrol2_manifest.xml @@ -0,0 +1,11 @@ +<manifest version="1.0" type="device"> + <hal format="hidl"> + <name>android.hardware.automotive.audiocontrol</name> + <transport>hwbinder</transport> + <version>2.0</version> + <interface> + <name>IAudioControl</name> + <instance>default</instance> + </interface> + </hal> +</manifest>
\ No newline at end of file diff --git a/automotive/audiocontrol/2.0/default/service.cpp b/automotive/audiocontrol/2.0/default/service.cpp new file mode 100644 index 0000000000..dcc46c31aa --- /dev/null +++ b/automotive/audiocontrol/2.0/default/service.cpp @@ -0,0 +1,52 @@ +/* + * Copyright (C) 2020 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#include <unistd.h> + +#include <android-base/logging.h> +#include <hidl/HidlTransportSupport.h> +#include <utils/Errors.h> +#include <utils/StrongPointer.h> + +#include "AudioControl.h" + +// libhidl: +using android::hardware::configureRpcThreadpool; +using android::hardware::joinRpcThreadpool; + +// Generated HIDL files +using android::hardware::automotive::audiocontrol::V2_0::IAudioControl; + +// The namespace in which all our implementation code lives +using namespace android::hardware::automotive::audiocontrol::V2_0::implementation; +using namespace android; + +// Main service entry point +int main() { + // Create an instance of our service class + android::sp<IAudioControl> service = new AudioControl(); + configureRpcThreadpool(1, true /*callerWillJoin*/); + + if (service->registerAsService() != OK) { + LOG(ERROR) << "registerAsService failed"; + return 1; + } + + // Join (forever) the thread pool we created for the service above + joinRpcThreadpool(); + + // We don't ever actually expect to return, so return an error if we do get here + return 2; +}
\ No newline at end of file diff --git a/automotive/audiocontrol/2.0/types.hal b/automotive/audiocontrol/2.0/types.hal new file mode 100644 index 0000000000..65b0988487 --- /dev/null +++ b/automotive/audiocontrol/2.0/types.hal @@ -0,0 +1,31 @@ +/* + * Copyright (C) 2020 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.hardware.automotive.audiocontrol@2.0; + +/** + * Changes in audio focus that can be experienced + */ +enum AudioFocusChange : uint32_t { + NONE = 0, + GAIN = 1, + GAIN_TRANSIENT = 2, + GAIN_TRANSIENT_MAY_DUCK = 3, + GAIN_TRANSIENT_EXCLUSIVE = 4, + LOSS = -1 * GAIN, + LOSS_TRANSIENT = -1 * GAIN_TRANSIENT, + LOSS_TRANSIENT_CAN_DUCK = -1 * GAIN_TRANSIENT_MAY_DUCK, +}; diff --git a/automotive/audiocontrol/2.0/vts/functional/Android.bp b/automotive/audiocontrol/2.0/vts/functional/Android.bp new file mode 100644 index 0000000000..520b042e37 --- /dev/null +++ b/automotive/audiocontrol/2.0/vts/functional/Android.bp @@ -0,0 +1,29 @@ +// +// Copyright (C) 2020 The Android Open Source Project +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// + +cc_test { + name: "VtsHalAudioControlV2_0TargetTest", + defaults: ["VtsHalTargetTestDefaults"], + srcs: ["VtsHalAudioControlV2_0TargetTest.cpp"], + static_libs: [ + "android.hardware.automotive.audiocontrol@2.0", + "libgmock", + ], + test_suites: [ + "general-tests", + "vts-core", + ], +} diff --git a/automotive/audiocontrol/2.0/vts/functional/VtsHalAudioControlV2_0TargetTest.cpp b/automotive/audiocontrol/2.0/vts/functional/VtsHalAudioControlV2_0TargetTest.cpp new file mode 100644 index 0000000000..0c106647c5 --- /dev/null +++ b/automotive/audiocontrol/2.0/vts/functional/VtsHalAudioControlV2_0TargetTest.cpp @@ -0,0 +1,155 @@ +/* + * Copyright (C) 2020 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#define LOG_TAG "VtsHalAudioControlTest" + +#include <gmock/gmock.h> +#include <gtest/gtest.h> +#include <hidl/GtestPrinter.h> + +#include <stdio.h> +#include <string.h> + +#include <hidl/HidlTransportSupport.h> +#include <hwbinder/ProcessState.h> +#include <log/log.h> +#include <utils/Errors.h> +#include <utils/StrongPointer.h> + +#include <android/hardware/audio/common/6.0/types.h> +#include <android/hardware/automotive/audiocontrol/2.0/IAudioControl.h> +#include <android/hardware/automotive/audiocontrol/2.0/types.h> +#include <android/log.h> + +using namespace ::android::hardware::automotive::audiocontrol::V2_0; +using ::android::sp; +using ::android::hardware::hidl_bitfield; +using ::android::hardware::hidl_enum_range; +using ::android::hardware::hidl_handle; +using ::android::hardware::hidl_string; +using ::android::hardware::hidl_vec; +using ::android::hardware::Return; +using ::android::hardware::Void; +using ::android::hardware::audio::common::V6_0::AudioUsage; + +// The main test class for the automotive AudioControl HAL +class CarAudioControlHidlTest : public testing::TestWithParam<std::string> { + public: + virtual void SetUp() override { + // Make sure we can connect to the driver + pAudioControl = IAudioControl::getService(GetParam()); + ASSERT_NE(pAudioControl.get(), nullptr); + } + + virtual void TearDown() override {} + + protected: + sp<IAudioControl> pAudioControl; // Every test needs access to the service +}; + +// +// Tests start here... +// + +/* + * Fader exercise test. Note that only a subjective observer could determine if the + * fader actually works. The only thing we can do is exercise the HAL and if the HAL crashes, + * we _might_ get a test failure if that breaks the connection to the driver. + */ +TEST_P(CarAudioControlHidlTest, FaderExercise) { + ALOGI("Fader exercise test (silent)"); + + // Set the fader all the way to the back + pAudioControl->setFadeTowardFront(-1.0f); + + // Set the fader all the way to the front + pAudioControl->setFadeTowardFront(1.0f); + + // Set the fader part way toward the back + pAudioControl->setFadeTowardFront(-0.333f); + + // Set the fader to a out of bounds value (driver should clamp) + pAudioControl->setFadeTowardFront(99999.9f); + + // Set the fader back to the middle + pAudioControl->setFadeTowardFront(0.0f); +} + +/* + * Balance exercise test. + */ +TEST_P(CarAudioControlHidlTest, BalanceExercise) { + ALOGI("Balance exercise test (silent)"); + + // Set the balance all the way to the left + pAudioControl->setBalanceTowardRight(-1.0f); + + // Set the balance all the way to the right + pAudioControl->setBalanceTowardRight(1.0f); + + // Set the balance part way toward the left + pAudioControl->setBalanceTowardRight(-0.333f); + + // Set the balance to a out of bounds value (driver should clamp) + pAudioControl->setBalanceTowardRight(99999.9f); + + // Set the balance back to the middle + pAudioControl->setBalanceTowardRight(0.0f); +} + +struct FocusListenerMock : public IFocusListener { + MOCK_METHOD(Return<void>, requestAudioFocus, + (hidl_bitfield<AudioUsage> usage, int zoneId, + hidl_bitfield<AudioFocusChange> focusGain)); + MOCK_METHOD(Return<void>, abandonAudioFocus, (hidl_bitfield<AudioUsage> usage, int zoneId)); +}; + +/* + * Test focus listener registration. + * + * Verifies that: + * - registerFocusListener succeeds; + * - registering a second listener succeeds in replacing the first; + * - closing handle does not crash; + */ +TEST_P(CarAudioControlHidlTest, FocusListenerRegistration) { + ALOGI("Focus listener test"); + + sp<FocusListenerMock> listener = new FocusListenerMock(); + + auto hidlResult = pAudioControl->registerFocusListener(listener); + ASSERT_TRUE(hidlResult.isOk()); + + sp<FocusListenerMock> listener2 = new FocusListenerMock(); + + auto hidlResult2 = pAudioControl->registerFocusListener(listener2); + ASSERT_TRUE(hidlResult2.isOk()); + + const sp<ICloseHandle>& closeHandle = hidlResult2; + closeHandle->close(); +}; + +TEST_P(CarAudioControlHidlTest, FocusChangeExercise) { + ALOGI("Focus Change test"); + + pAudioControl->onAudioFocusChange(AudioUsage::MEDIA | 0, 0, + AudioFocusChange::GAIN_TRANSIENT | 0); +}; + +INSTANTIATE_TEST_SUITE_P( + PerInstance, CarAudioControlHidlTest, + testing::ValuesIn(android::hardware::getAllHalInstanceNames(IAudioControl::descriptor)), + android::hardware::PrintInstanceNameToString);
\ No newline at end of file diff --git a/automotive/evs/1.1/IEvsEnumerator.hal b/automotive/evs/1.1/IEvsEnumerator.hal index 1695821baa..7752b0e330 100644 --- a/automotive/evs/1.1/IEvsEnumerator.hal +++ b/automotive/evs/1.1/IEvsEnumerator.hal @@ -47,4 +47,11 @@ interface IEvsEnumerator extends @1.0::IEvsEnumerator { * configured differently by another client. */ openCamera_1_1(string cameraId, Stream streamCfg) generates (IEvsCamera evsCamera); + + /** + * Tells whether this is EVS manager or HAL implementation. + * + * @return result False for EVS manager implementations and true for all others. + */ + isHardware() generates (bool result); }; diff --git a/automotive/evs/1.1/default/Android.bp b/automotive/evs/1.1/default/Android.bp index 88fd6575ad..a7c7b4282b 100644 --- a/automotive/evs/1.1/default/Android.bp +++ b/automotive/evs/1.1/default/Android.bp @@ -41,7 +41,7 @@ cc_binary { prebuilt_etc { name: "evs_default_configuration.xml", - + soc_specific: true, src: "resources/evs_default_configuration.xml", sub_dir: "automotive/evs", } diff --git a/automotive/evs/1.1/default/EvsEnumerator.cpp b/automotive/evs/1.1/default/EvsEnumerator.cpp index a010729ce6..cb7403a7da 100644 --- a/automotive/evs/1.1/default/EvsEnumerator.cpp +++ b/automotive/evs/1.1/default/EvsEnumerator.cpp @@ -42,7 +42,7 @@ EvsEnumerator::EvsEnumerator() { // Add sample camera data to our list of cameras // In a real driver, this would be expected to can the available hardware sConfigManager = - ConfigManager::Create("/etc/automotive/evs/evs_sample_configuration.xml"); + ConfigManager::Create("/vendor/etc/automotive/evs/evs_default_configuration.xml"); for (auto v : sConfigManager->getCameraList()) { sCameraList.emplace_back(v.c_str()); } diff --git a/automotive/evs/1.1/default/EvsEnumerator.h b/automotive/evs/1.1/default/EvsEnumerator.h index 475ec76b93..ca35dc6f8a 100644 --- a/automotive/evs/1.1/default/EvsEnumerator.h +++ b/automotive/evs/1.1/default/EvsEnumerator.h @@ -59,6 +59,7 @@ public: Return<void> getCameraList_1_1(getCameraList_1_1_cb _hidl_cb) override; Return<sp<IEvsCamera_1_1>> openCamera_1_1(const hidl_string& cameraId, const Stream& streamCfg) override; + Return<bool> isHardware() override { return true; } // Implementation details EvsEnumerator(); diff --git a/automotive/evs/1.1/default/ServiceNames.h b/automotive/evs/1.1/default/ServiceNames.h index 1178da5a9c..84b1697d0f 100644 --- a/automotive/evs/1.1/default/ServiceNames.h +++ b/automotive/evs/1.1/default/ServiceNames.h @@ -14,4 +14,4 @@ * limitations under the License. */ -const static char kEnumeratorServiceName[] = "EvsEnumeratorHw"; +const static char kEnumeratorServiceName[] = "hw/0"; diff --git a/automotive/evs/1.1/vts/functional/VtsHalEvsV1_1TargetTest.cpp b/automotive/evs/1.1/vts/functional/VtsHalEvsV1_1TargetTest.cpp index 4fc4e4c144..1a622452da 100644 --- a/automotive/evs/1.1/vts/functional/VtsHalEvsV1_1TargetTest.cpp +++ b/automotive/evs/1.1/vts/functional/VtsHalEvsV1_1TargetTest.cpp @@ -117,7 +117,7 @@ public: pEnumerator = getService<IEvsEnumerator>(service_name); ASSERT_NE(pEnumerator.get(), nullptr); - mIsHwModule = !service_name.compare(kEnumeratorName); + mIsHwModule = pEnumerator->isHardware(); } virtual void TearDown() override { diff --git a/automotive/evs/1.1/vts/fuzzing/Android.bp b/automotive/evs/1.1/vts/fuzzing/Android.bp new file mode 100644 index 0000000000..48427ee532 --- /dev/null +++ b/automotive/evs/1.1/vts/fuzzing/Android.bp @@ -0,0 +1,50 @@ +// +// Copyright 2020 The Android Open Source Project +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// + +cc_defaults { + name: "android.hardware.automotive.evs@fuzz-defaults", + defaults: ["VtsHalTargetTestDefaults"], + shared_libs: [ + "libui", + "libcamera_metadata", + ], + static_libs: [ + "android.hardware.automotive.evs@1.0", + "android.hardware.automotive.evs@1.1", + "android.hardware.automotive.evs@common-default-lib", + "android.hardware.graphics.common@1.0", + "android.hardware.graphics.common@1.1", + "android.hardware.graphics.common@1.2", + "android.hardware.camera.device@3.2", + ], + cflags: [ + "-O0", + "-g", + ], +} + +cc_fuzz { + name: "VtsHalEvsV1_1CameraOpenFuzz", + defaults: ["android.hardware.automotive.evs@fuzz-defaults"], + srcs: [ + "VtsHalEvsV1_1CameraOpenFuzz.cpp", + ], + fuzz_config: { + // wait for Haiku device ready + fuzz_on_haiku_device: false, + fuzz_on_haiku_host: false, + }, +} diff --git a/automotive/evs/1.1/vts/fuzzing/VtsHalEvsV1_1CameraOpenFuzz.cpp b/automotive/evs/1.1/vts/fuzzing/VtsHalEvsV1_1CameraOpenFuzz.cpp new file mode 100644 index 0000000000..4f05d21e69 --- /dev/null +++ b/automotive/evs/1.1/vts/fuzzing/VtsHalEvsV1_1CameraOpenFuzz.cpp @@ -0,0 +1,84 @@ +/* + * Copyright 2020 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "common.h" + +using ::android::hardware::automotive::evs::V1_0::DisplayDesc; +using ::android::hardware::camera::device::V3_2::Stream; +using IEvsCamera_1_1 = ::android::hardware::automotive::evs::V1_1::IEvsCamera; + +extern "C" int LLVMFuzzerInitialize(int* argc, char*** argv) { + UNUSED(argc); + UNUSED(argv); + pEnumerator = IEvsEnumerator::getService(kEnumeratorName); + sp<EvsDeathRecipient> dr = new EvsDeathRecipient(); + + pEnumerator->linkToDeath(dr, 0); + + loadCameraList(); + return 0; +} + +extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) { + FuzzedDataProvider fdp(data, size); + + std::vector<sp<IEvsCamera_1_1>> camList; + Stream nullCfg = {}; + + while (fdp.remaining_bytes() > 4) { + switch (fdp.ConsumeIntegralInRange<uint32_t>(0, 3)) { + case 0: // open camera + { + uint32_t whichCam = fdp.ConsumeIntegralInRange<uint32_t>(0, cameraInfo.size() - 1); + sp<IEvsCamera_1_1> pCam = + IEvsCamera_1_1::castFrom(pEnumerator->openCamera_1_1( + cameraInfo[whichCam].v1.cameraId, nullCfg)) + .withDefault(nullptr); + camList.emplace_back(pCam); + break; + } + case 1: // close camera + { + if (!camList.empty()) { + uint32_t whichCam = fdp.ConsumeIntegralInRange<uint32_t>(0, camList.size() - 1); + pEnumerator->closeCamera(camList[whichCam]); + } + break; + } + case 2: // get camera info + { + if (!camList.empty()) { + uint32_t whichCam = fdp.ConsumeIntegralInRange<uint32_t>(0, camList.size() - 1); + camList[whichCam]->getCameraInfo_1_1([](CameraDesc desc) { UNUSED(desc); }); + } + break; + } + case 3: // setMaxFramesInFlight + { + if (!camList.empty()) { + uint32_t whichCam = fdp.ConsumeIntegralInRange<uint32_t>(0, camList.size() - 1); + int32_t numFrames = fdp.ConsumeIntegral<int32_t>(); + camList[whichCam]->setMaxFramesInFlight(numFrames); + } + break; + } + default: + break; + } + } + + return 0; +} diff --git a/automotive/evs/1.1/vts/fuzzing/common.h b/automotive/evs/1.1/vts/fuzzing/common.h new file mode 100644 index 0000000000..af6fd545ea --- /dev/null +++ b/automotive/evs/1.1/vts/fuzzing/common.h @@ -0,0 +1,57 @@ +/* + * Copyright 2020 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#define LOG_TAG "VtsHalEvsTest" +#define UNUSED(x) (void)(x) + +const static char kEnumeratorName[] = "EvsEnumeratorHw"; + +#include <android/hardware/automotive/evs/1.1/IEvsEnumerator.h> +#include <fuzzer/FuzzedDataProvider.h> +#include <hidl/HidlTransportSupport.h> +#include <utils/StrongPointer.h> + +using namespace ::android::hardware::automotive::evs::V1_1; + +using ::android::sp; +using ::android::hardware::hidl_death_recipient; +using ::android::hardware::hidl_vec; + +static sp<IEvsEnumerator> pEnumerator; +static std::vector<CameraDesc> cameraInfo; + +class EvsDeathRecipient : public hidl_death_recipient { + public: + void serviceDied(uint64_t /*cookie*/, + const android::wp<::android::hidl::base::V1_0::IBase>& /*who*/) override { + abort(); + } +}; + +void loadCameraList() { + // SetUp() must run first! + assert(pEnumerator != nullptr); + + // Get the camera list + pEnumerator->getCameraList_1_1([](hidl_vec<CameraDesc> cameraList) { + ALOGI("Camera list callback received %zu cameras", cameraList.size()); + cameraInfo.reserve(cameraList.size()); + for (auto&& cam : cameraList) { + ALOGI("Found camera %s", cam.v1.cameraId.c_str()); + cameraInfo.push_back(cam); + } + }); +} diff --git a/automotive/vehicle/2.0/default/Android.bp b/automotive/vehicle/2.0/default/Android.bp index 2050038d62..a94a37e0f1 100644 --- a/automotive/vehicle/2.0/default/Android.bp +++ b/automotive/vehicle/2.0/default/Android.bp @@ -82,6 +82,20 @@ cc_library_static { ], } +// VHal virtualization utils +cc_library_static { + name: "android.hardware.automotive.vehicle@2.0-virtualization-utils", + vendor: true, + defaults: ["vhal_v2_0_defaults"], + srcs: [ + "impl/vhal_v2_0/virtualization/Utils.cpp", + ], + export_include_dirs: ["impl"], + shared_libs: [ + "libbase", + ], +} + cc_test { name: "android.hardware.automotive.vehicle@2.0-manager-unit-tests", vendor: true, @@ -133,3 +147,59 @@ cc_binary { "libqemu_pipe", ], } + +cc_binary { + name: "android.hardware.automotive.vehicle@2.0-virtualization-service", + defaults: ["vhal_v2_0_defaults"], + init_rc: ["android.hardware.automotive.vehicle@2.0-virtualization-service.rc"], + vendor: true, + relative_install_path: "hw", + srcs: [ + "impl/vhal_v2_0/virtualization/GrpcVehicleClient.cpp", + "VirtualizedVehicleService.cpp", + ], + shared_libs: [ + "libbase", + "libcutils", + "libjsoncpp", + "libprotobuf-cpp-full", + "libgrpc++", + ], + static_libs: [ + "android.hardware.automotive.vehicle@2.0-manager-lib", + "android.hardware.automotive.vehicle@2.0-default-impl-lib", + "android.hardware.automotive.vehicle@2.0-grpc", + "android.hardware.automotive.vehicle@2.0-virtualization-utils", + "libqemu_pipe", + ], + cflags: [ + "-Wno-unused-parameter" + ], +} + +cc_binary { + name: "android.hardware.automotive.vehicle@2.0-virtualization-grpc-server", + init_rc: ["android.hardware.automotive.vehicle@2.0-virtualization-grpc-server.rc"], + defaults: ["vhal_v2_0_defaults"], + vendor: true, + relative_install_path: "hw", + srcs: [ + "impl/vhal_v2_0/virtualization/GrpcVehicleServer.cpp", + "VirtualizationGrpcServer.cpp", + ], + shared_libs: [ + "libbase", + "libjsoncpp", + "libprotobuf-cpp-full", + "libgrpc++", + ], + static_libs: [ + "android.hardware.automotive.vehicle@2.0-manager-lib", + "android.hardware.automotive.vehicle@2.0-default-impl-lib", + "android.hardware.automotive.vehicle@2.0-grpc", + "android.hardware.automotive.vehicle@2.0-virtualization-utils", + ], + cflags: [ + "-Wno-unused-parameter" + ], +} diff --git a/automotive/vehicle/2.0/default/VirtualizationGrpcServer.cpp b/automotive/vehicle/2.0/default/VirtualizationGrpcServer.cpp new file mode 100644 index 0000000000..fb02c58952 --- /dev/null +++ b/automotive/vehicle/2.0/default/VirtualizationGrpcServer.cpp @@ -0,0 +1,15 @@ +#include <android-base/logging.h> + +#include "vhal_v2_0/virtualization/GrpcVehicleServer.h" +#include "vhal_v2_0/virtualization/Utils.h" + +int main(int argc, char* argv[]) { + namespace vhal_impl = android::hardware::automotive::vehicle::V2_0::impl; + + auto serverInfo = vhal_impl::VsockServerInfo::fromCommandLine(argc, argv); + CHECK(serverInfo.has_value()) << "Invalid server CID/port combination"; + + auto server = vhal_impl::makeGrpcVehicleServer(serverInfo->toUri()); + server->Start(); + return 0; +} diff --git a/automotive/vehicle/2.0/default/VirtualizedVehicleService.cpp b/automotive/vehicle/2.0/default/VirtualizedVehicleService.cpp new file mode 100644 index 0000000000..68813c9035 --- /dev/null +++ b/automotive/vehicle/2.0/default/VirtualizedVehicleService.cpp @@ -0,0 +1,56 @@ +/* + * Copyright (C) 2019 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include <android-base/logging.h> +#include <hidl/HidlTransportSupport.h> + +#include <vhal_v2_0/EmulatedVehicleConnector.h> +#include <vhal_v2_0/EmulatedVehicleHal.h> +#include <vhal_v2_0/VehicleHalManager.h> +#include <vhal_v2_0/virtualization/GrpcVehicleClient.h> +#include <vhal_v2_0/virtualization/Utils.h> + +using namespace android; +using namespace android::hardware; +using namespace android::hardware::automotive::vehicle::V2_0; + +int main(int argc, char* argv[]) { + namespace vhal_impl = android::hardware::automotive::vehicle::V2_0::impl; + + auto serverInfo = vhal_impl::VsockServerInfo::fromRoPropertyStore(); + CHECK(serverInfo.has_value()) << "Invalid server CID/port combination"; + + auto store = std::make_unique<VehiclePropertyStore>(); + auto connector = impl::makeGrpcVehicleClient(serverInfo->toUri()); + auto hal = std::make_unique<impl::EmulatedVehicleHal>(store.get(), connector.get()); + auto emulator = std::make_unique<impl::VehicleEmulator>(hal.get()); + auto service = std::make_unique<VehicleHalManager>(hal.get()); + + configureRpcThreadpool(4, true /* callerWillJoin */); + + LOG(INFO) << "Registering as service..."; + status_t status = service->registerAsService(); + + if (status != OK) { + LOG(ERROR) << "Unable to register vehicle service (" << status << ")"; + return 1; + } + + LOG(INFO) << "Ready"; + joinRpcThreadpool(); + + return 1; +} diff --git a/automotive/vehicle/2.0/default/android.hardware.automotive.vehicle@2.0-virtualization-grpc-server.rc b/automotive/vehicle/2.0/default/android.hardware.automotive.vehicle@2.0-virtualization-grpc-server.rc new file mode 100644 index 0000000000..1101b087eb --- /dev/null +++ b/automotive/vehicle/2.0/default/android.hardware.automotive.vehicle@2.0-virtualization-grpc-server.rc @@ -0,0 +1,10 @@ +# It is an interim state to run GRPC server as an Android service. +# Eventually it will run outside of Android (e.g., AGL), +# so the command line arguments are expected, though not conventionally used in Android +service vendor.vehicle-hal-2.0-server \ + /vendor/bin/hw/android.hardware.automotive.vehicle@2.0-virtualization-grpc-server \ + -server_cid ${ro.vendor.vehiclehal.server.cid:-pleaseconfigurethis} \ + -server_port ${ro.vendor.vehiclehal.server.port:-pleaseconfigurethis} + class hal + user vehicle_network + group system inet diff --git a/automotive/vehicle/2.0/default/android.hardware.automotive.vehicle@2.0-virtualization-service.rc b/automotive/vehicle/2.0/default/android.hardware.automotive.vehicle@2.0-virtualization-service.rc new file mode 100644 index 0000000000..234de591cc --- /dev/null +++ b/automotive/vehicle/2.0/default/android.hardware.automotive.vehicle@2.0-virtualization-service.rc @@ -0,0 +1,4 @@ +service vendor.vehicle-hal-2.0 /vendor/bin/hw/android.hardware.automotive.vehicle@2.0-virtualization-service + class hal + user vehicle_network + group system inet diff --git a/automotive/vehicle/2.0/default/impl/vhal_v2_0/proto/VehicleServer.proto b/automotive/vehicle/2.0/default/impl/vhal_v2_0/proto/VehicleServer.proto index 2cc6595248..6f71d654a7 100644 --- a/automotive/vehicle/2.0/default/impl/vhal_v2_0/proto/VehicleServer.proto +++ b/automotive/vehicle/2.0/default/impl/vhal_v2_0/proto/VehicleServer.proto @@ -35,13 +35,23 @@ message VehicleHalCallStatus { VehicleHalStatusCode status_code = 1; } +message WrappedVehiclePropValue { + VehiclePropValue value = 1; + // An indicator on whether we should update the status of the property + // - true: if the value is generated by (emulated/real) car, or; + // if the value is injected to 'fake' a on car event (for debugging purpose) + // - false: if the value is set by VHal (public interface), since Android + // cannot change status of property on a real car + bool update_status = 2; +} + service VehicleServer { rpc GetAllPropertyConfig(google.protobuf.Empty) returns (stream VehiclePropConfig) {} // Change the property value of the vehicle - rpc SetProperty(VehiclePropValue) returns (VehicleHalCallStatus) {} + rpc SetProperty(WrappedVehiclePropValue) returns (VehicleHalCallStatus) {} // Start a vehicle property value stream - rpc StartPropertyValuesStream(google.protobuf.Empty) returns (stream VehiclePropValue) {} + rpc StartPropertyValuesStream(google.protobuf.Empty) returns (stream WrappedVehiclePropValue) {} } diff --git a/automotive/vehicle/2.0/default/impl/vhal_v2_0/virtualization/GrpcVehicleClient.cpp b/automotive/vehicle/2.0/default/impl/vhal_v2_0/virtualization/GrpcVehicleClient.cpp new file mode 100644 index 0000000000..e329c5be01 --- /dev/null +++ b/automotive/vehicle/2.0/default/impl/vhal_v2_0/virtualization/GrpcVehicleClient.cpp @@ -0,0 +1,162 @@ +/* + * Copyright (C) 2019 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#include "GrpcVehicleClient.h" + +#include <condition_variable> +#include <mutex> + +#include <android-base/logging.h> +#include <grpc++/grpc++.h> + +#include "VehicleServer.grpc.pb.h" +#include "VehicleServer.pb.h" +#include "vhal_v2_0/ProtoMessageConverter.h" + +namespace android { +namespace hardware { +namespace automotive { +namespace vehicle { +namespace V2_0 { + +namespace impl { + +static std::shared_ptr<::grpc::ChannelCredentials> getChannelCredentials() { + // TODO(chenhaosjtuacm): get secured credentials here + return ::grpc::InsecureChannelCredentials(); +} + +class GrpcVehicleClientImpl : public EmulatedVehicleClient { + public: + GrpcVehicleClientImpl(const std::string& addr) + : mServiceAddr(addr), + mGrpcChannel(::grpc::CreateChannel(mServiceAddr, getChannelCredentials())), + mGrpcStub(vhal_proto::VehicleServer::NewStub(mGrpcChannel)) { + StartValuePollingThread(); + } + + ~GrpcVehicleClientImpl() { + mShuttingDownFlag.store(true); + mShutdownCV.notify_all(); + if (mPollingThread.joinable()) { + mPollingThread.join(); + } + } + + // methods from IVehicleClient + + std::vector<VehiclePropConfig> getAllPropertyConfig() const override; + + StatusCode setProperty(const VehiclePropValue& value, bool updateStatus) override; + + private: + void StartValuePollingThread(); + + // private data members + + std::string mServiceAddr; + std::shared_ptr<::grpc::Channel> mGrpcChannel; + std::unique_ptr<vhal_proto::VehicleServer::Stub> mGrpcStub; + std::thread mPollingThread; + + std::mutex mShutdownMutex; + std::condition_variable mShutdownCV; + std::atomic<bool> mShuttingDownFlag{false}; +}; + +std::unique_ptr<EmulatedVehicleClient> makeGrpcVehicleClient(const std::string& addr) { + return std::make_unique<GrpcVehicleClientImpl>(addr); +} + +std::vector<VehiclePropConfig> GrpcVehicleClientImpl::getAllPropertyConfig() const { + std::vector<VehiclePropConfig> configs; + ::grpc::ClientContext context; + auto config_stream = mGrpcStub->GetAllPropertyConfig(&context, ::google::protobuf::Empty()); + vhal_proto::VehiclePropConfig protoConfig; + while (config_stream->Read(&protoConfig)) { + VehiclePropConfig config; + proto_msg_converter::fromProto(&config, protoConfig); + configs.emplace_back(std::move(config)); + } + auto grpc_status = config_stream->Finish(); + if (!grpc_status.ok()) { + LOG(ERROR) << __func__ + << ": GRPC GetAllPropertyConfig Failed: " << grpc_status.error_message(); + configs.clear(); + } + + return configs; +} + +StatusCode GrpcVehicleClientImpl::setProperty(const VehiclePropValue& value, bool updateStatus) { + ::grpc::ClientContext context; + vhal_proto::WrappedVehiclePropValue wrappedProtoValue; + vhal_proto::VehicleHalCallStatus vhal_status; + proto_msg_converter::toProto(wrappedProtoValue.mutable_value(), value); + wrappedProtoValue.set_update_status(updateStatus); + + auto grpc_status = mGrpcStub->SetProperty(&context, wrappedProtoValue, &vhal_status); + if (!grpc_status.ok()) { + LOG(ERROR) << __func__ << ": GRPC SetProperty Failed: " << grpc_status.error_message(); + return StatusCode::INTERNAL_ERROR; + } + + return static_cast<StatusCode>(vhal_status.status_code()); +} + +void GrpcVehicleClientImpl::StartValuePollingThread() { + mPollingThread = std::thread([this]() { + while (!mShuttingDownFlag.load()) { + ::grpc::ClientContext context; + + std::atomic<bool> rpc_ok{true}; + std::thread shuttingdown_watcher([this, &rpc_ok, &context]() { + std::unique_lock<std::mutex> shutdownLock(mShutdownMutex); + mShutdownCV.wait(shutdownLock, [this, &rpc_ok]() { + return !rpc_ok.load() || mShuttingDownFlag.load(); + }); + context.TryCancel(); + }); + + auto value_stream = + mGrpcStub->StartPropertyValuesStream(&context, ::google::protobuf::Empty()); + vhal_proto::WrappedVehiclePropValue wrappedProtoValue; + while (!mShuttingDownFlag.load() && value_stream->Read(&wrappedProtoValue)) { + VehiclePropValue value; + proto_msg_converter::fromProto(&value, wrappedProtoValue.value()); + onPropertyValue(value, wrappedProtoValue.update_status()); + } + + rpc_ok.store(false); + mShutdownCV.notify_all(); + shuttingdown_watcher.join(); + + auto grpc_status = value_stream->Finish(); + // never reach here until connection lost + LOG(ERROR) << __func__ + << ": GRPC Value Streaming Failed: " << grpc_status.error_message(); + + // try to reconnect + } + }); +} + +} // namespace impl + +} // namespace V2_0 +} // namespace vehicle +} // namespace automotive +} // namespace hardware +} // namespace android diff --git a/automotive/vehicle/2.0/default/impl/vhal_v2_0/virtualization/GrpcVehicleClient.h b/automotive/vehicle/2.0/default/impl/vhal_v2_0/virtualization/GrpcVehicleClient.h new file mode 100644 index 0000000000..14eae7f057 --- /dev/null +++ b/automotive/vehicle/2.0/default/impl/vhal_v2_0/virtualization/GrpcVehicleClient.h @@ -0,0 +1,40 @@ +/* + * Copyright (C) 2019 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef android_hardware_automotive_vehicle_V2_0_impl_virtialization_GrpcVehicleClient_H_ +#define android_hardware_automotive_vehicle_V2_0_impl_virtialization_GrpcVehicleClient_H_ + +#include "vhal_v2_0/EmulatedVehicleConnector.h" + +namespace android { +namespace hardware { +namespace automotive { +namespace vehicle { +namespace V2_0 { + +namespace impl { + +std::unique_ptr<EmulatedVehicleClient> makeGrpcVehicleClient(const std::string& addr); + +} // namespace impl + +} // namespace V2_0 +} // namespace vehicle +} // namespace automotive +} // namespace hardware +} // namespace android + +#endif // android_hardware_automotive_vehicle_V2_0_impl_virtialization_GrpcVehicleClient_H_ diff --git a/automotive/vehicle/2.0/default/impl/vhal_v2_0/virtualization/GrpcVehicleServer.cpp b/automotive/vehicle/2.0/default/impl/vhal_v2_0/virtualization/GrpcVehicleServer.cpp new file mode 100644 index 0000000000..e30b3bec54 --- /dev/null +++ b/automotive/vehicle/2.0/default/impl/vhal_v2_0/virtualization/GrpcVehicleServer.cpp @@ -0,0 +1,229 @@ +/* + * Copyright (C) 2019 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#include "GrpcVehicleServer.h" + +#include <condition_variable> +#include <mutex> +#include <shared_mutex> + +#include <android-base/logging.h> +#include <grpc++/grpc++.h> + +#include "VehicleServer.grpc.pb.h" +#include "VehicleServer.pb.h" +#include "vhal_v2_0/ProtoMessageConverter.h" + +namespace android { +namespace hardware { +namespace automotive { +namespace vehicle { +namespace V2_0 { + +namespace impl { + +class GrpcVehicleServerImpl : public GrpcVehicleServer, public vhal_proto::VehicleServer::Service { + public: + GrpcVehicleServerImpl(const std::string& addr) : mServiceAddr(addr) { + setValuePool(&mValueObjectPool); + } + + // method from GrpcVehicleServer + void Start() override; + + // method from IVehicleServer + void onPropertyValueFromCar(const VehiclePropValue& value, bool updateStatus) override; + + // methods from vhal_proto::VehicleServer::Service + + ::grpc::Status GetAllPropertyConfig( + ::grpc::ServerContext* context, const ::google::protobuf::Empty* request, + ::grpc::ServerWriter<vhal_proto::VehiclePropConfig>* stream) override; + + ::grpc::Status SetProperty(::grpc::ServerContext* context, + const vhal_proto::WrappedVehiclePropValue* wrappedPropValue, + vhal_proto::VehicleHalCallStatus* status) override; + + ::grpc::Status StartPropertyValuesStream( + ::grpc::ServerContext* context, const ::google::protobuf::Empty* request, + ::grpc::ServerWriter<vhal_proto::WrappedVehiclePropValue>* stream) override; + + private: + // We keep long-lasting connection for streaming the prop values. + // For us, each connection can be represented as a function to send the new value, and + // an ID to identify this connection + struct ConnectionDescriptor { + using ValueWriterType = std::function<bool(const vhal_proto::WrappedVehiclePropValue&)>; + + ConnectionDescriptor(ValueWriterType&& value_writer) + : mValueWriter(std::move(value_writer)), + mConnectionID(CONNECTION_ID_COUNTER.fetch_add(1)) {} + + ConnectionDescriptor(const ConnectionDescriptor&) = delete; + + ConnectionDescriptor& operator=(const ConnectionDescriptor&) = delete; + + // This move constructor is NOT THREAD-SAFE, which means it cannot be moved + // while using. Since the connection descriptors are pretected by mConnectionMutex + // then we are fine here + ConnectionDescriptor(ConnectionDescriptor&& cd) + : mValueWriter(std::move(cd.mValueWriter)), + mConnectionID(cd.mConnectionID), + mIsAlive(cd.mIsAlive.load()) { + cd.mIsAlive.store(false); + } + + ValueWriterType mValueWriter; + uint64_t mConnectionID; + std::atomic<bool> mIsAlive{true}; + + static std::atomic<uint64_t> CONNECTION_ID_COUNTER; + }; + + std::string mServiceAddr; + VehiclePropValuePool mValueObjectPool; + mutable std::shared_mutex mConnectionMutex; + mutable std::shared_mutex mWriterMutex; + std::list<ConnectionDescriptor> mValueStreamingConnections; +}; + +std::atomic<uint64_t> GrpcVehicleServerImpl::ConnectionDescriptor::CONNECTION_ID_COUNTER = 0; + +static std::shared_ptr<::grpc::ServerCredentials> getServerCredentials() { + // TODO(chenhaosjtuacm): get secured credentials here + return ::grpc::InsecureServerCredentials(); +} + +GrpcVehicleServerPtr makeGrpcVehicleServer(const std::string& addr) { + return std::make_unique<GrpcVehicleServerImpl>(addr); +} + +void GrpcVehicleServerImpl::Start() { + ::grpc::ServerBuilder builder; + builder.RegisterService(this); + builder.AddListeningPort(mServiceAddr, getServerCredentials()); + std::unique_ptr<::grpc::Server> server(builder.BuildAndStart()); + + server->Wait(); +} + +void GrpcVehicleServerImpl::onPropertyValueFromCar(const VehiclePropValue& value, + bool updateStatus) { + vhal_proto::WrappedVehiclePropValue wrappedPropValue; + proto_msg_converter::toProto(wrappedPropValue.mutable_value(), value); + wrappedPropValue.set_update_status(updateStatus); + std::shared_lock read_lock(mConnectionMutex); + + bool has_terminated_connections = 0; + + for (auto& connection : mValueStreamingConnections) { + auto writeOK = connection.mValueWriter(wrappedPropValue); + if (!writeOK) { + LOG(ERROR) << __func__ << ": Server Write failed, connection lost. ID: " + << connection.mConnectionID; + has_terminated_connections = true; + connection.mIsAlive.store(false); + } + } + + if (!has_terminated_connections) { + return; + } + + read_lock.unlock(); + + std::unique_lock write_lock(mConnectionMutex); + + for (auto itr = mValueStreamingConnections.begin(); itr != mValueStreamingConnections.end();) { + if (!itr->mIsAlive.load()) { + itr = mValueStreamingConnections.erase(itr); + } else { + ++itr; + } + } +} + +::grpc::Status GrpcVehicleServerImpl::GetAllPropertyConfig( + ::grpc::ServerContext* context, const ::google::protobuf::Empty* request, + ::grpc::ServerWriter<vhal_proto::VehiclePropConfig>* stream) { + auto configs = onGetAllPropertyConfig(); + for (auto& config : configs) { + vhal_proto::VehiclePropConfig protoConfig; + proto_msg_converter::toProto(&protoConfig, config); + if (!stream->Write(protoConfig)) { + return ::grpc::Status(::grpc::StatusCode::ABORTED, "Connection lost."); + } + } + + return ::grpc::Status::OK; +} + +::grpc::Status GrpcVehicleServerImpl::SetProperty( + ::grpc::ServerContext* context, const vhal_proto::WrappedVehiclePropValue* wrappedPropValue, + vhal_proto::VehicleHalCallStatus* status) { + VehiclePropValue value; + proto_msg_converter::fromProto(&value, wrappedPropValue->value()); + + auto set_status = static_cast<int32_t>(onSetProperty(value, wrappedPropValue->update_status())); + if (!vhal_proto::VehicleHalStatusCode_IsValid(set_status)) { + return ::grpc::Status(::grpc::StatusCode::INTERNAL, "Unknown status code"); + } + + status->set_status_code(static_cast<vhal_proto::VehicleHalStatusCode>(set_status)); + + return ::grpc::Status::OK; +} + +::grpc::Status GrpcVehicleServerImpl::StartPropertyValuesStream( + ::grpc::ServerContext* context, const ::google::protobuf::Empty* request, + ::grpc::ServerWriter<vhal_proto::WrappedVehiclePropValue>* stream) { + std::mutex terminateMutex; + std::condition_variable terminateCV; + std::unique_lock<std::mutex> terminateLock(terminateMutex); + bool terminated{false}; + + auto callBack = [stream, &terminateMutex, &terminateCV, &terminated, + this](const vhal_proto::WrappedVehiclePropValue& value) { + std::unique_lock lock(mWriterMutex); + if (!stream->Write(value)) { + std::unique_lock<std::mutex> terminateLock(terminateMutex); + terminated = true; + terminateLock.unlock(); + terminateCV.notify_all(); + return false; + } + return true; + }; + + // Register connection + std::unique_lock lock(mConnectionMutex); + auto& conn = mValueStreamingConnections.emplace_back(std::move(callBack)); + lock.unlock(); + + // Never stop until connection lost + terminateCV.wait(terminateLock, [&terminated]() { return terminated; }); + + LOG(ERROR) << __func__ << ": Stream lost, ID : " << conn.mConnectionID; + + return ::grpc::Status(::grpc::StatusCode::ABORTED, "Connection lost."); +} + +} // namespace impl + +} // namespace V2_0 +} // namespace vehicle +} // namespace automotive +} // namespace hardware +} // namespace android diff --git a/automotive/vehicle/2.0/default/impl/vhal_v2_0/virtualization/GrpcVehicleServer.h b/automotive/vehicle/2.0/default/impl/vhal_v2_0/virtualization/GrpcVehicleServer.h new file mode 100644 index 0000000000..32f4eb203a --- /dev/null +++ b/automotive/vehicle/2.0/default/impl/vhal_v2_0/virtualization/GrpcVehicleServer.h @@ -0,0 +1,49 @@ +/* + * Copyright (C) 2019 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef android_hardware_automotive_vehicle_V2_0_impl_virtialization_GrpcVehicleServer_H_ +#define android_hardware_automotive_vehicle_V2_0_impl_virtialization_GrpcVehicleServer_H_ + +#include "vhal_v2_0/EmulatedVehicleConnector.h" + +namespace android { +namespace hardware { +namespace automotive { +namespace vehicle { +namespace V2_0 { + +namespace impl { + +// Connect to the Vehicle Client via GRPC +class GrpcVehicleServer : public EmulatedVehicleServer { + public: + // Start listening incoming calls, should never return if working normally + virtual void Start() = 0; +}; + +using GrpcVehicleServerPtr = std::unique_ptr<GrpcVehicleServer>; + +GrpcVehicleServerPtr makeGrpcVehicleServer(const std::string& addr); + +} // namespace impl + +} // namespace V2_0 +} // namespace vehicle +} // namespace automotive +} // namespace hardware +} // namespace android + +#endif // android_hardware_automotive_vehicle_V2_0_impl_virtialization_GrpcVehicleServer_H_ diff --git a/automotive/vehicle/2.0/default/impl/vhal_v2_0/virtualization/Utils.cpp b/automotive/vehicle/2.0/default/impl/vhal_v2_0/virtualization/Utils.cpp new file mode 100644 index 0000000000..184d8a47f7 --- /dev/null +++ b/automotive/vehicle/2.0/default/impl/vhal_v2_0/virtualization/Utils.cpp @@ -0,0 +1,115 @@ +/* + * Copyright (C) 2019 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "Utils.h" + +#include <cutils/properties.h> + +#include <getopt.h> +#include <stdlib.h> +#include <unistd.h> +#include <sstream> + +namespace android { +namespace hardware { +namespace automotive { +namespace vehicle { +namespace V2_0 { +namespace impl { + +std::string VsockServerInfo::toUri() { + std::stringstream uri_stream; + uri_stream << "vsock:" << serverCid << ":" << serverPort; + return uri_stream.str(); +} + +static std::optional<unsigned> parseUnsignedIntFromString(const char* optarg, const char* name) { + auto v = strtoul(optarg, nullptr, 0); + if (((v == ULONG_MAX) && (errno == ERANGE)) || (v > UINT_MAX)) { + LOG(WARNING) << name << " value is out of range: " << optarg; + } else if (v != 0) { + return v; + } else { + LOG(WARNING) << name << " value is invalid or missing: " << optarg; + } + + return std::nullopt; +} + +static std::optional<unsigned> getNumberFromProperty(const char* key) { + auto value = property_get_int64(key, -1); + if ((value <= 0) || (value > UINT_MAX)) { + LOG(WARNING) << key << " is missing or out of bounds"; + return std::nullopt; + } + + return static_cast<unsigned int>(value); +}; + +std::optional<VsockServerInfo> VsockServerInfo::fromCommandLine(int argc, char* argv[]) { + std::optional<unsigned int> cid; + std::optional<unsigned int> port; + + // unique values to identify the options + constexpr int OPT_VHAL_SERVER_CID = 1001; + constexpr int OPT_VHAL_SERVER_PORT_NUMBER = 1002; + + struct option longOptions[] = { + {"server_cid", 1, 0, OPT_VHAL_SERVER_CID}, + {"server_port", 1, 0, OPT_VHAL_SERVER_PORT_NUMBER}, + {}, + }; + + int optValue; + while ((optValue = getopt_long_only(argc, argv, ":", longOptions, 0)) != -1) { + switch (optValue) { + case OPT_VHAL_SERVER_CID: + cid = parseUnsignedIntFromString(optarg, "cid"); + break; + case OPT_VHAL_SERVER_PORT_NUMBER: + port = parseUnsignedIntFromString(optarg, "port"); + break; + default: + // ignore other options + break; + } + } + + if (cid && port) { + return VsockServerInfo{*cid, *port}; + } + return std::nullopt; +} + +std::optional<VsockServerInfo> VsockServerInfo::fromRoPropertyStore() { + constexpr const char* VHAL_SERVER_CID_PROPERTY_KEY = "ro.vendor.vehiclehal.server.cid"; + constexpr const char* VHAL_SERVER_PORT_PROPERTY_KEY = "ro.vendor.vehiclehal.server.port"; + + const auto cid = getNumberFromProperty(VHAL_SERVER_CID_PROPERTY_KEY); + const auto port = getNumberFromProperty(VHAL_SERVER_PORT_PROPERTY_KEY); + + if (cid && port) { + return VsockServerInfo{*cid, *port}; + } + return std::nullopt; +} + +} // namespace impl +} // namespace V2_0 +} // namespace vehicle +} // namespace automotive +} // namespace hardware +} // namespace android diff --git a/automotive/vehicle/2.0/default/impl/vhal_v2_0/virtualization/Utils.h b/automotive/vehicle/2.0/default/impl/vhal_v2_0/virtualization/Utils.h new file mode 100644 index 0000000000..8a8bce7c7a --- /dev/null +++ b/automotive/vehicle/2.0/default/impl/vhal_v2_0/virtualization/Utils.h @@ -0,0 +1,49 @@ +/* + * Copyright (C) 2019 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef android_hardware_automotive_vehicle_V2_0_impl_virtualization_Utils_H_ +#define android_hardware_automotive_vehicle_V2_0_impl_virtualization_Utils_H_ + +#include <optional> +#include <string> + +#include <android-base/logging.h> + +namespace android { +namespace hardware { +namespace automotive { +namespace vehicle { +namespace V2_0 { +namespace impl { + +struct VsockServerInfo { + unsigned int serverCid{0}; + unsigned int serverPort{0}; + + static std::optional<VsockServerInfo> fromCommandLine(int argc, char* argv[]); + static std::optional<VsockServerInfo> fromRoPropertyStore(); + + std::string toUri(); +}; + +} // namespace impl +} // namespace V2_0 +} // namespace vehicle +} // namespace automotive +} // namespace hardware +} // namespace android + +#endif // android_hardware_automotive_vehicle_V2_0_impl_virtualization_Utils_H_ diff --git a/automotive/vehicle/2.0/types.hal b/automotive/vehicle/2.0/types.hal index 6145ea2710..a91bd885b3 100644 --- a/automotive/vehicle/2.0/types.hal +++ b/automotive/vehicle/2.0/types.hal @@ -2438,6 +2438,217 @@ enum VehicleProperty : int32_t { | VehiclePropertyType:STRING | VehicleArea:GLOBAL), + /** + * Defines the initial Android user to be used during initialization. + * + * This property is called by the Android system when it initializes and it lets the HAL + * define which Android user should be started. + * + * This request is made by setting a VehiclePropValue (defined by InitialUserInfoRequest), + * and the HAL must respond with a property change event (defined by InitialUserInfoResponse). + * If the HAL doesn't respond after some time (defined by the Android system), the Android + * system will proceed as if HAL returned a response of action + * InitialUserInfoResponseAction:DEFAULT. + * + * For example, on first boot, the request could be: + * + * int32[0]: 42 // request id (arbitrary number set by Android system) + * int32[1]: 1 // InitialUserInfoRequestType::FIRST_BOOT + * int32[2]: 0 // id of current user (usersInfo.currentUser.userId) + * int32[3]: 1 // flag of current user (usersInfo.currentUser.flags = SYSTEM) + * int32[4]: 1 // number of existing users (usersInfo.numberUsers); + * int32[5]: 0 // user #0 (usersInfo.existingUsers[0].userId) + * int32[6]: 1 // flags of user #0 (usersInfo.existingUsers[0].flags) + * + * And if the HAL want to respond with the creation of an admin user called "Admin", the + * response would be: + * + * int32[0]: 42 // must match the request id from the request + * int32[1]: 2 // action = InitialUserInfoResponseAction::CREATE + * int32[2]: -1 // userToSwitchOrCreate.userId (not used as user will be created) + * int32[3]: 8 // userToSwitchOrCreate.flags = ADMIN + * string: "Admin" // userNameToCreate + * + * NOTE: if the HAL doesn't support user management, then it should not define this property, + * which in turn would disable the other user-related properties (for example, the Android + * system would never issue them and user-related requests from the HAL layer would be ignored + * by the Android System). But if it supports user management, then it must support all + * user-related properties (INITIAL_USER_INFO, SWITCH_USER, TODO(b/146207078):others). + * + * @change_mode VehiclePropertyChangeMode:ON_CHANGE + * @access VehiclePropertyAccess:READ_WRITE + */ + INITIAL_USER_INFO = ( + 0x0F07 + | VehiclePropertyGroup:SYSTEM + | VehiclePropertyType:MIXED + | VehicleArea:GLOBAL), + + /** + * Defines a request to switch the foreground Android user. + * + * This property is used primarily by the Android System to inform the HAL that the + * current foreground Android user is switching, but it could also be used by the HAL to request + * the Android system to switch users - the + * + * When the request is made by Android, it sets a VehiclePropValue and the HAL must responde + * with a property change event; when the HAL is making the request, it must also do it through + * a property change event (the main difference is that the request id will be positive in the + * former case, and negative in the latter; the SwitchUserMessageType will also be different). + * + * The format of both request is defined by SwitchUserRequest and the format of the response + * (when needed) is defined by SwitchUserResponse. How the HAL (or Android System) should + * proceed depends on the message type (which is defined by the SwitchUserMessageType + * parameter), as defined below. + * + * 1.LEGACY_ANDROID_SWITCH + * ----------------------- + * + * Called by the Android System to indicate the Android user is about to change, when the change + * request was made in a way that is not integrated with the HAL (for example, through + * adb shell am switch-user). + * + * The HAL can switch its internal user once it receives this request, but it doesn't need to + * reply back to the Android System. If its internal user cannot be changed for some reason, + * then it must wait for the SWITCH_USER(type=ANDROID_POST_SWITCH) call to recover + * (for example, it could issue a SWITCH_USER(type=VEHICLE_REQUEST) to switch back to + * the previous user), but ideally it should never fail (as switching back could result in a + * confusing experience for the end user). + * + * For example, if the system have users (0, 10, 11) and it's switching from 0 to 11 (where none + * of them have any special flag), the request would be: + * + * int32[0]: 42 // request id + * int32[1]: 1 // SwitchUserMessageType::LEGACY_ANDROID_SWITCH + * int32[2]: 11 // target user id + * int32[3]: 0 // target user flags (none) + * int32[4]: 10 // current user + * int32[5]: 0 // current user flags (none) + * int32[6]: 3 // number of users + * int32[7]: 0 // user #0 (Android user id 0) + * int32[8]: 0 // flags of user #0 (none) + * int32[9]: 10 // user #1 (Android user id 10) + * int32[10]: 0 // flags of user #1 (none) + * int32[11]: 11 // user #2 (Android user id 11) + * int32[12]: 0 // flags of user #2 (none) + * + * 2.ANDROID_SWITCH + * ---------------- + * Called by the Android System to indicate the Android user is about to change, but Android + * will wait for the HAL's response (up to some time) before proceeding. + * + * The HAL must switch its internal user once it receives this request, then respond back to + * Android with a SWITCH_USER(type=VEHICLE_RESPONSE) indicating whether its internal + * user was switched or not (through the SwitchUserStatus enum). + * + * For example, if Android has users (0, 10, 11) and it's switching from 10 to 11 (where + * none of them have any special flag), the request would be: + * + * int32[0]: 42 // request id + * int32[1]: 2 // SwitchUserMessageType::ANDROID_SWITCH + * int32[2]: 11 // target user id + * int32[3]: 0 // target user flags (none) + * int32[4]: 10 // current user + * int32[5]: 0 // current user flags (none) + * int32[6]: 3 // number of users + * int32[7]: 0 // 1st user (user 0) + * int32[8]: 0 // 1st user flags (none) + * int32[9]: 10 // 2nd user (user 10) + * int32[10]: 0 // 2nd user flags (none) + * int32[11]: 11 // 3rd user (user 11) + * int32[12]: 0 // 3rd user flags (none) + * + * If the request succeeded, the HAL must update the propery with: + * + * int32[0]: 42 // request id + * int32[1]: 3 // messageType = SwitchUserMessageType::VEHICLE_RESPONSE + * int32[2]: 1 // status = SwitchUserStatus::SUCCESS + * + * But if it failed, the response would be something like: + * + * int32[0]: 42 // request id + * int32[1]: 3 // messageType = SwitchUserMessageType::VEHICLE_RESPONSE + * int32[2]: 2 // status = SwitchUserStatus::FAILURE + * string: "108-D'OH!" // OEM-spefic error message + * + * 3.VEHICLE_RESPONSE + * ------------------ + * Called by the HAL to indicate whether a request of type ANDROID_SWITCH should proceed or + * abort - see the ANDROID_SWITCH section above for more info. + * + * 4.VEHICLE_REQUEST + * ------------------ + * Called by the HAL to request that the current foreground Android user is switched. + * + * This is useful in situations where Android started as one user, but the vehicle identified + * the driver as another user. For example, user A unlocked the car using the key fob of user B; + * the INITIAL_USER_INFO request returned user B, but then a face recognition subsubsystem + * identified the user as A. + * + * The HAL makes this request by a property change event (passing a negative request id), and + * the Android system will response by issuye an ANDROID_POST_SWITCH call which the same + * request id. + * + * For example, if the current foreground Android user is 10 and the HAL asked it to switch to + * 11, the request would be: + * + * int32[0]: -108 // request id + * int32[1]: 4 // messageType = SwitchUserMessageType::VEHICLE_REQUEST + * int32[2]: 11 // Android user id + * + * If the request succeeded and Android has 3 users (0, 10, 11), the response would be: + * + * int32[0]: -108 // request id + * int32[1]: 5 // messageType = SwitchUserMessageType::ANDROID_SWITCH + * int32[2]: 11 // target user id + * int32[3]: 11 // target user id flags (none) + * int32[4]: 11 // current user + * int32[5]: 0 // current user flags (none) + * int32[6]: 3 // number of users + * int32[7]: 0 // 1st user (user 0) + * int32[8]: 0 // 1st user flags (none) + * int32[9]: 10 // 2nd user (user 10) + * int32[10]: 4 // 2nd user flags (none) + * int32[11]: 11 // 3rd user (user 11) + * int32[12]: 3 // 3rd user flags (none) + * + * Notice that both the current and target user ids are the same - if the request failed, then + * they would be different (i.e, target user would be 11, but current user would still be 10). + * + * 5.ANDROID_POST_SWITCH + * --------------------- + * Called by the Android System after a request to switch a user was made + * + * This property is called after switch requests of any type (i.e., LEGACY_ANDROID_SWITCH, + * ANDROID_SWITCH, or VEHICLE_REQUEST) and can be used to determine if the request succeeded or + * failed: + * + * 1. When it succeeded, it's called when the Android user is in the boot locked state and the + * value of the current and target users ids in the response are different. This would be + * equivalent to receiving an Intent.ACTION_LOCKED_BOOT_COMPLETED in an Android app. + * 2. When it failed it's called right away and the value of the current and target users ids + * in the response are the same. + * + * The HAL can update its internal state once it receives this request, but it doesn't need to + * reply back to the Android System. + * + * Request: the first N values as defined by INITIAL_USER_INFO (where the request-specific + * value at index 1 is SwitchUserMessageType::ANDROID_POST_SWITCH), then 2 more values for the + * target user id (i.e., the Android user id that was requested to be switched to) and its flags + * (as defined by UserFlags). + * + * Response: none. + * + * Example: see VEHICLE_REQUEST section above. + * + * @change_mode VehiclePropertyChangeMode:ON_CHANGE + * @access VehiclePropertyAccess:READ_WRITE + */ + SWITCH_USER = ( + 0x0F08 + | VehiclePropertyGroup:SYSTEM + | VehiclePropertyType:MIXED + | VehicleArea:GLOBAL), }; /** @@ -3733,3 +3944,258 @@ enum VmsAvailabilityStateIntegerValuesIndex : VmsBaseMessageIntegerValuesIndex { enum VmsPublisherInformationIntegerValuesIndex : VmsBaseMessageIntegerValuesIndex { PUBLISHER_ID = 1, }; + +/** + * Information about a specific Android user. + */ +struct UserInfo { + + UserId userId; + + UserFlags flags; +}; + +/** + * Id of an Android user. + * + * Must be > 0 for valid ids, or -1 when it's not used. + */ +typedef int32_t UserId; + +/** + * Flags used to define the characteristics of an Android user. + */ +enum UserFlags: int32_t { + /** + * No flags. + */ + NONE = 0x0, + + /** + * System user. + * On automotive, that user is always running, although never on foreground (except during + * boot or exceptional circumstances). + */ + SYSTEM = 0x01, + + /** + * Guest users have restrictions. + */ + GUEST = 0x02, + + /** + * Ephemeral users have non-persistent state. + */ + EPHEMERAL = 0x04, + + /** + * Admin users have additional privileges such as permission to create other users. + */ + ADMIN = 0x08, +}; + +/** + * Information about all Android users. + * + * NOTE: this struct is not used in the HAL properties directly, it's part of other structs, which + * in turn are converted to a VehiclePropValue.RawValue through libraries provided by the default + * Vehicle HAL implementation. + */ +struct UsersInfo { + + /** The current foreground user. */ + UserInfo currentUser; + + /** Number of existing users (includes the current user). */ + int32_t numberUsers; + + /** List of existing users (includes the current user). */ + vec<UserInfo> existingUsers; + }; + +/** + * Id of a request related to user management. + * + * This id can be used by the Android system to map responses sent by the HAL, and vice-versa. + * + * For requests originated by Android, the value is positive (> 0), while for requests originated by + * the HAL it must be negative (< 0). + */ +typedef int32_t UserRequestId; + +/** + * Defines the format of a INITIAL_USER_INFO request made by the Android system. + * + * NOTE: this struct is not used in the HAL properties directly, it must be converted to + * VehiclePropValue.RawValue through libraries provided by the default Vehicle HAL implementation. + */ +struct InitialUserInfoRequest { + /** + * Arbitrary id used to map the HAL response to the request. + */ + UserRequestId requestId; + + /** + * Type of request. + */ + InitialUserInfoRequestType requestType; + + /** + * Information about the current state of the Android system. + */ + UsersInfo usersInfo; +}; + +/** + * Defines when a INITIAL_USER_INFO request was made. + */ +enum InitialUserInfoRequestType : int32_t { + /** At the first time Android was booted (or after a factory reset). */ + FIRST_BOOT = 1, + + /** At the first time Android was booted after the system was updated. */ + FIRST_BOOT_AFTER_OTA = 2, + + /** When Android was booted "from scratch". */ + COLD_BOOT = 3, + + /** When Android was resumed after the system was suspended to memory. */ + RESUME = 4, +}; + +/** + * Defines the format of a HAL response to a INITIAL_USER_INFO request. + * + * NOTE: this struct is not used in the HAL properties directly, it must be converted to + * VehiclePropValue.RawValue through libraries provided by the default Vehicle HAL implementation. + */ +struct InitialUserInfoResponse { + /** + * Id of the request being responded. + */ + UserRequestId requestId; + + /** + * which action the Android system should take. + */ + InitialUserInfoResponseAction action; + + /** + * Information about the user that should be switched to or created. + */ + UserInfo userToSwitchOrCreate; + + /** + * Name of the user that should be created. + */ + string userNameToCreate; +}; + +/** + * Defines which action the Android system should take in an INITIAL_USER_INFO request. + */ +enum InitialUserInfoResponseAction : int32_t { + /** + * Let the Android System decide what to do. + * + * For example, it might create a new user on first boot, and switch to the last + * active user afterwards. + */ + DEFAULT = 0, + + /** + * Switch to an existing Android user. + */ + SWITCH = 1, + + /** + * Create a new Android user (and switch to it). + */ + CREATE = 2, +}; + +/** + * Defines the format of a SWITCH_USER property. + * + * NOTE: this struct is not used in the HAL properties directly, it must be converted to + * VehiclePropValue.RawValue through libraries provided by the default Vehicle HAL implementation. + */ +struct SwitchUserRequest { + /** + * Arbitrary id used to map the response to the request. + */ + UserRequestId requestId; + + /** + * Type of message. + */ + SwitchUserMessageType messageType; + + /** + * Information about the Android user being switched to. + * + * Only the user id (but not the flags) should be set when the request is made by HAL. + */ + UserInfo targetUser; + + /** + * Information about the current state of the Android system. + * + * Should not be set when the request is made by HAL. + */ + UsersInfo usersInfo; +}; + +/** + * Defines the reason a SWITCH_USER call was made. + * + * The meaning of each constant is explained in that property. + */ +enum SwitchUserMessageType: int32_t { + LEGACY_ANDROID_SWITCH = 1, + ANDROID_SWITCH = 2, + VEHICLE_RESPONSE = 3, + VEHICLE_REQUEST = 4, + ANDROID_POST_SWITCH = 5, +}; + +/** + * Defines the result of a SwitchUserRequest. + * + * NOTE: this struct is not used in the HAL properties directly, it must be converted to + * VehiclePropValue.RawValue through libraries provided by the default Vehicle HAL implementation. + */ +struct SwitchUserResponse { + /** + * Id of the request being responded. + */ + UserRequestId requestId; + + /** + * Type of message. + */ + SwitchUserMessageType messageType; + + /** + * Status of the request. + */ + SwitchUserStatus status; + + /** + * HAL-specific error message. + * + * This argument is optional, and when defined (and the status is FAILURE), it's passed "as-is" + * to the caller. It could be used to show custom error messages to the end user. + */ + string failureMessage; +}; + +/** + * Status of the response to a SwitchUserRequest. + */ +enum SwitchUserStatus : int32_t { + /** The request succeeded and the HAL user was switched. */ + SUCCESS = 1, + /** The request failed and the HAL user remained the same. */ + FAILURE = 2, +}; diff --git a/biometrics/face/1.1/Android.bp b/biometrics/face/1.1/Android.bp new file mode 100644 index 0000000000..2206597dec --- /dev/null +++ b/biometrics/face/1.1/Android.bp @@ -0,0 +1,17 @@ +// This file is autogenerated by hidl-gen -Landroidbp. + +hidl_interface { + name: "android.hardware.biometrics.face@1.1", + root: "android.hardware", + vndk: { + enabled: true, + }, + srcs: [ + "IBiometricsFace.hal", + ], + interfaces: [ + "android.hardware.biometrics.face@1.0", + "android.hidl.base@1.0", + ], + gen_java: true, +} diff --git a/biometrics/face/1.1/IBiometricsFace.hal b/biometrics/face/1.1/IBiometricsFace.hal new file mode 100644 index 0000000000..84e7443c9c --- /dev/null +++ b/biometrics/face/1.1/IBiometricsFace.hal @@ -0,0 +1,117 @@ +/* + * Copyright (C) 2019 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.hardware.biometrics.face@1.1; + +import @1.0::IBiometricsFace; +import @1.0::Status; +import @1.0::Feature; + +/** + * The HAL interface for biometric face authentication. + */ +interface IBiometricsFace extends @1.0::IBiometricsFace { + /** + * Enrolls a user's face for a remote client, for example Android Auto. + * + * The HAL implementation is responsible for creating a secure communication + * channel and receiving the enrollment images from a mobile device with + * face authentication hardware. + * + * Note that the Hardware Authentication Token must be valid for the + * duration of enrollment and thus should be explicitly invalidated by a + * call to revokeChallenge() when enrollment is complete, to reduce the + * window of opportunity to re-use the challenge and HAT. For example, + * Settings calls generateChallenge() once to allow the user to enroll one + * or more faces or toggle secure settings without having to re-enter the + * PIN/pattern/password. Once the user completes the operation, Settings + * invokes revokeChallenge() to close the transaction. If the HAT is expired, + * the implementation must invoke onError with UNABLE_TO_PROCESS. + * + * Requirements for using this API: + * - Mobile devices MUST NOT delegate enrollment to another device by calling + * this API. This feature is intended only to allow enrollment on devices + * where it is impossible to enroll locally on the device. + * - The path MUST be protected by a secret key with rollback protection. + * - Synchronizing between devices MUST be accomplished by having both + * devices agree on a secret PIN entered by the user (similar to BT + * pairing procedure) and use a salted version of that PIN plus other secret + * to encrypt traffic. + * - All communication to/from the remote device MUST be encrypted and signed + * to prevent image injection and other man-in-the-middle type attacks. + * - generateChallenge() and revokeChallenge() MUST be implemented on both + * remote and local host (e.g. hash the result of the remote host with a + * local secret before responding to the API call) and any transmission of + * the challenge between hosts MUST be signed to prevent man-in-the-middle + * attacks. + * - In the event of a lost connection, the result of the last + * generateChallenge() MUST be invalidated and the process started over. + * - Both the remote and local host MUST honor the timeout and invalidate the + * challenge. + * + * This method triggers the IBiometricsFaceClientCallback#onEnrollResult() + * method. + * + * @param hat A valid Hardware Authentication Token, generated as a result + * of a generateChallenge() challenge being wrapped by the gatekeeper + * after a successful strong authentication request. + * @param timeoutSec A timeout in seconds, after which this enroll + * attempt is cancelled. Note that the framework can continue + * enrollment by calling this again with a valid HAT. This timeout is + * expected to be used to limit power usage if the device becomes idle + * during enrollment. The implementation is expected to send + * ERROR_TIMEOUT if this happens. + * @param disabledFeatures A list of features to be disabled during + * enrollment. Note that all features are enabled by default. + * @return status The status of this method call. + */ + enrollRemotely(vec<uint8_t> hat, uint32_t timeoutSec, vec<Feature> disabledFeatures) + generates (Status status); + + /** + * Enrolls a user's face. + * + * Note that the Hardware Authentication Token must be valid for the + * duration of enrollment and thus should be explicitly invalidated by a + * call to revokeChallenge() when enrollment is complete, to reduce the + * window of opportunity to re-use the challenge and HAT. For example, + * Settings calls generateChallenge() once to allow the user to enroll one + * or more faces or toggle secure settings without having to re-enter the + * PIN/pattern/password. Once the user completes the operation, Settings + * invokes revokeChallenge() to close the transaction. If the HAT is expired, + * the implementation must invoke onError with UNABLE_TO_PROCESS. + * + * This method triggers the IBiometricsFaceClientCallback#onEnrollResult() + * method. + * + * @param hat A valid Hardware Authentication Token, generated as a result + * of a generateChallenge() challenge being wrapped by the gatekeeper + * after a successful strong authentication request. + * @param timeoutSec A timeout in seconds, after which this enroll + * attempt is cancelled. Note that the framework can continue + * enrollment by calling this again with a valid HAT. This timeout is + * expected to be used to limit power usage if the device becomes idle + * during enrollment. The implementation is expected to send + * ERROR_TIMEOUT if this happens. + * @param disabledFeatures A list of features to be disabled during + * enrollment. Note that all features are enabled by default. + * @param windowId optional ID of a camera preview window for a + * single-camera device. Must be null if not used. + * @return status The status of this method call. + */ + enroll_1_1(vec<uint8_t> hat, uint32_t timeoutSec, vec<Feature> disabledFeatures, + handle windowId) generates (Status status); +}; diff --git a/biometrics/face/1.1/default/Android.bp b/biometrics/face/1.1/default/Android.bp new file mode 100644 index 0000000000..360071f3dd --- /dev/null +++ b/biometrics/face/1.1/default/Android.bp @@ -0,0 +1,36 @@ +/* + * Copyright 2020 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +cc_binary { + name: "android.hardware.biometrics.face@1.1-service.example", + defaults: ["hidl_defaults"], + vendor: true, + init_rc: ["android.hardware.biometrics.face@1.1-service.rc"], + vintf_fragments: ["manifest_face_default.xml"], + relative_install_path: "hw", + proprietary: true, + srcs: [ + "BiometricsFace.cpp", + "service.cpp", + ], + shared_libs: [ + "libhidlbase", + "libutils", + "liblog", + "android.hardware.biometrics.face@1.0", + "android.hardware.biometrics.face@1.1", + ], +} diff --git a/biometrics/face/1.1/default/BiometricsFace.cpp b/biometrics/face/1.1/default/BiometricsFace.cpp new file mode 100644 index 0000000000..2143880514 --- /dev/null +++ b/biometrics/face/1.1/default/BiometricsFace.cpp @@ -0,0 +1,129 @@ +/* + * Copyright 2020 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "BiometricsFace.h" + +namespace android::hardware::biometrics::face::implementation { +using android::hardware::biometrics::face::V1_0::FaceError; +using android::hardware::biometrics::face::V1_0::OptionalUint64; + +// Arbitrary value. +constexpr uint64_t kDeviceId = 123; +// Arbitrary value. +constexpr uint64_t kAuthenticatorId = 987; +// Arbitrary value. +constexpr uint64_t kLockoutDuration = 555; + +BiometricsFace::BiometricsFace() : mRandom(std::mt19937::default_seed) {} + +// Methods from IBiometricsFace follow. +Return<void> BiometricsFace::setCallback(const sp<IBiometricsFaceClientCallback>& clientCallback, + setCallback_cb _hidl_cb) { + mClientCallback = clientCallback; + _hidl_cb({Status::OK, kDeviceId}); + return Void(); +} + +Return<Status> BiometricsFace::setActiveUser(int32_t userId, const hidl_string& storePath) { + if (userId < 0 || storePath.empty() || std::string(storePath).find("/data") != 0) { + return Status::ILLEGAL_ARGUMENT; + } + mUserId = userId; + mClientCallback->onLockoutChanged(kLockoutDuration); + return Status::OK; +} + +Return<void> BiometricsFace::generateChallenge(uint32_t /* challengeTimeoutSec */, + generateChallenge_cb _hidl_cb) { + std::uniform_int_distribution<uint64_t> dist; + _hidl_cb({Status::OK, dist(mRandom)}); + return Void(); +} + +Return<Status> BiometricsFace::enroll(const hidl_vec<uint8_t>& /* hat */, uint32_t /* timeoutSec */, + const hidl_vec<Feature>& /* disabledFeatures */) { + // hat can never be valid in this implementation. + mClientCallback->onError(kDeviceId, mUserId, FaceError::UNABLE_TO_PROCESS, 0 /* vendorCode */); + return Status::OK; +} + +Return<Status> BiometricsFace::revokeChallenge() { + return Status::OK; +} + +Return<Status> BiometricsFace::setFeature(Feature /* feature */, bool /* enabled */, + const hidl_vec<uint8_t>& /* hat */, + uint32_t /* faceId */) { + // hat can never be valid in this implementation. + return Status::ILLEGAL_ARGUMENT; +} + +Return<void> BiometricsFace::getFeature(Feature /* feature */, uint32_t /* faceId */, + getFeature_cb _hidl_cb) { + // hat can never be valid in this implementation. + _hidl_cb({Status::ILLEGAL_ARGUMENT, false}); + return Void(); +} + +Return<void> BiometricsFace::getAuthenticatorId(getAuthenticatorId_cb _hidl_cb) { + _hidl_cb({Status::OK, kAuthenticatorId}); + return Void(); +} + +Return<Status> BiometricsFace::cancel() { + mClientCallback->onError(kDeviceId, mUserId, FaceError::CANCELED, 0 /* vendorCode */); + return Status::OK; +} + +Return<Status> BiometricsFace::enumerate() { + mClientCallback->onEnumerate(kDeviceId, {}, mUserId); + return Status::OK; +} + +Return<Status> BiometricsFace::remove(uint32_t /* faceId */) { + return Status::OK; +} + +Return<Status> BiometricsFace::authenticate(uint64_t /* operationId */) { + mClientCallback->onError(kDeviceId, mUserId, FaceError::HW_UNAVAILABLE, 0 /* vendorCode */); + return Status::OK; +} + +Return<Status> BiometricsFace::userActivity() { + return Status::OK; +} + +Return<Status> BiometricsFace::resetLockout(const hidl_vec<uint8_t>& /* hat */) { + return Status::OK; +} + +// Methods from ::android::hardware::biometrics::face::V1_1::IBiometricsFace follow. +Return<Status> BiometricsFace::enroll_1_1(const hidl_vec<uint8_t>& /* hat */, + uint32_t /* timeoutSec */, + const hidl_vec<Feature>& /* disabledFeatures */, + const hidl_handle& /* windowId */) { + mClientCallback->onError(kDeviceId, mUserId, FaceError::UNABLE_TO_PROCESS, 0 /* vendorCode */); + return Status::OK; +} + +Return<Status> BiometricsFace::enrollRemotely(const hidl_vec<uint8_t>& /* hat */, + uint32_t /* timeoutSec */, + const hidl_vec<Feature>& /* disabledFeatures */) { + mClientCallback->onError(kDeviceId, mUserId, FaceError::UNABLE_TO_PROCESS, 0 /* vendorCode */); + return Status::OK; +} + +} // namespace android::hardware::biometrics::face::implementation diff --git a/biometrics/face/1.1/default/BiometricsFace.h b/biometrics/face/1.1/default/BiometricsFace.h new file mode 100644 index 0000000000..5ce5771eae --- /dev/null +++ b/biometrics/face/1.1/default/BiometricsFace.h @@ -0,0 +1,88 @@ +/* + * Copyright 2020 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#pragma once + +#include <android/hardware/biometrics/face/1.1/IBiometricsFace.h> +#include <hidl/MQDescriptor.h> +#include <hidl/Status.h> +#include <random> + +namespace android::hardware::biometrics::face::implementation { + +using ::android::sp; +using ::android::hardware::hidl_array; +using ::android::hardware::hidl_memory; +using ::android::hardware::hidl_string; +using ::android::hardware::hidl_vec; +using ::android::hardware::Return; +using ::android::hardware::Void; +using ::android::hardware::biometrics::face::V1_0::Feature; +using ::android::hardware::biometrics::face::V1_0::IBiometricsFaceClientCallback; +using ::android::hardware::biometrics::face::V1_0::Status; + +class BiometricsFace : public V1_1::IBiometricsFace { + public: + BiometricsFace(); + + // Methods from ::android::hardware::biometrics::face::V1_0::IBiometricsFace follow. + Return<void> setCallback(const sp<IBiometricsFaceClientCallback>& clientCallback, + setCallback_cb _hidl_cb) override; + + Return<Status> setActiveUser(int32_t userId, const hidl_string& storePath) override; + + Return<void> generateChallenge(uint32_t challengeTimeoutSec, + generateChallenge_cb _hidl_cb) override; + + Return<Status> enroll(const hidl_vec<uint8_t>& hat, uint32_t timeoutSec, + const hidl_vec<Feature>& disabledFeatures) override; + + Return<Status> revokeChallenge() override; + + Return<Status> setFeature(Feature feature, bool enabled, const hidl_vec<uint8_t>& hat, + uint32_t faceId) override; + + Return<void> getFeature(Feature feature, uint32_t faceId, getFeature_cb _hidl_cb) override; + + Return<void> getAuthenticatorId(getAuthenticatorId_cb _hidl_cb) override; + + Return<Status> cancel() override; + + Return<Status> enumerate() override; + + Return<Status> remove(uint32_t faceId) override; + + Return<Status> authenticate(uint64_t operationId) override; + + Return<Status> userActivity() override; + + Return<Status> resetLockout(const hidl_vec<uint8_t>& hat) override; + + // Methods from ::android::hardware::biometrics::face::V1_1::IBiometricsFace follow. + Return<Status> enroll_1_1(const hidl_vec<uint8_t>& hat, uint32_t timeoutSec, + const hidl_vec<Feature>& disabledFeatures, + const hidl_handle& windowId) override; + + Return<Status> enrollRemotely(const hidl_vec<uint8_t>& hat, uint32_t timeoutSec, + const hidl_vec<Feature>& disabledFeatures) override; + + private: + std::mt19937 mRandom; + int32_t mUserId; + sp<IBiometricsFaceClientCallback> mClientCallback; +}; + +} // namespace android::hardware::biometrics::face::implementation diff --git a/biometrics/face/1.1/default/android.hardware.biometrics.face@1.1-service.rc b/biometrics/face/1.1/default/android.hardware.biometrics.face@1.1-service.rc new file mode 100644 index 0000000000..687e2d8c86 --- /dev/null +++ b/biometrics/face/1.1/default/android.hardware.biometrics.face@1.1-service.rc @@ -0,0 +1,10 @@ +service vendor.face-hal-1-1-default /vendor/bin/hw/android.hardware.biometrics.face@1.1-service.example + # "class hal" causes a race condition on some devices due to files created + # in /data. As a workaround, postpone startup until later in boot once + # /data is mounted. + class late_start + user system + group system + writepid /dev/cpuset/foreground/tasks + capabilities SYS_NICE + rlimit rtprio 10 10 diff --git a/biometrics/face/1.1/default/manifest_face_default.xml b/biometrics/face/1.1/default/manifest_face_default.xml new file mode 100644 index 0000000000..ec71d9c92b --- /dev/null +++ b/biometrics/face/1.1/default/manifest_face_default.xml @@ -0,0 +1,11 @@ +<manifest version="2.0" type="device"> + <hal format="hidl"> + <name>android.hardware.biometrics.face</name> + <transport>hwbinder</transport> + <version>1.1</version> + <interface> + <name>IBiometricsFace</name> + <instance>default</instance> + </interface> + </hal> +</manifest> diff --git a/biometrics/face/1.1/default/service.cpp b/biometrics/face/1.1/default/service.cpp new file mode 100644 index 0000000000..344bdb99b4 --- /dev/null +++ b/biometrics/face/1.1/default/service.cpp @@ -0,0 +1,50 @@ +/* + * Copyright (C) 2020 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#define LOG_TAG "android.hardware.biometrics.face@1.1-service" + +#include <android/hardware/biometrics/face/1.0/types.h> +#include <android/hardware/biometrics/face/1.1/IBiometricsFace.h> +#include <android/log.h> +#include <hidl/HidlSupport.h> +#include <hidl/HidlTransportSupport.h> +#include "BiometricsFace.h" + +using android::sp; +using android::hardware::configureRpcThreadpool; +using android::hardware::joinRpcThreadpool; +using android::hardware::biometrics::face::implementation::BiometricsFace; +using android::hardware::biometrics::face::V1_1::IBiometricsFace; + +int main() { + ALOGI("BiometricsFace HAL is being started."); + + configureRpcThreadpool(1, true /*callerWillJoin*/); + + android::sp<IBiometricsFace> face = new BiometricsFace(); + const android::status_t status = face->registerAsService(); + + if (status != android::OK) { + ALOGE("Error starting the BiometricsFace HAL."); + return 1; + } + + ALOGI("BiometricsFace HAL has started successfully."); + joinRpcThreadpool(); + + ALOGI("BiometricsFace HAL is terminating."); + return 1; // should never get here +} diff --git a/biometrics/face/1.1/vts/functional/Android.bp b/biometrics/face/1.1/vts/functional/Android.bp new file mode 100644 index 0000000000..ccbb3994e1 --- /dev/null +++ b/biometrics/face/1.1/vts/functional/Android.bp @@ -0,0 +1,29 @@ +/* + * Copyright 2020 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +cc_test { + name: "VtsHalBiometricsFaceV1_1TargetTest", + defaults: ["VtsHalTargetTestDefaults"], + srcs: ["VtsHalBiometricsFaceV1_1TargetTest.cpp"], + static_libs: [ + "android.hardware.biometrics.face@1.0", + "android.hardware.biometrics.face@1.1", + ], + test_suites: [ + "general-tests", + "vts-core", + ], +} diff --git a/biometrics/face/1.1/vts/functional/VtsHalBiometricsFaceV1_1TargetTest.cpp b/biometrics/face/1.1/vts/functional/VtsHalBiometricsFaceV1_1TargetTest.cpp new file mode 100644 index 0000000000..6ada44231f --- /dev/null +++ b/biometrics/face/1.1/vts/functional/VtsHalBiometricsFaceV1_1TargetTest.cpp @@ -0,0 +1,205 @@ +/* + * Copyright 2020 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#define LOG_TAG "biometrics_face_hidl_hal_test" + +#include <android/hardware/biometrics/face/1.0/IBiometricsFaceClientCallback.h> +#include <android/hardware/biometrics/face/1.1/IBiometricsFace.h> + +#include <VtsHalHidlTargetCallbackBase.h> +#include <android-base/logging.h> +#include <gtest/gtest.h> +#include <hidl/GtestPrinter.h> +#include <hidl/ServiceManagement.h> + +#include <chrono> +#include <cstdint> +#include <random> + +using android::sp; +using android::hardware::hidl_handle; +using android::hardware::hidl_vec; +using android::hardware::Return; +using android::hardware::Void; +using android::hardware::biometrics::face::V1_0::FaceAcquiredInfo; +using android::hardware::biometrics::face::V1_0::FaceError; +using android::hardware::biometrics::face::V1_0::IBiometricsFaceClientCallback; +using android::hardware::biometrics::face::V1_0::OptionalUint64; +using android::hardware::biometrics::face::V1_0::Status; +using android::hardware::biometrics::face::V1_1::IBiometricsFace; + +namespace { + +// Arbitrary, nonexistent userId +constexpr uint32_t kUserId = 9; +constexpr uint32_t kTimeoutSec = 3; +constexpr auto kTimeout = std::chrono::seconds(kTimeoutSec); +constexpr char kFacedataDir[] = "/data/vendor_de/0/facedata"; +constexpr char kCallbackNameOnError[] = "onError"; + +// Callback arguments that need to be captured for the tests. +struct FaceCallbackArgs { + // The error passed to the last onError() callback. + FaceError error; + + // The userId passed to the last callback. + int32_t userId; +}; + +// Test callback class for the BiometricsFace HAL. +// The HAL will call these callback methods to notify about completed operations +// or encountered errors. +class FaceCallback : public ::testing::VtsHalHidlTargetCallbackBase<FaceCallbackArgs>, + public IBiometricsFaceClientCallback { + public: + Return<void> onEnrollResult(uint64_t, uint32_t, int32_t, uint32_t) override { return Void(); } + + Return<void> onAuthenticated(uint64_t, uint32_t, int32_t, const hidl_vec<uint8_t>&) override { + return Void(); + } + + Return<void> onAcquired(uint64_t, int32_t, FaceAcquiredInfo, int32_t) override { + return Void(); + } + + Return<void> onError(uint64_t, int32_t userId, FaceError error, int32_t) override { + FaceCallbackArgs args = {}; + args.error = error; + args.userId = userId; + NotifyFromCallback(kCallbackNameOnError, args); + return Void(); + } + + Return<void> onRemoved(uint64_t, const hidl_vec<uint32_t>&, int32_t) override { return Void(); } + + Return<void> onEnumerate(uint64_t, const hidl_vec<uint32_t>&, int32_t) override { + return Void(); + } + + Return<void> onLockoutChanged(uint64_t) override { return Void(); } +}; + +// Test class for the BiometricsFace HAL. +class FaceHidlTest : public ::testing::TestWithParam<std::string> { + public: + void SetUp() override { + mService = IBiometricsFace::getService(GetParam()); + ASSERT_NE(mService, nullptr); + mCallback = new FaceCallback(); + mCallback->SetWaitTimeoutDefault(kTimeout); + Return<void> ret1 = mService->setCallback(mCallback, [](const OptionalUint64& res) { + ASSERT_EQ(Status::OK, res.status); + // Makes sure the "deviceId" represented by "res.value" is not 0. + // 0 would mean the HIDL is not available. + ASSERT_NE(0UL, res.value); + }); + ASSERT_TRUE(ret1.isOk()); + Return<Status> ret2 = mService->setActiveUser(kUserId, kFacedataDir); + ASSERT_EQ(Status::OK, static_cast<Status>(ret2)); + } + + void TearDown() override {} + + sp<IBiometricsFace> mService; + sp<FaceCallback> mCallback; +}; + +// enroll with an invalid (all zeroes) HAT should fail. +TEST_P(FaceHidlTest, Enroll2_2ZeroHatTest) { + // Filling HAT with zeros + hidl_vec<uint8_t> token(69); + for (size_t i = 0; i < 69; i++) { + token[i] = 0; + } + + hidl_handle windowId = nullptr; + Return<Status> ret = mService->enroll_1_1(token, kTimeoutSec, {}, windowId); + ASSERT_EQ(Status::OK, static_cast<Status>(ret)); + + // onError should be called with a meaningful (nonzero) error. + auto res = mCallback->WaitForCallback(kCallbackNameOnError); + EXPECT_TRUE(res.no_timeout); + EXPECT_EQ(kUserId, res.args->userId); + EXPECT_EQ(FaceError::UNABLE_TO_PROCESS, res.args->error); +} + +// enroll with an invalid HAT should fail. +TEST_P(FaceHidlTest, Enroll2_2GarbageHatTest) { + // Filling HAT with pseudorandom invalid data. + // Using default seed to make the test reproducible. + std::mt19937 gen(std::mt19937::default_seed); + std::uniform_int_distribution<uint8_t> dist; + hidl_vec<uint8_t> token(69); + for (size_t i = 0; i < 69; ++i) { + token[i] = dist(gen); + } + + hidl_handle windowId = nullptr; + Return<Status> ret = mService->enroll_1_1(token, kTimeoutSec, {}, windowId); + ASSERT_EQ(Status::OK, static_cast<Status>(ret)); + + // onError should be called with a meaningful (nonzero) error. + auto res = mCallback->WaitForCallback(kCallbackNameOnError); + EXPECT_TRUE(res.no_timeout); + EXPECT_EQ(kUserId, res.args->userId); + EXPECT_EQ(FaceError::UNABLE_TO_PROCESS, res.args->error); +} + +// enroll with an invalid (all zeroes) HAT should fail. +TEST_P(FaceHidlTest, EnrollRemotelyZeroHatTest) { + // Filling HAT with zeros + hidl_vec<uint8_t> token(69); + for (size_t i = 0; i < 69; i++) { + token[i] = 0; + } + + Return<Status> ret = mService->enrollRemotely(token, kTimeoutSec, {}); + ASSERT_EQ(Status::OK, static_cast<Status>(ret)); + + // onError should be called with a meaningful (nonzero) error. + auto res = mCallback->WaitForCallback(kCallbackNameOnError); + EXPECT_TRUE(res.no_timeout); + EXPECT_EQ(kUserId, res.args->userId); + EXPECT_EQ(FaceError::UNABLE_TO_PROCESS, res.args->error); +} + +// enroll with an invalid HAT should fail. +TEST_P(FaceHidlTest, EnrollRemotelyGarbageHatTest) { + // Filling HAT with pseudorandom invalid data. + // Using default seed to make the test reproducible. + std::mt19937 gen(std::mt19937::default_seed); + std::uniform_int_distribution<uint8_t> dist; + hidl_vec<uint8_t> token(69); + for (size_t i = 0; i < 69; ++i) { + token[i] = dist(gen); + } + + Return<Status> ret = mService->enrollRemotely(token, kTimeoutSec, {}); + ASSERT_EQ(Status::OK, static_cast<Status>(ret)); + + // onError should be called with a meaningful (nonzero) error. + auto res = mCallback->WaitForCallback(kCallbackNameOnError); + EXPECT_TRUE(res.no_timeout); + EXPECT_EQ(kUserId, res.args->userId); + EXPECT_EQ(FaceError::UNABLE_TO_PROCESS, res.args->error); +} + +} // anonymous namespace + +INSTANTIATE_TEST_SUITE_P( + PerInstance, FaceHidlTest, + testing::ValuesIn(android::hardware::getAllHalInstanceNames(IBiometricsFace::descriptor)), + android::hardware::PrintInstanceNameToString); diff --git a/biometrics/fingerprint/2.2/Android.bp b/biometrics/fingerprint/2.2/Android.bp new file mode 100644 index 0000000000..6c769ac22e --- /dev/null +++ b/biometrics/fingerprint/2.2/Android.bp @@ -0,0 +1,19 @@ +// This file is autogenerated by hidl-gen -Landroidbp. + +hidl_interface { + name: "android.hardware.biometrics.fingerprint@2.2", + root: "android.hardware", + vndk: { + enabled: true, + }, + srcs: [ + "types.hal", + "IBiometricsFingerprint.hal", + "IBiometricsFingerprintClientCallback.hal", + ], + interfaces: [ + "android.hardware.biometrics.fingerprint@2.1", + "android.hidl.base@1.0", + ], + gen_java: true, +} diff --git a/biometrics/fingerprint/2.2/IBiometricsFingerprint.hal b/biometrics/fingerprint/2.2/IBiometricsFingerprint.hal new file mode 100644 index 0000000000..06510344ef --- /dev/null +++ b/biometrics/fingerprint/2.2/IBiometricsFingerprint.hal @@ -0,0 +1,58 @@ +/* + * Copyright 2020 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.hardware.biometrics.fingerprint@2.2; + +import @2.1::IBiometricsFingerprint; +import @2.1::RequestStatus; + +interface IBiometricsFingerprint extends @2.1::IBiometricsFingerprint { + /** + * Fingerprint enroll request: + * Switches the HAL state machine to collect and store a new fingerprint + * template. Switches back as soon as enroll is complete, signalled by + * (fingerprintMsg.type == FINGERPRINT_TEMPLATE_ENROLLING && + * fingerprintMsg.data.enroll.samplesRemaining == 0) + * or after timeoutSec seconds. + * The fingerprint template must be assigned to the group gid. + * + * @param hat a valid Hardware Authentication Token (HAT), generated + * as a result of a preEnroll() call. + * @param gid a framework defined fingerprint set (group) id. + * @param timeoutSec a timeout in seconds. + * @param windowId optional ID of an illumination window for optical under + * display fingerprint sensors. Must contain a null pointer if not used. + * + * @return debugErrno is a value the framework logs in case it is not 0. + * + * A notify() function may be called with a more detailed error structure. + */ + enroll_2_2(vec<uint8_t> hat, uint32_t gid, uint32_t timeoutSec, handle windowId) + generates (RequestStatus debugErrno); + + /** + * Authenticates an operation identified by operationId + * + * @param operationId operation id. + * @param gid fingerprint group id. + * @param windowId optional ID of an illumination window for optical under + * display fingerprint sensors. Must contain a null pointer if not used. + * + * @return debugErrno is a value the framework logs in case it is not 0. + */ + authenticate_2_2(uint64_t operationId, uint32_t gid, handle windowId) + generates (RequestStatus debugErrno); +}; diff --git a/biometrics/fingerprint/2.2/IBiometricsFingerprintClientCallback.hal b/biometrics/fingerprint/2.2/IBiometricsFingerprintClientCallback.hal new file mode 100644 index 0000000000..14c2b12b76 --- /dev/null +++ b/biometrics/fingerprint/2.2/IBiometricsFingerprintClientCallback.hal @@ -0,0 +1,35 @@ +/* + * Copyright 2020 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.hardware.biometrics.fingerprint@2.2; + +import @2.1::IBiometricsFingerprintClientCallback; + +/* + * This HAL interface communicates asynchronous results from the + * fingerprint driver in response to user actions on the fingerprint sensor + */ +interface IBiometricsFingerprintClientCallback extends @2.1::IBiometricsFingerprintClientCallback { + /** + * Sent when a fingerprint image is acquired by the sensor + * @param deviceId the instance of this fingerprint device + * @param acquiredInfo a message about the quality of the acquired image + * @param vendorCode a vendor-specific message about the quality of the image. Only + * valid when acquiredInfo == ACQUIRED_VENDOR + */ + oneway onAcquired_2_2(uint64_t deviceId, FingerprintAcquiredInfo acquiredInfo, + int32_t vendorCode); +}; diff --git a/biometrics/fingerprint/2.2/types.hal b/biometrics/fingerprint/2.2/types.hal new file mode 100644 index 0000000000..2c1d3f374c --- /dev/null +++ b/biometrics/fingerprint/2.2/types.hal @@ -0,0 +1,42 @@ +/* + * Copyright 2020 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.hardware.biometrics.fingerprint@2.2; + +import @2.1::FingerprintAcquiredInfo; + +/** + * Fingerprint acquisition info is meant as feedback for the current operation. + * Anything but START and ACQUIRED_GOOD must be shown to the user as feedback on + * how to take action on the current operation. For example, + * ACQUIRED_IMAGER_DIRTY may be used to tell the user to clean the sensor if it + * is detected to be dirty. + * If this causes the current operation to fail, an additional ERROR_CANCELED + * must be sent to stop the operation in progress (e.g. enrollment). + * In general, these messages will result in a "Try again" message. + */ +enum FingerprintAcquiredInfo : @2.1::FingerprintAcquiredInfo { + /** + * This message represents the earliest message sent at the beginning of the + * authentication pipeline. It is expected to be used to measure latency. For + * example, in a camera-based authentication system it's expected to be sent + * prior to camera initialization. Note this should be sent whenever + * authentication is restarted (see IBiometricsFace#userActivity). + * The framework will measure latency based on the time between the last START + * message and the onAuthenticated callback. + */ + START = 7, +}; diff --git a/biometrics/fingerprint/2.2/vts/functional/Android.bp b/biometrics/fingerprint/2.2/vts/functional/Android.bp new file mode 100644 index 0000000000..496570c64c --- /dev/null +++ b/biometrics/fingerprint/2.2/vts/functional/Android.bp @@ -0,0 +1,29 @@ +/* + * Copyright 2020 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +cc_test { + name: "VtsHalBiometricsFingerprintV2_2TargetTest", + defaults: ["VtsHalTargetTestDefaults"], + srcs: ["VtsHalBiometricsFingerprintV2_2TargetTest.cpp"], + static_libs: [ + "android.hardware.biometrics.fingerprint@2.1", + "android.hardware.biometrics.fingerprint@2.2", + ], + test_suites: [ + "general-tests", + "vts-core", + ], +} diff --git a/biometrics/fingerprint/2.2/vts/functional/VtsHalBiometricsFingerprintV2_2TargetTest.cpp b/biometrics/fingerprint/2.2/vts/functional/VtsHalBiometricsFingerprintV2_2TargetTest.cpp new file mode 100644 index 0000000000..50bd4ab5e0 --- /dev/null +++ b/biometrics/fingerprint/2.2/vts/functional/VtsHalBiometricsFingerprintV2_2TargetTest.cpp @@ -0,0 +1,166 @@ +/* + * Copyright (C) 2020 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#define LOG_TAG "fingerprint_hidl_hal_test" + +#include <VtsHalHidlTargetCallbackBase.h> +#include <android-base/properties.h> +#include <android/hardware/biometrics/fingerprint/2.1/IBiometricsFingerprintClientCallback.h> +#include <android/hardware/biometrics/fingerprint/2.2/IBiometricsFingerprint.h> +#include <gtest/gtest.h> +#include <hidl/GtestPrinter.h> +#include <hidl/HidlSupport.h> +#include <hidl/ServiceManagement.h> + +#include <cinttypes> +#include <random> + +using android::sp; +using android::base::GetUintProperty; +using android::hardware::hidl_handle; +using android::hardware::hidl_vec; +using android::hardware::Return; +using android::hardware::Void; +using android::hardware::biometrics::fingerprint::V2_1::FingerprintAcquiredInfo; +using android::hardware::biometrics::fingerprint::V2_1::FingerprintError; +using android::hardware::biometrics::fingerprint::V2_1::IBiometricsFingerprintClientCallback; +using android::hardware::biometrics::fingerprint::V2_1::RequestStatus; +using android::hardware::biometrics::fingerprint::V2_2::IBiometricsFingerprint; + +namespace { + +constexpr uint32_t kTimeoutSec = 3; +constexpr auto kTimeout = std::chrono::seconds(kTimeoutSec); +constexpr uint32_t kGroupId = 99; +constexpr char kCallbackNameOnError[] = "onError"; + +// Callback arguments that need to be captured for the tests. +struct FingerprintCallbackArgs { + // The error passed to the last onError() callback. + FingerprintError error; + + // The deviceId passed to the last callback. + uint64_t deviceId; +}; + +// Test callback class for the BiometricsFingerprint HAL. +// The HAL will call these callback methods to notify about completed operations +// or encountered errors. +class FingerprintCallback : public ::testing::VtsHalHidlTargetCallbackBase<FingerprintCallbackArgs>, + public IBiometricsFingerprintClientCallback { + public: + Return<void> onEnrollResult(uint64_t, uint32_t, uint32_t, uint32_t) override { return Void(); } + + Return<void> onAcquired(uint64_t, FingerprintAcquiredInfo, int32_t) override { return Void(); } + + Return<void> onAuthenticated(uint64_t, uint32_t, uint32_t, const hidl_vec<uint8_t>&) override { + return Void(); + } + + Return<void> onError(uint64_t deviceId, FingerprintError error, int32_t) override { + FingerprintCallbackArgs args = {}; + args.error = error; + args.deviceId = deviceId; + NotifyFromCallback(kCallbackNameOnError, args); + return Void(); + } + + Return<void> onRemoved(uint64_t, uint32_t, uint32_t, uint32_t) override { return Void(); } + + Return<void> onEnumerate(uint64_t, uint32_t, uint32_t, uint32_t) override { return Void(); } +}; + +class FingerprintHidlTest : public ::testing::TestWithParam<std::string> { + public: + void SetUp() override { + mService = IBiometricsFingerprint::getService(GetParam()); + ASSERT_NE(mService, nullptr); + mCallback = new FingerprintCallback(); + mCallback->SetWaitTimeoutDefault(kTimeout); + Return<uint64_t> ret1 = mService->setNotify(mCallback); + ASSERT_NE(0UL, static_cast<uint64_t>(ret1)); + + /* + * Devices shipped from now on will instead store + * fingerprint data under /data/vendor_de/<user-id>/fpdata. + * Support for /data/vendor_de and /data/vendor_ce has been added to vold. + */ + + auto api_level = GetUintProperty<uint64_t>("ro.product.first_api_level", 0); + if (api_level == 0) { + api_level = GetUintProperty<uint64_t>("ro.build.version.sdk", 0); + } + ASSERT_NE(api_level, 0); + + // 27 is the API number for O-MR1 + string tmpDir; + if (api_level <= 27) { + tmpDir = "/data/system/users/0/fpdata/"; + } else { + tmpDir = "/data/vendor_de/0/fpdata/"; + } + + Return<RequestStatus> res = mService->setActiveGroup(kGroupId, tmpDir); + ASSERT_EQ(RequestStatus::SYS_OK, static_cast<RequestStatus>(res)); + } + + sp<IBiometricsFingerprint> mService; + sp<FingerprintCallback> mCallback; +}; + +// Enroll with an invalid (all zeroes) HAT should fail. +TEST_P(FingerprintHidlTest, EnrollZeroHatTest) { + // Filling HAT with zeros + hidl_vec<uint8_t> token(69); + for (size_t i = 0; i < 69; i++) { + token[i] = 0; + } + + hidl_handle windowId = nullptr; + Return<RequestStatus> ret = mService->enroll_2_2(token, kGroupId, kTimeoutSec, windowId); + ASSERT_EQ(RequestStatus::SYS_OK, static_cast<RequestStatus>(ret)); + + // At least one call to onError should occur + auto res = mCallback->WaitForCallback(kCallbackNameOnError); + ASSERT_NE(FingerprintError::ERROR_NO_ERROR, res.args->error); +} + +// Enroll with an invalid (null) HAT should fail. +TEST_P(FingerprintHidlTest, EnrollGarbageHatTest) { + // Filling HAT with pseudorandom invalid data. + // Using default seed to make the test reproducible. + std::mt19937 gen(std::mt19937::default_seed); + std::uniform_int_distribution<uint8_t> dist; + hidl_vec<uint8_t> token(69); + for (size_t i = 0; i < 69; ++i) { + token[i] = dist(gen); + } + + hidl_handle windowId = nullptr; + Return<RequestStatus> ret = mService->enroll_2_2(token, kGroupId, kTimeoutSec, windowId); + ASSERT_EQ(RequestStatus::SYS_OK, static_cast<RequestStatus>(ret)); + + // At least one call to onError should occur + auto res = mCallback->WaitForCallback(kCallbackNameOnError); + ASSERT_NE(FingerprintError::ERROR_NO_ERROR, res.args->error); +} + +} // anonymous namespace + +INSTANTIATE_TEST_SUITE_P(PerInstance, FingerprintHidlTest, + testing::ValuesIn(android::hardware::getAllHalInstanceNames( + IBiometricsFingerprint::descriptor)), + android::hardware::PrintInstanceNameToString); diff --git a/camera/device/3.4/default/ExternalCameraDeviceSession.cpp b/camera/device/3.4/default/ExternalCameraDeviceSession.cpp index 9ff0d74687..5f8674219c 100644 --- a/camera/device/3.4/default/ExternalCameraDeviceSession.cpp +++ b/camera/device/3.4/default/ExternalCameraDeviceSession.cpp @@ -81,8 +81,6 @@ bool tryLock(std::mutex& mutex) return locked; } -buffer_handle_t sEmptyBuffer = nullptr; - } // Anonymous namespace // Static instances @@ -119,8 +117,8 @@ bool ExternalCameraDeviceSession::initialize() { std::string make, model; if (ret < 0) { ALOGW("%s v4l2 QUERYCAP failed", __FUNCTION__); - make = "Generic UVC webcam"; - model = "Generic UVC webcam"; + mExifMake = "Generic UVC webcam"; + mExifModel = "Generic UVC webcam"; } else { // capability.card is UTF-8 encoded char card[32]; @@ -134,11 +132,11 @@ bool ExternalCameraDeviceSession::initialize() { } } if (j == 0 || card[j - 1] != '\0') { - make = "Generic UVC webcam"; - model = "Generic UVC webcam"; + mExifMake = "Generic UVC webcam"; + mExifModel = "Generic UVC webcam"; } else { - make = card; - model = card; + mExifMake = card; + mExifModel = card; } } @@ -147,7 +145,7 @@ bool ExternalCameraDeviceSession::initialize() { ALOGE("%s: init OutputThread failed!", __FUNCTION__); return true; } - mOutputThread->setExifMakeModel(make, model); + mOutputThread->setExifMakeModel(mExifMake, mExifModel); status_t status = initDefaultRequests(); if (status != OK) { @@ -161,7 +159,7 @@ bool ExternalCameraDeviceSession::initialize() { ALOGE("%s: invalid request fmq", __FUNCTION__); return true; } - mResultMetadataQueue = std::make_shared<RequestMetadataQueue>( + mResultMetadataQueue = std::make_shared<ResultMetadataQueue>( kMetadataMsgQueueSize, false /* non blocking */); if (!mResultMetadataQueue->isValid()) { ALOGE("%s: invalid result fmq", __FUNCTION__); @@ -183,7 +181,7 @@ bool ExternalCameraDeviceSession::isInitFailed() { } void ExternalCameraDeviceSession::initOutputThread() { - mOutputThread = new OutputThread(this, mCroppingType); + mOutputThread = new OutputThread(this, mCroppingType, mCameraCharacteristics); } void ExternalCameraDeviceSession::closeOutputThread() { @@ -518,35 +516,9 @@ Status ExternalCameraDeviceSession::importBufferLocked(int32_t streamId, uint64_t bufId, buffer_handle_t buf, /*out*/buffer_handle_t** outBufPtr, bool allowEmptyBuf) { - - if (buf == nullptr && bufId == BUFFER_ID_NO_BUFFER) { - if (allowEmptyBuf) { - *outBufPtr = &sEmptyBuffer; - return Status::OK; - } else { - ALOGE("%s: bufferId %" PRIu64 " has null buffer handle!", __FUNCTION__, bufId); - return Status::ILLEGAL_ARGUMENT; - } - } - - CirculatingBuffers& cbs = mCirculatingBuffers[streamId]; - if (cbs.count(bufId) == 0) { - if (buf == nullptr) { - ALOGE("%s: bufferId %" PRIu64 " has null buffer handle!", __FUNCTION__, bufId); - return Status::ILLEGAL_ARGUMENT; - } - // Register a newly seen buffer - buffer_handle_t importedBuf = buf; - sHandleImporter.importBuffer(importedBuf); - if (importedBuf == nullptr) { - ALOGE("%s: output buffer for stream %d is invalid!", __FUNCTION__, streamId); - return Status::INTERNAL_ERROR; - } else { - cbs[bufId] = importedBuf; - } - } - *outBufPtr = &cbs[bufId]; - return Status::OK; + return importBufferImpl( + mCirculatingBuffers, sHandleImporter, streamId, + bufId, buf, outBufPtr, allowEmptyBuf); } Status ExternalCameraDeviceSession::importRequestLockedImpl( @@ -791,15 +763,32 @@ void ExternalCameraDeviceSession::notifyError( //TODO: refactor with processCaptureResult Status ExternalCameraDeviceSession::processCaptureRequestError( - const std::shared_ptr<HalRequest>& req) { + const std::shared_ptr<HalRequest>& req, + /*out*/std::vector<NotifyMsg>* outMsgs, + /*out*/std::vector<CaptureResult>* outResults) { ATRACE_CALL(); // Return V4L2 buffer to V4L2 buffer queue - enqueueV4l2Frame(req->frameIn); + sp<V3_4::implementation::V4L2Frame> v4l2Frame = + static_cast<V3_4::implementation::V4L2Frame*>(req->frameIn.get()); + enqueueV4l2Frame(v4l2Frame); - // NotifyShutter - notifyShutter(req->frameNumber, req->shutterTs); + if (outMsgs == nullptr) { + notifyShutter(req->frameNumber, req->shutterTs); + notifyError(/*frameNum*/req->frameNumber, /*stream*/-1, ErrorCode::ERROR_REQUEST); + } else { + NotifyMsg shutter; + shutter.type = MsgType::SHUTTER; + shutter.msg.shutter.frameNumber = req->frameNumber; + shutter.msg.shutter.timestamp = req->shutterTs; - notifyError(/*frameNum*/req->frameNumber, /*stream*/-1, ErrorCode::ERROR_REQUEST); + NotifyMsg error; + error.type = MsgType::ERROR; + error.msg.error.frameNumber = req->frameNumber; + error.msg.error.errorStreamId = -1; + error.msg.error.errorCode = ErrorCode::ERROR_REQUEST; + outMsgs->push_back(shutter); + outMsgs->push_back(error); + } // Fill output buffers hidl_vec<CaptureResult> results; @@ -826,16 +815,22 @@ Status ExternalCameraDeviceSession::processCaptureRequestError( mInflightFrames.erase(req->frameNumber); } - // Callback into framework - invokeProcessCaptureResultCallback(results, /* tryWriteFmq */true); - freeReleaseFences(results); + if (outResults == nullptr) { + // Callback into framework + invokeProcessCaptureResultCallback(results, /* tryWriteFmq */true); + freeReleaseFences(results); + } else { + outResults->push_back(result); + } return Status::OK; } Status ExternalCameraDeviceSession::processCaptureResult(std::shared_ptr<HalRequest>& req) { ATRACE_CALL(); // Return V4L2 buffer to V4L2 buffer queue - enqueueV4l2Frame(req->frameIn); + sp<V3_4::implementation::V4L2Frame> v4l2Frame = + static_cast<V3_4::implementation::V4L2Frame*>(req->frameIn.get()); + enqueueV4l2Frame(v4l2Frame); // NotifyShutter notifyShutter(req->frameNumber, req->shutterTs); @@ -923,29 +918,10 @@ void ExternalCameraDeviceSession::invokeProcessCaptureResultCallback( mProcessCaptureResultLock.unlock(); } -void ExternalCameraDeviceSession::freeReleaseFences(hidl_vec<CaptureResult>& results) { - for (auto& result : results) { - if (result.inputBuffer.releaseFence.getNativeHandle() != nullptr) { - native_handle_t* handle = const_cast<native_handle_t*>( - result.inputBuffer.releaseFence.getNativeHandle()); - native_handle_close(handle); - native_handle_delete(handle); - } - for (auto& buf : result.outputBuffers) { - if (buf.releaseFence.getNativeHandle() != nullptr) { - native_handle_t* handle = const_cast<native_handle_t*>( - buf.releaseFence.getNativeHandle()); - native_handle_close(handle); - native_handle_delete(handle); - } - } - } - return; -} - ExternalCameraDeviceSession::OutputThread::OutputThread( - wp<ExternalCameraDeviceSession> parent, - CroppingType ct) : mParent(parent), mCroppingType(ct) {} + wp<OutputThreadInterface> parent, CroppingType ct, + const common::V1_0::helper::CameraMetadata& chars) : + mParent(parent), mCroppingType(ct), mCameraCharacteristics(chars) {} ExternalCameraDeviceSession::OutputThread::~OutputThread() {} @@ -955,88 +931,6 @@ void ExternalCameraDeviceSession::OutputThread::setExifMakeModel( mExifModel = model; } -uint32_t ExternalCameraDeviceSession::OutputThread::getFourCcFromLayout( - const YCbCrLayout& layout) { - intptr_t cb = reinterpret_cast<intptr_t>(layout.cb); - intptr_t cr = reinterpret_cast<intptr_t>(layout.cr); - if (std::abs(cb - cr) == 1 && layout.chromaStep == 2) { - // Interleaved format - if (layout.cb > layout.cr) { - return V4L2_PIX_FMT_NV21; - } else { - return V4L2_PIX_FMT_NV12; - } - } else if (layout.chromaStep == 1) { - // Planar format - if (layout.cb > layout.cr) { - return V4L2_PIX_FMT_YVU420; // YV12 - } else { - return V4L2_PIX_FMT_YUV420; // YU12 - } - } else { - return FLEX_YUV_GENERIC; - } -} - -int ExternalCameraDeviceSession::OutputThread::getCropRect( - CroppingType ct, const Size& inSize, const Size& outSize, IMapper::Rect* out) { - if (out == nullptr) { - ALOGE("%s: out is null", __FUNCTION__); - return -1; - } - - uint32_t inW = inSize.width; - uint32_t inH = inSize.height; - uint32_t outW = outSize.width; - uint32_t outH = outSize.height; - - // Handle special case where aspect ratio is close to input but scaled - // dimension is slightly larger than input - float arIn = ASPECT_RATIO(inSize); - float arOut = ASPECT_RATIO(outSize); - if (isAspectRatioClose(arIn, arOut)) { - out->left = 0; - out->top = 0; - out->width = inW; - out->height = inH; - return 0; - } - - if (ct == VERTICAL) { - uint64_t scaledOutH = static_cast<uint64_t>(outH) * inW / outW; - if (scaledOutH > inH) { - ALOGE("%s: Output size %dx%d cannot be vertically cropped from input size %dx%d", - __FUNCTION__, outW, outH, inW, inH); - return -1; - } - scaledOutH = scaledOutH & ~0x1; // make it multiple of 2 - - out->left = 0; - out->top = ((inH - scaledOutH) / 2) & ~0x1; - out->width = inW; - out->height = static_cast<int32_t>(scaledOutH); - ALOGV("%s: crop %dx%d to %dx%d: top %d, scaledH %d", - __FUNCTION__, inW, inH, outW, outH, out->top, static_cast<int32_t>(scaledOutH)); - } else { - uint64_t scaledOutW = static_cast<uint64_t>(outW) * inH / outH; - if (scaledOutW > inW) { - ALOGE("%s: Output size %dx%d cannot be horizontally cropped from input size %dx%d", - __FUNCTION__, outW, outH, inW, inH); - return -1; - } - scaledOutW = scaledOutW & ~0x1; // make it multiple of 2 - - out->left = ((inW - scaledOutW) / 2) & ~0x1; - out->top = 0; - out->width = static_cast<int32_t>(scaledOutW); - out->height = inH; - ALOGV("%s: crop %dx%d to %dx%d: top %d, scaledW %d", - __FUNCTION__, inW, inH, outW, outH, out->top, static_cast<int32_t>(scaledOutW)); - } - - return 0; -} - int ExternalCameraDeviceSession::OutputThread::cropAndScaleLocked( sp<AllocatedFrame>& in, const Size& outSz, YCbCrLayout* out) { Size inSz = {in->mWidth, in->mHeight}; @@ -1274,265 +1168,6 @@ int ExternalCameraDeviceSession::OutputThread::cropAndScaleThumbLocked( return 0; } -int ExternalCameraDeviceSession::OutputThread::formatConvertLocked( - const YCbCrLayout& in, const YCbCrLayout& out, Size sz, uint32_t format) { - int ret = 0; - switch (format) { - case V4L2_PIX_FMT_NV21: - ret = libyuv::I420ToNV21( - static_cast<uint8_t*>(in.y), - in.yStride, - static_cast<uint8_t*>(in.cb), - in.cStride, - static_cast<uint8_t*>(in.cr), - in.cStride, - static_cast<uint8_t*>(out.y), - out.yStride, - static_cast<uint8_t*>(out.cr), - out.cStride, - sz.width, - sz.height); - if (ret != 0) { - ALOGE("%s: convert to NV21 buffer failed! ret %d", - __FUNCTION__, ret); - return ret; - } - break; - case V4L2_PIX_FMT_NV12: - ret = libyuv::I420ToNV12( - static_cast<uint8_t*>(in.y), - in.yStride, - static_cast<uint8_t*>(in.cb), - in.cStride, - static_cast<uint8_t*>(in.cr), - in.cStride, - static_cast<uint8_t*>(out.y), - out.yStride, - static_cast<uint8_t*>(out.cb), - out.cStride, - sz.width, - sz.height); - if (ret != 0) { - ALOGE("%s: convert to NV12 buffer failed! ret %d", - __FUNCTION__, ret); - return ret; - } - break; - case V4L2_PIX_FMT_YVU420: // YV12 - case V4L2_PIX_FMT_YUV420: // YU12 - // TODO: maybe we can speed up here by somehow save this copy? - ret = libyuv::I420Copy( - static_cast<uint8_t*>(in.y), - in.yStride, - static_cast<uint8_t*>(in.cb), - in.cStride, - static_cast<uint8_t*>(in.cr), - in.cStride, - static_cast<uint8_t*>(out.y), - out.yStride, - static_cast<uint8_t*>(out.cb), - out.cStride, - static_cast<uint8_t*>(out.cr), - out.cStride, - sz.width, - sz.height); - if (ret != 0) { - ALOGE("%s: copy to YV12 or YU12 buffer failed! ret %d", - __FUNCTION__, ret); - return ret; - } - break; - case FLEX_YUV_GENERIC: - // TODO: b/72261744 write to arbitrary flexible YUV layout. Slow. - ALOGE("%s: unsupported flexible yuv layout" - " y %p cb %p cr %p y_str %d c_str %d c_step %d", - __FUNCTION__, out.y, out.cb, out.cr, - out.yStride, out.cStride, out.chromaStep); - return -1; - default: - ALOGE("%s: unknown YUV format 0x%x!", __FUNCTION__, format); - return -1; - } - return 0; -} - -int ExternalCameraDeviceSession::OutputThread::encodeJpegYU12( - const Size & inSz, const YCbCrLayout& inLayout, - int jpegQuality, const void *app1Buffer, size_t app1Size, - void *out, const size_t maxOutSize, size_t &actualCodeSize) -{ - /* libjpeg is a C library so we use C-style "inheritance" by - * putting libjpeg's jpeg_destination_mgr first in our custom - * struct. This allows us to cast jpeg_destination_mgr* to - * CustomJpegDestMgr* when we get it passed to us in a callback */ - struct CustomJpegDestMgr { - struct jpeg_destination_mgr mgr; - JOCTET *mBuffer; - size_t mBufferSize; - size_t mEncodedSize; - bool mSuccess; - } dmgr; - - jpeg_compress_struct cinfo = {}; - jpeg_error_mgr jerr; - - /* Initialize error handling with standard callbacks, but - * then override output_message (to print to ALOG) and - * error_exit to set a flag and print a message instead - * of killing the whole process */ - cinfo.err = jpeg_std_error(&jerr); - - cinfo.err->output_message = [](j_common_ptr cinfo) { - char buffer[JMSG_LENGTH_MAX]; - - /* Create the message */ - (*cinfo->err->format_message)(cinfo, buffer); - ALOGE("libjpeg error: %s", buffer); - }; - cinfo.err->error_exit = [](j_common_ptr cinfo) { - (*cinfo->err->output_message)(cinfo); - if(cinfo->client_data) { - auto & dmgr = - *reinterpret_cast<CustomJpegDestMgr*>(cinfo->client_data); - dmgr.mSuccess = false; - } - }; - /* Now that we initialized some callbacks, let's create our compressor */ - jpeg_create_compress(&cinfo); - - /* Initialize our destination manager */ - dmgr.mBuffer = static_cast<JOCTET*>(out); - dmgr.mBufferSize = maxOutSize; - dmgr.mEncodedSize = 0; - dmgr.mSuccess = true; - cinfo.client_data = static_cast<void*>(&dmgr); - - /* These lambdas become C-style function pointers and as per C++11 spec - * may not capture anything */ - dmgr.mgr.init_destination = [](j_compress_ptr cinfo) { - auto & dmgr = reinterpret_cast<CustomJpegDestMgr&>(*cinfo->dest); - dmgr.mgr.next_output_byte = dmgr.mBuffer; - dmgr.mgr.free_in_buffer = dmgr.mBufferSize; - ALOGV("%s:%d jpeg start: %p [%zu]", - __FUNCTION__, __LINE__, dmgr.mBuffer, dmgr.mBufferSize); - }; - - dmgr.mgr.empty_output_buffer = [](j_compress_ptr cinfo __unused) { - ALOGV("%s:%d Out of buffer", __FUNCTION__, __LINE__); - return 0; - }; - - dmgr.mgr.term_destination = [](j_compress_ptr cinfo) { - auto & dmgr = reinterpret_cast<CustomJpegDestMgr&>(*cinfo->dest); - dmgr.mEncodedSize = dmgr.mBufferSize - dmgr.mgr.free_in_buffer; - ALOGV("%s:%d Done with jpeg: %zu", __FUNCTION__, __LINE__, dmgr.mEncodedSize); - }; - cinfo.dest = reinterpret_cast<struct jpeg_destination_mgr*>(&dmgr); - - /* We are going to be using JPEG in raw data mode, so we are passing - * straight subsampled planar YCbCr and it will not touch our pixel - * data or do any scaling or anything */ - cinfo.image_width = inSz.width; - cinfo.image_height = inSz.height; - cinfo.input_components = 3; - cinfo.in_color_space = JCS_YCbCr; - - /* Initialize defaults and then override what we want */ - jpeg_set_defaults(&cinfo); - - jpeg_set_quality(&cinfo, jpegQuality, 1); - jpeg_set_colorspace(&cinfo, JCS_YCbCr); - cinfo.raw_data_in = 1; - cinfo.dct_method = JDCT_IFAST; - - /* Configure sampling factors. The sampling factor is JPEG subsampling 420 - * because the source format is YUV420. Note that libjpeg sampling factors - * are... a little weird. Sampling of Y=2,U=1,V=1 means there is 1 U and - * 1 V value for each 2 Y values */ - cinfo.comp_info[0].h_samp_factor = 2; - cinfo.comp_info[0].v_samp_factor = 2; - cinfo.comp_info[1].h_samp_factor = 1; - cinfo.comp_info[1].v_samp_factor = 1; - cinfo.comp_info[2].h_samp_factor = 1; - cinfo.comp_info[2].v_samp_factor = 1; - - /* Let's not hardcode YUV420 in 6 places... 5 was enough */ - int maxVSampFactor = std::max( { - cinfo.comp_info[0].v_samp_factor, - cinfo.comp_info[1].v_samp_factor, - cinfo.comp_info[2].v_samp_factor - }); - int cVSubSampling = cinfo.comp_info[0].v_samp_factor / - cinfo.comp_info[1].v_samp_factor; - - /* Start the compressor */ - jpeg_start_compress(&cinfo, TRUE); - - /* Compute our macroblock height, so we can pad our input to be vertically - * macroblock aligned. - * TODO: Does it need to be horizontally MCU aligned too? */ - - size_t mcuV = DCTSIZE*maxVSampFactor; - size_t paddedHeight = mcuV * ((inSz.height + mcuV - 1) / mcuV); - - /* libjpeg uses arrays of row pointers, which makes it really easy to pad - * data vertically (unfortunately doesn't help horizontally) */ - std::vector<JSAMPROW> yLines (paddedHeight); - std::vector<JSAMPROW> cbLines(paddedHeight/cVSubSampling); - std::vector<JSAMPROW> crLines(paddedHeight/cVSubSampling); - - uint8_t *py = static_cast<uint8_t*>(inLayout.y); - uint8_t *pcr = static_cast<uint8_t*>(inLayout.cr); - uint8_t *pcb = static_cast<uint8_t*>(inLayout.cb); - - for(uint32_t i = 0; i < paddedHeight; i++) - { - /* Once we are in the padding territory we still point to the last line - * effectively replicating it several times ~ CLAMP_TO_EDGE */ - int li = std::min(i, inSz.height - 1); - yLines[i] = static_cast<JSAMPROW>(py + li * inLayout.yStride); - if(i < paddedHeight / cVSubSampling) - { - crLines[i] = static_cast<JSAMPROW>(pcr + li * inLayout.cStride); - cbLines[i] = static_cast<JSAMPROW>(pcb + li * inLayout.cStride); - } - } - - /* If APP1 data was passed in, use it */ - if(app1Buffer && app1Size) - { - jpeg_write_marker(&cinfo, JPEG_APP0 + 1, - static_cast<const JOCTET*>(app1Buffer), app1Size); - } - - /* While we still have padded height left to go, keep giving it one - * macroblock at a time. */ - while (cinfo.next_scanline < cinfo.image_height) { - const uint32_t batchSize = DCTSIZE * maxVSampFactor; - const uint32_t nl = cinfo.next_scanline; - JSAMPARRAY planes[3]{ &yLines[nl], - &cbLines[nl/cVSubSampling], - &crLines[nl/cVSubSampling] }; - - uint32_t done = jpeg_write_raw_data(&cinfo, planes, batchSize); - - if (done != batchSize) { - ALOGE("%s: compressed %u lines, expected %u (total %u/%u)", - __FUNCTION__, done, batchSize, cinfo.next_scanline, - cinfo.image_height); - return -1; - } - } - - /* This will flush everything */ - jpeg_finish_compress(&cinfo); - - /* Grab the actual code size and set it */ - actualCodeSize = dmgr.mEncodedSize; - - return 0; -} - /* * TODO: There needs to be a mechanism to discover allocated buffer size * in the HAL. @@ -1555,25 +1190,9 @@ Size ExternalCameraDeviceSession::getMaxJpegResolution() const { } Size ExternalCameraDeviceSession::getMaxThumbResolution() const { - Size thumbSize { 0, 0 }; - camera_metadata_ro_entry entry = - mCameraCharacteristics.find(ANDROID_JPEG_AVAILABLE_THUMBNAIL_SIZES); - for(uint32_t i = 0; i < entry.count; i += 2) { - Size sz { static_cast<uint32_t>(entry.data.i32[i]), - static_cast<uint32_t>(entry.data.i32[i+1]) }; - if(sz.width * sz.height > thumbSize.width * thumbSize.height) { - thumbSize = sz; - } - } - - if (thumbSize.width * thumbSize.height == 0) { - ALOGW("%s: non-zero thumbnail size not available", __FUNCTION__); - } - - return thumbSize; + return getMaxThumbnailResolution(mCameraCharacteristics); } - ssize_t ExternalCameraDeviceSession::getJpegBufferSize( uint32_t width, uint32_t height) const { // Constant from camera3.h @@ -1616,7 +1235,7 @@ ssize_t ExternalCameraDeviceSession::getJpegBufferSize( int ExternalCameraDeviceSession::OutputThread::createJpegLocked( HalStreamBuffer &halBuf, - const std::shared_ptr<HalRequest>& req) + const common::V1_0::helper::CameraMetadata& setting) { ATRACE_CALL(); int ret; @@ -1645,17 +1264,17 @@ int ExternalCameraDeviceSession::OutputThread::createJpegLocked( Size thumbSize; bool outputThumbnail = true; - if (req->setting.exists(ANDROID_JPEG_QUALITY)) { - camera_metadata_entry entry = - req->setting.find(ANDROID_JPEG_QUALITY); + if (setting.exists(ANDROID_JPEG_QUALITY)) { + camera_metadata_ro_entry entry = + setting.find(ANDROID_JPEG_QUALITY); jpegQuality = entry.data.u8[0]; } else { return lfail("%s: ANDROID_JPEG_QUALITY not set",__FUNCTION__); } - if (req->setting.exists(ANDROID_JPEG_THUMBNAIL_QUALITY)) { - camera_metadata_entry entry = - req->setting.find(ANDROID_JPEG_THUMBNAIL_QUALITY); + if (setting.exists(ANDROID_JPEG_THUMBNAIL_QUALITY)) { + camera_metadata_ro_entry entry = + setting.find(ANDROID_JPEG_THUMBNAIL_QUALITY); thumbQuality = entry.data.u8[0]; } else { return lfail( @@ -1663,9 +1282,9 @@ int ExternalCameraDeviceSession::OutputThread::createJpegLocked( __FUNCTION__); } - if (req->setting.exists(ANDROID_JPEG_THUMBNAIL_SIZE)) { - camera_metadata_entry entry = - req->setting.find(ANDROID_JPEG_THUMBNAIL_SIZE); + if (setting.exists(ANDROID_JPEG_THUMBNAIL_SIZE)) { + camera_metadata_ro_entry entry = + setting.find(ANDROID_JPEG_THUMBNAIL_SIZE); thumbSize = Size { static_cast<uint32_t>(entry.data.i32[0]), static_cast<uint32_t>(entry.data.i32[1]) }; @@ -1732,8 +1351,8 @@ int ExternalCameraDeviceSession::OutputThread::createJpegLocked( /* Combine camera characteristics with request settings to form EXIF * metadata */ - common::V1_0::helper::CameraMetadata meta(parent->mCameraCharacteristics); - meta.append(req->setting); + common::V1_0::helper::CameraMetadata meta(mCameraCharacteristics); + meta.append(setting); /* Generate EXIF object */ std::unique_ptr<ExifUtils> utils(ExifUtils::create()); @@ -1838,7 +1457,7 @@ bool ExternalCameraDeviceSession::OutputThread::threadLoop() { // TODO: see if we can save some computation by converting to YV12 here uint8_t* inData; size_t inDataSize; - if (req->frameIn->map(&inData, &inDataSize) != 0) { + if (req->frameIn->getData(&inData, &inDataSize) != 0) { lk.unlock(); return onDeviceError("%s: V4L2 buffer map failed", __FUNCTION__); } @@ -1899,7 +1518,7 @@ bool ExternalCameraDeviceSession::OutputThread::threadLoop() { // Gralloc lockYCbCr the buffer switch (halBuf.format) { case PixelFormat::BLOB: { - int ret = createJpegLocked(halBuf, req); + int ret = createJpegLocked(halBuf, req->setting); if(ret != 0) { lk.unlock(); @@ -1949,8 +1568,8 @@ bool ExternalCameraDeviceSession::OutputThread::threadLoop() { } Size sz {halBuf.width, halBuf.height}; - ATRACE_BEGIN("formatConvertLocked"); - ret = formatConvertLocked(cropAndScaled, outLayout, sz, outputFourcc); + ATRACE_BEGIN("formatConvert"); + ret = formatConvert(cropAndScaled, outLayout, sz, outputFourcc); ATRACE_END(); if (ret != 0) { lk.unlock(); @@ -2055,6 +1674,14 @@ Status ExternalCameraDeviceSession::OutputThread::allocateIntermediateBuffers( return Status::OK; } +void ExternalCameraDeviceSession::OutputThread::clearIntermediateBuffers() { + std::lock_guard<std::mutex> lk(mBufferLock); + mYu12Frame.clear(); + mYu12ThumbFrame.clear(); + mIntermediateBuffers.clear(); + mBlobBufferSize = 0; +} + Status ExternalCameraDeviceSession::OutputThread::submitRequest( const std::shared_ptr<HalRequest>& req) { std::unique_lock<std::mutex> lk(mRequestListLock); @@ -2090,6 +1717,32 @@ void ExternalCameraDeviceSession::OutputThread::flush() { } } +std::list<std::shared_ptr<HalRequest>> +ExternalCameraDeviceSession::OutputThread::switchToOffline() { + ATRACE_CALL(); + std::list<std::shared_ptr<HalRequest>> emptyList; + auto parent = mParent.promote(); + if (parent == nullptr) { + ALOGE("%s: session has been disconnected!", __FUNCTION__); + return emptyList; + } + + std::unique_lock<std::mutex> lk(mRequestListLock); + std::list<std::shared_ptr<HalRequest>> reqs = std::move(mRequestList); + mRequestList.clear(); + if (mProcessingRequest) { + std::chrono::seconds timeout = std::chrono::seconds(kFlushWaitTimeoutSec); + auto st = mRequestDoneCond.wait_for(lk, timeout); + if (st == std::cv_status::timeout) { + ALOGE("%s: wait for inflight request finish timeout!", __FUNCTION__); + } + } + lk.unlock(); + clearIntermediateBuffers(); + ALOGV("%s: returning %zu request for offline processing", __FUNCTION__, reqs.size()); + return reqs; +} + void ExternalCameraDeviceSession::OutputThread::waitForNextRequest( std::shared_ptr<HalRequest>* out) { ATRACE_CALL(); @@ -2733,6 +2386,7 @@ Status ExternalCameraDeviceSession::configureStreams( return Status::INTERNAL_ERROR; } + mBlobBufferSize = blobBufferSize; status = mOutputThread->allocateIntermediateBuffers(v4lSize, mMaxThumbResolution, config.streams, blobBufferSize); if (status != Status::OK) { @@ -2916,16 +2570,6 @@ status_t ExternalCameraDeviceSession::initDefaultRequests() { status_t ExternalCameraDeviceSession::fillCaptureResult( common::V1_0::helper::CameraMetadata &md, nsecs_t timestamp) { - // android.control - // For USB camera, we don't know the AE state. Set the state to converged to - // indicate the frame should be good to use. Then apps don't have to wait the - // AE state. - const uint8_t aeState = ANDROID_CONTROL_AE_STATE_CONVERGED; - UPDATE(md, ANDROID_CONTROL_AE_STATE, &aeState, 1); - - const uint8_t ae_lock = ANDROID_CONTROL_AE_LOCK_OFF; - UPDATE(md, ANDROID_CONTROL_AE_LOCK, &ae_lock, 1); - bool afTrigger = false; { std::lock_guard<std::mutex> lk(mAfTriggerLock); @@ -2951,46 +2595,10 @@ status_t ExternalCameraDeviceSession::fillCaptureResult( } UPDATE(md, ANDROID_CONTROL_AF_STATE, &afState, 1); - // Set AWB state to converged to indicate the frame should be good to use. - const uint8_t awbState = ANDROID_CONTROL_AWB_STATE_CONVERGED; - UPDATE(md, ANDROID_CONTROL_AWB_STATE, &awbState, 1); - - const uint8_t awbLock = ANDROID_CONTROL_AWB_LOCK_OFF; - UPDATE(md, ANDROID_CONTROL_AWB_LOCK, &awbLock, 1); - - camera_metadata_ro_entry active_array_size = - mCameraCharacteristics.find(ANDROID_SENSOR_INFO_ACTIVE_ARRAY_SIZE); + camera_metadata_ro_entry activeArraySize = + mCameraCharacteristics.find(ANDROID_SENSOR_INFO_ACTIVE_ARRAY_SIZE); - if (active_array_size.count == 0) { - ALOGE("%s: cannot find active array size!", __FUNCTION__); - return -EINVAL; - } - - const uint8_t flashState = ANDROID_FLASH_STATE_UNAVAILABLE; - UPDATE(md, ANDROID_FLASH_STATE, &flashState, 1); - - // This means pipeline latency of X frame intervals. The maximum number is 4. - const uint8_t requestPipelineMaxDepth = 4; - UPDATE(md, ANDROID_REQUEST_PIPELINE_DEPTH, &requestPipelineMaxDepth, 1); - - // android.scaler - const int32_t crop_region[] = { - active_array_size.data.i32[0], active_array_size.data.i32[1], - active_array_size.data.i32[2], active_array_size.data.i32[3], - }; - UPDATE(md, ANDROID_SCALER_CROP_REGION, crop_region, ARRAY_SIZE(crop_region)); - - // android.sensor - UPDATE(md, ANDROID_SENSOR_TIMESTAMP, ×tamp, 1); - - // android.statistics - const uint8_t lensShadingMapMode = ANDROID_STATISTICS_LENS_SHADING_MAP_MODE_OFF; - UPDATE(md, ANDROID_STATISTICS_LENS_SHADING_MAP_MODE, &lensShadingMapMode, 1); - - const uint8_t sceneFlicker = ANDROID_STATISTICS_SCENE_FLICKER_NONE; - UPDATE(md, ANDROID_STATISTICS_SCENE_FLICKER, &sceneFlicker, 1); - - return OK; + return fillCaptureResultCommon(md, timestamp, activeArraySize); } #undef ARRAY_SIZE diff --git a/camera/device/3.4/default/ExternalCameraUtils.cpp b/camera/device/3.4/default/ExternalCameraUtils.cpp index e25deff797..4a6381ea7b 100644 --- a/camera/device/3.4/default/ExternalCameraUtils.cpp +++ b/camera/device/3.4/default/ExternalCameraUtils.cpp @@ -18,10 +18,23 @@ #include <log/log.h> #include <cmath> +#include <cstring> #include <sys/mman.h> #include <linux/videodev2.h> + +#define HAVE_JPEG // required for libyuv.h to export MJPEG decode APIs +#include <libyuv.h> + +#include <jpeglib.h> + #include "ExternalCameraUtils.h" +namespace { + +buffer_handle_t sEmptyBuffer = nullptr; + +} // Anonymous namespace + namespace android { namespace hardware { namespace camera { @@ -29,10 +42,13 @@ namespace device { namespace V3_4 { namespace implementation { +Frame::Frame(uint32_t width, uint32_t height, uint32_t fourcc) : + mWidth(width), mHeight(height), mFourcc(fourcc) {} + V4L2Frame::V4L2Frame( uint32_t w, uint32_t h, uint32_t fourcc, int bufIdx, int fd, uint32_t dataSize, uint64_t offset) : - mWidth(w), mHeight(h), mFourcc(fourcc), + Frame(w, h, fourcc), mBufferIndex(bufIdx), mFd(fd), mDataSize(dataSize), mOffset(offset) {} int V4L2Frame::map(uint8_t** data, size_t* dataSize) { @@ -75,9 +91,13 @@ V4L2Frame::~V4L2Frame() { unmap(); } +int V4L2Frame::getData(uint8_t** outData, size_t* dataSize) { + return map(outData, dataSize); +} + AllocatedFrame::AllocatedFrame( uint32_t w, uint32_t h) : - mWidth(w), mHeight(h), mFourcc(V4L2_PIX_FMT_YUV420) {}; + Frame(w, h, V4L2_PIX_FMT_YUV420) {}; AllocatedFrame::~AllocatedFrame() {} @@ -106,6 +126,17 @@ int AllocatedFrame::allocate(YCbCrLayout* out) { return 0; } +int AllocatedFrame::getData(uint8_t** outData, size_t* dataSize) { + YCbCrLayout layout; + int ret = allocate(&layout); + if (ret != 0) { + return ret; + } + *outData = mData.data(); + *dataSize = mData.size(); + return 0; +} + int AllocatedFrame::getLayout(YCbCrLayout* out) { IMapper::Rect noCrop = {0, 0, static_cast<int32_t>(mWidth), @@ -150,8 +181,520 @@ double SupportedV4L2Format::FrameRate::getDouble() const { return durationDenominator / static_cast<double>(durationNumerator); } +::android::hardware::camera::common::V1_0::Status importBufferImpl( + /*inout*/std::map<int, CirculatingBuffers>& circulatingBuffers, + /*inout*/HandleImporter& handleImporter, + int32_t streamId, + uint64_t bufId, buffer_handle_t buf, + /*out*/buffer_handle_t** outBufPtr, + bool allowEmptyBuf) { + using ::android::hardware::camera::common::V1_0::Status; + if (buf == nullptr && bufId == BUFFER_ID_NO_BUFFER) { + if (allowEmptyBuf) { + *outBufPtr = &sEmptyBuffer; + return Status::OK; + } else { + ALOGE("%s: bufferId %" PRIu64 " has null buffer handle!", __FUNCTION__, bufId); + return Status::ILLEGAL_ARGUMENT; + } + } + + CirculatingBuffers& cbs = circulatingBuffers[streamId]; + if (cbs.count(bufId) == 0) { + if (buf == nullptr) { + ALOGE("%s: bufferId %" PRIu64 " has null buffer handle!", __FUNCTION__, bufId); + return Status::ILLEGAL_ARGUMENT; + } + // Register a newly seen buffer + buffer_handle_t importedBuf = buf; + handleImporter.importBuffer(importedBuf); + if (importedBuf == nullptr) { + ALOGE("%s: output buffer for stream %d is invalid!", __FUNCTION__, streamId); + return Status::INTERNAL_ERROR; + } else { + cbs[bufId] = importedBuf; + } + } + *outBufPtr = &cbs[bufId]; + return Status::OK; +} + +uint32_t getFourCcFromLayout(const YCbCrLayout& layout) { + intptr_t cb = reinterpret_cast<intptr_t>(layout.cb); + intptr_t cr = reinterpret_cast<intptr_t>(layout.cr); + if (std::abs(cb - cr) == 1 && layout.chromaStep == 2) { + // Interleaved format + if (layout.cb > layout.cr) { + return V4L2_PIX_FMT_NV21; + } else { + return V4L2_PIX_FMT_NV12; + } + } else if (layout.chromaStep == 1) { + // Planar format + if (layout.cb > layout.cr) { + return V4L2_PIX_FMT_YVU420; // YV12 + } else { + return V4L2_PIX_FMT_YUV420; // YU12 + } + } else { + return FLEX_YUV_GENERIC; + } +} + +int getCropRect( + CroppingType ct, const Size& inSize, const Size& outSize, IMapper::Rect* out) { + if (out == nullptr) { + ALOGE("%s: out is null", __FUNCTION__); + return -1; + } + + uint32_t inW = inSize.width; + uint32_t inH = inSize.height; + uint32_t outW = outSize.width; + uint32_t outH = outSize.height; + + // Handle special case where aspect ratio is close to input but scaled + // dimension is slightly larger than input + float arIn = ASPECT_RATIO(inSize); + float arOut = ASPECT_RATIO(outSize); + if (isAspectRatioClose(arIn, arOut)) { + out->left = 0; + out->top = 0; + out->width = inW; + out->height = inH; + return 0; + } + + if (ct == VERTICAL) { + uint64_t scaledOutH = static_cast<uint64_t>(outH) * inW / outW; + if (scaledOutH > inH) { + ALOGE("%s: Output size %dx%d cannot be vertically cropped from input size %dx%d", + __FUNCTION__, outW, outH, inW, inH); + return -1; + } + scaledOutH = scaledOutH & ~0x1; // make it multiple of 2 + + out->left = 0; + out->top = ((inH - scaledOutH) / 2) & ~0x1; + out->width = inW; + out->height = static_cast<int32_t>(scaledOutH); + ALOGV("%s: crop %dx%d to %dx%d: top %d, scaledH %d", + __FUNCTION__, inW, inH, outW, outH, out->top, static_cast<int32_t>(scaledOutH)); + } else { + uint64_t scaledOutW = static_cast<uint64_t>(outW) * inH / outH; + if (scaledOutW > inW) { + ALOGE("%s: Output size %dx%d cannot be horizontally cropped from input size %dx%d", + __FUNCTION__, outW, outH, inW, inH); + return -1; + } + scaledOutW = scaledOutW & ~0x1; // make it multiple of 2 + + out->left = ((inW - scaledOutW) / 2) & ~0x1; + out->top = 0; + out->width = static_cast<int32_t>(scaledOutW); + out->height = inH; + ALOGV("%s: crop %dx%d to %dx%d: top %d, scaledW %d", + __FUNCTION__, inW, inH, outW, outH, out->top, static_cast<int32_t>(scaledOutW)); + } + + return 0; +} + +int formatConvert( + const YCbCrLayout& in, const YCbCrLayout& out, Size sz, uint32_t format) { + int ret = 0; + switch (format) { + case V4L2_PIX_FMT_NV21: + ret = libyuv::I420ToNV21( + static_cast<uint8_t*>(in.y), + in.yStride, + static_cast<uint8_t*>(in.cb), + in.cStride, + static_cast<uint8_t*>(in.cr), + in.cStride, + static_cast<uint8_t*>(out.y), + out.yStride, + static_cast<uint8_t*>(out.cr), + out.cStride, + sz.width, + sz.height); + if (ret != 0) { + ALOGE("%s: convert to NV21 buffer failed! ret %d", + __FUNCTION__, ret); + return ret; + } + break; + case V4L2_PIX_FMT_NV12: + ret = libyuv::I420ToNV12( + static_cast<uint8_t*>(in.y), + in.yStride, + static_cast<uint8_t*>(in.cb), + in.cStride, + static_cast<uint8_t*>(in.cr), + in.cStride, + static_cast<uint8_t*>(out.y), + out.yStride, + static_cast<uint8_t*>(out.cb), + out.cStride, + sz.width, + sz.height); + if (ret != 0) { + ALOGE("%s: convert to NV12 buffer failed! ret %d", + __FUNCTION__, ret); + return ret; + } + break; + case V4L2_PIX_FMT_YVU420: // YV12 + case V4L2_PIX_FMT_YUV420: // YU12 + // TODO: maybe we can speed up here by somehow save this copy? + ret = libyuv::I420Copy( + static_cast<uint8_t*>(in.y), + in.yStride, + static_cast<uint8_t*>(in.cb), + in.cStride, + static_cast<uint8_t*>(in.cr), + in.cStride, + static_cast<uint8_t*>(out.y), + out.yStride, + static_cast<uint8_t*>(out.cb), + out.cStride, + static_cast<uint8_t*>(out.cr), + out.cStride, + sz.width, + sz.height); + if (ret != 0) { + ALOGE("%s: copy to YV12 or YU12 buffer failed! ret %d", + __FUNCTION__, ret); + return ret; + } + break; + case FLEX_YUV_GENERIC: + // TODO: b/72261744 write to arbitrary flexible YUV layout. Slow. + ALOGE("%s: unsupported flexible yuv layout" + " y %p cb %p cr %p y_str %d c_str %d c_step %d", + __FUNCTION__, out.y, out.cb, out.cr, + out.yStride, out.cStride, out.chromaStep); + return -1; + default: + ALOGE("%s: unknown YUV format 0x%x!", __FUNCTION__, format); + return -1; + } + return 0; +} + +int encodeJpegYU12( + const Size & inSz, const YCbCrLayout& inLayout, + int jpegQuality, const void *app1Buffer, size_t app1Size, + void *out, const size_t maxOutSize, size_t &actualCodeSize) +{ + /* libjpeg is a C library so we use C-style "inheritance" by + * putting libjpeg's jpeg_destination_mgr first in our custom + * struct. This allows us to cast jpeg_destination_mgr* to + * CustomJpegDestMgr* when we get it passed to us in a callback */ + struct CustomJpegDestMgr { + struct jpeg_destination_mgr mgr; + JOCTET *mBuffer; + size_t mBufferSize; + size_t mEncodedSize; + bool mSuccess; + } dmgr; + + jpeg_compress_struct cinfo = {}; + jpeg_error_mgr jerr; + + /* Initialize error handling with standard callbacks, but + * then override output_message (to print to ALOG) and + * error_exit to set a flag and print a message instead + * of killing the whole process */ + cinfo.err = jpeg_std_error(&jerr); + + cinfo.err->output_message = [](j_common_ptr cinfo) { + char buffer[JMSG_LENGTH_MAX]; + + /* Create the message */ + (*cinfo->err->format_message)(cinfo, buffer); + ALOGE("libjpeg error: %s", buffer); + }; + cinfo.err->error_exit = [](j_common_ptr cinfo) { + (*cinfo->err->output_message)(cinfo); + if(cinfo->client_data) { + auto & dmgr = + *reinterpret_cast<CustomJpegDestMgr*>(cinfo->client_data); + dmgr.mSuccess = false; + } + }; + /* Now that we initialized some callbacks, let's create our compressor */ + jpeg_create_compress(&cinfo); + + /* Initialize our destination manager */ + dmgr.mBuffer = static_cast<JOCTET*>(out); + dmgr.mBufferSize = maxOutSize; + dmgr.mEncodedSize = 0; + dmgr.mSuccess = true; + cinfo.client_data = static_cast<void*>(&dmgr); + + /* These lambdas become C-style function pointers and as per C++11 spec + * may not capture anything */ + dmgr.mgr.init_destination = [](j_compress_ptr cinfo) { + auto & dmgr = reinterpret_cast<CustomJpegDestMgr&>(*cinfo->dest); + dmgr.mgr.next_output_byte = dmgr.mBuffer; + dmgr.mgr.free_in_buffer = dmgr.mBufferSize; + ALOGV("%s:%d jpeg start: %p [%zu]", + __FUNCTION__, __LINE__, dmgr.mBuffer, dmgr.mBufferSize); + }; + + dmgr.mgr.empty_output_buffer = [](j_compress_ptr cinfo __unused) { + ALOGV("%s:%d Out of buffer", __FUNCTION__, __LINE__); + return 0; + }; + + dmgr.mgr.term_destination = [](j_compress_ptr cinfo) { + auto & dmgr = reinterpret_cast<CustomJpegDestMgr&>(*cinfo->dest); + dmgr.mEncodedSize = dmgr.mBufferSize - dmgr.mgr.free_in_buffer; + ALOGV("%s:%d Done with jpeg: %zu", __FUNCTION__, __LINE__, dmgr.mEncodedSize); + }; + cinfo.dest = reinterpret_cast<struct jpeg_destination_mgr*>(&dmgr); + + /* We are going to be using JPEG in raw data mode, so we are passing + * straight subsampled planar YCbCr and it will not touch our pixel + * data or do any scaling or anything */ + cinfo.image_width = inSz.width; + cinfo.image_height = inSz.height; + cinfo.input_components = 3; + cinfo.in_color_space = JCS_YCbCr; + + /* Initialize defaults and then override what we want */ + jpeg_set_defaults(&cinfo); + + jpeg_set_quality(&cinfo, jpegQuality, 1); + jpeg_set_colorspace(&cinfo, JCS_YCbCr); + cinfo.raw_data_in = 1; + cinfo.dct_method = JDCT_IFAST; + + /* Configure sampling factors. The sampling factor is JPEG subsampling 420 + * because the source format is YUV420. Note that libjpeg sampling factors + * are... a little weird. Sampling of Y=2,U=1,V=1 means there is 1 U and + * 1 V value for each 2 Y values */ + cinfo.comp_info[0].h_samp_factor = 2; + cinfo.comp_info[0].v_samp_factor = 2; + cinfo.comp_info[1].h_samp_factor = 1; + cinfo.comp_info[1].v_samp_factor = 1; + cinfo.comp_info[2].h_samp_factor = 1; + cinfo.comp_info[2].v_samp_factor = 1; + + /* Let's not hardcode YUV420 in 6 places... 5 was enough */ + int maxVSampFactor = std::max( { + cinfo.comp_info[0].v_samp_factor, + cinfo.comp_info[1].v_samp_factor, + cinfo.comp_info[2].v_samp_factor + }); + int cVSubSampling = cinfo.comp_info[0].v_samp_factor / + cinfo.comp_info[1].v_samp_factor; + + /* Start the compressor */ + jpeg_start_compress(&cinfo, TRUE); + + /* Compute our macroblock height, so we can pad our input to be vertically + * macroblock aligned. + * TODO: Does it need to be horizontally MCU aligned too? */ + + size_t mcuV = DCTSIZE*maxVSampFactor; + size_t paddedHeight = mcuV * ((inSz.height + mcuV - 1) / mcuV); + + /* libjpeg uses arrays of row pointers, which makes it really easy to pad + * data vertically (unfortunately doesn't help horizontally) */ + std::vector<JSAMPROW> yLines (paddedHeight); + std::vector<JSAMPROW> cbLines(paddedHeight/cVSubSampling); + std::vector<JSAMPROW> crLines(paddedHeight/cVSubSampling); + + uint8_t *py = static_cast<uint8_t*>(inLayout.y); + uint8_t *pcr = static_cast<uint8_t*>(inLayout.cr); + uint8_t *pcb = static_cast<uint8_t*>(inLayout.cb); + + for(uint32_t i = 0; i < paddedHeight; i++) + { + /* Once we are in the padding territory we still point to the last line + * effectively replicating it several times ~ CLAMP_TO_EDGE */ + int li = std::min(i, inSz.height - 1); + yLines[i] = static_cast<JSAMPROW>(py + li * inLayout.yStride); + if(i < paddedHeight / cVSubSampling) + { + crLines[i] = static_cast<JSAMPROW>(pcr + li * inLayout.cStride); + cbLines[i] = static_cast<JSAMPROW>(pcb + li * inLayout.cStride); + } + } + + /* If APP1 data was passed in, use it */ + if(app1Buffer && app1Size) + { + jpeg_write_marker(&cinfo, JPEG_APP0 + 1, + static_cast<const JOCTET*>(app1Buffer), app1Size); + } + + /* While we still have padded height left to go, keep giving it one + * macroblock at a time. */ + while (cinfo.next_scanline < cinfo.image_height) { + const uint32_t batchSize = DCTSIZE * maxVSampFactor; + const uint32_t nl = cinfo.next_scanline; + JSAMPARRAY planes[3]{ &yLines[nl], + &cbLines[nl/cVSubSampling], + &crLines[nl/cVSubSampling] }; + + uint32_t done = jpeg_write_raw_data(&cinfo, planes, batchSize); + + if (done != batchSize) { + ALOGE("%s: compressed %u lines, expected %u (total %u/%u)", + __FUNCTION__, done, batchSize, cinfo.next_scanline, + cinfo.image_height); + return -1; + } + } + + /* This will flush everything */ + jpeg_finish_compress(&cinfo); + + /* Grab the actual code size and set it */ + actualCodeSize = dmgr.mEncodedSize; + + return 0; +} + +Size getMaxThumbnailResolution(const common::V1_0::helper::CameraMetadata& chars) { + Size thumbSize { 0, 0 }; + camera_metadata_ro_entry entry = + chars.find(ANDROID_JPEG_AVAILABLE_THUMBNAIL_SIZES); + for(uint32_t i = 0; i < entry.count; i += 2) { + Size sz { static_cast<uint32_t>(entry.data.i32[i]), + static_cast<uint32_t>(entry.data.i32[i+1]) }; + if(sz.width * sz.height > thumbSize.width * thumbSize.height) { + thumbSize = sz; + } + } + + if (thumbSize.width * thumbSize.height == 0) { + ALOGW("%s: non-zero thumbnail size not available", __FUNCTION__); + } + + return thumbSize; +} + +void freeReleaseFences(hidl_vec<V3_2::CaptureResult>& results) { + for (auto& result : results) { + if (result.inputBuffer.releaseFence.getNativeHandle() != nullptr) { + native_handle_t* handle = const_cast<native_handle_t*>( + result.inputBuffer.releaseFence.getNativeHandle()); + native_handle_close(handle); + native_handle_delete(handle); + } + for (auto& buf : result.outputBuffers) { + if (buf.releaseFence.getNativeHandle() != nullptr) { + native_handle_t* handle = const_cast<native_handle_t*>( + buf.releaseFence.getNativeHandle()); + native_handle_close(handle); + native_handle_delete(handle); + } + } + } + return; +} + +#define ARRAY_SIZE(a) (sizeof(a) / sizeof(a[0])) +#define UPDATE(md, tag, data, size) \ +do { \ + if ((md).update((tag), (data), (size))) { \ + ALOGE("Update " #tag " failed!"); \ + return BAD_VALUE; \ + } \ +} while (0) + +status_t fillCaptureResultCommon( + common::V1_0::helper::CameraMetadata &md, nsecs_t timestamp, + camera_metadata_ro_entry& activeArraySize) { + if (activeArraySize.count < 4) { + ALOGE("%s: cannot find active array size!", __FUNCTION__); + return -EINVAL; + } + // android.control + // For USB camera, we don't know the AE state. Set the state to converged to + // indicate the frame should be good to use. Then apps don't have to wait the + // AE state. + const uint8_t aeState = ANDROID_CONTROL_AE_STATE_CONVERGED; + UPDATE(md, ANDROID_CONTROL_AE_STATE, &aeState, 1); + + const uint8_t ae_lock = ANDROID_CONTROL_AE_LOCK_OFF; + UPDATE(md, ANDROID_CONTROL_AE_LOCK, &ae_lock, 1); + + // Set AWB state to converged to indicate the frame should be good to use. + const uint8_t awbState = ANDROID_CONTROL_AWB_STATE_CONVERGED; + UPDATE(md, ANDROID_CONTROL_AWB_STATE, &awbState, 1); + + const uint8_t awbLock = ANDROID_CONTROL_AWB_LOCK_OFF; + UPDATE(md, ANDROID_CONTROL_AWB_LOCK, &awbLock, 1); + + const uint8_t flashState = ANDROID_FLASH_STATE_UNAVAILABLE; + UPDATE(md, ANDROID_FLASH_STATE, &flashState, 1); + + // This means pipeline latency of X frame intervals. The maximum number is 4. + const uint8_t requestPipelineMaxDepth = 4; + UPDATE(md, ANDROID_REQUEST_PIPELINE_DEPTH, &requestPipelineMaxDepth, 1); + + // android.scaler + const int32_t crop_region[] = { + activeArraySize.data.i32[0], activeArraySize.data.i32[1], + activeArraySize.data.i32[2], activeArraySize.data.i32[3], + }; + UPDATE(md, ANDROID_SCALER_CROP_REGION, crop_region, ARRAY_SIZE(crop_region)); + + // android.sensor + UPDATE(md, ANDROID_SENSOR_TIMESTAMP, ×tamp, 1); + + // android.statistics + const uint8_t lensShadingMapMode = ANDROID_STATISTICS_LENS_SHADING_MAP_MODE_OFF; + UPDATE(md, ANDROID_STATISTICS_LENS_SHADING_MAP_MODE, &lensShadingMapMode, 1); + + const uint8_t sceneFlicker = ANDROID_STATISTICS_SCENE_FLICKER_NONE; + UPDATE(md, ANDROID_STATISTICS_SCENE_FLICKER, &sceneFlicker, 1); + + return OK; +} + +#undef ARRAY_SIZE +#undef UPDATE + } // namespace implementation } // namespace V3_4 + +namespace V3_6 { +namespace implementation { + +AllocatedV4L2Frame::AllocatedV4L2Frame(sp<V3_4::implementation::V4L2Frame> frameIn) : + Frame(frameIn->mWidth, frameIn->mHeight, frameIn->mFourcc) { + uint8_t* dataIn; + size_t dataSize; + if (frameIn->getData(&dataIn, &dataSize) != 0) { + ALOGE("%s: map input V4L2 frame failed!", __FUNCTION__); + return; + } + + mData.resize(dataSize); + std::memcpy(mData.data(), dataIn, dataSize); +} + +int AllocatedV4L2Frame::getData(uint8_t** outData, size_t* dataSize) { + if (outData == nullptr || dataSize == nullptr) { + ALOGE("%s: outData(%p)/dataSize(%p) must not be null", __FUNCTION__, outData, dataSize); + return -1; + } + + *outData = mData.data(); + *dataSize = mData.size(); + return 0; +} + +AllocatedV4L2Frame::~AllocatedV4L2Frame() {} + +} // namespace implementation +} // namespace V3_6 } // namespace device diff --git a/camera/device/3.4/default/include/ext_device_v3_4_impl/ExternalCameraDeviceSession.h b/camera/device/3.4/default/include/ext_device_v3_4_impl/ExternalCameraDeviceSession.h index 71b7c17dd6..ecab9cfa03 100644 --- a/camera/device/3.4/default/include/ext_device_v3_4_impl/ExternalCameraDeviceSession.h +++ b/camera/device/3.4/default/include/ext_device_v3_4_impl/ExternalCameraDeviceSession.h @@ -14,8 +14,8 @@ * limitations under the License. */ -#ifndef ANDROID_HARDWARE_CAMERA_DEVICE_V3_4_EXTCAMERADEVICE3SESSION_H -#define ANDROID_HARDWARE_CAMERA_DEVICE_V3_4_EXTCAMERADEVICE3SESSION_H +#ifndef ANDROID_HARDWARE_CAMERA_DEVICE_V3_4_EXTCAMERADEVICESESSION_H +#define ANDROID_HARDWARE_CAMERA_DEVICE_V3_4_EXTCAMERADEVICESESSION_H #include <android/hardware/camera/device/3.2/ICameraDevice.h> #include <android/hardware/camera/device/3.4/ICameraDeviceSession.h> @@ -84,7 +84,8 @@ using ::android::sp; using ::android::Mutex; using ::android::base::unique_fd; -struct ExternalCameraDeviceSession : public virtual RefBase { +struct ExternalCameraDeviceSession : public virtual RefBase, + public virtual OutputThreadInterface { ExternalCameraDeviceSession(const sp<ICameraDeviceCallback>&, const ExternalCameraConfig& cfg, @@ -110,6 +111,82 @@ struct ExternalCameraDeviceSession : public virtual RefBase { static const int kMaxStallStream = 1; static const uint32_t kMaxBytesPerPixel = 2; + class OutputThread : public android::Thread { + public: + OutputThread(wp<OutputThreadInterface> parent, CroppingType, + const common::V1_0::helper::CameraMetadata&); + virtual ~OutputThread(); + + Status allocateIntermediateBuffers( + const Size& v4lSize, const Size& thumbSize, + const hidl_vec<Stream>& streams, + uint32_t blobBufferSize); + Status submitRequest(const std::shared_ptr<HalRequest>&); + void flush(); + void dump(int fd); + virtual bool threadLoop() override; + + void setExifMakeModel(const std::string& make, const std::string& model); + + // The remaining request list is returned for offline processing + std::list<std::shared_ptr<HalRequest>> switchToOffline(); + + protected: + // Methods to request output buffer in parallel + // No-op for device@3.4. Implemented in device@3.5 + virtual int requestBufferStart(const std::vector<HalStreamBuffer>&) { return 0; } + virtual int waitForBufferRequestDone( + /*out*/std::vector<HalStreamBuffer>*) { return 0; } + + static const int kFlushWaitTimeoutSec = 3; // 3 sec + static const int kReqWaitTimeoutMs = 33; // 33ms + static const int kReqWaitTimesMax = 90; // 33ms * 90 ~= 3 sec + + void waitForNextRequest(std::shared_ptr<HalRequest>* out); + void signalRequestDone(); + + int cropAndScaleLocked( + sp<AllocatedFrame>& in, const Size& outSize, + YCbCrLayout* out); + + int cropAndScaleThumbLocked( + sp<AllocatedFrame>& in, const Size& outSize, + YCbCrLayout* out); + + int createJpegLocked(HalStreamBuffer &halBuf, + const common::V1_0::helper::CameraMetadata& settings); + + void clearIntermediateBuffers(); + + const wp<OutputThreadInterface> mParent; + const CroppingType mCroppingType; + const common::V1_0::helper::CameraMetadata mCameraCharacteristics; + + mutable std::mutex mRequestListLock; // Protect acccess to mRequestList, + // mProcessingRequest and mProcessingFrameNumer + std::condition_variable mRequestCond; // signaled when a new request is submitted + std::condition_variable mRequestDoneCond; // signaled when a request is done processing + std::list<std::shared_ptr<HalRequest>> mRequestList; + bool mProcessingRequest = false; + uint32_t mProcessingFrameNumer = 0; + + // V4L2 frameIn + // (MJPG decode)-> mYu12Frame + // (Scale)-> mScaledYu12Frames + // (Format convert) -> output gralloc frames + mutable std::mutex mBufferLock; // Protect access to intermediate buffers + sp<AllocatedFrame> mYu12Frame; + sp<AllocatedFrame> mYu12ThumbFrame; + std::unordered_map<Size, sp<AllocatedFrame>, SizeHasher> mIntermediateBuffers; + std::unordered_map<Size, sp<AllocatedFrame>, SizeHasher> mScaledYu12Frames; + YCbCrLayout mYu12FrameLayout; + YCbCrLayout mYu12ThumbFrameLayout; + uint32_t mBlobBufferSize = 0; // 0 -> HAL derive buffer size, else: use given size + + std::string mExifMake; + std::string mExifModel; + }; + protected: // Methods from ::android::hardware::camera::device::V3_2::ICameraDeviceSession follow @@ -150,27 +227,22 @@ protected: ICameraDeviceSession::processCaptureRequest_3_4_cb _hidl_cb); protected: - struct HalStreamBuffer { - int32_t streamId; - uint64_t bufferId; - uint32_t width; - uint32_t height; - PixelFormat format; - V3_2::BufferUsageFlags usage; - buffer_handle_t* bufPtr; - int acquireFence; - bool fenceTimeout; - }; + // Methods from OutputThreadInterface + virtual Status importBuffer(int32_t streamId, + uint64_t bufId, buffer_handle_t buf, + /*out*/buffer_handle_t** outBufPtr, + bool allowEmptyBuf) override; - struct HalRequest { - uint32_t frameNumber; - common::V1_0::helper::CameraMetadata setting; - sp<V4L2Frame> frameIn; - nsecs_t shutterTs; - std::vector<HalStreamBuffer> buffers; - }; + virtual Status processCaptureResult(std::shared_ptr<HalRequest>&) override; + + virtual Status processCaptureRequestError(const std::shared_ptr<HalRequest>&, + /*out*/std::vector<NotifyMsg>* msgs = nullptr, + /*out*/std::vector<CaptureResult>* results = nullptr) override; - static const uint64_t BUFFER_ID_NO_BUFFER = 0; + virtual ssize_t getJpegBufferSize(uint32_t width, uint32_t height) const override; + + virtual void notifyError(uint32_t frameNumber, int32_t streamId, ErrorCode ec) override; + // End of OutputThreadInterface methods Status constructDefaultRequestSettingsRaw(RequestTemplate type, V3_2::CameraMetadata *outMetadata); @@ -219,11 +291,6 @@ protected: // Optional argument for ICameraDeviceSession@3.5 impl bool allowEmptyBuf = false); - Status importBuffer(int32_t streamId, - uint64_t bufId, buffer_handle_t buf, - /*out*/buffer_handle_t** outBufPtr, - bool allowEmptyBuf); - Status importBufferLocked(int32_t streamId, uint64_t bufId, buffer_handle_t buf, /*out*/buffer_handle_t** outBufPtr, @@ -236,106 +303,15 @@ protected: Status processOneCaptureRequest(const CaptureRequest& request); - Status processCaptureResult(std::shared_ptr<HalRequest>&); - Status processCaptureRequestError(const std::shared_ptr<HalRequest>&); void notifyShutter(uint32_t frameNumber, nsecs_t shutterTs); - void notifyError(uint32_t frameNumber, int32_t streamId, ErrorCode ec); void invokeProcessCaptureResultCallback( hidl_vec<CaptureResult> &results, bool tryWriteFmq); - static void freeReleaseFences(hidl_vec<CaptureResult>&); Size getMaxJpegResolution() const; Size getMaxThumbResolution() const; - ssize_t getJpegBufferSize(uint32_t width, uint32_t height) const; - int waitForV4L2BufferReturnLocked(std::unique_lock<std::mutex>& lk); - class OutputThread : public android::Thread { - public: - OutputThread(wp<ExternalCameraDeviceSession> parent, CroppingType); - virtual ~OutputThread(); - - Status allocateIntermediateBuffers( - const Size& v4lSize, const Size& thumbSize, - const hidl_vec<Stream>& streams, - uint32_t blobBufferSize); - Status submitRequest(const std::shared_ptr<HalRequest>&); - void flush(); - void dump(int fd); - virtual bool threadLoop() override; - - void setExifMakeModel(const std::string& make, const std::string& model); - - protected: - // Methods to request output buffer in parallel - // No-op for device@3.4. Implemented in device@3.5 - virtual int requestBufferStart(const std::vector<HalStreamBuffer>&) { return 0; } - virtual int waitForBufferRequestDone( - /*out*/std::vector<HalStreamBuffer>*) { return 0; } - - static const uint32_t FLEX_YUV_GENERIC = static_cast<uint32_t>('F') | - static_cast<uint32_t>('L') << 8 | static_cast<uint32_t>('E') << 16 | - static_cast<uint32_t>('X') << 24; - // returns FLEX_YUV_GENERIC for formats other than YV12/YU12/NV12/NV21 - static uint32_t getFourCcFromLayout(const YCbCrLayout&); - static int getCropRect( - CroppingType ct, const Size& inSize, const Size& outSize, IMapper::Rect* out); - - static const int kFlushWaitTimeoutSec = 3; // 3 sec - static const int kReqWaitTimeoutMs = 33; // 33ms - static const int kReqWaitTimesMax = 90; // 33ms * 90 ~= 3 sec - - void waitForNextRequest(std::shared_ptr<HalRequest>* out); - void signalRequestDone(); - - int cropAndScaleLocked( - sp<AllocatedFrame>& in, const Size& outSize, - YCbCrLayout* out); - - int cropAndScaleThumbLocked( - sp<AllocatedFrame>& in, const Size& outSize, - YCbCrLayout* out); - - int formatConvertLocked(const YCbCrLayout& in, const YCbCrLayout& out, - Size sz, uint32_t format); - - static int encodeJpegYU12(const Size &inSz, - const YCbCrLayout& inLayout, int jpegQuality, - const void *app1Buffer, size_t app1Size, - void *out, size_t maxOutSize, - size_t &actualCodeSize); - - int createJpegLocked(HalStreamBuffer &halBuf, const std::shared_ptr<HalRequest>& req); - - const wp<ExternalCameraDeviceSession> mParent; - const CroppingType mCroppingType; - - mutable std::mutex mRequestListLock; // Protect acccess to mRequestList, - // mProcessingRequest and mProcessingFrameNumer - std::condition_variable mRequestCond; // signaled when a new request is submitted - std::condition_variable mRequestDoneCond; // signaled when a request is done processing - std::list<std::shared_ptr<HalRequest>> mRequestList; - bool mProcessingRequest = false; - uint32_t mProcessingFrameNumer = 0; - - // V4L2 frameIn - // (MJPG decode)-> mYu12Frame - // (Scale)-> mScaledYu12Frames - // (Format convert) -> output gralloc frames - mutable std::mutex mBufferLock; // Protect access to intermediate buffers - sp<AllocatedFrame> mYu12Frame; - sp<AllocatedFrame> mYu12ThumbFrame; - std::unordered_map<Size, sp<AllocatedFrame>, SizeHasher> mIntermediateBuffers; - std::unordered_map<Size, sp<AllocatedFrame>, SizeHasher> mScaledYu12Frames; - YCbCrLayout mYu12FrameLayout; - YCbCrLayout mYu12ThumbFrameLayout; - uint32_t mBlobBufferSize = 0; // 0 -> HAL derive buffer size, else: use given size - - std::string mExifMake; - std::string mExifModel; - }; - // Protect (most of) HIDL interface methods from synchronized-entering mutable Mutex mInterfaceLock; @@ -381,12 +357,6 @@ protected: std::mutex mInflightFramesLock; // protect mInflightFrames std::unordered_set<uint32_t> mInflightFrames; - // buffers currently circulating between HAL and camera service - // key: bufferId sent via HIDL interface - // value: imported buffer_handle_t - // Buffer will be imported during processCaptureRequest and will be freed - // when the its stream is deleted or camera device session is closed - typedef std::unordered_map<uint64_t, buffer_handle_t> CirculatingBuffers; // Stream ID -> circulating buffers map std::map<int, CirculatingBuffers> mCirculatingBuffers; // Protect mCirculatingBuffers, must not lock mLock after acquiring this lock @@ -395,6 +365,8 @@ protected: std::mutex mAfTriggerLock; // protect mAfTrigger bool mAfTrigger = false; + uint32_t mBlobBufferSize = 0; + static HandleImporter sHandleImporter; /* Beginning of members not changed after initialize() */ @@ -410,6 +382,9 @@ protected: const Size mMaxThumbResolution; const Size mMaxJpegResolution; + + std::string mExifMake; + std::string mExifModel; /* End of members not changed after initialize() */ private: @@ -484,4 +459,4 @@ private: } // namespace hardware } // namespace android -#endif // ANDROID_HARDWARE_CAMERA_DEVICE_V3_4_EXTCAMERADEVICE3SESSION_H +#endif // ANDROID_HARDWARE_CAMERA_DEVICE_V3_4_EXTCAMERADEVICESESSION_H diff --git a/camera/device/3.4/default/include/ext_device_v3_4_impl/ExternalCameraDevice_3_4.h b/camera/device/3.4/default/include/ext_device_v3_4_impl/ExternalCameraDevice_3_4.h index bd7980780b..1958fcbab5 100644 --- a/camera/device/3.4/default/include/ext_device_v3_4_impl/ExternalCameraDevice_3_4.h +++ b/camera/device/3.4/default/include/ext_device_v3_4_impl/ExternalCameraDevice_3_4.h @@ -105,7 +105,7 @@ protected: // Calls into virtual member function. Do not use it in constructor status_t initCameraCharacteristics(); // Init available capabilities keys - status_t initAvailableCapabilities( + virtual status_t initAvailableCapabilities( ::android::hardware::camera::common::V1_0::helper::CameraMetadata*); // Init non-device dependent keys virtual status_t initDefaultCharsKeys( diff --git a/camera/device/3.4/default/include/ext_device_v3_4_impl/ExternalCameraUtils.h b/camera/device/3.4/default/include/ext_device_v3_4_impl/ExternalCameraUtils.h index 341c62218d..74f75eb246 100644 --- a/camera/device/3.4/default/include/ext_device_v3_4_impl/ExternalCameraUtils.h +++ b/camera/device/3.4/default/include/ext_device_v3_4_impl/ExternalCameraUtils.h @@ -17,16 +17,27 @@ #ifndef ANDROID_HARDWARE_CAMERA_DEVICE_V3_4_EXTCAMUTIL_H #define ANDROID_HARDWARE_CAMERA_DEVICE_V3_4_EXTCAMUTIL_H +#include <android/hardware/camera/common/1.0/types.h> +#include <android/hardware/camera/device/3.2/types.h> +#include <android/hardware/graphics/common/1.0/types.h> #include <android/hardware/graphics/mapper/2.0/IMapper.h> #include <inttypes.h> #include <mutex> +#include <unordered_map> #include <unordered_set> #include <vector> #include "tinyxml2.h" // XML parsing #include "utils/LightRefBase.h" +#include "utils/Timers.h" +#include <CameraMetadata.h> +#include <HandleImporter.h> -using android::hardware::graphics::mapper::V2_0::IMapper; -using android::hardware::graphics::mapper::V2_0::YCbCrLayout; + +using ::android::hardware::graphics::mapper::V2_0::IMapper; +using ::android::hardware::graphics::mapper::V2_0::YCbCrLayout; +using ::android::hardware::camera::common::V1_0::helper::HandleImporter; +using ::android::hardware::camera::common::V1_0::Status; +using ::android::hardware::camera::device::V3_2::ErrorCode; namespace android { namespace hardware { @@ -113,16 +124,28 @@ struct SupportedV4L2Format { std::vector<FrameRate> frameRates; }; +// A Base class with basic information about a frame +struct Frame : public VirtualLightRefBase { +public: + Frame(uint32_t width, uint32_t height, uint32_t fourcc); + const uint32_t mWidth; + const uint32_t mHeight; + const uint32_t mFourcc; + + // getData might involve map/allocation + virtual int getData(uint8_t** outData, size_t* dataSize) = 0; +}; + // A class provide access to a dequeued V4L2 frame buffer (mostly in MJPG format) // Also contains necessary information to enqueue the buffer back to V4L2 buffer queue -class V4L2Frame : public virtual VirtualLightRefBase { +class V4L2Frame : public Frame { public: V4L2Frame(uint32_t w, uint32_t h, uint32_t fourcc, int bufIdx, int fd, uint32_t dataSize, uint64_t offset); ~V4L2Frame() override; - const uint32_t mWidth; - const uint32_t mHeight; - const uint32_t mFourcc; + + virtual int getData(uint8_t** outData, size_t* dataSize) override; + const int mBufferIndex; // for later enqueue int map(uint8_t** data, size_t* dataSize); int unmap(); @@ -137,13 +160,13 @@ private: // A RAII class representing a CPU allocated YUV frame used as intermeidate buffers // when generating output images. -class AllocatedFrame : public virtual VirtualLightRefBase { +class AllocatedFrame : public Frame { public: - AllocatedFrame(uint32_t w, uint32_t h); // TODO: use Size? + AllocatedFrame(uint32_t w, uint32_t h); // only support V4L2_PIX_FMT_YUV420 for now ~AllocatedFrame() override; - const uint32_t mWidth; - const uint32_t mHeight; - const uint32_t mFourcc; // Only support YU12 format for now + + virtual int getData(uint8_t** outData, size_t* dataSize) override; + int allocate(YCbCrLayout* out = nullptr); int getLayout(YCbCrLayout* out); int getCroppedLayout(const IMapper::Rect&, YCbCrLayout* out); // return non-zero for bad input @@ -165,8 +188,110 @@ const float kMinAspectRatio = 1.f; bool isAspectRatioClose(float ar1, float ar2); +struct HalStreamBuffer { + int32_t streamId; + uint64_t bufferId; + uint32_t width; + uint32_t height; + ::android::hardware::graphics::common::V1_0::PixelFormat format; + ::android::hardware::camera::device::V3_2::BufferUsageFlags usage; + buffer_handle_t* bufPtr; + int acquireFence; + bool fenceTimeout; +}; + +struct HalRequest { + uint32_t frameNumber; + common::V1_0::helper::CameraMetadata setting; + sp<Frame> frameIn; + nsecs_t shutterTs; + std::vector<HalStreamBuffer> buffers; +}; + +static const uint64_t BUFFER_ID_NO_BUFFER = 0; + +// buffers currently circulating between HAL and camera service +// key: bufferId sent via HIDL interface +// value: imported buffer_handle_t +// Buffer will be imported during processCaptureRequest (or requestStreamBuffer +// in the case of HAL buffer manager is enabled) and will be freed +// when the stream is deleted or camera device session is closed +typedef std::unordered_map<uint64_t, buffer_handle_t> CirculatingBuffers; + +::android::hardware::camera::common::V1_0::Status importBufferImpl( + /*inout*/std::map<int, CirculatingBuffers>& circulatingBuffers, + /*inout*/HandleImporter& handleImporter, + int32_t streamId, + uint64_t bufId, buffer_handle_t buf, + /*out*/buffer_handle_t** outBufPtr, + bool allowEmptyBuf); + +static const uint32_t FLEX_YUV_GENERIC = static_cast<uint32_t>('F') | + static_cast<uint32_t>('L') << 8 | static_cast<uint32_t>('E') << 16 | + static_cast<uint32_t>('X') << 24; + +// returns FLEX_YUV_GENERIC for formats other than YV12/YU12/NV12/NV21 +uint32_t getFourCcFromLayout(const YCbCrLayout&); + +using ::android::hardware::camera::external::common::Size; +int getCropRect(CroppingType ct, const Size& inSize, + const Size& outSize, IMapper::Rect* out); + +int formatConvert(const YCbCrLayout& in, const YCbCrLayout& out, Size sz, uint32_t format); + +int encodeJpegYU12(const Size &inSz, + const YCbCrLayout& inLayout, int jpegQuality, + const void *app1Buffer, size_t app1Size, + void *out, size_t maxOutSize, + size_t &actualCodeSize); + +Size getMaxThumbnailResolution(const common::V1_0::helper::CameraMetadata&); + +void freeReleaseFences(hidl_vec<V3_2::CaptureResult>&); + +status_t fillCaptureResultCommon(common::V1_0::helper::CameraMetadata& md, nsecs_t timestamp, + camera_metadata_ro_entry& activeArraySize); + +// Interface for OutputThread calling back to parent +struct OutputThreadInterface : public virtual RefBase { + virtual ::android::hardware::camera::common::V1_0::Status importBuffer( + int32_t streamId, uint64_t bufId, buffer_handle_t buf, + /*out*/buffer_handle_t** outBufPtr, bool allowEmptyBuf) = 0; + + virtual void notifyError(uint32_t frameNumber, int32_t streamId, ErrorCode ec) = 0; + + // Callbacks are fired within the method if msgs/results are nullptr. + // Otherwise the callbacks will be returned and caller is responsible to + // fire the callback later + virtual ::android::hardware::camera::common::V1_0::Status processCaptureRequestError( + const std::shared_ptr<HalRequest>&, + /*out*/std::vector<V3_2::NotifyMsg>* msgs = nullptr, + /*out*/std::vector<V3_2::CaptureResult>* results = nullptr) = 0; + + virtual ::android::hardware::camera::common::V1_0::Status processCaptureResult( + std::shared_ptr<HalRequest>&) = 0; + + virtual ssize_t getJpegBufferSize(uint32_t width, uint32_t height) const = 0; +}; + } // namespace implementation } // namespace V3_4 + +namespace V3_6 { +namespace implementation { + +// A CPU copy of a mapped V4L2Frame. Will map the input V4L2 frame. +class AllocatedV4L2Frame : public V3_4::implementation::Frame { +public: + AllocatedV4L2Frame(sp<V3_4::implementation::V4L2Frame> frameIn); + ~AllocatedV4L2Frame() override; + virtual int getData(uint8_t** outData, size_t* dataSize) override; +private: + std::vector<uint8_t> mData; +}; + +} // namespace implementation +} // namespace V3_6 } // namespace device } // namespace camera } // namespace hardware diff --git a/camera/device/3.5/default/ExternalCameraDeviceSession.cpp b/camera/device/3.5/default/ExternalCameraDeviceSession.cpp index 00c1d0de39..287ac324ec 100644 --- a/camera/device/3.5/default/ExternalCameraDeviceSession.cpp +++ b/camera/device/3.5/default/ExternalCameraDeviceSession.cpp @@ -80,7 +80,7 @@ Status ExternalCameraDeviceSession::importRequestLocked( ExternalCameraDeviceSession::BufferRequestThread::BufferRequestThread( - wp<ExternalCameraDeviceSession> parent, + wp<OutputThreadInterface> parent, sp<V3_5::ICameraDeviceCallback> callbacks) : mParent(parent), mCallbacks(callbacks) {} @@ -254,7 +254,8 @@ void ExternalCameraDeviceSession::initOutputThread() { mBufferRequestThread = new BufferRequestThread(this, mCallback_3_5); mBufferRequestThread->run("ExtCamBufReq", PRIORITY_DISPLAY); } - mOutputThread = new OutputThread(this, mCroppingType, mBufferRequestThread); + mOutputThread = new OutputThread( + this, mCroppingType, mCameraCharacteristics, mBufferRequestThread); } void ExternalCameraDeviceSession::closeOutputThreadImpl() { @@ -271,10 +272,11 @@ void ExternalCameraDeviceSession::closeOutputThread() { } ExternalCameraDeviceSession::OutputThread::OutputThread( - wp<ExternalCameraDeviceSession> parent, + wp<OutputThreadInterface> parent, CroppingType ct, + const common::V1_0::helper::CameraMetadata& chars, sp<BufferRequestThread> bufReqThread) : - V3_4::implementation::ExternalCameraDeviceSession::OutputThread(parent, ct), + V3_4::implementation::ExternalCameraDeviceSession::OutputThread(parent, ct, chars), mBufferRequestThread(bufReqThread) {} ExternalCameraDeviceSession::OutputThread::~OutputThread() {} diff --git a/camera/device/3.5/default/include/ext_device_v3_5_impl/ExternalCameraDeviceSession.h b/camera/device/3.5/default/include/ext_device_v3_5_impl/ExternalCameraDeviceSession.h index 281f93a13b..e89ef45f52 100644 --- a/camera/device/3.5/default/include/ext_device_v3_5_impl/ExternalCameraDeviceSession.h +++ b/camera/device/3.5/default/include/ext_device_v3_5_impl/ExternalCameraDeviceSession.h @@ -14,8 +14,8 @@ * limitations under the License. */ -#ifndef ANDROID_HARDWARE_CAMERA_DEVICE_V3_5_EXTCAMERADEVICE3SESSION_H -#define ANDROID_HARDWARE_CAMERA_DEVICE_V3_5_EXTCAMERADEVICE3SESSION_H +#ifndef ANDROID_HARDWARE_CAMERA_DEVICE_V3_5_EXTCAMERADEVICESESSION_H +#define ANDROID_HARDWARE_CAMERA_DEVICE_V3_5_EXTCAMERADEVICESESSION_H #include <android/hardware/camera/device/3.5/ICameraDeviceCallback.h> #include <android/hardware/camera/device/3.5/ICameraDeviceSession.h> @@ -72,6 +72,7 @@ using ::android::base::unique_fd; using ::android::hardware::camera::device::V3_4::implementation::SupportedV4L2Format; using ::android::hardware::camera::device::V3_4::implementation::CroppingType; +using ::android::hardware::camera::device::V3_4::implementation::HalStreamBuffer; struct ExternalCameraDeviceSession : public V3_4::implementation::ExternalCameraDeviceSession { @@ -97,33 +98,10 @@ struct ExternalCameraDeviceSession : public V3_4::implementation::ExternalCamera config, supportedFormats, devCfg); } -protected: - // Methods from v3.4 and earlier will trampoline to inherited implementation - Return<void> configureStreams_3_5( - const StreamConfiguration& requestedConfiguration, - ICameraDeviceSession::configureStreams_3_5_cb _hidl_cb); - - Return<void> signalStreamFlush( - const hidl_vec<int32_t>& requests, - uint32_t streamConfigCounter); - - Return<void> isReconfigurationRequired(const V3_2::CameraMetadata& oldSessionParams, - const V3_2::CameraMetadata& newSessionParams, - ICameraDeviceSession::isReconfigurationRequired_cb _hidl_cb); - - virtual void initOutputThread() override; - virtual void closeOutputThread() override; - void closeOutputThreadImpl(); - - virtual Status importRequestLocked( - const CaptureRequest& request, - hidl_vec<buffer_handle_t*>& allBufPtrs, - hidl_vec<int>& allFences) override; - class BufferRequestThread : public android::Thread { public: BufferRequestThread( - wp<ExternalCameraDeviceSession> parent, + wp<OutputThreadInterface> parent, sp<V3_5::ICameraDeviceCallback> callbacks); int requestBufferStart(const std::vector<HalStreamBuffer>&); @@ -135,7 +113,7 @@ protected: private: void waitForNextRequest(); - const wp<ExternalCameraDeviceSession> mParent; + const wp<OutputThreadInterface> mParent; const sp<V3_5::ICameraDeviceCallback> mCallbacks; std::mutex mLock; @@ -158,13 +136,12 @@ protected: std::condition_variable mRequestDoneCond; // signaled when a request is done }; - sp<BufferRequestThread> mBufferRequestThread; - class OutputThread : public V3_4::implementation::ExternalCameraDeviceSession::OutputThread { public: // TODO: pass buffer request thread to OutputThread ctor - OutputThread(wp<ExternalCameraDeviceSession> parent, CroppingType, + OutputThread(wp<OutputThreadInterface> parent, CroppingType, + const common::V1_0::helper::CameraMetadata&, sp<BufferRequestThread> bufReqThread); virtual ~OutputThread(); @@ -177,6 +154,31 @@ protected: const sp<BufferRequestThread> mBufferRequestThread; }; +protected: + // Methods from v3.4 and earlier will trampoline to inherited implementation + Return<void> configureStreams_3_5( + const StreamConfiguration& requestedConfiguration, + ICameraDeviceSession::configureStreams_3_5_cb _hidl_cb); + + Return<void> signalStreamFlush( + const hidl_vec<int32_t>& requests, + uint32_t streamConfigCounter); + + Return<void> isReconfigurationRequired(const V3_2::CameraMetadata& oldSessionParams, + const V3_2::CameraMetadata& newSessionParams, + ICameraDeviceSession::isReconfigurationRequired_cb _hidl_cb); + + virtual void initOutputThread() override; + virtual void closeOutputThread() override; + void closeOutputThreadImpl(); + + virtual Status importRequestLocked( + const CaptureRequest& request, + hidl_vec<buffer_handle_t*>& allBufPtrs, + hidl_vec<int>& allFences) override; + + sp<BufferRequestThread> mBufferRequestThread; + sp<V3_5::ICameraDeviceCallback> mCallback_3_5; bool mSupportBufMgr; @@ -270,4 +272,4 @@ private: } // namespace hardware } // namespace android -#endif // ANDROID_HARDWARE_CAMERA_DEVICE_V3_5_EXTCAMERADEVICE3SESSION_H +#endif // ANDROID_HARDWARE_CAMERA_DEVICE_V3_5_EXTCAMERADEVICESESSION_H diff --git a/camera/device/3.6/default/Android.bp b/camera/device/3.6/default/Android.bp new file mode 100644 index 0000000000..a2ddebdd24 --- /dev/null +++ b/camera/device/3.6/default/Android.bp @@ -0,0 +1,67 @@ +// +// Copyright (C) 2020 The Android Open Source Project +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// + +cc_library_headers { + name: "camera.device@3.6-external-impl_headers", + vendor: true, + export_include_dirs: ["include/ext_device_v3_6_impl"] +} + +cc_library_shared { + name: "camera.device@3.6-external-impl", + defaults: ["hidl_defaults"], + proprietary: true, + vendor: true, + srcs: [ + "ExternalCameraDevice.cpp", + "ExternalCameraDeviceSession.cpp", + "ExternalCameraOfflineSession.cpp", + ], + shared_libs: [ + "libhidlbase", + "libutils", + "libcutils", + "camera.device@3.2-impl", + "camera.device@3.3-impl", + "camera.device@3.4-external-impl", + "camera.device@3.5-external-impl", + "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.graphics.mapper@2.0", + "android.hardware.graphics.mapper@3.0", + "android.hardware.graphics.mapper@4.0", + "liblog", + "libhardware", + "libcamera_metadata", + "libfmq", + "libsync", + "libyuv", + "libjpeg", + "libexif", + "libtinyxml2" + ], + static_libs: [ + "android.hardware.camera.common@1.0-helper", + ], + local_include_dirs: ["include/ext_device_v3_6_impl"], + export_shared_lib_headers: [ + "libfmq", + ], +} diff --git a/camera/device/3.6/default/ExternalCameraDevice.cpp b/camera/device/3.6/default/ExternalCameraDevice.cpp new file mode 100644 index 0000000000..244c7dd460 --- /dev/null +++ b/camera/device/3.6/default/ExternalCameraDevice.cpp @@ -0,0 +1,91 @@ +/* + * Copyright (C) 2020 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#define LOG_TAG "ExtCamDev@3.6" +//#define LOG_NDEBUG 0 +#include <log/log.h> + +#include "ExternalCameraDevice_3_6.h" + +namespace android { +namespace hardware { +namespace camera { +namespace device { +namespace V3_6 { +namespace implementation { + +ExternalCameraDevice::ExternalCameraDevice( + const std::string& cameraId, const ExternalCameraConfig& cfg) : + V3_5::implementation::ExternalCameraDevice(cameraId, cfg) {} + +ExternalCameraDevice::~ExternalCameraDevice() {} + +sp<V3_4::implementation::ExternalCameraDeviceSession> ExternalCameraDevice::createSession( + const sp<V3_2::ICameraDeviceCallback>& cb, + const ExternalCameraConfig& cfg, + const std::vector<SupportedV4L2Format>& sortedFormats, + const CroppingType& croppingType, + const common::V1_0::helper::CameraMetadata& chars, + const std::string& cameraId, + unique_fd v4l2Fd) { + return new ExternalCameraDeviceSession( + cb, cfg, sortedFormats, croppingType, chars, cameraId, std::move(v4l2Fd)); +} + +#define UPDATE(tag, data, size) \ +do { \ + if (metadata->update((tag), (data), (size))) { \ + ALOGE("Update " #tag " failed!"); \ + return -EINVAL; \ + } \ +} while (0) + +status_t ExternalCameraDevice::initAvailableCapabilities( + ::android::hardware::camera::common::V1_0::helper::CameraMetadata* metadata) { + status_t res = + V3_4::implementation::ExternalCameraDevice::initAvailableCapabilities(metadata); + + if (res != OK) { + return res; + } + + camera_metadata_entry caps = metadata->find(ANDROID_REQUEST_AVAILABLE_CAPABILITIES); + std::vector<uint8_t> availableCapabilities; + + for (size_t i = 0; i < caps.count; i++) { + uint8_t capability = caps.data.u8[i]; + availableCapabilities.push_back(capability); + } + + // Add OFFLINE_PROCESSING capability to device 3.6 + availableCapabilities.push_back(ANDROID_REQUEST_AVAILABLE_CAPABILITIES_OFFLINE_PROCESSING); + + UPDATE(ANDROID_REQUEST_AVAILABLE_CAPABILITIES, + availableCapabilities.data(), + availableCapabilities.size()); + + return OK; +} + +#undef UPDATE + +} // namespace implementation +} // namespace V3_6 +} // namespace device +} // namespace camera +} // namespace hardware +} // namespace android + diff --git a/camera/device/3.6/default/ExternalCameraDeviceSession.cpp b/camera/device/3.6/default/ExternalCameraDeviceSession.cpp new file mode 100644 index 0000000000..0cc81bbe9e --- /dev/null +++ b/camera/device/3.6/default/ExternalCameraDeviceSession.cpp @@ -0,0 +1,357 @@ +/* + * Copyright (C) 2020 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#define LOG_TAG "ExtCamDevSsn@3.6" +#include <android/log.h> + +#include <utils/Trace.h> +#include "ExternalCameraDeviceSession.h" + +namespace android { +namespace hardware { +namespace camera { +namespace device { +namespace V3_6 { +namespace implementation { + +ExternalCameraDeviceSession::ExternalCameraDeviceSession( + const sp<V3_2::ICameraDeviceCallback>& callback, + const ExternalCameraConfig& cfg, + const std::vector<SupportedV4L2Format>& sortedFormats, + const CroppingType& croppingType, + const common::V1_0::helper::CameraMetadata& chars, + const std::string& cameraId, + unique_fd v4l2Fd) : + V3_5::implementation::ExternalCameraDeviceSession( + callback, cfg, sortedFormats, croppingType, chars, cameraId, std::move(v4l2Fd)) { +} + +ExternalCameraDeviceSession::~ExternalCameraDeviceSession() {} + + +Return<void> ExternalCameraDeviceSession::configureStreams_3_6( + const StreamConfiguration& requestedConfiguration, + ICameraDeviceSession::configureStreams_3_6_cb _hidl_cb) { + V3_2::StreamConfiguration config_v32; + V3_3::HalStreamConfiguration outStreams_v33; + V3_6::HalStreamConfiguration outStreams; + const V3_4::StreamConfiguration& requestedConfiguration_3_4 = requestedConfiguration.v3_4; + Mutex::Autolock _il(mInterfaceLock); + + config_v32.operationMode = requestedConfiguration_3_4.operationMode; + config_v32.streams.resize(requestedConfiguration_3_4.streams.size()); + uint32_t blobBufferSize = 0; + int numStallStream = 0; + for (size_t i = 0; i < config_v32.streams.size(); i++) { + config_v32.streams[i] = requestedConfiguration_3_4.streams[i].v3_2; + if (config_v32.streams[i].format == PixelFormat::BLOB) { + blobBufferSize = requestedConfiguration_3_4.streams[i].bufferSize; + numStallStream++; + } + } + + // Fail early if there are multiple BLOB streams + if (numStallStream > kMaxStallStream) { + ALOGE("%s: too many stall streams (expect <= %d, got %d)", __FUNCTION__, + kMaxStallStream, numStallStream); + _hidl_cb(Status::ILLEGAL_ARGUMENT, outStreams); + return Void(); + } + + Status status = configureStreams(config_v32, &outStreams_v33, blobBufferSize); + + fillOutputStream3_6(outStreams_v33, &outStreams); + + _hidl_cb(status, outStreams); + return Void(); +} + +Return<void> ExternalCameraDeviceSession::switchToOffline( + const hidl_vec<int32_t>& streamsToKeep, + ICameraDeviceSession::switchToOffline_cb _hidl_cb) { + std::vector<NotifyMsg> msgs; + std::vector<CaptureResult> results; + CameraOfflineSessionInfo info; + sp<ICameraOfflineSession> session; + + Status st = switchToOffline(streamsToKeep, &msgs, &results, &info, &session); + + mCallback->notify(msgs); + hidl_vec<CaptureResult> hidlResults(std::move(results)); + invokeProcessCaptureResultCallback(hidlResults, /* tryWriteFmq */true); + V3_4::implementation::freeReleaseFences(hidlResults); + + _hidl_cb(st, info, session); + return Void(); +} + +void ExternalCameraDeviceSession::fillOutputStream3_6( + const V3_3::HalStreamConfiguration& outStreams_v33, + /*out*/V3_6::HalStreamConfiguration* outStreams_v36) { + if (outStreams_v36 == nullptr) { + ALOGE("%s: outStreams_v36 must not be null!", __FUNCTION__); + return; + } + Mutex::Autolock _l(mLock); + outStreams_v36->streams.resize(outStreams_v33.streams.size()); + for (size_t i = 0; i < outStreams_v36->streams.size(); i++) { + outStreams_v36->streams[i].v3_4.v3_3 = outStreams_v33.streams[i]; + outStreams_v36->streams[i].supportOffline = + supportOfflineLocked(outStreams_v33.streams[i].v3_2.id); + } +} + +bool ExternalCameraDeviceSession::supportOfflineLocked(int32_t streamId) { + const Stream& stream = mStreamMap[streamId]; + if (stream.format == PixelFormat::BLOB && + stream.dataSpace == static_cast<int32_t>(Dataspace::V0_JFIF)) { + return true; + } + // TODO: support YUV output stream? + return false; +} + +bool ExternalCameraDeviceSession::canDropRequest(const hidl_vec<int32_t>& offlineStreams, + std::shared_ptr<V3_4::implementation::HalRequest> halReq) { + for (const auto& buffer : halReq->buffers) { + for (auto offlineStreamId : offlineStreams) { + if (buffer.streamId == offlineStreamId) { + return false; + } + } + } + // Only drop a request completely if it has no offline output + return true; +} + +void ExternalCameraDeviceSession::fillOfflineSessionInfo(const hidl_vec<int32_t>& offlineStreams, + std::deque<std::shared_ptr<HalRequest>>& offlineReqs, + const std::map<int, CirculatingBuffers>& circulatingBuffers, + /*out*/CameraOfflineSessionInfo* info) { + if (info == nullptr) { + ALOGE("%s: output info must not be null!", __FUNCTION__); + return; + } + + info->offlineStreams.resize(offlineStreams.size()); + info->offlineRequests.resize(offlineReqs.size()); + + std::unordered_map<int32_t, uint32_t> outstandingBufs(offlineStreams.size()); + for (const auto streamId : offlineStreams) { + outstandingBufs.insert({streamId, 0}); + } + // Fill in offline reqs and count outstanding buffers + for (size_t i = 0; i < offlineReqs.size(); i++) { + info->offlineRequests[i].frameNumber = offlineReqs[i]->frameNumber; + info->offlineRequests[i].pendingStreams.resize(offlineReqs[i]->buffers.size()); + for (size_t bIdx = 0; bIdx < offlineReqs[i]->buffers.size(); bIdx++) { + int32_t streamId = offlineReqs[i]->buffers[bIdx].streamId; + info->offlineRequests[i].pendingStreams[bIdx] = streamId; + outstandingBufs[streamId]++; + } + } + + for (size_t i = 0; i < offlineStreams.size(); i++) { + int32_t streamId = offlineStreams[i]; + info->offlineStreams[i].id = streamId; + info->offlineStreams[i].numOutstandingBuffers = outstandingBufs[streamId]; + const CirculatingBuffers& bufIdMap = circulatingBuffers.at(streamId); + info->offlineStreams[i].circulatingBufferIds.resize(bufIdMap.size()); + size_t bIdx = 0; + for (const auto& pair : bufIdMap) { + // Fill in bufferId + info->offlineStreams[i].circulatingBufferIds[bIdx++] = pair.first; + } + + } +} + +Status ExternalCameraDeviceSession::switchToOffline(const hidl_vec<int32_t>& offlineStreams, + /*out*/std::vector<NotifyMsg>* msgs, + /*out*/std::vector<CaptureResult>* results, + /*out*/CameraOfflineSessionInfo* info, + /*out*/sp<ICameraOfflineSession>* session) { + ATRACE_CALL(); + if (offlineStreams.size() > 1) { + ALOGE("%s: more than one offline stream is not supported", __FUNCTION__); + return Status::ILLEGAL_ARGUMENT; + } + + if (msgs == nullptr || results == nullptr || info == nullptr || session == nullptr) { + ALOGE("%s: output arguments (%p, %p, %p, %p) must not be null", __FUNCTION__, + msgs, results, info, session); + return Status::ILLEGAL_ARGUMENT; + } + + msgs->clear(); + results->clear(); + + Mutex::Autolock _il(mInterfaceLock); + Status status = initStatus(); + if (status != Status::OK) { + return status; + } + + Mutex::Autolock _l(mLock); + for (auto streamId : offlineStreams) { + if (!supportOfflineLocked(streamId)) { + return Status::ILLEGAL_ARGUMENT; + } + } + + // pause output thread and get all remaining inflight requests + auto remainingReqs = mOutputThread->switchToOffline(); + std::vector<std::shared_ptr<V3_4::implementation::HalRequest>> halReqs; + + // Send out buffer/request error for remaining requests and filter requests + // to be handled in offline mode + for (auto& halReq : remainingReqs) { + bool dropReq = canDropRequest(offlineStreams, halReq); + if (dropReq) { + // Request is dropped completely. Just send request error and + // there is no need to send the request to offline session + processCaptureRequestError(halReq, msgs, results); + continue; + } + + // All requests reach here must have at least one offline stream output + NotifyMsg shutter; + shutter.type = MsgType::SHUTTER; + shutter.msg.shutter.frameNumber = halReq->frameNumber; + shutter.msg.shutter.timestamp = halReq->shutterTs; + msgs->push_back(shutter); + + std::vector<V3_4::implementation::HalStreamBuffer> offlineBuffers; + for (const auto& buffer : halReq->buffers) { + bool dropBuffer = true; + for (auto offlineStreamId : offlineStreams) { + if (buffer.streamId == offlineStreamId) { + dropBuffer = false; + break; + } + } + if (dropBuffer) { + NotifyMsg error; + error.type = MsgType::ERROR; + error.msg.error.frameNumber = halReq->frameNumber; + error.msg.error.errorStreamId = buffer.streamId; + error.msg.error.errorCode = ErrorCode::ERROR_BUFFER; + msgs->push_back(error); + + CaptureResult result; + result.frameNumber = halReq->frameNumber; + result.partialResult = 0; // buffer only result + result.inputBuffer.streamId = -1; + result.outputBuffers.resize(1); + result.outputBuffers[0].streamId = buffer.streamId; + result.outputBuffers[0].bufferId = buffer.bufferId; + result.outputBuffers[0].status = BufferStatus::ERROR; + if (buffer.acquireFence >= 0) { + native_handle_t* handle = native_handle_create(/*numFds*/1, /*numInts*/0); + handle->data[0] = buffer.acquireFence; + result.outputBuffers[0].releaseFence.setTo(handle, /*shouldOwn*/false); + } + results->push_back(result); + } else { + offlineBuffers.push_back(buffer); + } + } + halReq->buffers = offlineBuffers; + halReqs.push_back(halReq); + } + + // convert hal requests to offline request + std::deque<std::shared_ptr<HalRequest>> offlineReqs(halReqs.size()); + for (auto& v4lReq : halReqs) { + std::shared_ptr<HalRequest> halReq = std::make_shared<HalRequest>(); + halReq->frameNumber = v4lReq->frameNumber; + halReq->setting = v4lReq->setting; + halReq->shutterTs = v4lReq->shutterTs; + halReq->buffers = v4lReq->buffers; + sp<V3_4::implementation::V4L2Frame> v4l2Frame = + static_cast<V3_4::implementation::V4L2Frame*>(v4lReq->frameIn.get()); + halReq->frameIn = new AllocatedV4L2Frame(v4l2Frame); + offlineReqs.push_back(halReq); + // enqueue V4L2 frame + enqueueV4l2Frame(v4l2Frame); + } + + // Collect buffer caches/streams + hidl_vec<Stream> streamInfos; + streamInfos.resize(offlineStreams.size()); + std::map<int, CirculatingBuffers> circulatingBuffers; + { + Mutex::Autolock _l(mCbsLock); + size_t idx = 0; + for(auto streamId : offlineStreams) { + circulatingBuffers[streamId] = mCirculatingBuffers.at(streamId); + mCirculatingBuffers.erase(streamId); + streamInfos[idx++] = mStreamMap.at(streamId); + mStreamMap.erase(streamId); + } + } + + fillOfflineSessionInfo(offlineStreams, offlineReqs, circulatingBuffers, info); + + // create the offline session object + bool afTrigger; + { + std::lock_guard<std::mutex> lk(mAfTriggerLock); + afTrigger = mAfTrigger; + } + sp<ExternalCameraOfflineSession> sessionImpl = new ExternalCameraOfflineSession( + mCroppingType, mCameraCharacteristics, mCameraId, + mExifMake, mExifModel, mBlobBufferSize, afTrigger, + streamInfos, offlineReqs, circulatingBuffers); + + bool initFailed = sessionImpl->initialize(); + if (initFailed) { + ALOGE("%s: offline session initialize failed!", __FUNCTION__); + return Status::INTERNAL_ERROR; + } + + // cleanup stream and buffer caches + { + Mutex::Autolock _l(mCbsLock); + for(auto pair : mStreamMap) { + cleanupBuffersLocked(/*Stream ID*/pair.first); + } + mCirculatingBuffers.clear(); + } + mStreamMap.clear(); + + // update inflight records + { + std::lock_guard<std::mutex> lk(mInflightFramesLock); + mInflightFrames.clear(); + } + + // stop v4l2 streaming + if (v4l2StreamOffLocked() !=0) { + ALOGE("%s: stop V4L2 streaming failed!", __FUNCTION__); + return Status::INTERNAL_ERROR; + } + + *session = sessionImpl->getInterface(); + return Status::OK; +} + +} // namespace implementation +} // namespace V3_6 +} // namespace device +} // namespace camera +} // namespace hardware +} // namespace android diff --git a/camera/device/3.6/default/ExternalCameraOfflineSession.cpp b/camera/device/3.6/default/ExternalCameraOfflineSession.cpp new file mode 100644 index 0000000000..e606fda832 --- /dev/null +++ b/camera/device/3.6/default/ExternalCameraOfflineSession.cpp @@ -0,0 +1,554 @@ +/* + * Copyright (C) 2020 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#define LOG_TAG "ExtCamOfflnSsn@3.6" +#define ATRACE_TAG ATRACE_TAG_CAMERA +#include <android/log.h> + +#include <linux/videodev2.h> +#include <sync/sync.h> + +#define HAVE_JPEG // required for libyuv.h to export MJPEG decode APIs +#include <libyuv.h> + +#include <utils/Trace.h> +#include "ExternalCameraOfflineSession.h" + +namespace { + +// Size of request/result metadata fast message queue. Change to 0 to always use hwbinder buffer. +static constexpr size_t kMetadataMsgQueueSize = 1 << 18 /* 256kB */; + +} // anonymous namespace + +namespace android { +namespace hardware { +namespace camera { +namespace device { +namespace V3_6 { +namespace implementation { + +// static instance +HandleImporter ExternalCameraOfflineSession::sHandleImporter; + +using V3_5::implementation::ExternalCameraDeviceSession; + +ExternalCameraOfflineSession::ExternalCameraOfflineSession( + const CroppingType& croppingType, + const common::V1_0::helper::CameraMetadata& chars, + const std::string& cameraId, + const std::string& exifMake, + const std::string& exifModel, + const uint32_t blobBufferSize, + const bool afTrigger, + const hidl_vec<Stream>& offlineStreams, + std::deque<std::shared_ptr<HalRequest>>& offlineReqs, + const std::map<int, CirculatingBuffers>& circulatingBuffers) : + mCroppingType(croppingType), mChars(chars), mCameraId(cameraId), + mExifMake(exifMake), mExifModel(exifModel), mBlobBufferSize(blobBufferSize), + mAfTrigger(afTrigger), mOfflineStreams(offlineStreams), mOfflineReqs(offlineReqs), + mCirculatingBuffers(circulatingBuffers) {} + +ExternalCameraOfflineSession::~ExternalCameraOfflineSession() { + close(); +} + +bool ExternalCameraOfflineSession::initialize() { + mResultMetadataQueue = std::make_shared<ResultMetadataQueue>( + kMetadataMsgQueueSize, false /* non blocking */); + if (!mResultMetadataQueue->isValid()) { + ALOGE("%s: invalid result fmq", __FUNCTION__); + return true; + } + return false; +} + +void ExternalCameraOfflineSession::initOutputThread() { + if (mOutputThread != nullptr) { + ALOGE("%s: OutputThread already exist!", __FUNCTION__); + return; + } + + mBufferRequestThread = new ExternalCameraDeviceSession::BufferRequestThread( + this, mCallback); + mBufferRequestThread->run("ExtCamBufReq", PRIORITY_DISPLAY); + + mOutputThread = new OutputThread(this, mCroppingType, mChars, + mBufferRequestThread, mOfflineReqs); + + mOutputThread->setExifMakeModel(mExifMake, mExifModel); + + Size inputSize = { mOfflineReqs[0]->frameIn->mWidth, mOfflineReqs[0]->frameIn->mHeight}; + Size maxThumbSize = V3_4::implementation::getMaxThumbnailResolution(mChars); + mOutputThread->allocateIntermediateBuffers( + inputSize, maxThumbSize, mOfflineStreams, mBlobBufferSize); + + mOutputThread->run("ExtCamOfflnOut", PRIORITY_DISPLAY); +} + +bool ExternalCameraOfflineSession::OutputThread::threadLoop() { + auto parent = mParent.promote(); + if (parent == nullptr) { + ALOGE("%s: session has been disconnected!", __FUNCTION__); + return false; + } + + if (mOfflineReqs.empty()) { + ALOGI("%s: all offline requests are processed. Stopping.", __FUNCTION__); + return false; + } + + std::shared_ptr<HalRequest> req = mOfflineReqs.front(); + mOfflineReqs.pop_front(); + + auto onDeviceError = [&](auto... args) { + ALOGE(args...); + parent->notifyError( + req->frameNumber, /*stream*/-1, ErrorCode::ERROR_DEVICE); + signalRequestDone(); + return false; + }; + + if (req->frameIn->mFourcc != V4L2_PIX_FMT_MJPEG && req->frameIn->mFourcc != V4L2_PIX_FMT_Z16) { + return onDeviceError("%s: do not support V4L2 format %c%c%c%c", __FUNCTION__, + req->frameIn->mFourcc & 0xFF, + (req->frameIn->mFourcc >> 8) & 0xFF, + (req->frameIn->mFourcc >> 16) & 0xFF, + (req->frameIn->mFourcc >> 24) & 0xFF); + } + + int res = requestBufferStart(req->buffers); + if (res != 0) { + ALOGE("%s: send BufferRequest failed! res %d", __FUNCTION__, res); + return onDeviceError("%s: failed to send buffer request!", __FUNCTION__); + } + + std::unique_lock<std::mutex> lk(mBufferLock); + // Convert input V4L2 frame to YU12 of the same size + // TODO: see if we can save some computation by converting to YV12 here + uint8_t* inData; + size_t inDataSize; + if (req->frameIn->getData(&inData, &inDataSize) != 0) { + lk.unlock(); + return onDeviceError("%s: V4L2 buffer map failed", __FUNCTION__); + } + + // TODO: in some special case maybe we can decode jpg directly to gralloc output? + if (req->frameIn->mFourcc == V4L2_PIX_FMT_MJPEG) { + ATRACE_BEGIN("MJPGtoI420"); + int res = libyuv::MJPGToI420( + inData, inDataSize, static_cast<uint8_t*>(mYu12FrameLayout.y), mYu12FrameLayout.yStride, + static_cast<uint8_t*>(mYu12FrameLayout.cb), mYu12FrameLayout.cStride, + static_cast<uint8_t*>(mYu12FrameLayout.cr), mYu12FrameLayout.cStride, + mYu12Frame->mWidth, mYu12Frame->mHeight, mYu12Frame->mWidth, mYu12Frame->mHeight); + ATRACE_END(); + + if (res != 0) { + // For some webcam, the first few V4L2 frames might be malformed... + ALOGE("%s: Convert V4L2 frame to YU12 failed! res %d", __FUNCTION__, res); + lk.unlock(); + Status st = parent->processCaptureRequestError(req); + if (st != Status::OK) { + return onDeviceError("%s: failed to process capture request error!", __FUNCTION__); + } + signalRequestDone(); + return true; + } + } + + ATRACE_BEGIN("Wait for BufferRequest done"); + res = waitForBufferRequestDone(&req->buffers); + ATRACE_END(); + + if (res != 0) { + ALOGE("%s: wait for BufferRequest done failed! res %d", __FUNCTION__, res); + lk.unlock(); + return onDeviceError("%s: failed to process buffer request error!", __FUNCTION__); + } + + ALOGV("%s processing new request", __FUNCTION__); + const int kSyncWaitTimeoutMs = 500; + for (auto& halBuf : req->buffers) { + if (*(halBuf.bufPtr) == nullptr) { + ALOGW("%s: buffer for stream %d missing", __FUNCTION__, halBuf.streamId); + halBuf.fenceTimeout = true; + } else if (halBuf.acquireFence >= 0) { + int ret = sync_wait(halBuf.acquireFence, kSyncWaitTimeoutMs); + if (ret) { + halBuf.fenceTimeout = true; + } else { + ::close(halBuf.acquireFence); + halBuf.acquireFence = -1; + } + } + + if (halBuf.fenceTimeout) { + continue; + } + + // Gralloc lockYCbCr the buffer + switch (halBuf.format) { + case PixelFormat::BLOB: { + int ret = createJpegLocked(halBuf, req->setting); + + if(ret != 0) { + lk.unlock(); + return onDeviceError("%s: createJpegLocked failed with %d", + __FUNCTION__, ret); + } + } break; + case PixelFormat::Y16: { + void* outLayout = sHandleImporter.lock(*(halBuf.bufPtr), halBuf.usage, inDataSize); + + std::memcpy(outLayout, inData, inDataSize); + + int relFence = sHandleImporter.unlock(*(halBuf.bufPtr)); + if (relFence >= 0) { + halBuf.acquireFence = relFence; + } + } break; + case PixelFormat::YCBCR_420_888: + case PixelFormat::YV12: { + IMapper::Rect outRect {0, 0, + static_cast<int32_t>(halBuf.width), + static_cast<int32_t>(halBuf.height)}; + YCbCrLayout outLayout = sHandleImporter.lockYCbCr( + *(halBuf.bufPtr), halBuf.usage, outRect); + ALOGV("%s: outLayout y %p cb %p cr %p y_str %d c_str %d c_step %d", + __FUNCTION__, outLayout.y, outLayout.cb, outLayout.cr, + outLayout.yStride, outLayout.cStride, outLayout.chromaStep); + + // Convert to output buffer size/format + uint32_t outputFourcc = V3_4::implementation::getFourCcFromLayout(outLayout); + ALOGV("%s: converting to format %c%c%c%c", __FUNCTION__, + outputFourcc & 0xFF, + (outputFourcc >> 8) & 0xFF, + (outputFourcc >> 16) & 0xFF, + (outputFourcc >> 24) & 0xFF); + + YCbCrLayout cropAndScaled; + ATRACE_BEGIN("cropAndScaleLocked"); + int ret = cropAndScaleLocked( + mYu12Frame, + Size { halBuf.width, halBuf.height }, + &cropAndScaled); + ATRACE_END(); + if (ret != 0) { + lk.unlock(); + return onDeviceError("%s: crop and scale failed!", __FUNCTION__); + } + + Size sz {halBuf.width, halBuf.height}; + ATRACE_BEGIN("formatConvert"); + ret = V3_4::implementation::formatConvert(cropAndScaled, outLayout, sz, outputFourcc); + ATRACE_END(); + if (ret != 0) { + lk.unlock(); + return onDeviceError("%s: format coversion failed!", __FUNCTION__); + } + int relFence = sHandleImporter.unlock(*(halBuf.bufPtr)); + if (relFence >= 0) { + halBuf.acquireFence = relFence; + } + } break; + default: + lk.unlock(); + return onDeviceError("%s: unknown output format %x", __FUNCTION__, halBuf.format); + } + } // for each buffer + mScaledYu12Frames.clear(); + + // Don't hold the lock while calling back to parent + lk.unlock(); + Status st = parent->processCaptureResult(req); + if (st != Status::OK) { + return onDeviceError("%s: failed to process capture result!", __FUNCTION__); + } + signalRequestDone(); + return true; +} + +Status ExternalCameraOfflineSession::importBuffer(int32_t streamId, + uint64_t bufId, buffer_handle_t buf, + /*out*/buffer_handle_t** outBufPtr, + bool allowEmptyBuf) { + Mutex::Autolock _l(mCbsLock); + return V3_4::implementation::importBufferImpl( + mCirculatingBuffers, sHandleImporter, streamId, + bufId, buf, outBufPtr, allowEmptyBuf); + return Status::OK; +}; + +#define UPDATE(md, tag, data, size) \ +do { \ + if ((md).update((tag), (data), (size))) { \ + ALOGE("Update " #tag " failed!"); \ + return BAD_VALUE; \ + } \ +} while (0) + +status_t ExternalCameraOfflineSession::fillCaptureResult( + common::V1_0::helper::CameraMetadata &md, nsecs_t timestamp) { + bool afTrigger = false; + { + std::lock_guard<std::mutex> lk(mAfTriggerLock); + afTrigger = mAfTrigger; + if (md.exists(ANDROID_CONTROL_AF_TRIGGER)) { + camera_metadata_entry entry = md.find(ANDROID_CONTROL_AF_TRIGGER); + if (entry.data.u8[0] == ANDROID_CONTROL_AF_TRIGGER_START) { + mAfTrigger = afTrigger = true; + } else if (entry.data.u8[0] == ANDROID_CONTROL_AF_TRIGGER_CANCEL) { + mAfTrigger = afTrigger = false; + } + } + } + + // For USB camera, the USB camera handles everything and we don't have control + // over AF. We only simply fake the AF metadata based on the request + // received here. + uint8_t afState; + if (afTrigger) { + afState = ANDROID_CONTROL_AF_STATE_FOCUSED_LOCKED; + } else { + afState = ANDROID_CONTROL_AF_STATE_INACTIVE; + } + UPDATE(md, ANDROID_CONTROL_AF_STATE, &afState, 1); + + camera_metadata_ro_entry activeArraySize = + mChars.find(ANDROID_SENSOR_INFO_ACTIVE_ARRAY_SIZE); + + return V3_4::implementation::fillCaptureResultCommon(md, timestamp, activeArraySize); +} + +#undef UPDATE + +Status ExternalCameraOfflineSession::processCaptureResult(std::shared_ptr<HalRequest>& req) { + ATRACE_CALL(); + // Fill output buffers + hidl_vec<CaptureResult> results; + results.resize(1); + CaptureResult& result = results[0]; + result.frameNumber = req->frameNumber; + result.partialResult = 1; + result.inputBuffer.streamId = -1; + result.outputBuffers.resize(req->buffers.size()); + for (size_t i = 0; i < req->buffers.size(); i++) { + result.outputBuffers[i].streamId = req->buffers[i].streamId; + result.outputBuffers[i].bufferId = req->buffers[i].bufferId; + if (req->buffers[i].fenceTimeout) { + result.outputBuffers[i].status = BufferStatus::ERROR; + if (req->buffers[i].acquireFence >= 0) { + native_handle_t* handle = native_handle_create(/*numFds*/1, /*numInts*/0); + handle->data[0] = req->buffers[i].acquireFence; + result.outputBuffers[i].releaseFence.setTo(handle, /*shouldOwn*/false); + } + notifyError(req->frameNumber, req->buffers[i].streamId, ErrorCode::ERROR_BUFFER); + } else { + result.outputBuffers[i].status = BufferStatus::OK; + // TODO: refactor + if (req->buffers[i].acquireFence >= 0) { + native_handle_t* handle = native_handle_create(/*numFds*/1, /*numInts*/0); + handle->data[0] = req->buffers[i].acquireFence; + result.outputBuffers[i].releaseFence.setTo(handle, /*shouldOwn*/false); + } + } + } + + // Fill capture result metadata + fillCaptureResult(req->setting, req->shutterTs); + const camera_metadata_t *rawResult = req->setting.getAndLock(); + V3_2::implementation::convertToHidl(rawResult, &result.result); + req->setting.unlock(rawResult); + + // Callback into framework + invokeProcessCaptureResultCallback(results, /* tryWriteFmq */true); + V3_4::implementation::freeReleaseFences(results); + return Status::OK; +}; + +void ExternalCameraOfflineSession::invokeProcessCaptureResultCallback( + hidl_vec<CaptureResult> &results, bool tryWriteFmq) { + if (mProcessCaptureResultLock.tryLock() != OK) { + const nsecs_t NS_TO_SECOND = 1000000000; + ALOGV("%s: previous call is not finished! waiting 1s...", __FUNCTION__); + if (mProcessCaptureResultLock.timedLock(/* 1s */NS_TO_SECOND) != OK) { + ALOGE("%s: cannot acquire lock in 1s, cannot proceed", + __FUNCTION__); + return; + } + } + if (tryWriteFmq && mResultMetadataQueue->availableToWrite() > 0) { + for (CaptureResult &result : results) { + if (result.result.size() > 0) { + if (mResultMetadataQueue->write(result.result.data(), result.result.size())) { + result.fmqResultSize = result.result.size(); + result.result.resize(0); + } else { + ALOGW("%s: couldn't utilize fmq, fall back to hwbinder", __FUNCTION__); + result.fmqResultSize = 0; + } + } else { + result.fmqResultSize = 0; + } + } + } + auto status = mCallback->processCaptureResult(results); + if (!status.isOk()) { + ALOGE("%s: processCaptureResult ERROR : %s", __FUNCTION__, + status.description().c_str()); + } + + mProcessCaptureResultLock.unlock(); +} + +Status ExternalCameraOfflineSession::processCaptureRequestError( + const std::shared_ptr<HalRequest>& req, + /*out*/std::vector<NotifyMsg>* outMsgs, + /*out*/std::vector<CaptureResult>* outResults) { + ATRACE_CALL(); + + if (outMsgs == nullptr) { + notifyError(/*frameNum*/req->frameNumber, /*stream*/-1, ErrorCode::ERROR_REQUEST); + } else { + NotifyMsg shutter; + shutter.type = MsgType::SHUTTER; + shutter.msg.shutter.frameNumber = req->frameNumber; + shutter.msg.shutter.timestamp = req->shutterTs; + + NotifyMsg error; + error.type = MsgType::ERROR; + error.msg.error.frameNumber = req->frameNumber; + error.msg.error.errorStreamId = -1; + error.msg.error.errorCode = ErrorCode::ERROR_REQUEST; + outMsgs->push_back(shutter); + outMsgs->push_back(error); + } + + // Fill output buffers + hidl_vec<CaptureResult> results; + results.resize(1); + CaptureResult& result = results[0]; + result.frameNumber = req->frameNumber; + result.partialResult = 1; + result.inputBuffer.streamId = -1; + result.outputBuffers.resize(req->buffers.size()); + for (size_t i = 0; i < req->buffers.size(); i++) { + result.outputBuffers[i].streamId = req->buffers[i].streamId; + result.outputBuffers[i].bufferId = req->buffers[i].bufferId; + result.outputBuffers[i].status = BufferStatus::ERROR; + if (req->buffers[i].acquireFence >= 0) { + native_handle_t* handle = native_handle_create(/*numFds*/1, /*numInts*/0); + handle->data[0] = req->buffers[i].acquireFence; + result.outputBuffers[i].releaseFence.setTo(handle, /*shouldOwn*/false); + } + } + + if (outResults == nullptr) { + // Callback into framework + invokeProcessCaptureResultCallback(results, /* tryWriteFmq */true); + V3_4::implementation::freeReleaseFences(results); + } else { + outResults->push_back(result); + } + return Status::OK; +}; + +ssize_t ExternalCameraOfflineSession::getJpegBufferSize( + uint32_t /*width*/, uint32_t /*height*/) const { + // Empty implementation here as the jpeg buffer size is passed in by ctor + return 0; +}; + +void ExternalCameraOfflineSession::notifyError(uint32_t frameNumber, int32_t streamId, ErrorCode ec) { + NotifyMsg msg; + msg.type = MsgType::ERROR; + msg.msg.error.frameNumber = frameNumber; + msg.msg.error.errorStreamId = streamId; + msg.msg.error.errorCode = ec; + mCallback->notify({msg}); +}; + +Return<void> ExternalCameraOfflineSession::setCallback(const sp<ICameraDeviceCallback>& cb) { + Mutex::Autolock _il(mInterfaceLock); + if (mCallback != nullptr && cb != nullptr) { + ALOGE("%s: callback must not be set twice!", __FUNCTION__); + return Void(); + } + mCallback = cb; + + initOutputThread(); + + if (mOutputThread == nullptr) { + ALOGE("%s: init OutputThread failed!", __FUNCTION__); + } + return Void(); +} + +Return<void> ExternalCameraOfflineSession::getCaptureResultMetadataQueue( + V3_3::ICameraDeviceSession::getCaptureResultMetadataQueue_cb _hidl_cb) { + Mutex::Autolock _il(mInterfaceLock); + _hidl_cb(*mResultMetadataQueue->getDesc()); + return Void(); +} + +void ExternalCameraOfflineSession::cleanupBuffersLocked(int id) { + for (auto& pair : mCirculatingBuffers.at(id)) { + sHandleImporter.freeBuffer(pair.second); + } + mCirculatingBuffers[id].clear(); + mCirculatingBuffers.erase(id); +} + +Return<void> ExternalCameraOfflineSession::close() { + Mutex::Autolock _il(mInterfaceLock); + { + Mutex::Autolock _l(mLock); + if (mClosed) { + ALOGW("%s: offline session already closed!", __FUNCTION__); + return Void(); + } + } + if (mBufferRequestThread) { + mBufferRequestThread->requestExit(); + mBufferRequestThread->join(); + mBufferRequestThread.clear(); + } + if (mOutputThread) { + mOutputThread->flush(); + mOutputThread->requestExit(); + mOutputThread->join(); + mOutputThread.clear(); + } + + Mutex::Autolock _l(mLock); + // free all buffers + { + Mutex::Autolock _cbl(mCbsLock); + for(auto stream : mOfflineStreams) { + cleanupBuffersLocked(stream.id); + } + } + mCallback.clear(); + mClosed = true; + return Void(); +} + +} // namespace implementation +} // namespace V3_6 +} // namespace device +} // namespace camera +} // namespace hardware +} // namespace android diff --git a/camera/device/3.6/default/OWNERS b/camera/device/3.6/default/OWNERS new file mode 100644 index 0000000000..f48a95c5b3 --- /dev/null +++ b/camera/device/3.6/default/OWNERS @@ -0,0 +1 @@ +include platform/frameworks/av:/camera/OWNERS diff --git a/camera/device/3.6/default/include/ext_device_v3_6_impl/ExternalCameraDeviceSession.h b/camera/device/3.6/default/include/ext_device_v3_6_impl/ExternalCameraDeviceSession.h new file mode 100644 index 0000000000..db0d9a548b --- /dev/null +++ b/camera/device/3.6/default/include/ext_device_v3_6_impl/ExternalCameraDeviceSession.h @@ -0,0 +1,208 @@ +/* + * Copyright (C) 2020 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef ANDROID_HARDWARE_CAMERA_DEVICE_V3_6_EXTCAMERADEVICESESSION_H +#define ANDROID_HARDWARE_CAMERA_DEVICE_V3_6_EXTCAMERADEVICESESSION_H + +#include <android/hardware/camera/device/3.5/ICameraDeviceCallback.h> +#include <android/hardware/camera/device/3.6/ICameraDeviceSession.h> +#include <../../3.5/default/include/ext_device_v3_5_impl/ExternalCameraDeviceSession.h> +#include "ExternalCameraOfflineSession.h" + +namespace android { +namespace hardware { +namespace camera { +namespace device { +namespace V3_6 { +namespace implementation { + +using ::android::hardware::camera::device::V3_2::BufferCache; +using ::android::hardware::camera::device::V3_2::CameraMetadata; +using ::android::hardware::camera::device::V3_2::CaptureRequest; +using ::android::hardware::camera::device::V3_2::CaptureResult; +using ::android::hardware::camera::device::V3_5::ICameraDeviceCallback; +using ::android::hardware::camera::device::V3_2::RequestTemplate; +using ::android::hardware::camera::device::V3_2::Stream; +using ::android::hardware::camera::device::V3_5::StreamConfiguration; +using ::android::hardware::camera::device::V3_6::ICameraDeviceSession; +using ::android::hardware::camera::device::V3_6::ICameraOfflineSession; +using ::android::hardware::camera::common::V1_0::Status; +using ::android::hardware::camera::external::common::ExternalCameraConfig; +using ::android::hardware::graphics::common::V1_0::PixelFormat; +using ::android::hardware::MQDescriptorSync; +using ::android::hardware::Return; +using ::android::hardware::Void; +using ::android::hardware::hidl_vec; +using ::android::hardware::hidl_string; +using ::android::sp; +using ::android::Mutex; +using ::android::base::unique_fd; + +using ::android::hardware::camera::device::V3_4::implementation::SupportedV4L2Format; +using ::android::hardware::camera::device::V3_4::implementation::CroppingType; + +struct ExternalCameraDeviceSession : public V3_5::implementation::ExternalCameraDeviceSession { + + ExternalCameraDeviceSession(const sp<V3_2::ICameraDeviceCallback>&, + const ExternalCameraConfig& cfg, + const std::vector<SupportedV4L2Format>& sortedFormats, + const CroppingType& croppingType, + const common::V1_0::helper::CameraMetadata& chars, + const std::string& cameraId, + unique_fd v4l2Fd); + virtual ~ExternalCameraDeviceSession(); + + // Retrieve the HIDL interface, split into its own class to avoid inheritance issues when + // dealing with minor version revs and simultaneous implementation and interface inheritance + virtual sp<V3_4::ICameraDeviceSession> getInterface() override { + return new TrampolineSessionInterface_3_6(this); + } + +protected: + // Methods from v3.5 and earlier will trampoline to inherited implementation + Return<void> configureStreams_3_6( + const StreamConfiguration& requestedConfiguration, + ICameraDeviceSession::configureStreams_3_6_cb _hidl_cb); + + Return<void> switchToOffline( + const hidl_vec<int32_t>& streamsToKeep, + ICameraDeviceSession::switchToOffline_cb _hidl_cb); + + void fillOutputStream3_6(const V3_3::HalStreamConfiguration& outStreams_v33, + /*out*/V3_6::HalStreamConfiguration* outStreams_v36); + bool supportOfflineLocked(int32_t streamId); + + // Main body of switchToOffline. This method does not invoke any callbacks + // but instead returns the necessary callbacks in output arguments so callers + // can callback later without holding any locks + Status switchToOffline(const hidl_vec<int32_t>& offlineStreams, + /*out*/std::vector<NotifyMsg>* msgs, + /*out*/std::vector<CaptureResult>* results, + /*out*/CameraOfflineSessionInfo* info, + /*out*/sp<ICameraOfflineSession>* session); + + // Whether a request can be completely dropped when switching to offline + bool canDropRequest(const hidl_vec<int32_t>& offlineStreams, + std::shared_ptr<V3_4::implementation::HalRequest> halReq); + + void fillOfflineSessionInfo(const hidl_vec<int32_t>& offlineStreams, + std::deque<std::shared_ptr<HalRequest>>& offlineReqs, + const std::map<int, CirculatingBuffers>& circulatingBuffers, + /*out*/CameraOfflineSessionInfo* info); + +private: + + struct TrampolineSessionInterface_3_6 : public ICameraDeviceSession { + TrampolineSessionInterface_3_6(sp<ExternalCameraDeviceSession> parent) : + mParent(parent) {} + + virtual Return<void> constructDefaultRequestSettings( + RequestTemplate type, + V3_3::ICameraDeviceSession::constructDefaultRequestSettings_cb _hidl_cb) override { + return mParent->constructDefaultRequestSettings(type, _hidl_cb); + } + + virtual Return<void> configureStreams( + const V3_2::StreamConfiguration& requestedConfiguration, + V3_3::ICameraDeviceSession::configureStreams_cb _hidl_cb) override { + return mParent->configureStreams(requestedConfiguration, _hidl_cb); + } + + virtual Return<void> processCaptureRequest(const hidl_vec<V3_2::CaptureRequest>& requests, + const hidl_vec<V3_2::BufferCache>& cachesToRemove, + V3_3::ICameraDeviceSession::processCaptureRequest_cb _hidl_cb) override { + return mParent->processCaptureRequest(requests, cachesToRemove, _hidl_cb); + } + + virtual Return<void> getCaptureRequestMetadataQueue( + V3_3::ICameraDeviceSession::getCaptureRequestMetadataQueue_cb _hidl_cb) override { + return mParent->getCaptureRequestMetadataQueue(_hidl_cb); + } + + virtual Return<void> getCaptureResultMetadataQueue( + V3_3::ICameraDeviceSession::getCaptureResultMetadataQueue_cb _hidl_cb) override { + return mParent->getCaptureResultMetadataQueue(_hidl_cb); + } + + virtual Return<Status> flush() override { + return mParent->flush(); + } + + virtual Return<void> close() override { + return mParent->close(); + } + + virtual Return<void> configureStreams_3_3( + const V3_2::StreamConfiguration& requestedConfiguration, + configureStreams_3_3_cb _hidl_cb) override { + return mParent->configureStreams_3_3(requestedConfiguration, _hidl_cb); + } + + virtual Return<void> configureStreams_3_4( + const V3_4::StreamConfiguration& requestedConfiguration, + configureStreams_3_4_cb _hidl_cb) override { + return mParent->configureStreams_3_4(requestedConfiguration, _hidl_cb); + } + + virtual Return<void> processCaptureRequest_3_4(const hidl_vec<V3_4::CaptureRequest>& requests, + const hidl_vec<V3_2::BufferCache>& cachesToRemove, + ICameraDeviceSession::processCaptureRequest_3_4_cb _hidl_cb) override { + return mParent->processCaptureRequest_3_4(requests, cachesToRemove, _hidl_cb); + } + + virtual Return<void> configureStreams_3_5( + const StreamConfiguration& requestedConfiguration, + configureStreams_3_5_cb _hidl_cb) override { + return mParent->configureStreams_3_5(requestedConfiguration, _hidl_cb); + } + + virtual Return<void> signalStreamFlush( + const hidl_vec<int32_t>& requests, + uint32_t streamConfigCounter) override { + return mParent->signalStreamFlush(requests, streamConfigCounter); + } + + virtual Return<void> isReconfigurationRequired(const V3_2::CameraMetadata& oldSessionParams, + const V3_2::CameraMetadata& newSessionParams, + ICameraDeviceSession::isReconfigurationRequired_cb _hidl_cb) override { + return mParent->isReconfigurationRequired(oldSessionParams, newSessionParams, _hidl_cb); + } + + virtual Return<void> configureStreams_3_6( + const StreamConfiguration& requestedConfiguration, + configureStreams_3_6_cb _hidl_cb) override { + return mParent->configureStreams_3_6(requestedConfiguration, _hidl_cb); + } + + virtual Return<void> switchToOffline( + const hidl_vec<int32_t>& streamsToKeep, + switchToOffline_cb _hidl_cb) override { + return mParent->switchToOffline(streamsToKeep, _hidl_cb); + } + + private: + sp<ExternalCameraDeviceSession> mParent; + }; +}; + +} // namespace implementation +} // namespace V3_6 +} // namespace device +} // namespace camera +} // namespace hardware +} // namespace android + +#endif // ANDROID_HARDWARE_CAMERA_DEVICE_V3_6_EXTCAMERADEVICESESSION_H diff --git a/camera/device/3.6/default/include/ext_device_v3_6_impl/ExternalCameraDevice_3_6.h b/camera/device/3.6/default/include/ext_device_v3_6_impl/ExternalCameraDevice_3_6.h new file mode 100644 index 0000000000..046c9d817e --- /dev/null +++ b/camera/device/3.6/default/include/ext_device_v3_6_impl/ExternalCameraDevice_3_6.h @@ -0,0 +1,77 @@ +/* + * Copyright (C) 2020 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef ANDROID_HARDWARE_CAMERA_DEVICE_V3_6_EXTCAMERADEVICE_H +#define ANDROID_HARDWARE_CAMERA_DEVICE_V3_6_EXTCAMERADEVICE_H + +#include "ExternalCameraDeviceSession.h" +#include <../../../../3.5/default/include/ext_device_v3_5_impl/ExternalCameraDevice_3_5.h> + +namespace android { +namespace hardware { +namespace camera { +namespace device { +namespace V3_6 { +namespace implementation { + +using namespace ::android::hardware::camera::device; +using ::android::hardware::camera::device::V3_5::ICameraDevice; +using ::android::hardware::camera::common::V1_0::CameraResourceCost; +using ::android::hardware::camera::common::V1_0::TorchMode; +using ::android::hardware::camera::common::V1_0::Status; +using ::android::hardware::camera::external::common::ExternalCameraConfig; +using ::android::hardware::camera::external::common::Size; +using ::android::hardware::Return; +using ::android::hardware::Void; +using ::android::hardware::hidl_vec; +using ::android::hardware::hidl_string; +using ::android::sp; + +/* + * The camera device HAL implementation is opened lazily (via the open call) + */ +struct ExternalCameraDevice : public V3_5::implementation::ExternalCameraDevice { + + // Called by external camera provider HAL. + // Provider HAL must ensure the uniqueness of CameraDevice object per cameraId, or there could + // be multiple CameraDevice trying to access the same physical camera. Also, provider will have + // to keep track of all CameraDevice objects in order to notify CameraDevice when the underlying + // camera is detached. + ExternalCameraDevice(const std::string& cameraId, const ExternalCameraConfig& cfg); + virtual ~ExternalCameraDevice(); + +protected: + virtual sp<V3_4::implementation::ExternalCameraDeviceSession> createSession( + const sp<V3_2::ICameraDeviceCallback>&, + const ExternalCameraConfig& cfg, + const std::vector<SupportedV4L2Format>& sortedFormats, + const CroppingType& croppingType, + const common::V1_0::helper::CameraMetadata& chars, + const std::string& cameraId, + unique_fd v4l2Fd) override; + + virtual status_t initAvailableCapabilities( + ::android::hardware::camera::common::V1_0::helper::CameraMetadata*) override; +}; + +} // namespace implementation +} // namespace V3_6 +} // namespace device +} // namespace camera +} // namespace hardware +} // namespace android + +#endif // ANDROID_HARDWARE_CAMERA_DEVICE_V3_6_EXTCAMERADEVICE_H diff --git a/camera/device/3.6/default/include/ext_device_v3_6_impl/ExternalCameraOfflineSession.h b/camera/device/3.6/default/include/ext_device_v3_6_impl/ExternalCameraOfflineSession.h new file mode 100644 index 0000000000..230b67c43c --- /dev/null +++ b/camera/device/3.6/default/include/ext_device_v3_6_impl/ExternalCameraOfflineSession.h @@ -0,0 +1,232 @@ +/* + * Copyright (C) 2020 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef ANDROID_HARDWARE_CAMERA_DEVICE_V3_6_EXTCAMERAOFFLINESESSION_H +#define ANDROID_HARDWARE_CAMERA_DEVICE_V3_6_EXTCAMERAOFFLINESESSION_H + +#include <android/hardware/camera/device/3.5/ICameraDeviceCallback.h> +#include <android/hardware/camera/device/3.6/ICameraDeviceSession.h> +#include <android/hardware/camera/device/3.6/ICameraOfflineSession.h> +#include <android/hardware/camera/common/1.0/types.h> +#include <fmq/MessageQueue.h> +#include <hidl/MQDescriptor.h> +#include <deque> +#include <../../3.4/default/include/ext_device_v3_4_impl/ExternalCameraUtils.h> +#include <../../3.5/default/include/ext_device_v3_5_impl/ExternalCameraDeviceSession.h> +#include <HandleImporter.h> +#include <Exif.h> +#include <android-base/unique_fd.h> + +namespace android { +namespace hardware { +namespace camera { +namespace device { +namespace V3_6 { +namespace implementation { + +using ::android::hardware::camera::device::V3_2::BufferCache; +using ::android::hardware::camera::device::V3_5::BufferRequest; +using ::android::hardware::camera::device::V3_5::BufferRequestStatus; +using ::android::hardware::camera::device::V3_2::BufferStatus; +using ::android::hardware::camera::device::V3_2::CameraMetadata; +using ::android::hardware::camera::device::V3_2::CaptureRequest; +using ::android::hardware::camera::device::V3_2::CaptureResult; +using ::android::hardware::camera::device::V3_2::ErrorCode; +using ::android::hardware::camera::device::V3_5::ICameraDeviceCallback; +using ::android::hardware::camera::device::V3_2::MsgType; +using ::android::hardware::camera::device::V3_2::NotifyMsg; +using ::android::hardware::camera::device::V3_2::RequestTemplate; +using ::android::hardware::camera::device::V3_2::Stream; +using ::android::hardware::camera::device::V3_5::StreamConfiguration; +using ::android::hardware::camera::device::V3_2::StreamConfigurationMode; +using ::android::hardware::camera::device::V3_2::StreamRotation; +using ::android::hardware::camera::device::V3_2::StreamType; +using ::android::hardware::camera::device::V3_2::DataspaceFlags; +using ::android::hardware::camera::device::V3_2::CameraBlob; +using ::android::hardware::camera::device::V3_2::CameraBlobId; +using ::android::hardware::camera::device::V3_4::HalStreamConfiguration; +using ::android::hardware::camera::device::V3_6::ICameraOfflineSession; +using ::android::hardware::camera::common::V1_0::Status; +using ::android::hardware::camera::common::V1_0::helper::HandleImporter; +using ::android::hardware::camera::common::V1_0::helper::ExifUtils; +using ::android::hardware::camera::external::common::ExternalCameraConfig; +using ::android::hardware::camera::external::common::Size; +using ::android::hardware::camera::external::common::SizeHasher; +using ::android::hardware::graphics::common::V1_0::BufferUsage; +using ::android::hardware::graphics::common::V1_0::Dataspace; +using ::android::hardware::graphics::common::V1_0::PixelFormat; +using ::android::hardware::kSynchronizedReadWrite; +using ::android::hardware::MessageQueue; +using ::android::hardware::MQDescriptorSync; +using ::android::hardware::Return; +using ::android::hardware::Void; +using ::android::hardware::hidl_vec; +using ::android::hardware::hidl_string; +using ::android::sp; +using ::android::Mutex; +using ::android::base::unique_fd; + +using ::android::hardware::camera::device::V3_4::implementation::SupportedV4L2Format; +using ::android::hardware::camera::device::V3_4::implementation::CroppingType; +using ::android::hardware::camera::device::V3_4::implementation::CirculatingBuffers; +using ::android::hardware::camera::device::V3_4::implementation::HalRequest; +using ::android::hardware::camera::device::V3_4::implementation::OutputThreadInterface; + +struct ExternalCameraOfflineSession : public virtual RefBase, + public virtual OutputThreadInterface { + + ExternalCameraOfflineSession( + const CroppingType& croppingType, + const common::V1_0::helper::CameraMetadata& chars, + const std::string& cameraId, + const std::string& exifMake, + const std::string& exifModel, + uint32_t blobBufferSize, + bool afTrigger, + const hidl_vec<Stream>& offlineStreams, + std::deque<std::shared_ptr<HalRequest>>& offlineReqs, + const std::map<int, CirculatingBuffers>& circulatingBuffers); + + bool initialize(); + + virtual ~ExternalCameraOfflineSession(); + + // Retrieve the HIDL interface, split into its own class to avoid inheritance issues when + // dealing with minor version revs and simultaneous implementation and interface inheritance + virtual sp<V3_6::ICameraOfflineSession> getInterface() { + return new TrampolineSessionInterface_3_6(this); + } + +protected: + + // Methods from OutputThreadInterface + virtual Status importBuffer(int32_t streamId, + uint64_t bufId, buffer_handle_t buf, + /*out*/buffer_handle_t** outBufPtr, + bool allowEmptyBuf) override; + + virtual Status processCaptureResult(std::shared_ptr<HalRequest>&) override; + + virtual Status processCaptureRequestError(const std::shared_ptr<HalRequest>&, + /*out*/std::vector<NotifyMsg>* msgs = nullptr, + /*out*/std::vector<CaptureResult>* results = nullptr) override; + + virtual ssize_t getJpegBufferSize(uint32_t width, uint32_t height) const override; + + virtual void notifyError(uint32_t frameNumber, int32_t streamId, ErrorCode ec) override; + // End of OutputThreadInterface methods + + class OutputThread : public V3_5::implementation::ExternalCameraDeviceSession::OutputThread { + public: + OutputThread( + wp<OutputThreadInterface> parent, CroppingType ct, + const common::V1_0::helper::CameraMetadata& chars, + sp<V3_5::implementation::ExternalCameraDeviceSession::BufferRequestThread> bufReqThread, + std::deque<std::shared_ptr<HalRequest>>& offlineReqs) : + V3_5::implementation::ExternalCameraDeviceSession::OutputThread( + parent, ct, chars, bufReqThread), + mOfflineReqs(offlineReqs) {} + + virtual bool threadLoop() override; + + protected: + std::deque<std::shared_ptr<HalRequest>> mOfflineReqs; + }; // OutputThread + + + Return<void> setCallback(const sp<ICameraDeviceCallback>& cb); + + Return<void> getCaptureResultMetadataQueue( + V3_3::ICameraDeviceSession::getCaptureResultMetadataQueue_cb _hidl_cb); + + Return<void> close(); + + void initOutputThread(); + + void invokeProcessCaptureResultCallback( + hidl_vec<CaptureResult> &results, bool tryWriteFmq); + + status_t fillCaptureResult(common::V1_0::helper::CameraMetadata& md, nsecs_t timestamp); + + void cleanupBuffersLocked(int id); + + // Protect (most of) HIDL interface methods from synchronized-entering + mutable Mutex mInterfaceLock; + + mutable Mutex mLock; // Protect all data members except otherwise noted + + bool mClosed = false; + const CroppingType mCroppingType; + const common::V1_0::helper::CameraMetadata mChars; + const std::string mCameraId; + const std::string mExifMake; + const std::string mExifModel; + const uint32_t mBlobBufferSize; + + std::mutex mAfTriggerLock; // protect mAfTrigger + bool mAfTrigger; + + const hidl_vec<Stream> mOfflineStreams; + std::deque<std::shared_ptr<HalRequest>> mOfflineReqs; + + // Protect mCirculatingBuffers, must not lock mLock after acquiring this lock + mutable Mutex mCbsLock; + std::map<int, CirculatingBuffers> mCirculatingBuffers; + + static HandleImporter sHandleImporter; + + using ResultMetadataQueue = MessageQueue<uint8_t, kSynchronizedReadWrite>; + std::shared_ptr<ResultMetadataQueue> mResultMetadataQueue; + + // Protect against invokeProcessCaptureResultCallback() + Mutex mProcessCaptureResultLock; + + sp<ICameraDeviceCallback> mCallback; + + sp<V3_5::implementation::ExternalCameraDeviceSession::BufferRequestThread> mBufferRequestThread; + sp<OutputThread> mOutputThread; +private: + + struct TrampolineSessionInterface_3_6 : public ICameraOfflineSession { + TrampolineSessionInterface_3_6(sp<ExternalCameraOfflineSession> parent) : + mParent(parent) {} + + virtual Return<void> setCallback(const sp<ICameraDeviceCallback>& cb) override { + return mParent->setCallback(cb); + } + + virtual Return<void> getCaptureResultMetadataQueue( + V3_3::ICameraDeviceSession::getCaptureResultMetadataQueue_cb _hidl_cb) override { + return mParent->getCaptureResultMetadataQueue(_hidl_cb); + } + + virtual Return<void> close() override { + return mParent->close(); + } + + private: + sp<ExternalCameraOfflineSession> mParent; + }; +}; + +} // namespace implementation +} // namespace V3_6 +} // namespace device +} // namespace camera +} // namespace hardware +} // namespace android + +#endif // ANDROID_HARDWARE_CAMERA_DEVICE_V3_6_EXTCAMERAOFFLINESESSION_H diff --git a/camera/metadata/3.5/types.hal b/camera/metadata/3.5/types.hal index 4c063dde75..4e2252cc5c 100644 --- a/camera/metadata/3.5/types.hal +++ b/camera/metadata/3.5/types.hal @@ -72,6 +72,23 @@ enum CameraMetadataTag : @3.4::CameraMetadataTag { ANDROID_CONTROL_END_3_5, + /** android.scaler.availableRotateAndCropModes [static, byte[], public] + * + * <p>List of rotate-and-crop modes for ANDROID_SCALER_ROTATE_AND_CROP that are supported by this camera device.</p> + * + * @see ANDROID_SCALER_ROTATE_AND_CROP + */ + ANDROID_SCALER_AVAILABLE_ROTATE_AND_CROP_MODES = android.hardware.camera.metadata@3.4::CameraMetadataTag:ANDROID_SCALER_END_3_4, + + /** android.scaler.rotateAndCrop [dynamic, enum, public] + * + * <p>Whether a rotation-and-crop operation is applied to processed + * outputs from the camera.</p> + */ + ANDROID_SCALER_ROTATE_AND_CROP, + + ANDROID_SCALER_END_3_5, + }; /* @@ -103,3 +120,14 @@ enum CameraMetadataEnumAndroidRequestAvailableCapabilities : ANDROID_REQUEST_AVAILABLE_CAPABILITIES_SYSTEM_CAMERA, ANDROID_REQUEST_AVAILABLE_CAPABILITIES_OFFLINE_PROCESSING, }; + +/** android.scaler.rotateAndCrop enumeration values + * @see ANDROID_SCALER_ROTATE_AND_CROP + */ +enum CameraMetadataEnumAndroidScalerRotateAndCrop : uint32_t { + ANDROID_SCALER_ROTATE_AND_CROP_NONE, + ANDROID_SCALER_ROTATE_AND_CROP_90, + ANDROID_SCALER_ROTATE_AND_CROP_180, + ANDROID_SCALER_ROTATE_AND_CROP_270, + ANDROID_SCALER_ROTATE_AND_CROP_AUTO, +}; diff --git a/camera/provider/2.4/ICameraProvider.hal b/camera/provider/2.4/ICameraProvider.hal index 74c3ff1693..105629dec3 100644 --- a/camera/provider/2.4/ICameraProvider.hal +++ b/camera/provider/2.4/ICameraProvider.hal @@ -115,7 +115,7 @@ interface ICameraProvider { * INTERNAL_ERROR: * A camera ID list cannot be created. This may be due to * a failure to initialize the camera subsystem, for example. - * @return cameraDeviceServiceNames The vector of internal camera device + * @return cameraDeviceNames The vector of internal camera device * names known to this provider. */ getCameraIdList() diff --git a/camera/provider/2.4/ICameraProviderCallback.hal b/camera/provider/2.4/ICameraProviderCallback.hal index 63dd3c516f..8822305c3a 100644 --- a/camera/provider/2.4/ICameraProviderCallback.hal +++ b/camera/provider/2.4/ICameraProviderCallback.hal @@ -39,7 +39,7 @@ interface ICameraProviderCallback { * are already present, as soon as the callbacks are available through * setCallback. * - * @param cameraDeviceServiceName The name of the camera device that has a + * @param cameraDeviceName The name of the camera device that has a * new status. * @param newStatus The new status that device is in. * @@ -57,7 +57,7 @@ interface ICameraProviderCallback { * android.flash.info.available is reported as true via the * ICameraDevice::getCameraCharacteristics call. * - * @param cameraDeviceServiceName The name of the camera device that has a + * @param cameraDeviceName The name of the camera device that has a * new status. * @param newStatus The new status that device is in. * diff --git a/camera/provider/2.4/default/Android.bp b/camera/provider/2.4/default/Android.bp index e38e0f7574..1c51d46fa0 100644 --- a/camera/provider/2.4/default/Android.bp +++ b/camera/provider/2.4/default/Android.bp @@ -50,6 +50,7 @@ cc_library_shared { "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.graphics.mapper@2.0", "android.hardware.graphics.mapper@3.0", @@ -61,6 +62,7 @@ cc_library_shared { "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", @@ -74,7 +76,8 @@ cc_library_shared { ], header_libs: [ "camera.device@3.4-external-impl_headers", - "camera.device@3.5-external-impl_headers" + "camera.device@3.5-external-impl_headers", + "camera.device@3.6-external-impl_headers" ], export_include_dirs: ["."], } diff --git a/camera/provider/2.4/default/ExternalCameraProviderImpl_2_4.cpp b/camera/provider/2.4/default/ExternalCameraProviderImpl_2_4.cpp index a6fd288125..2bfced2c62 100644 --- a/camera/provider/2.4/default/ExternalCameraProviderImpl_2_4.cpp +++ b/camera/provider/2.4/default/ExternalCameraProviderImpl_2_4.cpp @@ -26,6 +26,7 @@ #include "ExternalCameraProviderImpl_2_4.h" #include "ExternalCameraDevice_3_4.h" #include "ExternalCameraDevice_3_5.h" +#include "ExternalCameraDevice_3_6.h" namespace android { namespace hardware { @@ -73,6 +74,7 @@ ExternalCameraProviderImpl_2_4::ExternalCameraProviderImpl_2_4() : switch(mPreferredHal3MinorVersion) { case 4: case 5: + case 6: // OK break; default: @@ -171,6 +173,12 @@ Return<void> ExternalCameraProviderImpl_2_4::getCameraDeviceInterface_V3_x( cameraId, mCfg); break; } + case 6: { + ALOGV("Constructing v3.6 external camera device"); + deviceImpl = new device::V3_6::implementation::ExternalCameraDevice( + cameraId, mCfg); + break; + } default: ALOGE("%s: Unknown HAL minor version %d!", __FUNCTION__, mPreferredHal3MinorVersion); _hidl_cb(Status::INTERNAL_ERROR, nullptr); @@ -202,7 +210,9 @@ void ExternalCameraProviderImpl_2_4::addExternalCamera(const char* devName) { ALOGI("ExtCam: adding %s to External Camera HAL!", devName); Mutex::Autolock _l(mLock); std::string deviceName; - if (mPreferredHal3MinorVersion == 5) { + if (mPreferredHal3MinorVersion == 6) { + deviceName = std::string("device@3.6/external/") + devName; + } else if (mPreferredHal3MinorVersion == 5) { deviceName = std::string("device@3.5/external/") + devName; } else { deviceName = std::string("device@3.4/external/") + devName; @@ -249,7 +259,9 @@ void ExternalCameraProviderImpl_2_4::deviceAdded(const char* devName) { void ExternalCameraProviderImpl_2_4::deviceRemoved(const char* devName) { Mutex::Autolock _l(mLock); std::string deviceName; - if (mPreferredHal3MinorVersion == 5) { + if (mPreferredHal3MinorVersion == 6) { + deviceName = std::string("device@3.6/external/") + devName; + } else if (mPreferredHal3MinorVersion == 5) { deviceName = std::string("device@3.5/external/") + devName; } else { deviceName = std::string("device@3.4/external/") + devName; diff --git a/camera/provider/2.4/vts/functional/Android.bp b/camera/provider/2.4/vts/functional/Android.bp index d14ccfaf2e..15f5c9ad4b 100644 --- a/camera/provider/2.4/vts/functional/Android.bp +++ b/camera/provider/2.4/vts/functional/Android.bp @@ -41,6 +41,7 @@ cc_test { "android.hardware.camera.metadata@3.4", "android.hardware.camera.provider@2.4", "android.hardware.camera.provider@2.5", + "android.hardware.camera.provider@2.6", "android.hardware.graphics.common@1.0", "android.hidl.allocator@1.0", "libgrallocusage", diff --git a/camera/provider/2.4/vts/functional/VtsHalCameraProviderV2_4TargetTest.cpp b/camera/provider/2.4/vts/functional/VtsHalCameraProviderV2_4TargetTest.cpp index c9f9bf6856..b4092ca211 100644 --- a/camera/provider/2.4/vts/functional/VtsHalCameraProviderV2_4TargetTest.cpp +++ b/camera/provider/2.4/vts/functional/VtsHalCameraProviderV2_4TargetTest.cpp @@ -40,15 +40,17 @@ #include <android/hardware/camera/metadata/3.4/types.h> #include <android/hardware/camera/provider/2.4/ICameraProvider.h> #include <android/hardware/camera/provider/2.5/ICameraProvider.h> +#include <android/hardware/camera/provider/2.6/ICameraProvider.h> +#include <android/hardware/camera/provider/2.6/ICameraProviderCallback.h> #include <android/hidl/manager/1.0/IServiceManager.h> #include <binder/MemoryHeapBase.h> #include <cutils/properties.h> #include <fmq/MessageQueue.h> #include <grallocusage/GrallocUsageConversion.h> +#include <gtest/gtest.h> #include <gui/BufferItemConsumer.h> #include <gui/BufferQueue.h> #include <gui/Surface.h> -#include <gtest/gtest.h> #include <hardware/gralloc.h> #include <hardware/gralloc1.h> #include <hidl/GtestPrinter.h> @@ -537,7 +539,7 @@ public: uint32_t id; ASSERT_TRUE(parseProviderName(service_name, &mProviderType, &id)); - castProvider(mProvider, &mProvider2_5); + castProvider(mProvider, &mProvider2_5, &mProvider2_6); notifyDeviceState(provider::V2_5::DeviceState::NORMAL); } virtual void TearDown() override {} @@ -709,8 +711,9 @@ public: sp<ICameraDeviceSession> *session /*out*/, camera_metadata_t **staticMeta /*out*/, ::android::sp<ICameraDevice> *device = nullptr/*out*/); - void castProvider(const sp<provider::V2_4::ICameraProvider> &provider, - sp<provider::V2_5::ICameraProvider> *provider2_5 /*out*/); + void castProvider(const sp<provider::V2_4::ICameraProvider>& provider, + sp<provider::V2_5::ICameraProvider>* provider2_5 /*out*/, + sp<provider::V2_6::ICameraProvider>* provider2_6 /*out*/); void castSession(const sp<ICameraDeviceSession> &session, int32_t deviceVersion, sp<device::V3_3::ICameraDeviceSession> *session3_3 /*out*/, sp<device::V3_4::ICameraDeviceSession> *session3_4 /*out*/, @@ -937,6 +940,7 @@ protected: // Camera provider service sp<ICameraProvider> mProvider; sp<::android::hardware::camera::provider::V2_5::ICameraProvider> mProvider2_5; + sp<::android::hardware::camera::provider::V2_6::ICameraProvider> mProvider2_6; // Camera provider type. std::string mProviderType; @@ -1649,6 +1653,33 @@ TEST_P(CameraHidlTest, setCallback) { return Void(); } }; + + struct ProviderCb2_6 + : public ::android::hardware::camera::provider::V2_6::ICameraProviderCallback { + virtual Return<void> cameraDeviceStatusChange(const hidl_string& cameraDeviceName, + CameraDeviceStatus newStatus) override { + ALOGI("camera device status callback name %s, status %d", cameraDeviceName.c_str(), + (int)newStatus); + return Void(); + } + + virtual Return<void> torchModeStatusChange(const hidl_string& cameraDeviceName, + TorchModeStatus newStatus) override { + ALOGI("Torch mode status callback name %s, status %d", cameraDeviceName.c_str(), + (int)newStatus); + return Void(); + } + + virtual Return<void> physicalCameraDeviceStatusChange( + const hidl_string& cameraDeviceName, const hidl_string& physicalCameraDeviceName, + CameraDeviceStatus newStatus) override { + ALOGI("physical camera device status callback name %s, physical camera name %s," + " status %d", + cameraDeviceName.c_str(), physicalCameraDeviceName.c_str(), (int)newStatus); + return Void(); + } + }; + sp<ProviderCb> cb = new ProviderCb; auto status = mProvider->setCallback(cb); ASSERT_TRUE(status.isOk()); @@ -1656,6 +1687,16 @@ TEST_P(CameraHidlTest, setCallback) { status = mProvider->setCallback(nullptr); ASSERT_TRUE(status.isOk()); ASSERT_EQ(Status::OK, status); + + if (mProvider2_6.get() != nullptr) { + sp<ProviderCb2_6> cb = new ProviderCb2_6; + auto status = mProvider2_6->setCallback(cb); + ASSERT_TRUE(status.isOk()); + ASSERT_EQ(Status::OK, status); + status = mProvider2_6->setCallback(nullptr); + ASSERT_TRUE(status.isOk()); + ASSERT_EQ(Status::OK, status); + } } // Test if ICameraProvider::getCameraDeviceInterface returns Status::OK and non-null device @@ -5596,12 +5637,19 @@ void CameraHidlTest::castDevice(const sp<device::V3_2::ICameraDevice> &device, } //Cast camera provider to corresponding version if available -void CameraHidlTest::castProvider(const sp<ICameraProvider> &provider, - sp<provider::V2_5::ICameraProvider> *provider2_5 /*out*/) { +void CameraHidlTest::castProvider(const sp<ICameraProvider>& provider, + sp<provider::V2_5::ICameraProvider>* provider2_5 /*out*/, + sp<provider::V2_6::ICameraProvider>* provider2_6 /*out*/) { ASSERT_NE(nullptr, provider2_5); - auto castResult = provider::V2_5::ICameraProvider::castFrom(provider); - if (castResult.isOk()) { - *provider2_5 = castResult; + auto castResult2_5 = provider::V2_5::ICameraProvider::castFrom(provider); + if (castResult2_5.isOk()) { + *provider2_5 = castResult2_5; + } + + ASSERT_NE(nullptr, provider2_6); + auto castResult2_6 = provider::V2_6::ICameraProvider::castFrom(provider); + if (castResult2_6.isOk()) { + *provider2_6 = castResult2_6; } } diff --git a/camera/provider/2.5/default/Android.bp b/camera/provider/2.5/default/Android.bp index 4563362ddb..9ddf651440 100644 --- a/camera/provider/2.5/default/Android.bp +++ b/camera/provider/2.5/default/Android.bp @@ -52,6 +52,8 @@ cc_library_shared { "android.hardware.camera.provider@2.4-external", "android.hardware.camera.provider@2.5", "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", @@ -72,7 +74,8 @@ cc_library_shared { ], header_libs: [ "camera.device@3.4-external-impl_headers", - "camera.device@3.5-external-impl_headers" + "camera.device@3.5-external-impl_headers", + "camera.device@3.6-external-impl_headers" ], export_include_dirs: ["."], } @@ -165,7 +168,10 @@ cc_binary { "android.hardware.camera.provider@2.5", "android.hardware.camera.provider@2.5-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", @@ -179,5 +185,6 @@ cc_binary { "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", ], } diff --git a/camera/provider/2.6/Android.bp b/camera/provider/2.6/Android.bp new file mode 100644 index 0000000000..e69819c863 --- /dev/null +++ b/camera/provider/2.6/Android.bp @@ -0,0 +1,26 @@ +// This file is autogenerated by hidl-gen -Landroidbp. + +hidl_interface { + name: "android.hardware.camera.provider@2.6", + root: "android.hardware", + vndk: { + enabled: true, + }, + srcs: [ + "types.hal", + "ICameraProvider.hal", + "ICameraProviderCallback.hal", + ], + interfaces: [ + "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.provider@2.4", + "android.hardware.camera.provider@2.5", + "android.hardware.graphics.common@1.0", + "android.hidl.base@1.0", + ], + gen_java: false, +} diff --git a/camera/provider/2.6/ICameraProvider.hal b/camera/provider/2.6/ICameraProvider.hal new file mode 100644 index 0000000000..0948db6ea3 --- /dev/null +++ b/camera/provider/2.6/ICameraProvider.hal @@ -0,0 +1,110 @@ +/* + * Copyright (C) 2020 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.hardware.camera.provider@2.6; + +import @2.5::ICameraProvider; +import android.hardware.camera.common@1.0::Status; +import android.hardware.camera.device@3.4::StreamConfiguration; + +/** + * Camera provider HAL + * + * @2.6::adds support for the getConcurrentStreamingCameraIds() and + * isConcurrentStreamCombinationSupported() + * @2.6::ICameraProviderCallback to receive physical camera availability + * callbacks for logical multi-cameras. + */ +interface ICameraProvider extends @2.5::ICameraProvider { + /** + * getConcurrentStreamingCameraIds + * + * Get a vector of combinations of camera device ids that are able to + * configure streams concurrently. Each camera device advertised in a + * combination MUST at the very least support the following streams while + * streaming concurrently with the other camera ids in the combination. + * + * Target 1 Target 2 + * --------------------------------------------- + * | Type | Size | Type | Size | + * --------------------------------------------- + * | YUV | 1280 X 720 | | + * --------------------------------------------- + * | PRIV | 1280 X 720 | | + * --------------------------------------------- + * | YUV | 1280 X 720 | YUV |1280 X 720| + * --------------------------------------------- + * | PRIV | 1280 X 720 | PRIV |1280 X 720| + * --------------------------------------------- + * | PRIV | 1280 X 720 | YUV |1280 X 720| + * --------------------------------------------- + + * @return status Status code for the operation + * @return cameraIds a list of camera id combinations that support + * concurrent stream configurations with the minimum guarantees + * specified. + */ + getConcurrentStreamingCameraIds() generates (Status status, vec<vec<string>> cameraIds); + + /** + * isConcurrentStreamCombinationSupported: + * + * Check for device support of specific camera stream combinations while + * streaming concurrently with other devices. + * + * The per device streamList must contain at least one output-capable stream, and may + * not contain more than one input-capable stream. + * In contrast to regular stream configuration the framework does not create + * or initialize any actual streams. This means that Hal must not use or + * consider the stream "id" value. + * + * ------------------------------------------------------------------------ + * + * Preconditions: + * + * The framework can call this method at any time before, during and + * after active session configuration per device. This means that calls must not + * impact the performance of pending camera requests in any way. In + * particular there must not be any glitches or delays during normal + * camera streaming. + * + * The framework must not call this method with any combination of camera + * ids that is not a subset of the camera ids advertised by getConcurrentStreamingCameraIds of + * the same provider. + * + * Performance requirements: + * This call is expected to be significantly faster than stream + * configuration. In general HW and SW camera settings must not be + * changed and there must not be a user-visible impact on camera performance. + * + * @param configs a vector of camera ids and their corresponding stream + * configurations that need to be queried for support. + * + * @return status Status code for the operation, one of: + * OK: + * On successful stream combination query. + * METHOD_NOT_SUPPORTED: + * The camera provider does not support stream combination query. + * INTERNAL_ERROR: + * The stream combination query cannot complete due to internal + * error. + * @return true in case the stream combination is supported, false otherwise. + * + * + */ + isConcurrentStreamCombinationSupported(vec<CameraIdAndStreamCombination> configs) + generates (Status status, bool queryStatus); +}; diff --git a/camera/provider/2.6/ICameraProviderCallback.hal b/camera/provider/2.6/ICameraProviderCallback.hal new file mode 100644 index 0000000000..42c1092676 --- /dev/null +++ b/camera/provider/2.6/ICameraProviderCallback.hal @@ -0,0 +1,54 @@ +/* + * Copyright (C) 2020 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.hardware.camera.provider@2.6; + +import android.hardware.camera.common@1.0::types; +import android.hardware.camera.provider@2.4::ICameraProviderCallback; + +/** + * Callback functions for a camera provider HAL to use to inform the camera + * service of changes to the camera subsystem. + * + * Version 2.6 adds support for physical camera device status callback for + * multi-camera. + */ +interface ICameraProviderCallback extends @2.4::ICameraProviderCallback { + + /** + * cameraPhysicalDeviceStatusChange: + * + * Callback to the camera service to indicate that the state of a physical + * camera device of a logical multi-camera has changed. + * + * On camera service startup, when ICameraProvider::setCallback is invoked, + * the camera service must assume that all physical devices backing internal + * multi-camera devices are in the CAMERA_DEVICE_STATUS_PRESENT state. + * + * The provider must call this method to inform the camera service of any + * initially NOT_PRESENT physical devices, as soon as the callbacks are available + * through setCallback. + * + * @param cameraDeviceName The name of the logical multi-camera whose + * physical camera has a new status. + * @param physicalCameraDeviceName The name of the physical camera device + * that has a new status. + * @param newStatus The new status that device is in. + * + */ + physicalCameraDeviceStatusChange(string cameraDeviceName, + string physicalCameraDeviceName, CameraDeviceStatus newStatus); +}; diff --git a/camera/provider/2.6/types.hal b/camera/provider/2.6/types.hal new file mode 100644 index 0000000000..24c62aa24d --- /dev/null +++ b/camera/provider/2.6/types.hal @@ -0,0 +1,30 @@ +/* + * Copyright (C) 2020 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.hardware.camera.provider@2.6; + +import android.hardware.camera.device@3.4::StreamConfiguration; + +/** + * CameraIdAndStreamCombination: + * Pairs the cameraId and the StreamConfiguration to be + * tested with other concurrent camera id and StreamConfigurations + */ +struct CameraIdAndStreamCombination { + string cameraId; + + @3.4::StreamConfiguration streamConfiguration; +}; diff --git a/compatibility_matrices/Android.mk b/compatibility_matrices/Android.mk index 7c7f87f655..6d204cb952 100644 --- a/compatibility_matrices/Android.mk +++ b/compatibility_matrices/Android.mk @@ -115,27 +115,6 @@ LOCAL_MODULE := framework_compatibility_matrix.xml LOCAL_REQUIRED_MODULES := $(my_framework_matrix_deps) include $(BUILD_PHONY_PACKAGE) -# Final Framework Compatibility Matrix for OTA -include $(CLEAR_VARS) -include $(LOCAL_PATH)/clear_vars.mk -LOCAL_MODULE := verified_assembled_system_matrix.xml -LOCAL_MODULE_PATH := $(PRODUCT_OUT) -LOCAL_REQUIRED_MODULES := $(my_framework_matrix_deps) -LOCAL_GENERATED_SOURCES := $(call module-installed-files,$(LOCAL_REQUIRED_MODULES)) -LOCAL_ADD_VBMETA_VERSION_OVERRIDE := true - -ifdef BUILT_VENDOR_MANIFEST -LOCAL_GEN_FILE_DEPENDENCIES += $(BUILT_VENDOR_MANIFEST) -LOCAL_ASSEMBLE_VINTF_FLAGS += -c "$(BUILT_VENDOR_MANIFEST)" -endif - -ifneq ($(PRODUCT_OTA_ENFORCE_VINTF_KERNEL_REQUIREMENTS),true) -LOCAL_ASSEMBLE_VINTF_FLAGS += --no-kernel-requirements -endif - -include $(BUILD_FRAMEWORK_COMPATIBILITY_MATRIX) -BUILT_SYSTEM_MATRIX := $(LOCAL_BUILT_MODULE) - my_system_matrix_deps := my_framework_matrix_deps := my_empty_manifest := diff --git a/compatibility_matrices/compatibility_matrix.current.xml b/compatibility_matrices/compatibility_matrix.current.xml index 24f114b811..9a8f336ff3 100644 --- a/compatibility_matrices/compatibility_matrix.current.xml +++ b/compatibility_matrices/compatibility_matrix.current.xml @@ -9,7 +9,6 @@ </hal> <hal format="hidl" optional="false"> <name>android.hardware.audio</name> - <version>5.0</version> <version>6.0</version> <interface> <name>IDevicesFactory</name> @@ -18,7 +17,6 @@ </hal> <hal format="hidl" optional="false"> <name>android.hardware.audio.effect</name> - <version>5.0</version> <version>6.0</version> <interface> <name>IEffectsFactory</name> @@ -47,6 +45,7 @@ <interface> <name>IEvsEnumerator</name> <instance>default</instance> + <regex-instance>[a-z]+/[0-9]+</regex-instance> </interface> </hal> <hal format="aidl" optional="true"> @@ -66,7 +65,7 @@ </hal> <hal format="hidl" optional="true"> <name>android.hardware.biometrics.face</name> - <version>1.0</version> + <version>1.0-1</version> <interface> <name>IBiometricsFace</name> <instance>default</instance> @@ -122,7 +121,7 @@ </hal> <hal format="hidl" optional="true"> <name>android.hardware.camera.provider</name> - <version>2.4-5</version> + <version>2.4-6</version> <interface> <name>ICameraProvider</name> <regex-instance>[^/]+/[0-9]+</regex-instance> @@ -174,7 +173,7 @@ </hal> <hal format="hidl" optional="true"> <name>android.hardware.dumpstate</name> - <version>1.0</version> + <version>1.1</version> <interface> <name>IDumpstateDevice</name> <instance>default</instance> @@ -289,6 +288,13 @@ <instance>default</instance> </interface> </hal> + <hal format="aidl" optional="true"> + <name>android.hardware.light</name> + <interface> + <name>ILights</name> + <instance>default</instance> + </interface> + </hal> <hal format="hidl" optional="true"> <name>android.hardware.media.c2</name> <version>1.0-1</version> @@ -400,7 +406,7 @@ </hal> <hal format="hidl" optional="true"> <name>android.hardware.secure_element</name> - <version>1.0</version> + <version>1.0-2</version> <interface> <name>ISecureElement</name> <regex-instance>eSE[1-9][0-9]*</regex-instance> @@ -489,14 +495,6 @@ </interface> </hal> <hal format="hidl" optional="true"> - <name>android.hardware.vibrator</name> - <version>1.0-3</version> - <interface> - <name>IVibrator</name> - <instance>default</instance> - </interface> - </hal> - <hal format="hidl" optional="true"> <name>android.hardware.vr</name> <version>1.0</version> <interface> diff --git a/current.txt b/current.txt index 8724170496..3be26c2f74 100644 --- a/current.txt +++ b/current.txt @@ -451,9 +451,9 @@ ca15a738dedc2f4981925f7d7ff29c22bc3f8a848403dcf0c592c167de09d9af android.hardwar 443659bb9e27221e5da0d16c7a0ecb2dc3a9a03acc8a0b2196b47c50735e2d2e android.hardware.audio.effect@5.0::IVirtualizerEffect 78fed26a781cdca1b3bcb37520bff705d7764ee81db9cfd37014953c7ad2596e android.hardware.audio.effect@5.0::IVisualizerEffect 6385b6accab8a544e2ee54ba7bf5aa55dff6153bcedd80fdaae16fe9e0be7050 android.hardware.audio.effect@5.0::types +95aa2f59e29e2f84d8e84320ace9b6682b426a16e897b4bd241375cbee0e07f3 android.hardware.biometrics.face@1.0::types e18ff318f3fc43db37f554696dc4e551abb9b119bde53950f73e28ce33a97a40 android.hardware.biometrics.face@1.0::IBiometricsFace b6e55d7795bbafd011fb95a3b6d3954bf66c349e14cf107f3b72032ce3ceb448 android.hardware.biometrics.face@1.0::IBiometricsFaceClientCallback -95aa2f59e29e2f84d8e84320ace9b6682b426a16e897b4bd241375cbee0e07f3 android.hardware.biometrics.face@1.0::types ecedc58dbcdb13503c19c0ab160ac1dd0530bb1471164149282dd1463c684185 android.hardware.bluetooth.audio@2.0::IBluetoothAudioPort fb9c40e4deab40be5476477078fe3d8a4a4495fd9deef4321878d169d675c633 android.hardware.bluetooth.audio@2.0::IBluetoothAudioProvider f7431f3e3e4e3387fc6f27a6cf423eddcd824a395dc4349d302c995ab44a9895 android.hardware.bluetooth.audio@2.0::IBluetoothAudioProvidersFactory @@ -587,6 +587,9 @@ d3a344b7bd4c0d2658ae7209f55a979b8f53f361fd00f4fca29d5baa56d11fd2 android.hardwar 2410dd02d67786a732d36e80b0f8ccf55086604ef37f9838e2013ff2c571e404 android.hardware.camera.device@3.5::types cd06a7911b9acd4a653bbf7133888878fbcb3f84be177c7a3f1becaae3d8618f android.hardware.camera.metadata@3.2::types a05277065c28ebecd58118bd240fb8c55757361e8648c01f7c4dacdb7f2a95dc android.hardware.camera.metadata@3.3::types +9cb3df2bde2c6cd5fd96b7c41555420cacd7e276a556c684af91b7461c86460f android.hardware.gnss@1.0::IGnssCallback +bceee81ec1b59324abd05932b5620fda5a6589597c9cb3953ba7f3ea02cccd3e android.hardware.camera.provider@2.4::ICameraProvider +2ce820dc4f3c6d85721b65150ed2157c6e2e2055f866fb6c6ba4790f14408d66 android.hardware.camera.provider@2.4::ICameraProviderCallback b69a7615c508acf5c5201efd1bfa3262167874fc3594e2db5a3ff93addd8ac75 android.hardware.keymaster@4.0::IKeymasterDevice eb2fa0c883c2185d514be0b84c179b283753ef0c1b77b45b4f359bd23bba8b75 android.hardware.neuralnetworks@1.0::IPreparedModel 8eac60e1f724d141c71c69f06d4544acb720a55dfbbcd97fa01bb3d25ee4e2f5 android.hardware.neuralnetworks@1.0::types @@ -597,6 +600,8 @@ fb382e986c10b8fbb797a8546e8f9ea6d1107bfe6f3fb7e57f6bbbf1f807a906 android.hardwar a785a57447a81e9c130eef6904c3a5c256076c6a04588c40620ebd6fa2660d77 android.hardware.radio@1.2::types 1a6e2bd289f22931c526b21916910f1d4c436b7acb9556e4243de4ce8e6cc2e4 android.hardware.soundtrigger@2.0::ISoundTriggerHwCallback fd65298e1e09e0e3c781ab18305920d757dbe55a3b459ce17814ec5cf6dfee99 android.hardware.wifi@1.0::IWifiP2pIface +ff5dd821c2c7a9c78607159c4d788960b725487263c49d956ca5fa3d37008b45 android.hardware.wifi@1.2::IWifiStaIface +5751f230e86a36111e7c5b995577cbf89d8df76c8e6c7641199198f3db3a93f7 android.hardware.wifi@1.3::IWifiStaIface # HALs released in Android R e966a3437d6a98d9d9e14e9d672088771716031900c0deb55a0946c751a03a44 android.hardware.audio@6.0::types @@ -623,21 +628,32 @@ dd377f404a8e71f6191d295e10067db629b0f0c28e594af906f2bea5d87fe2cc android.hardwar 5237c42d3913ef569f07bec802568084b615155d05a7951e75085da54856508c android.hardware.audio.effect@6.0::IPresetReverbEffect 282193799d60bff27a84c65a36218c1e7d8f582f5828e2e059383d1b90aa56bd android.hardware.audio.effect@6.0::IVirtualizerEffect 0868e00f7c5ee16723bda1a8f57099763d04100ae7126a1c2d3a9a87c844a7e8 android.hardware.audio.effect@6.0::IVisualizerEffect +7e8e1c3d0173c5d503dd01cecff8e3864478557ca6b9e8cc2291598b1a4aea62 android.hardware.biometrics.face@1.1::IBiometricsFace +ae6315fd42196478ac08441cb489d854118001bca5b9b9fd58af5110952be30e android.hardware.biometrics.fingerprint@2.2::types +6828bbf18dc5d0f00c73341a10c8e4d574346c1abb1c2ed682ba5e9f8a3240d9 android.hardware.biometrics.fingerprint@2.2::IBiometricsFingerprint +82cad99f5feb2ea9bcd4579055edf4af8feb9fc602a6e4827ddd727d254d4991 android.hardware.biometrics.fingerprint@2.2::IBiometricsFingerprintClientCallback 79e115c8f8970b8b914bafc66df5425e065fda4dcda97222966ef12451d2a1cc android.hardware.bluetooth@1.1::IBluetoothHci 40ab2c6866c18d32baf6e49e3053949e79601f56963a791e93e68b9ee18f718d android.hardware.bluetooth@1.1::IBluetoothHciCallbacks 07d0a252b2d8fa35887908a996ba395cf392968395fc30afab791f46e0c22a52 android.hardware.boot@1.1::IBootControl 74049a402be913963edfdd80828a53736570e9d8124a1bf18166b6ed46a6b0ab android.hardware.boot@1.1::types +e88840e0558439cb54837514ddccd43877094951758f367e9c638084eb7455a6 android.hardware.camera.provider@2.6::ICameraProvider +8f8d9463508ff9cae88eb35c429fd0e2dbca0ca8f5de7fdf836cc0c4370becb6 android.hardware.camera.provider@2.6::ICameraProviderCallback c1aa508d00b66ed5feefea398fd5edf28fa651ac89773adad7dfda4e0a73a952 android.hardware.cas@1.2::ICas 9811f867def49b420d8c707f7e38d3bdd64f835244e1d2a5e9762ab9835672dc android.hardware.cas@1.2::ICasListener f18695dd36ee205640b8326a17453858a7b4596653aaa6ef0016b0aef1bd4dac android.hardware.cas@1.2::IMediaCasService 4d85e814f94949dae4dc6cb82bbd7d6bb24ffafda6ddb2eac928d2a4fc2e21ce android.hardware.cas@1.2::types 66931c2506fbb5af61f20138cb05e0a09e7bf67d6964c231d27c648933bb33ec android.hardware.drm@1.3::ICryptoFactory 994d08ab27d613022c258a9ec48cece7adf2a305e92df5d76ef923e2c6665f64 android.hardware.drm@1.3::IDrmFactory -3dacec7801968e1e4479724dc0180442d9e915466bff051f80996266b1a51c2c android.hardware.gnss@2.1::IGnss +881aa8720fb1d69aa9843bfab69d810ab7654a61d2f5ab5e2626cbf240f24eaf android.hardware.dumpstate@1.1::types +13b33f623521ded51a6c0f7ea5b77e97066d0aa1e38a83c2873f08ad67294f89 android.hardware.dumpstate@1.1::IDumpstateDevice +769d346927a94fd40ee80a5a976d8d15cf022ef99c5900738f4a82f26c0ed229 android.hardware.gnss@2.1::types +88371e0edf69a1f72bfc45ecb2335e9b145e87339d3eecc92664a1fb200213ba android.hardware.gnss@2.1::IGnss ba62e1e8993bfb9f27fa04816fa0f2241ae2d01edfa3d0c04182e2e5de80045c android.hardware.gnss@2.1::IGnssCallback ccdf3c0fb2c02a6d4dc57afb276c3497ae8172b80b00ebc0bf8a0238dd38b01d android.hardware.gnss@2.1::IGnssConfiguration 5a125c49ca83629e22afc8c39e865509343bfa2c38f0baea9a186bbac103492d android.hardware.gnss@2.1::IGnssMeasurement -0bfb291708dd4a7c6ec6b9883e2b8592357edde8d7e962ef83918e4a2154ce69 android.hardware.gnss@2.1::IGnssMeasurementCallback +d7bf37660a0946de9599dcbae997b077ee3e604fc2044534d40d3da04297a5d3 android.hardware.gnss@2.1::IGnssMeasurementCallback +6670e7780803a8c696c6391fda5589a334b1b37dc7be9393792ed35035413633 android.hardware.gnss.measurement_corrections@1.1::IMeasurementCorrections +a3f439b782a6a92aaf3c0250f3526e94e8bf8844c3d578f0815e21b12c431346 android.hardware.gnss.measurement_corrections@1.1::types ce8dbe76eb9ee94b46ef98f725be992e760a5751073d4f4912484026541371f3 android.hardware.health@2.1::IHealth 26f04510a0b57aba5167c5c0a7c2f077c2acbb98b81902a072517829fd9fd67f android.hardware.health@2.1::IHealthInfoCallback db47f4ceceb1f06c656f39caa70c557b0f8471ef59fd58611bea667ffca20101 android.hardware.health@2.1::types @@ -647,15 +663,16 @@ bbeee9604128ede83ee755b67e73b5ad29e6e1dbac9ec41fea6ffe2745b0c50a android.hardwar 6e1e28a96c90ba78d47257faea3f3bb4e6360affbbfa5822f0dc31211f9266ff android.hardware.identity@1.0::IWritableIdentityCredential 27ae3724053940462114228872b3ffaf0b8e6177d5ba97f5a76339d12b8a99dd android.hardware.keymaster@4.1::IKeymasterDevice adb0efdf1462e9b2e742c0dcadd598666aac551f178be06e755bfcdf5797abd0 android.hardware.keymaster@4.1::IOperation -ac429fca0da4ce91218768ec31b64ded88251f8a26d8c4f27c06abdc5b1926d9 android.hardware.keymaster@4.1::types +ddcf89cd8ee2df0d32aee55050826446fb64f7aafde0a7cd946c64f61b1a364c android.hardware.keymaster@4.1::types df9c79c4fdde2821550c6d5c3d07f5ec0adfb1b702561ce543c906ddef698703 android.hardware.media.c2@1.1::IComponent a3eddd9bbdc87e8c22764070037dd1154f1cf006e6fba93364c4f85d4c134a19 android.hardware.media.c2@1.1::IComponentStore 65c16331e57f6dd68b3971f06f78fe9e3209afb60630c31705aa355f9a52bf0d android.hardware.neuralnetworks@1.3::IBuffer d1f382d14e1384b907d5bb5780df7f01934650d556fedbed2f15a90773c657d6 android.hardware.neuralnetworks@1.3::IDevice 4167dc3ad35e9cd0d2057d4868c7675ae2c3c9d05bbd614c1f5dccfa5fd68797 android.hardware.neuralnetworks@1.3::IExecutionCallback -7d23020248194abbee8091cc624f39a5a6d7ccba338b172d5d2d3df0cceffbee android.hardware.neuralnetworks@1.3::IPreparedModel +2fa3679ad7c94b5e88724adcd560c561041068a4ca565c63830e68101988746a android.hardware.neuralnetworks@1.3::IFencedExecutionCallback +237b23b126a66f3432658020fed78cdd06ba6297459436fe6bae0ba753370833 android.hardware.neuralnetworks@1.3::IPreparedModel 0439a1fbbec7f16e5e4c653d85ac685d51bfafbae15b8f8cca530acdd7d6a8ce android.hardware.neuralnetworks@1.3::IPreparedModelCallback -ee65638f8af3f9f4f222e7208eaa9f1f8e7f8e0a21545846ba67d0e27624efa1 android.hardware.neuralnetworks@1.3::types +3646950b10f7cacdafca13609b0e18496cea942f3bdfe920494661856eff48bb android.hardware.neuralnetworks@1.3::types 3e01d4446cd69fd1c48f8572efd97487bc179564b32bd795800b97bbe10be37b android.hardware.wifi@1.4::IWifi c67aaf26a7a40d14ea61e70e20afacbd0bb906df1704d585ac8599fbb69dd44b android.hardware.wifi.hostapd@1.2::IHostapd 11f6448d15336361180391c8ebcdfd2d7cf77b3782d577e594d583aadc9c2877 android.hardware.wifi.hostapd@1.2::types @@ -664,20 +681,14 @@ a64467bae843569f0d465c5be7f0c7a5b987985b55a3ef4794dd5afc68538650 android.hardwar def77c7db95d374f11a111bfc4ed60f92451303642a43276c4e291988fcee625 android.hardware.wifi.supplicant@1.3::ISupplicantStaIfaceCallback 62cf050c593c1ec34b49178b5bdde72dd9b80d9bad3eb184e4f0cd564d28678c android.hardware.wifi.supplicant@1.3::ISupplicantStaNetwork 98592d193a717066facf91428426e5abe211e3bd718bc372e29fb944ddbe6e7c android.hardware.wifi.supplicant@1.3::types -## -# BEGIN Radio HAL Merge Conflict Avoidance Buffer - STOPSHIP if present -## -616456d7ce4435d88995f9fe0025a76bca14bd70799e4ca3ff4bae74d54d1166 android.hardware.radio@1.5::types -c68f5bd87f747f8e7968ff66ecc548b2d26f8e186b7bb805c11d6c883a838fc6 android.hardware.radio@1.5::IRadio +88fb40d98b89cfaafad33b06c95e5dcd51c4470962e8fbe80ed22884407f98a1 android.hardware.radio@1.5::types +8062d0a1a03594dd8b448adcf6f08856b5720f7e33f9b785a21d3ef74a4f211d android.hardware.radio@1.5::IRadio e96ae1c3a9c0689002ec2318e9c587f4f607c16a75a3cd38788b77eb91072021 android.hardware.radio@1.5::IRadioIndication -9e962eff568dc8c712d83846f8c27460de5005ed9b836d3e08390e8aa56b5a46 android.hardware.radio@1.5::IRadioResponse -5971a891d7d8843e9fb9f44583a9a0a265ec42fd5e4e1c95c9803454d21fabf7 android.hardware.radio.config@1.3::types +7f2439b48bda2961c6d629d0415eee66d519142cf9537f05e9d285153c70ca85 android.hardware.radio@1.5::IRadioResponse +dcc8872337f0135e81970e1d8d5fd7139160dc80e9be76f0ae05290fa7e472b8 android.hardware.radio.config@1.3::types a2977755bc5f1ef47f04b7f2400632efda6218e1515dba847da487145cfabc4f android.hardware.radio.config@1.3::IRadioConfig 742360c775313438b0f82256eac62fb5bbc76a6ae6f388573f3aa142fb2c1eea android.hardware.radio.config@1.3::IRadioConfigIndication 0006ab8e8b0910cbd3bbb08d5f17d5fac7d65a2bdad5f2334e4851db9d1e6fa8 android.hardware.radio.config@1.3::IRadioConfigResponse -## -# END Radio HAL Merge Conflict Avoidance Buffer - STOPSHIP if present -## -51d1c8d285e0456da2a3fdfbf4700c6277165d5e83219894d651c8ea0e39aa8b android.hardware.soundtrigger@2.3::types +4a6517ea4ad807855428b0101d8e1a486497bd88ab4300ba3b2be43d46d32580 android.hardware.soundtrigger@2.3::types 12d7533ff0754f45bf59ab300799074570a99a676545652c2c23abc73cb4515d android.hardware.soundtrigger@2.3::ISoundTriggerHw 7746fda1fbf9c7c132bae701cc5a161309e4f5e7f3e8065811045975ee86196d android.hardware.usb.gadget@1.1::IUsbGadget diff --git a/drm/1.0/default/OWNERS b/drm/1.0/default/OWNERS new file mode 100644 index 0000000000..ecb421cc1f --- /dev/null +++ b/drm/1.0/default/OWNERS @@ -0,0 +1,6 @@ +edwinwong@google.com +fredgc@google.com +jtinker@google.com +kylealexander@google.com +rfrias@google.com +robertshih@google.com diff --git a/drm/1.0/vts/OWNERS b/drm/1.0/vts/OWNERS new file mode 100644 index 0000000000..ecb421cc1f --- /dev/null +++ b/drm/1.0/vts/OWNERS @@ -0,0 +1,6 @@ +edwinwong@google.com +fredgc@google.com +jtinker@google.com +kylealexander@google.com +rfrias@google.com +robertshih@google.com diff --git a/drm/1.0/vts/functional/Android.bp b/drm/1.0/vts/functional/Android.bp index 4fbd54df50..235bfb406f 100644 --- a/drm/1.0/vts/functional/Android.bp +++ b/drm/1.0/vts/functional/Android.bp @@ -14,23 +14,75 @@ // limitations under the License. // -cc_test { - name: "VtsHalDrmV1_0TargetTest", +cc_library_static { + name: "libdrmvtshelper", defaults: ["VtsHalTargetTestDefaults"], + local_include_dirs: [ + "include", + ], srcs: [ - "drm_hal_clearkey_test.cpp", - "drm_hal_vendor_test.cpp", "vendor_modules.cpp", ], static_libs: [ + "android.hardware.drm@1.0-helper", + ], + export_include_dirs: ["include"], +} + +cc_library_static { + name: "android.hardware.drm@1.0-vts", + defaults: ["VtsHalTargetTestDefaults"], + local_include_dirs: [ + "include", + ], + srcs: [ + "drm_hal_clearkey_test.cpp", + "drm_hal_vendor_test.cpp", + ], + shared_libs: [ "android.hardware.drm@1.0", + "android.hidl.allocator@1.0", + "android.hidl.memory@1.0", + "libhidlmemory", + "libnativehelper", + ], + static_libs: [ "android.hardware.drm@1.0-helper", + "libcrypto_static", + "libdrmvtshelper", + ], + export_shared_lib_headers: [ + "android.hardware.drm@1.0", + "android.hidl.allocator@1.0", + "android.hidl.memory@1.0", + "libhidlmemory", + "libnativehelper", + ], + export_include_dirs: [ + "include", + ], +} + +cc_test { + name: "VtsHalDrmV1_0TargetTest", + defaults: ["VtsHalTargetTestDefaults"], + srcs: [ + "drm_hal_test_main.cpp", + ], + whole_static_libs: [ + "android.hardware.drm@1.0-vts", + ], + shared_libs: [ + "android.hardware.drm@1.0", "android.hidl.allocator@1.0", "android.hidl.memory@1.0", "libhidlmemory", "libnativehelper", - "libssl", + ], + static_libs: [ + "android.hardware.drm@1.0-helper", "libcrypto_static", + "libdrmvtshelper", ], test_suites: [ "general-tests", diff --git a/drm/1.0/vts/functional/drm_hal_clearkey_test.cpp b/drm/1.0/vts/functional/drm_hal_clearkey_test.cpp index 5713d2ef16..ebdc2d7350 100644 --- a/drm/1.0/vts/functional/drm_hal_clearkey_test.cpp +++ b/drm/1.0/vts/functional/drm_hal_clearkey_test.cpp @@ -16,127 +16,26 @@ #define LOG_TAG "drm_hal_clearkey_test@1.0" -#include <android/hardware/drm/1.0/ICryptoFactory.h> -#include <android/hardware/drm/1.0/ICryptoPlugin.h> -#include <android/hardware/drm/1.0/IDrmFactory.h> -#include <android/hardware/drm/1.0/IDrmPlugin.h> -#include <android/hardware/drm/1.0/types.h> -#include <android/hidl/allocator/1.0/IAllocator.h> -#include <gtest/gtest.h> -#include <hidl/GtestPrinter.h> -#include <hidl/HidlSupport.h> -#include <hidl/ServiceManagement.h> -#include <hidlmemory/mapping.h> -#include <log/log.h> #include <openssl/aes.h> -#include <memory> #include <random> -using ::android::hardware::drm::V1_0::BufferType; -using ::android::hardware::drm::V1_0::DestinationBuffer; -using ::android::hardware::drm::V1_0::ICryptoFactory; -using ::android::hardware::drm::V1_0::ICryptoPlugin; -using ::android::hardware::drm::V1_0::IDrmFactory; -using ::android::hardware::drm::V1_0::IDrmPlugin; -using ::android::hardware::drm::V1_0::KeyedVector; -using ::android::hardware::drm::V1_0::KeyValue; -using ::android::hardware::drm::V1_0::KeyRequestType; -using ::android::hardware::drm::V1_0::KeyType; -using ::android::hardware::drm::V1_0::Mode; -using ::android::hardware::drm::V1_0::Pattern; -using ::android::hardware::drm::V1_0::SecureStop; -using ::android::hardware::drm::V1_0::SecureStopId; -using ::android::hardware::drm::V1_0::SessionId; -using ::android::hardware::drm::V1_0::SharedBuffer; -using ::android::hardware::drm::V1_0::Status; -using ::android::hardware::drm::V1_0::SubSample; - -using ::android::hardware::hidl_array; -using ::android::hardware::hidl_string; -using ::android::hardware::hidl_memory; -using ::android::hardware::hidl_vec; -using ::android::hardware::Return; -using ::android::hidl::allocator::V1_0::IAllocator; -using ::android::hidl::memory::V1_0::IMemory; -using ::android::sp; +#include "android/hardware/drm/1.0/vts/drm_hal_clearkey_test.h" using std::string; -using std::unique_ptr; using std::random_device; using std::map; using std::mt19937; using std::vector; -/** - * These clearkey tests use white box knowledge of the legacy clearkey - * plugin to verify that the HIDL HAL services and interfaces are working. - * It is not intended to verify any vendor's HAL implementation. If you - * are looking for vendor HAL tests, see drm_hal_vendor_test.cpp - */ -#define ASSERT_OK(ret) ASSERT_TRUE(ret.isOk()) -#define EXPECT_OK(ret) EXPECT_TRUE(ret.isOk()) - -static const uint8_t kCommonPsshBoxUUID[16] = { - 0x10, 0x77, 0xEF, 0xEC, 0xC0, 0xB2, 0x4D, 0x02, - 0xAC, 0xE3, 0x3C, 0x1E, 0x52, 0xE2, 0xFB, 0x4B}; - -// To be used in mpd to specify drm scheme for players -static const uint8_t kClearKeyUUID[16] = { - 0xE2, 0x71, 0x9D, 0x58, 0xA9, 0x85, 0xB3, 0xC9, - 0x78, 0x1A, 0xB0, 0x30, 0xAF, 0x78, 0xD3, 0x0E}; - static const uint8_t kInvalidUUID[16] = { 0x10, 0x20, 0x30, 0x40, 0x50, 0x60, 0x70, 0x80, 0x10, 0x20, 0x30, 0x40, 0x50, 0x60, 0x70, 0x80}; -class DrmHalClearkeyFactoryTest : public ::testing::TestWithParam<std::string> { - public: - void SetUp() override { - const ::testing::TestInfo* const test_info = - ::testing::UnitTest::GetInstance()->current_test_info(); - ALOGD("Running test %s.%s", test_info->test_case_name(), - test_info->name()); - - const std::string instanceName = GetParam(); - drmFactory = IDrmFactory::getService(instanceName); - ASSERT_NE(nullptr, drmFactory.get()); - cryptoFactory = ICryptoFactory::getService(instanceName); - ASSERT_NE(nullptr, cryptoFactory.get()); - - const bool drmClearKey = drmFactory->isCryptoSchemeSupported(kClearKeyUUID); - const bool cryptoClearKey = cryptoFactory->isCryptoSchemeSupported(kClearKeyUUID); - EXPECT_EQ(drmClearKey, cryptoClearKey); - const bool supportsClearKey = drmClearKey && cryptoClearKey; - - const bool drmCommonPsshBox = drmFactory->isCryptoSchemeSupported(kCommonPsshBoxUUID); - const bool cryptoCommonPsshBox = cryptoFactory->isCryptoSchemeSupported(kCommonPsshBoxUUID); - EXPECT_EQ(drmCommonPsshBox, cryptoCommonPsshBox); - const bool supportsCommonPsshBox = drmCommonPsshBox && cryptoCommonPsshBox; - - EXPECT_EQ(supportsClearKey, supportsCommonPsshBox); - correspondsToThisTest = supportsClearKey && supportsCommonPsshBox; - - if (instanceName == "clearkey") { - EXPECT_TRUE(correspondsToThisTest); - - // TODO(b/147449315) - // Only the clearkey plugged into the "default" instance supports - // this test. Currently the "clearkey" instance fails some tests - // here. - GTEST_SKIP() << "Clearkey tests don't work with 'clearkey' instance yet."; - } - - if (!correspondsToThisTest) { - GTEST_SKIP() << "Cannot test clearkey features on non-clearkey DRM modules"; - } - } - - protected: - sp<IDrmFactory> drmFactory; - sp<ICryptoFactory> cryptoFactory; - - bool correspondsToThisTest; -}; +namespace android { +namespace hardware { +namespace drm { +namespace V1_0 { +namespace vts { /** * Ensure the factory doesn't support an invalid scheme UUID @@ -264,48 +163,6 @@ TEST_P(DrmHalClearkeyFactoryTest, CreateInvalidCryptoPlugin) { EXPECT_OK(res); } -class DrmHalClearkeyPluginTest : public DrmHalClearkeyFactoryTest { - public: - virtual void SetUp() override { - // Create factories - DrmHalClearkeyFactoryTest::SetUp(); - - if (!correspondsToThisTest) { - GTEST_SKIP() << "Cannot test clearkey features on non-clearkey DRM modules"; - } - - ASSERT_NE(nullptr, drmFactory.get()); - hidl_string packageName("android.hardware.drm.test"); - auto res = drmFactory->createPlugin( - kClearKeyUUID, packageName, - [this](Status status, const sp<IDrmPlugin>& plugin) { - EXPECT_EQ(Status::OK, status); - ASSERT_NE(nullptr, plugin.get()); - drmPlugin = plugin; - }); - ASSERT_OK(res); - - hidl_vec<uint8_t> initVec; - res = cryptoFactory->createPlugin( - kClearKeyUUID, initVec, - [this](Status status, const sp<ICryptoPlugin>& plugin) { - EXPECT_EQ(Status::OK, status); - ASSERT_NE(nullptr, plugin.get()); - cryptoPlugin = plugin; - }); - ASSERT_OK(res); - } - - SessionId openSession(); - void closeSession(const SessionId& sessionId); - hidl_vec<uint8_t> loadKeys(const SessionId& sessionId, const KeyType& type); - sp<IMemory> getDecryptMemory(size_t size, size_t index); - - protected: - sp<IDrmPlugin> drmPlugin; - sp<ICryptoPlugin> cryptoPlugin; -}; - /** * DrmPlugin tests */ @@ -966,30 +823,6 @@ TEST_P(DrmHalClearkeyPluginTest, SetMediaDrmSessionEmptySession) { * Decrypt tests */ -class DrmHalClearkeyDecryptTest : public DrmHalClearkeyPluginTest { - public: - void SetUp() override { - DrmHalClearkeyPluginTest::SetUp(); - - if (!correspondsToThisTest) { - GTEST_SKIP() << "Cannot test clearkey features on non-clearkey DRM modules"; - } - } - void fillRandom(const sp<IMemory>& memory); - hidl_array<uint8_t, 16> toHidlArray(const vector<uint8_t>& vec) { - EXPECT_EQ(16u, vec.size()); - return hidl_array<uint8_t, 16>(&vec[0]); - } - uint32_t decrypt(Mode mode, uint8_t* iv, const hidl_vec<SubSample>& subSamples, - const Pattern& pattern, Status status); - void aes_ctr_decrypt(uint8_t* dest, uint8_t* src, uint8_t* iv, - const hidl_vec<SubSample>& subSamples, const vector<uint8_t>& key); - void aes_cbc_decrypt(uint8_t* dest, uint8_t* src, uint8_t* iv, - const hidl_vec<SubSample>& subSamples, const vector<uint8_t>& key); - void decryptWithInvalidKeys(hidl_vec<uint8_t>& invalidResponse, - vector<uint8_t>& iv, const Pattern& noPattern, const vector<SubSample>& subSamples); -}; - void DrmHalClearkeyDecryptTest::fillRandom(const sp<IMemory>& memory) { random_device rd; mt19937 rand(rd()); @@ -1300,20 +1133,8 @@ TEST_P(DrmHalClearkeyDecryptTest, DecryptWithKeyTooLong) { decryptWithInvalidKeys(invalidResponse, iv, noPattern, subSamples); } -static const std::set<std::string> kAllInstances = [] { - std::vector<std::string> drmInstances = - android::hardware::getAllHalInstanceNames(IDrmFactory::descriptor); - std::vector<std::string> cryptoInstances = - android::hardware::getAllHalInstanceNames(ICryptoFactory::descriptor); - std::set<std::string> allInstances; - allInstances.insert(drmInstances.begin(), drmInstances.end()); - allInstances.insert(cryptoInstances.begin(), cryptoInstances.end()); - return allInstances; -}(); - -INSTANTIATE_TEST_SUITE_P(PerInstance, DrmHalClearkeyFactoryTest, testing::ValuesIn(kAllInstances), - android::hardware::PrintInstanceNameToString); -INSTANTIATE_TEST_SUITE_P(PerInstance, DrmHalClearkeyPluginTest, testing::ValuesIn(kAllInstances), - android::hardware::PrintInstanceNameToString); -INSTANTIATE_TEST_SUITE_P(PerInstance, DrmHalClearkeyDecryptTest, testing::ValuesIn(kAllInstances), - android::hardware::PrintInstanceNameToString); +} // namespace vts +} // namespace V1_0 +} // namespace drm +} // namespace hardware +} // namespace android diff --git a/drm/1.0/vts/functional/drm_hal_test_main.cpp b/drm/1.0/vts/functional/drm_hal_test_main.cpp new file mode 100644 index 0000000000..fd2538b051 --- /dev/null +++ b/drm/1.0/vts/functional/drm_hal_test_main.cpp @@ -0,0 +1,71 @@ +/* + * Copyright (C) 2020 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#define LOG_TAG "drm_hal_vendor_test@1.0" + +#include "vendor_modules.h" +#include "android/hardware/drm/1.0/vts/drm_hal_vendor_test.h" +#include "android/hardware/drm/1.0/vts/drm_hal_clearkey_test.h" + +using ::android::hardware::drm::V1_0::ICryptoFactory; +using ::android::hardware::drm::V1_0::IDrmFactory; + +using ::android::hardware::drm::V1_0::vts::DrmHalClearkeyFactoryTest; +using ::android::hardware::drm::V1_0::vts::DrmHalClearkeyPluginTest; +using ::android::hardware::drm::V1_0::vts::DrmHalClearkeyDecryptTest; + +using ::android::hardware::drm::V1_0::vts::DrmHalVendorFactoryTest; +using ::android::hardware::drm::V1_0::vts::DrmHalVendorPluginTest; +using ::android::hardware::drm::V1_0::vts::DrmHalVendorDecryptTest; + +/** + * Instantiate the set of test cases for each vendor module + */ + +static const std::vector<DrmHalTestParam> kAllInstances = [] { + std::vector<std::string> drmInstances = + android::hardware::getAllHalInstanceNames(IDrmFactory::descriptor); + std::vector<std::string> cryptoInstances = + android::hardware::getAllHalInstanceNames(ICryptoFactory::descriptor); + std::set<std::string> allInstances; + allInstances.insert(drmInstances.begin(), drmInstances.end()); + allInstances.insert(cryptoInstances.begin(), cryptoInstances.end()); + + std::vector<DrmHalTestParam> allInstanceUuidCombos; + auto noUUID = [](std::string s) { return DrmHalTestParam(s); }; + std::transform(allInstances.begin(), allInstances.end(), + std::back_inserter(allInstanceUuidCombos), noUUID); + return allInstanceUuidCombos; +}(); + +INSTANTIATE_TEST_CASE_P(DrmHalVendorFactoryTestCases, DrmHalVendorFactoryTest, + testing::ValuesIn(kAllInstances), + drm_vts::PrintParamInstanceToString); + +INSTANTIATE_TEST_CASE_P(DrmHalVendorPluginTestCases, DrmHalVendorPluginTest, + testing::ValuesIn(kAllInstances), + drm_vts::PrintParamInstanceToString); + +INSTANTIATE_TEST_CASE_P(DrmHalVendorDecryptTestCases, DrmHalVendorDecryptTest, + testing::ValuesIn(kAllInstances), + drm_vts::PrintParamInstanceToString); + +INSTANTIATE_TEST_SUITE_P(PerInstance, DrmHalClearkeyFactoryTest, testing::ValuesIn(kAllInstances), + drm_vts::PrintParamInstanceToString); +INSTANTIATE_TEST_SUITE_P(PerInstance, DrmHalClearkeyPluginTest, testing::ValuesIn(kAllInstances), + drm_vts::PrintParamInstanceToString); +INSTANTIATE_TEST_SUITE_P(PerInstance, DrmHalClearkeyDecryptTest, testing::ValuesIn(kAllInstances), + drm_vts::PrintParamInstanceToString); diff --git a/drm/1.0/vts/functional/drm_hal_vendor_module_api.h b/drm/1.0/vts/functional/drm_hal_vendor_module_api.h index b8b205265d..a8b5ade31c 100644..120000 --- a/drm/1.0/vts/functional/drm_hal_vendor_module_api.h +++ b/drm/1.0/vts/functional/drm_hal_vendor_module_api.h @@ -1,234 +1 @@ -/* - * Copyright (C) 2017 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 DRM_HAL_VENDOR_MODULE_API_H -#define DRM_HAL_VENDOR_MODULE_API_H - -#include <stdint.h> -#include <map> -#include <string> -#include <vector> - -/** - * The DRM and Crypto HALs interact with vendor-provided HAL implementations - * that have DRM-specific capabilities. Since the VTS tests cannot contain - * DRM-specific functionality, supporting modules are required to enable VTS - * to validate HAL implementations in a generic way. If the vendor-specific - * VTS module is not provided for a given drm HAL implementation, only very - * small subset of functionality can be verified. - * - * As an example, a DRM HAL implementation interacts with a DRM-specific - * license server to obtain licenses for decrypting content. The DRM HAL - * implementation generates a key request message, delivers it to the server - * and receives a key response message which is then loaded into the HAL. Once - * the keys are loaded, the Crypto HAL decryption functionality and performance - * and other associated APIs can be tested by the common VTS test suite. - * - * Vendor-specific VTS modules are shared libraries used by the DRM VTS test. - * They provide a set of functions to support VTS testing of the DRM HAL module. - * - * The modules are placed in a common location on the file system. The VTS test - * scans through all vendor-provided support libraries and runs the VTS test - * suite on each library that is found. - * - * The vendor-specific module exposes an extern “C” vendorModuleFactory() - * function that returns a DrmHalVTSVendorModule instance. DrmHalVTSVendorModule - * instances are versioned, where each version is represented by subclass of - * DrmHalVTSVendorModule that corresponds to the API version. For example, a - * vendor-specific module that implements version 1 of the API would return a - * DrmHalVTSVendorModule_V1 from the vendorModuleFactory() function. - */ - -class DrmHalVTSVendorModule; - -extern "C" { -/** - * The factory method for creating DrmHalVTSVendorModule instances. The returned - * instance will be a subclass of DrmHalVTSVendorModule that corresponds to the - * supported API version. - */ -DrmHalVTSVendorModule* vendorModuleFactory(); -}; - -class DrmHalVTSVendorModule { - public: - DrmHalVTSVendorModule() : installed(true) {} - virtual ~DrmHalVTSVendorModule() {} - - /** - * Return the vendor-specific module API version. The version is an integer - * value with initial version 1. The API version indicates which subclass - * version DrmHalVTSVendorModule this instance is. - */ - virtual uint32_t getAPIVersion() const = 0; - - /** - * Return the UUID for the DRM HAL implementation. Protection System - * Specific - * UUID (see http://dashif.org/identifiers/protection/) - */ - virtual std::vector<uint8_t> getUUID() const = 0; - - /** - * Return the service name for the DRM HAL implementation. If the hal is a - * legacy - * drm plugin, i.e. not running as a HIDL service, return the empty string. - */ - virtual std::string getServiceName() const = 0; - - /** - * Set a flag in the vendor module to indicate whether or not the drm - * scheme corresponding to this module is installed on the device. - */ - void setInstalled(bool flag) {installed = flag;} - bool isInstalled() const {return installed;} - - private: - bool installed; - DrmHalVTSVendorModule(const DrmHalVTSVendorModule&) = delete; - void operator=(const DrmHalVTSVendorModule&) = delete; -}; - -/** - * API Version 1. This is the baseline version that supports a minimal set - * of VTS tests. - */ -class DrmHalVTSVendorModule_V1 : public DrmHalVTSVendorModule { - public: - DrmHalVTSVendorModule_V1() {} - virtual ~DrmHalVTSVendorModule_V1() {} - - virtual uint32_t getAPIVersion() const { return 1; } - - /** - * Handle a provisioning request. This function will be called if the HAL - * module's getProvisionRequest returns a provision request. The vendor - * module should process the provisioning request, either by sending it - * to a provisioning server, or generating a mock response. The resulting - * provisioning response is returned to the VTS test. - * - * @param provisioningRequest the provisioning request recieved from - * the DRM HAL - * @param url the default url the HAL implementation provided with the - * provisioning request - * @return the generated provisioning response - */ - virtual std::vector<uint8_t> handleProvisioningRequest( - const std::vector<uint8_t>& provisioningRequest, - const std::string& url) = 0; - - /** - * Content configuration specifies content-specific parameters associated - * with a key request/response transaction. It allows the VTS test to - * request keys and use them to perform decryption. - */ - struct ContentConfiguration { - /** - * Assign a name for this configuration that will be referred to - * in log messages. - */ - const std::string name; - - /** - * Server to use when requesting a key response. This url will be - * passed as a parameter to the vendor vts module along with the - * key request to perform the key request transaction. - */ - const std::string serverUrl; - - /** - * Initialization data provided to getKeyRequest, e.g. PSSH for CENC - * content - */ - const std::vector<uint8_t> initData; - - /** - * Mime type provided to getKeyRequest, e.g. "video/mp4", or "cenc" - */ - const std::string mimeType; - - /** - * Optional parameters to be associated with the key request - */ - const std::map<std::string, std::string> optionalParameters; - - /** - * Define license policy attributes for the content configuration. - * These attributes can affect which tests are able to be applied. - */ - struct Policy { - /** - * Indicate if the license policy allows offline playback. - * Content configurated with this policy supports KeyType::OFFLINE - * key requests/responses. A vendor module should provide at least - * one content configuration where allowOffline is true if the drm - * scheme supports offline content. - */ - bool allowOffline; - } policy; - - /** - * The keys that will be available once the keys are loaded - */ - struct Key { - /** - * Indicate if the key content is configured to require secure - * buffers, where the output buffers are protected and cannot be - * accessed by the non-secure cpu. A vendor module should provide - * at least one content configurations where isSecure is false, to - * allow decrypt result verification tests to be run. - */ - bool isSecure; - - /** - * A key ID identifies a key to use for decryption - */ - const std::vector<uint8_t> keyId; - - /** - * The clear content key is provided to generate expected values for - * validating decryption. - */ - const std::vector<uint8_t> clearContentKey; - }; - std::vector<Key> keys; - }; - - /** - * Return a list of content configurations that can be exercised by the - * VTS test. - */ - virtual std::vector<ContentConfiguration> - getContentConfigurations() const = 0; - - /** - * Handle a key request. This function will be called if the HAL - * module's getKeyRequest returns a key request. The vendor - * module should process the key request, either by sending it - * to a license server, or by generating a mock response. The resulting - * key response is returned to the VTS test. - * - * @param keyRequest the key request recieved from the DRM HAL - * @param serverUrl the url of the key server that was supplied - * by the ContentConfiguration - * @return the generated key response - */ - virtual std::vector<uint8_t> handleKeyRequest( - const std::vector<uint8_t>& keyRequest, - const std::string& serverUrl) = 0; -}; - -#endif // DRM_HAL_VENDOR_MODULE_API_H +include/drm_hal_vendor_module_api.h
\ No newline at end of file diff --git a/drm/1.0/vts/functional/drm_hal_vendor_test.cpp b/drm/1.0/vts/functional/drm_hal_vendor_test.cpp index 2259985712..5c6c98b32f 100644 --- a/drm/1.0/vts/functional/drm_hal_vendor_test.cpp +++ b/drm/1.0/vts/functional/drm_hal_vendor_test.cpp @@ -16,138 +16,46 @@ #define LOG_TAG "drm_hal_vendor_test@1.0" -#include <android/hardware/drm/1.0/ICryptoFactory.h> -#include <android/hardware/drm/1.0/ICryptoPlugin.h> -#include <android/hardware/drm/1.0/IDrmFactory.h> -#include <android/hardware/drm/1.0/IDrmPlugin.h> -#include <android/hardware/drm/1.0/IDrmPluginListener.h> -#include <android/hardware/drm/1.0/types.h> -#include <android/hidl/allocator/1.0/IAllocator.h> -#include <gtest/gtest.h> -#include <hidl/GtestPrinter.h> -#include <hidl/ServiceManagement.h> -#include <hidlmemory/mapping.h> -#include <log/log.h> #include <openssl/aes.h> -#include <memory> #include <random> #include "drm_hal_vendor_module_api.h" #include "vendor_modules.h" #include <VtsHalHidlTargetCallbackBase.h> -using ::android::hardware::drm::V1_0::BufferType; -using ::android::hardware::drm::V1_0::DestinationBuffer; -using ::android::hardware::drm::V1_0::EventType; -using ::android::hardware::drm::V1_0::ICryptoFactory; -using ::android::hardware::drm::V1_0::ICryptoPlugin; -using ::android::hardware::drm::V1_0::IDrmFactory; -using ::android::hardware::drm::V1_0::IDrmPlugin; -using ::android::hardware::drm::V1_0::IDrmPluginListener; -using ::android::hardware::drm::V1_0::KeyedVector; -using ::android::hardware::drm::V1_0::KeyRequestType; -using ::android::hardware::drm::V1_0::KeyStatus; -using ::android::hardware::drm::V1_0::KeyStatusType; -using ::android::hardware::drm::V1_0::KeyType; -using ::android::hardware::drm::V1_0::KeyValue; -using ::android::hardware::drm::V1_0::Mode; -using ::android::hardware::drm::V1_0::Pattern; -using ::android::hardware::drm::V1_0::SecureStop; -using ::android::hardware::drm::V1_0::SecureStopId; -using ::android::hardware::drm::V1_0::SessionId; -using ::android::hardware::drm::V1_0::SharedBuffer; -using ::android::hardware::drm::V1_0::Status; -using ::android::hardware::drm::V1_0::SubSample; - -using ::android::hardware::hidl_array; -using ::android::hardware::hidl_memory; -using ::android::hardware::hidl_string; -using ::android::hardware::hidl_vec; -using ::android::hardware::Return; -using ::android::hardware::Void; -using ::android::hidl::allocator::V1_0::IAllocator; -using ::android::hidl::memory::V1_0::IMemory; -using ::android::sp; - -using std::string; -using std::unique_ptr; +#include "android/hardware/drm/1.0/vts/drm_hal_vendor_test.h" + using std::random_device; -using std::map; using std::mt19937; -using std::vector; - -using ContentConfiguration = ::DrmHalVTSVendorModule_V1::ContentConfiguration; -using Key = ::DrmHalVTSVendorModule_V1::ContentConfiguration::Key; - -#define ASSERT_OK(ret) ASSERT_TRUE(ret.isOk()) -#define EXPECT_OK(ret) EXPECT_TRUE(ret.isOk()) - -#define RETURN_IF_SKIPPED \ - if (vendorModule == nullptr || !vendorModule->isInstalled()) { \ - GTEST_SKIP() << "This drm scheme not supported." \ - << " library:" << GetParam() << " service-name:" \ - << (vendorModule == nullptr ? "N/A" : vendorModule->getServiceName()) \ - << std::endl; \ - return; \ - } static const uint8_t kInvalidUUID[16] = { 0x10, 0x20, 0x30, 0x40, 0x50, 0x60, 0x70, 0x80, 0x10, 0x20, 0x30, 0x40, 0x50, 0x60, 0x70, 0x80, }; -static drm_vts::VendorModules* gVendorModules = nullptr; - -class DrmHalVendorFactoryTest : public testing::TestWithParam<std::string> { - public: - DrmHalVendorFactoryTest() - : vendorModule( - static_cast<DrmHalVTSVendorModule_V1*>(gVendorModules->getModule(GetParam()))) {} - - virtual ~DrmHalVendorFactoryTest() {} - - virtual void SetUp() { - const ::testing::TestInfo* const test_info = - ::testing::UnitTest::GetInstance()->current_test_info(); - ALOGD("Running test %s.%s from vendor module %s", test_info->test_case_name(), - test_info->name(), GetParam().c_str()); - - const std::string instance = GetParam(); - if (instance == "widevine") { - ASSERT_NE(nullptr, vendorModule.get()); - } - - if (vendorModule == nullptr) { - GTEST_SKIP() << "No vendor module available"; - } else { - ASSERT_EQ(instance, vendorModule->getServiceName()); - contentConfigurations = vendorModule->getContentConfigurations(); - } - - drmFactory = IDrmFactory::getService(instance); - ASSERT_NE(nullptr, drmFactory.get()); - cryptoFactory = ICryptoFactory::getService(instance); - ASSERT_NE(nullptr, cryptoFactory.get()); - - // If drm scheme not installed skip subsequent tests - if (!drmFactory->isCryptoSchemeSupported(getVendorUUID())) { - // no GTEST_SKIP since only some tests require the module - vendorModule->setInstalled(false); - } - } - - protected: - hidl_array<uint8_t, 16> getVendorUUID() { - if (vendorModule == nullptr) return {}; - vector<uint8_t> uuid = vendorModule->getUUID(); - return hidl_array<uint8_t, 16>(&uuid[0]); +static drm_vts::VendorModules* gVendorModules = [] { +#if defined(__LP64__) + const char* kModulePath = "/data/local/tmp/64/lib"; +#else + const char* kModulePath = "/data/local/tmp/32/lib"; +#endif + auto modules = new drm_vts::VendorModules(kModulePath); + if (modules->getPathList().size() == 0) { + std::cerr << "WARNING: No vendor modules found in " << kModulePath << + ", all vendor tests will be skipped" << std::endl; } + return modules; +}(); - sp<IDrmFactory> drmFactory; - sp<ICryptoFactory> cryptoFactory; - unique_ptr<DrmHalVTSVendorModule_V1> vendorModule; - vector<ContentConfiguration> contentConfigurations; -}; +namespace android { +namespace hardware { +namespace drm { +namespace V1_0 { +namespace vts { + +DrmHalVendorFactoryTest::DrmHalVendorFactoryTest() + : vendorModule(static_cast<DrmHalVTSVendorModule_V1*>( + gVendorModules->getModuleByName(GetParam().instance_))) {} // getModuleByName TEST_P(DrmHalVendorFactoryTest, ValidateConfigurations) { const char* kVendorStr = "Vendor module "; @@ -195,8 +103,8 @@ TEST_P(DrmHalVendorFactoryTest, EmptyPluginUUIDNotSupported) { */ TEST_P(DrmHalVendorFactoryTest, PluginConfigUUIDSupported) { RETURN_IF_SKIPPED; - EXPECT_TRUE(drmFactory->isCryptoSchemeSupported(getVendorUUID())); - EXPECT_TRUE(cryptoFactory->isCryptoSchemeSupported(getVendorUUID())); + EXPECT_TRUE(drmFactory->isCryptoSchemeSupported(getUUID())); + EXPECT_TRUE(cryptoFactory->isCryptoSchemeSupported(getUUID())); } /** @@ -232,7 +140,7 @@ TEST_P(DrmHalVendorFactoryTest, CreateVendorDrmPlugin) { RETURN_IF_SKIPPED; hidl_string packageName("android.hardware.drm.test"); auto res = drmFactory->createPlugin( - getVendorUUID(), packageName, + getUUID(), packageName, [&](Status status, const sp<IDrmPlugin>& plugin) { EXPECT_EQ(Status::OK, status); EXPECT_NE(nullptr, plugin.get()); @@ -247,7 +155,7 @@ TEST_P(DrmHalVendorFactoryTest, CreateVendorCryptoPlugin) { RETURN_IF_SKIPPED; hidl_vec<uint8_t> initVec; auto res = cryptoFactory->createPlugin( - getVendorUUID(), initVec, + getUUID(), initVec, [&](Status status, const sp<ICryptoPlugin>& plugin) { EXPECT_EQ(Status::OK, status); EXPECT_NE(nullptr, plugin.get()); @@ -285,50 +193,6 @@ TEST_P(DrmHalVendorFactoryTest, CreateInvalidCryptoPlugin) { EXPECT_OK(res); } -class DrmHalVendorPluginTest : public DrmHalVendorFactoryTest { - public: - virtual ~DrmHalVendorPluginTest() {} - virtual void SetUp() override { - // Create factories - DrmHalVendorFactoryTest::SetUp(); - RETURN_IF_SKIPPED; - - hidl_string packageName("android.hardware.drm.test"); - auto res = drmFactory->createPlugin( - getVendorUUID(), packageName, - [this](Status status, const sp<IDrmPlugin>& plugin) { - EXPECT_EQ(Status::OK, status); - ASSERT_NE(nullptr, plugin.get()); - drmPlugin = plugin; - }); - ASSERT_OK(res); - - hidl_vec<uint8_t> initVec; - res = cryptoFactory->createPlugin( - getVendorUUID(), initVec, - [this](Status status, const sp<ICryptoPlugin>& plugin) { - EXPECT_EQ(Status::OK, status); - ASSERT_NE(nullptr, plugin.get()); - cryptoPlugin = plugin; - }); - ASSERT_OK(res); - } - - virtual void TearDown() override {} - - SessionId openSession(); - void closeSession(const SessionId& sessionId); - sp<IMemory> getDecryptMemory(size_t size, size_t index); - KeyedVector toHidlKeyedVector(const map<string, string>& params); - hidl_vec<uint8_t> loadKeys(const SessionId& sessionId, - const ContentConfiguration& configuration, - const KeyType& type); - - protected: - sp<IDrmPlugin> drmPlugin; - sp<ICryptoPlugin> cryptoPlugin; -}; - /** * DrmPlugin tests */ @@ -1193,7 +1057,7 @@ sp<IMemory> DrmHalVendorPluginTest::getDecryptMemory(size_t size, EXPECT_OK(res); - sp<IMemory> mappedMemory = mapMemory(hidlMemory); + sp<IMemory> mappedMemory = android::hardware::mapMemory(hidlMemory); EXPECT_NE(nullptr, mappedMemory.get()); res = cryptoPlugin->setSharedBufferBase(hidlMemory, index); EXPECT_OK(res); @@ -1237,29 +1101,6 @@ TEST_P(DrmHalVendorPluginTest, SetMediaDrmSessionEmptySession) { * Decrypt tests */ -class DrmHalVendorDecryptTest : public DrmHalVendorPluginTest { - public: - DrmHalVendorDecryptTest() = default; - virtual ~DrmHalVendorDecryptTest() {} - - protected: - void fillRandom(const sp<IMemory>& memory); - hidl_array<uint8_t, 16> toHidlArray(const vector<uint8_t>& vec) { - EXPECT_EQ(vec.size(), 16u); - return hidl_array<uint8_t, 16>(&vec[0]); - } - hidl_vec<KeyValue> queryKeyStatus(SessionId sessionId); - void removeKeys(SessionId sessionId); - uint32_t decrypt(Mode mode, bool isSecure, - const hidl_array<uint8_t, 16>& keyId, uint8_t* iv, - const hidl_vec<SubSample>& subSamples, const Pattern& pattern, - const vector<uint8_t>& key, Status expectedStatus); - void aes_ctr_decrypt(uint8_t* dest, uint8_t* src, uint8_t* iv, - const hidl_vec<SubSample>& subSamples, const vector<uint8_t>& key); - void aes_cbc_decrypt(uint8_t* dest, uint8_t* src, uint8_t* iv, - const hidl_vec<SubSample>& subSamples, const vector<uint8_t>& key); -}; - void DrmHalVendorDecryptTest::fillRandom(const sp<IMemory>& memory) { random_device rd; mt19937 rand(rd()); @@ -1566,47 +1407,8 @@ TEST_P(DrmHalVendorDecryptTest, AttemptDecryptWithKeysRemoved) { } } - -/** - * Instantiate the set of test cases for each vendor module - */ - -static const std::set<std::string> kAllInstances = [] { - std::vector<std::string> drmInstances = - android::hardware::getAllHalInstanceNames(IDrmFactory::descriptor); - std::vector<std::string> cryptoInstances = - android::hardware::getAllHalInstanceNames(ICryptoFactory::descriptor); - std::set<std::string> allInstances; - allInstances.insert(drmInstances.begin(), drmInstances.end()); - allInstances.insert(cryptoInstances.begin(), cryptoInstances.end()); - return allInstances; -}(); - -INSTANTIATE_TEST_CASE_P(DrmHalVendorFactoryTestCases, DrmHalVendorFactoryTest, - testing::ValuesIn(kAllInstances), - android::hardware::PrintInstanceNameToString); - -INSTANTIATE_TEST_CASE_P(DrmHalVendorPluginTestCases, DrmHalVendorPluginTest, - testing::ValuesIn(kAllInstances), - android::hardware::PrintInstanceNameToString); - -INSTANTIATE_TEST_CASE_P(DrmHalVendorDecryptTestCases, DrmHalVendorDecryptTest, - testing::ValuesIn(kAllInstances), - android::hardware::PrintInstanceNameToString); - -int main(int argc, char** argv) { -#if defined(__LP64__) - const char* kModulePath = "/data/local/tmp/64/lib"; -#else - const char* kModulePath = "/data/local/tmp/32/lib"; -#endif - gVendorModules = new drm_vts::VendorModules(kModulePath); - if (gVendorModules->getPathList().size() == 0) { - std::cerr << "WARNING: No vendor modules found in " << kModulePath << - ", all vendor tests will be skipped" << std::endl; - } - ::testing::InitGoogleTest(&argc, argv); - int status = RUN_ALL_TESTS(); - ALOGI("Test result = %d", status); - return status; -} +} // namespace vts +} // namespace V1_0 +} // namespace drm +} // namespace hardware +} // namespace android diff --git a/drm/1.0/vts/functional/include/android/hardware/drm/1.0/vts/drm_hal_clearkey_test.h b/drm/1.0/vts/functional/include/android/hardware/drm/1.0/vts/drm_hal_clearkey_test.h new file mode 100644 index 0000000000..ca707b81fb --- /dev/null +++ b/drm/1.0/vts/functional/include/android/hardware/drm/1.0/vts/drm_hal_clearkey_test.h @@ -0,0 +1,201 @@ +/* + * Copyright (C) 2020 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef DRM_HAL_CLEARKEY_TEST_H +#define DRM_HAL_CLEARKEY_TEST_H + +#include <android/hardware/drm/1.0/ICryptoFactory.h> +#include <android/hardware/drm/1.0/ICryptoPlugin.h> +#include <android/hardware/drm/1.0/IDrmFactory.h> +#include <android/hardware/drm/1.0/IDrmPlugin.h> +#include <android/hardware/drm/1.0/types.h> +#include <android/hidl/allocator/1.0/IAllocator.h> + +#include <gtest/gtest.h> +#include <hidl/HidlSupport.h> +#include <hidl/ServiceManagement.h> +#include <hidlmemory/mapping.h> +#include <log/log.h> + +#include "drm_vts_helper.h" + +using ::android::hidl::allocator::V1_0::IAllocator; +using ::android::hidl::memory::V1_0::IMemory; + +using ::drm_vts::DrmHalTestParam; +using ::drm_vts::PrintParamInstanceToString; + +using std::string; +using std::map; +using std::vector; + +/** + * These clearkey tests use white box knowledge of the legacy clearkey + * plugin to verify that the HIDL HAL services and interfaces are working. + * It is not intended to verify any vendor's HAL implementation. If you + * are looking for vendor HAL tests, see drm_hal_vendor_test.cpp + */ +#define ASSERT_OK(ret) ASSERT_TRUE(ret.isOk()) +#define EXPECT_OK(ret) EXPECT_TRUE(ret.isOk()) + +namespace android { +namespace hardware { +namespace drm { +namespace V1_0 { +namespace vts { + +class DrmHalClearkeyFactoryTest : public ::testing::TestWithParam<DrmHalTestParam> { + public: + void SetUp() override { + const ::testing::TestInfo* const test_info = + ::testing::UnitTest::GetInstance()->current_test_info(); + ALOGD("Running test %s.%s", test_info->test_case_name(), + test_info->name()); + + const std::string instanceName = GetParam().instance_; + drmFactory = IDrmFactory::getService(instanceName); + ASSERT_NE(nullptr, drmFactory.get()); + cryptoFactory = ICryptoFactory::getService(instanceName); + ASSERT_NE(nullptr, cryptoFactory.get()); + + const bool drmClearKey = drmFactory->isCryptoSchemeSupported(kClearKeyUUID); + const bool cryptoClearKey = cryptoFactory->isCryptoSchemeSupported(kClearKeyUUID); + EXPECT_EQ(drmClearKey, cryptoClearKey); + const bool supportsClearKey = drmClearKey && cryptoClearKey; + + const bool drmCommonPsshBox = drmFactory->isCryptoSchemeSupported(kCommonPsshBoxUUID); + const bool cryptoCommonPsshBox = cryptoFactory->isCryptoSchemeSupported(kCommonPsshBoxUUID); + EXPECT_EQ(drmCommonPsshBox, cryptoCommonPsshBox); + const bool supportsCommonPsshBox = drmCommonPsshBox && cryptoCommonPsshBox; + + EXPECT_EQ(supportsClearKey, supportsCommonPsshBox); + correspondsToThisTest = supportsClearKey && supportsCommonPsshBox; + + if (instanceName == "clearkey") { + EXPECT_TRUE(correspondsToThisTest); + + // TODO(b/147449315) + // Only the clearkey plugged into the "default" instance supports + // this test. Currently the "clearkey" instance fails some tests + // here. + GTEST_SKIP() << "Clearkey tests don't work with 'clearkey' instance yet."; + } + + if (!correspondsToThisTest) { + GTEST_SKIP() << "Cannot test clearkey features on non-clearkey DRM modules"; + } + } + + protected: + static constexpr uint8_t kCommonPsshBoxUUID[16] = { + 0x10, 0x77, 0xEF, 0xEC, 0xC0, 0xB2, 0x4D, 0x02, + 0xAC, 0xE3, 0x3C, 0x1E, 0x52, 0xE2, 0xFB, 0x4B}; + + // To be used in mpd to specify drm scheme for players + static constexpr uint8_t kClearKeyUUID[16] = { + 0xE2, 0x71, 0x9D, 0x58, 0xA9, 0x85, 0xB3, 0xC9, + 0x78, 0x1A, 0xB0, 0x30, 0xAF, 0x78, 0xD3, 0x0E}; + + sp<IDrmFactory> drmFactory; + sp<ICryptoFactory> cryptoFactory; + + bool correspondsToThisTest; +}; + +class DrmHalClearkeyPluginTest : public DrmHalClearkeyFactoryTest { + public: + virtual void SetUp() override { + // Create factories + DrmHalClearkeyFactoryTest::SetUp(); + + if (!correspondsToThisTest) { + GTEST_SKIP() << "Cannot test clearkey features on non-clearkey DRM modules"; + } + + ASSERT_NE(nullptr, drmFactory.get()); + hidl_string packageName("android.hardware.drm.test"); + auto res = drmFactory->createPlugin( + getUUID(), packageName, + [this](Status status, const sp<IDrmPlugin>& plugin) { + EXPECT_EQ(Status::OK, status); + ASSERT_NE(nullptr, plugin.get()); + drmPlugin = plugin; + }); + ASSERT_OK(res); + + hidl_vec<uint8_t> initVec; + res = cryptoFactory->createPlugin( + getUUID(), initVec, + [this](Status status, const sp<ICryptoPlugin>& plugin) { + EXPECT_EQ(Status::OK, status); + ASSERT_NE(nullptr, plugin.get()); + cryptoPlugin = plugin; + }); + ASSERT_OK(res); + } + + SessionId openSession(); + void closeSession(const SessionId& sessionId); + hidl_vec<uint8_t> loadKeys(const SessionId& sessionId, const KeyType& type); + sp<IMemory> getDecryptMemory(size_t size, size_t index); + + protected: + hidl_array<uint8_t, 16> getUUID() { + if (GetParamUUID() == hidl_array<uint8_t, 16>()) { + return kClearKeyUUID; + } + return GetParamUUID(); + } + + hidl_array<uint8_t, 16> GetParamUUID() { + return GetParam().scheme_; + } + + sp<IDrmPlugin> drmPlugin; + sp<ICryptoPlugin> cryptoPlugin; +}; + +class DrmHalClearkeyDecryptTest : public DrmHalClearkeyPluginTest { + public: + void SetUp() override { + DrmHalClearkeyPluginTest::SetUp(); + + if (!correspondsToThisTest) { + GTEST_SKIP() << "Cannot test clearkey features on non-clearkey DRM modules"; + } + } + void fillRandom(const sp<IMemory>& memory); + hidl_array<uint8_t, 16> toHidlArray(const vector<uint8_t>& vec) { + EXPECT_EQ(16u, vec.size()); + return hidl_array<uint8_t, 16>(&vec[0]); + } + uint32_t decrypt(Mode mode, uint8_t* iv, const hidl_vec<SubSample>& subSamples, + const Pattern& pattern, Status status); + void aes_ctr_decrypt(uint8_t* dest, uint8_t* src, uint8_t* iv, + const hidl_vec<SubSample>& subSamples, const vector<uint8_t>& key); + void aes_cbc_decrypt(uint8_t* dest, uint8_t* src, uint8_t* iv, + const hidl_vec<SubSample>& subSamples, const vector<uint8_t>& key); + void decryptWithInvalidKeys(hidl_vec<uint8_t>& invalidResponse, + vector<uint8_t>& iv, const Pattern& noPattern, const vector<SubSample>& subSamples); +}; + +} // namespace vts +} // namespace V1_0 +} // namespace drm +} // namespace hardware +} // namespace android + +#endif // DRM_HAL_CLEARKEY_TEST_H diff --git a/drm/1.0/vts/functional/include/android/hardware/drm/1.0/vts/drm_hal_vendor_test.h b/drm/1.0/vts/functional/include/android/hardware/drm/1.0/vts/drm_hal_vendor_test.h new file mode 100644 index 0000000000..468d335df4 --- /dev/null +++ b/drm/1.0/vts/functional/include/android/hardware/drm/1.0/vts/drm_hal_vendor_test.h @@ -0,0 +1,209 @@ +/* + * Copyright (C) 2020 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef DRM_HAL_VENDOR_TEST_H +#define DRM_HAL_VENDOR_TEST_H + +#include <android/hardware/drm/1.0/ICryptoFactory.h> +#include <android/hardware/drm/1.0/ICryptoPlugin.h> +#include <android/hardware/drm/1.0/IDrmFactory.h> +#include <android/hardware/drm/1.0/IDrmPlugin.h> +#include <android/hardware/drm/1.0/IDrmPluginListener.h> +#include <android/hardware/drm/1.0/types.h> +#include <android/hidl/allocator/1.0/IAllocator.h> + +#include <gtest/gtest.h> +#include <hidl/HidlSupport.h> +#include <hidl/ServiceManagement.h> +#include <hidlmemory/mapping.h> +#include <log/log.h> + +#include <memory> +#include <set> +#include <vector> + +#include "drm_hal_vendor_module_api.h" +#include "drm_vts_helper.h" +#include "vendor_modules.h" +#include <VtsHalHidlTargetCallbackBase.h> + +using ::android::hidl::allocator::V1_0::IAllocator; +using ::android::hidl::memory::V1_0::IMemory; + +using ::drm_vts::DrmHalTestParam; +using ::drm_vts::PrintParamInstanceToString; + +using std::string; +using std::unique_ptr; +using std::map; +using std::vector; + +using ContentConfiguration = ::DrmHalVTSVendorModule_V1::ContentConfiguration; +using Key = ::DrmHalVTSVendorModule_V1::ContentConfiguration::Key; + +#define ASSERT_OK(ret) ASSERT_TRUE(ret.isOk()) +#define EXPECT_OK(ret) EXPECT_TRUE(ret.isOk()) + +#define RETURN_IF_SKIPPED \ + if (vendorModule == nullptr || !vendorModule->isInstalled()) { \ + GTEST_SKIP() << "This drm scheme not supported." \ + << " library:" << GetParam() << " service-name:" \ + << (vendorModule == nullptr ? "N/A" : vendorModule->getServiceName()) \ + << std::endl; \ + return; \ + } + +namespace android { +namespace hardware { +namespace drm { +namespace V1_0 { +namespace vts { + +class DrmHalVendorFactoryTest : public testing::TestWithParam<DrmHalTestParam> { + public: + DrmHalVendorFactoryTest(); + virtual ~DrmHalVendorFactoryTest() {} + + virtual void SetUp() { + const ::testing::TestInfo* const test_info = + ::testing::UnitTest::GetInstance()->current_test_info(); + ALOGD("Running test %s.%s from vendor module %s", test_info->test_case_name(), + test_info->name(), GetParam().instance_.c_str()); + + const std::string instance = GetParam().instance_; + if (instance == "widevine") { + ASSERT_NE(nullptr, vendorModule.get()); + } + + if (vendorModule == nullptr) { + GTEST_SKIP() << "No vendor module available"; + } else { + ASSERT_EQ(instance, vendorModule->getServiceName()); + contentConfigurations = vendorModule->getContentConfigurations(); + } + + drmFactory = IDrmFactory::getService(instance); + ASSERT_NE(nullptr, drmFactory.get()); + cryptoFactory = ICryptoFactory::getService(instance); + ASSERT_NE(nullptr, cryptoFactory.get()); + + // If drm scheme not installed skip subsequent tests + if (!drmFactory->isCryptoSchemeSupported(getUUID())) { + // no GTEST_SKIP since only some tests require the module + vendorModule->setInstalled(false); + hidl_array<uint8_t, 16> noUUID; + ASSERT_EQ(GetParamUUID(), noUUID) << "param uuid unsupported"; + } + } + + protected: + hidl_array<uint8_t, 16> getUUID() { + if (GetParamUUID() == hidl_array<uint8_t, 16>()) { + return getVendorUUID(); + } + return GetParamUUID(); + } + + hidl_array<uint8_t, 16> getVendorUUID() { + if (vendorModule == nullptr) return {}; + vector<uint8_t> uuid = vendorModule->getUUID(); + return hidl_array<uint8_t, 16>(&uuid[0]); + } + + hidl_array<uint8_t, 16> GetParamUUID() { + return GetParam().scheme_; + } + + sp<IDrmFactory> drmFactory; + sp<ICryptoFactory> cryptoFactory; + unique_ptr<DrmHalVTSVendorModule_V1> vendorModule; + vector<ContentConfiguration> contentConfigurations; +}; + +class DrmHalVendorPluginTest : public DrmHalVendorFactoryTest { + public: + virtual ~DrmHalVendorPluginTest() {} + virtual void SetUp() override { + // Create factories + DrmHalVendorFactoryTest::SetUp(); + RETURN_IF_SKIPPED; + + hidl_string packageName("android.hardware.drm.test"); + auto res = drmFactory->createPlugin( + getVendorUUID(), packageName, + [this](Status status, const sp<IDrmPlugin>& plugin) { + EXPECT_EQ(Status::OK, status); + ASSERT_NE(nullptr, plugin.get()); + drmPlugin = plugin; + }); + ASSERT_OK(res); + + hidl_vec<uint8_t> initVec; + res = cryptoFactory->createPlugin( + getVendorUUID(), initVec, + [this](Status status, const sp<ICryptoPlugin>& plugin) { + EXPECT_EQ(Status::OK, status); + ASSERT_NE(nullptr, plugin.get()); + cryptoPlugin = plugin; + }); + ASSERT_OK(res); + } + + virtual void TearDown() override {} + + SessionId openSession(); + void closeSession(const SessionId& sessionId); + sp<IMemory> getDecryptMemory(size_t size, size_t index); + KeyedVector toHidlKeyedVector(const map<string, string>& params); + hidl_vec<uint8_t> loadKeys(const SessionId& sessionId, + const ContentConfiguration& configuration, + const KeyType& type); + + protected: + sp<IDrmPlugin> drmPlugin; + sp<ICryptoPlugin> cryptoPlugin; +}; + +class DrmHalVendorDecryptTest : public DrmHalVendorPluginTest { + public: + DrmHalVendorDecryptTest() = default; + virtual ~DrmHalVendorDecryptTest() {} + + protected: + void fillRandom(const sp<IMemory>& memory); + hidl_array<uint8_t, 16> toHidlArray(const vector<uint8_t>& vec) { + EXPECT_EQ(vec.size(), 16u); + return hidl_array<uint8_t, 16>(&vec[0]); + } + hidl_vec<KeyValue> queryKeyStatus(SessionId sessionId); + void removeKeys(SessionId sessionId); + uint32_t decrypt(Mode mode, bool isSecure, + const hidl_array<uint8_t, 16>& keyId, uint8_t* iv, + const hidl_vec<SubSample>& subSamples, const Pattern& pattern, + const vector<uint8_t>& key, Status expectedStatus); + void aes_ctr_decrypt(uint8_t* dest, uint8_t* src, uint8_t* iv, + const hidl_vec<SubSample>& subSamples, const vector<uint8_t>& key); + void aes_cbc_decrypt(uint8_t* dest, uint8_t* src, uint8_t* iv, + const hidl_vec<SubSample>& subSamples, const vector<uint8_t>& key); +}; + +} // namespace vts +} // namespace V1_0 +} // namespace drm +} // namespace hardware +} // namespace android + +#endif // DRM_HAL_VENDOR_TEST_H diff --git a/drm/1.0/vts/functional/include/drm_hal_vendor_module_api.h b/drm/1.0/vts/functional/include/drm_hal_vendor_module_api.h new file mode 100644 index 0000000000..b8b205265d --- /dev/null +++ b/drm/1.0/vts/functional/include/drm_hal_vendor_module_api.h @@ -0,0 +1,234 @@ +/* + * Copyright (C) 2017 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 DRM_HAL_VENDOR_MODULE_API_H +#define DRM_HAL_VENDOR_MODULE_API_H + +#include <stdint.h> +#include <map> +#include <string> +#include <vector> + +/** + * The DRM and Crypto HALs interact with vendor-provided HAL implementations + * that have DRM-specific capabilities. Since the VTS tests cannot contain + * DRM-specific functionality, supporting modules are required to enable VTS + * to validate HAL implementations in a generic way. If the vendor-specific + * VTS module is not provided for a given drm HAL implementation, only very + * small subset of functionality can be verified. + * + * As an example, a DRM HAL implementation interacts with a DRM-specific + * license server to obtain licenses for decrypting content. The DRM HAL + * implementation generates a key request message, delivers it to the server + * and receives a key response message which is then loaded into the HAL. Once + * the keys are loaded, the Crypto HAL decryption functionality and performance + * and other associated APIs can be tested by the common VTS test suite. + * + * Vendor-specific VTS modules are shared libraries used by the DRM VTS test. + * They provide a set of functions to support VTS testing of the DRM HAL module. + * + * The modules are placed in a common location on the file system. The VTS test + * scans through all vendor-provided support libraries and runs the VTS test + * suite on each library that is found. + * + * The vendor-specific module exposes an extern “C” vendorModuleFactory() + * function that returns a DrmHalVTSVendorModule instance. DrmHalVTSVendorModule + * instances are versioned, where each version is represented by subclass of + * DrmHalVTSVendorModule that corresponds to the API version. For example, a + * vendor-specific module that implements version 1 of the API would return a + * DrmHalVTSVendorModule_V1 from the vendorModuleFactory() function. + */ + +class DrmHalVTSVendorModule; + +extern "C" { +/** + * The factory method for creating DrmHalVTSVendorModule instances. The returned + * instance will be a subclass of DrmHalVTSVendorModule that corresponds to the + * supported API version. + */ +DrmHalVTSVendorModule* vendorModuleFactory(); +}; + +class DrmHalVTSVendorModule { + public: + DrmHalVTSVendorModule() : installed(true) {} + virtual ~DrmHalVTSVendorModule() {} + + /** + * Return the vendor-specific module API version. The version is an integer + * value with initial version 1. The API version indicates which subclass + * version DrmHalVTSVendorModule this instance is. + */ + virtual uint32_t getAPIVersion() const = 0; + + /** + * Return the UUID for the DRM HAL implementation. Protection System + * Specific + * UUID (see http://dashif.org/identifiers/protection/) + */ + virtual std::vector<uint8_t> getUUID() const = 0; + + /** + * Return the service name for the DRM HAL implementation. If the hal is a + * legacy + * drm plugin, i.e. not running as a HIDL service, return the empty string. + */ + virtual std::string getServiceName() const = 0; + + /** + * Set a flag in the vendor module to indicate whether or not the drm + * scheme corresponding to this module is installed on the device. + */ + void setInstalled(bool flag) {installed = flag;} + bool isInstalled() const {return installed;} + + private: + bool installed; + DrmHalVTSVendorModule(const DrmHalVTSVendorModule&) = delete; + void operator=(const DrmHalVTSVendorModule&) = delete; +}; + +/** + * API Version 1. This is the baseline version that supports a minimal set + * of VTS tests. + */ +class DrmHalVTSVendorModule_V1 : public DrmHalVTSVendorModule { + public: + DrmHalVTSVendorModule_V1() {} + virtual ~DrmHalVTSVendorModule_V1() {} + + virtual uint32_t getAPIVersion() const { return 1; } + + /** + * Handle a provisioning request. This function will be called if the HAL + * module's getProvisionRequest returns a provision request. The vendor + * module should process the provisioning request, either by sending it + * to a provisioning server, or generating a mock response. The resulting + * provisioning response is returned to the VTS test. + * + * @param provisioningRequest the provisioning request recieved from + * the DRM HAL + * @param url the default url the HAL implementation provided with the + * provisioning request + * @return the generated provisioning response + */ + virtual std::vector<uint8_t> handleProvisioningRequest( + const std::vector<uint8_t>& provisioningRequest, + const std::string& url) = 0; + + /** + * Content configuration specifies content-specific parameters associated + * with a key request/response transaction. It allows the VTS test to + * request keys and use them to perform decryption. + */ + struct ContentConfiguration { + /** + * Assign a name for this configuration that will be referred to + * in log messages. + */ + const std::string name; + + /** + * Server to use when requesting a key response. This url will be + * passed as a parameter to the vendor vts module along with the + * key request to perform the key request transaction. + */ + const std::string serverUrl; + + /** + * Initialization data provided to getKeyRequest, e.g. PSSH for CENC + * content + */ + const std::vector<uint8_t> initData; + + /** + * Mime type provided to getKeyRequest, e.g. "video/mp4", or "cenc" + */ + const std::string mimeType; + + /** + * Optional parameters to be associated with the key request + */ + const std::map<std::string, std::string> optionalParameters; + + /** + * Define license policy attributes for the content configuration. + * These attributes can affect which tests are able to be applied. + */ + struct Policy { + /** + * Indicate if the license policy allows offline playback. + * Content configurated with this policy supports KeyType::OFFLINE + * key requests/responses. A vendor module should provide at least + * one content configuration where allowOffline is true if the drm + * scheme supports offline content. + */ + bool allowOffline; + } policy; + + /** + * The keys that will be available once the keys are loaded + */ + struct Key { + /** + * Indicate if the key content is configured to require secure + * buffers, where the output buffers are protected and cannot be + * accessed by the non-secure cpu. A vendor module should provide + * at least one content configurations where isSecure is false, to + * allow decrypt result verification tests to be run. + */ + bool isSecure; + + /** + * A key ID identifies a key to use for decryption + */ + const std::vector<uint8_t> keyId; + + /** + * The clear content key is provided to generate expected values for + * validating decryption. + */ + const std::vector<uint8_t> clearContentKey; + }; + std::vector<Key> keys; + }; + + /** + * Return a list of content configurations that can be exercised by the + * VTS test. + */ + virtual std::vector<ContentConfiguration> + getContentConfigurations() const = 0; + + /** + * Handle a key request. This function will be called if the HAL + * module's getKeyRequest returns a key request. The vendor + * module should process the key request, either by sending it + * to a license server, or by generating a mock response. The resulting + * key response is returned to the VTS test. + * + * @param keyRequest the key request recieved from the DRM HAL + * @param serverUrl the url of the key server that was supplied + * by the ContentConfiguration + * @return the generated key response + */ + virtual std::vector<uint8_t> handleKeyRequest( + const std::vector<uint8_t>& keyRequest, + const std::string& serverUrl) = 0; +}; + +#endif // DRM_HAL_VENDOR_MODULE_API_H diff --git a/drm/1.0/vts/functional/include/drm_vts_helper.h b/drm/1.0/vts/functional/include/drm_vts_helper.h new file mode 100644 index 0000000000..1f02af9460 --- /dev/null +++ b/drm/1.0/vts/functional/include/drm_vts_helper.h @@ -0,0 +1,58 @@ +/* + * Copyright (C) 2020 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef DRM_VTS_HELPER_H +#define DRM_VTS_HELPER_H + +#include <hidl/GtestPrinter.h> +#include <hidl/HidlSupport.h> + +#include <array> +#include <chrono> +#include <iostream> +#include <map> +#include <memory> +#include <string> +#include <utility> +#include <vector> + +namespace drm_vts { + +using ::android::hardware::hidl_array; + +struct DrmHalTestParam { + const std::string instance_; + const hidl_array<uint8_t, 16> scheme_{}; + DrmHalTestParam(const std::string& instance) : instance_(instance) {} + DrmHalTestParam(const std::string& instance, const hidl_array<uint8_t, 16>& scheme) + : instance_(instance), scheme_(scheme) {} +}; + +inline std::ostream& operator<<(std::ostream& stream, const DrmHalTestParam& val) { + stream << val.instance_ << ", " << android::hardware::toString(val.scheme_); + return stream; +} + +inline std::string PrintParamInstanceToString( + const testing::TestParamInfo<DrmHalTestParam>& info) { + // test names need to be unique -> index prefix + std::string name = std::to_string(info.index) + "/" + info.param.instance_; + return android::hardware::Sanitize(name); +}; + +} // namespace drm_vts + +#endif // DRM_VTS_HELPER_H diff --git a/drm/1.0/vts/functional/vendor_modules.h b/drm/1.0/vts/functional/include/vendor_modules.h index 8330b0a1ff..3f6fa1573d 100644 --- a/drm/1.0/vts/functional/vendor_modules.h +++ b/drm/1.0/vts/functional/include/vendor_modules.h @@ -51,6 +51,11 @@ class VendorModules { */ std::vector<std::string> getPathList() const {return mPathList;} + /** + * Retrieve a DrmHalVTSVendorModule given a service name. + */ + DrmHalVTSVendorModule* getModuleByName(const std::string& name); + private: std::vector<std::string> mPathList; std::map<std::string, std::unique_ptr<SharedLibrary>> mOpenLibraries; diff --git a/drm/1.0/vts/functional/vendor_modules.cpp b/drm/1.0/vts/functional/vendor_modules.cpp index 98430f550e..53927bd182 100644 --- a/drm/1.0/vts/functional/vendor_modules.cpp +++ b/drm/1.0/vts/functional/vendor_modules.cpp @@ -23,6 +23,7 @@ #include <utils/String8.h> #include <SharedLibrary.h> +#include "drm_hal_vendor_module_api.h" #include "vendor_modules.h" using std::string; @@ -69,4 +70,15 @@ DrmHalVTSVendorModule* VendorModules::getModule(const string& path) { ModuleFactory moduleFactory = reinterpret_cast<ModuleFactory>(symbol); return (*moduleFactory)(); } + +DrmHalVTSVendorModule* VendorModules::getModuleByName(const string& name) { + for (const auto &path : mPathList) { + auto module = getModule(path); + if (module->getServiceName() == name) { + return module; + } + + } + return NULL; +} }; diff --git a/drm/1.1/vts/OWNERS b/drm/1.1/vts/OWNERS new file mode 100644 index 0000000000..ecb421cc1f --- /dev/null +++ b/drm/1.1/vts/OWNERS @@ -0,0 +1,6 @@ +edwinwong@google.com +fredgc@google.com +jtinker@google.com +kylealexander@google.com +rfrias@google.com +robertshih@google.com diff --git a/drm/1.1/vts/functional/Android.bp b/drm/1.1/vts/functional/Android.bp index fb09563ada..e08d760ea4 100644 --- a/drm/1.1/vts/functional/Android.bp +++ b/drm/1.1/vts/functional/Android.bp @@ -14,21 +14,56 @@ // limitations under the License. // -cc_test { - name: "VtsHalDrmV1_1TargetTest", +cc_library_static { + name: "android.hardware.drm@1.1-vts", defaults: ["VtsHalTargetTestDefaults"], + include_dirs: [ + "hardware/interfaces/drm/1.0/vts/functional", + ], + local_include_dirs: [ + "include", + ], srcs: [ "drm_hal_clearkey_test.cpp", ], + shared_libs: [ + "android.hardware.drm@1.0", + "android.hardware.drm@1.1", + "android.hidl.allocator@1.0", + "android.hidl.memory@1.0", + "libhidlmemory", + "libnativehelper", + ], static_libs: [ + "libdrmvtshelper", + ], + export_shared_lib_headers: [ "android.hardware.drm@1.0", "android.hardware.drm@1.1", - "android.hardware.drm@1.0-helper", "android.hidl.allocator@1.0", "android.hidl.memory@1.0", "libhidlmemory", "libnativehelper", - "libssl", + ], + export_static_lib_headers: [ + "libdrmvtshelper", + ], + export_include_dirs: [ + "include", + ], +} + +cc_test { + name: "VtsHalDrmV1_1TargetTest", + defaults: ["VtsHalTargetTestDefaults"], + srcs: [ + "drm_hal_test_main.cpp", + ], + whole_static_libs: [ + "android.hardware.drm@1.1-vts" + ], + shared_libs: [ + "android.hardware.drm@1.1", ], test_suites: [ "general-tests", diff --git a/drm/1.1/vts/functional/drm_hal_clearkey_test.cpp b/drm/1.1/vts/functional/drm_hal_clearkey_test.cpp index bf99ef6cd8..fba973357d 100644 --- a/drm/1.1/vts/functional/drm_hal_clearkey_test.cpp +++ b/drm/1.1/vts/functional/drm_hal_clearkey_test.cpp @@ -16,205 +16,20 @@ #define LOG_TAG "drm_hal_clearkey_test@1.1" -#include <android/hardware/drm/1.0/ICryptoPlugin.h> -#include <android/hardware/drm/1.0/IDrmPlugin.h> -#include <android/hardware/drm/1.0/types.h> -#include <android/hardware/drm/1.1/ICryptoFactory.h> -#include <android/hardware/drm/1.1/IDrmFactory.h> -#include <android/hardware/drm/1.1/IDrmPlugin.h> -#include <android/hardware/drm/1.1/types.h> -#include <android/hidl/allocator/1.0/IAllocator.h> -#include <android/hidl/manager/1.2/IServiceManager.h> -#include <gtest/gtest.h> -#include <hidl/GtestPrinter.h> -#include <hidl/HidlSupport.h> -#include <hidl/ServiceManagement.h> -#include <hidlmemory/mapping.h> #include <log/log.h> -#include <openssl/aes.h> -#include <memory> -#include <random> - -namespace drm = ::android::hardware::drm; -using ::android::hardware::drm::V1_0::BufferType; -using ::android::hardware::drm::V1_0::DestinationBuffer; -using ::android::hardware::drm::V1_0::ICryptoPlugin; -using ::android::hardware::drm::V1_0::KeyedVector; -using ::android::hardware::drm::V1_0::KeyValue; -using ::android::hardware::drm::V1_0::KeyType; -using ::android::hardware::drm::V1_0::Mode; -using ::android::hardware::drm::V1_0::Pattern; -using ::android::hardware::drm::V1_0::SecureStop; -using ::android::hardware::drm::V1_0::SecureStopId; -using ::android::hardware::drm::V1_0::SessionId; -using ::android::hardware::drm::V1_0::SharedBuffer; -using ::android::hardware::drm::V1_0::Status; -using ::android::hardware::drm::V1_0::SubSample; -using ::android::hardware::drm::V1_0::SubSample; - -using ::android::hardware::drm::V1_1::DrmMetricGroup; -using ::android::hardware::drm::V1_1::HdcpLevel; -using ::android::hardware::drm::V1_1::ICryptoFactory; -using ::android::hardware::drm::V1_1::IDrmFactory; -using ::android::hardware::drm::V1_1::IDrmPlugin; -using ::android::hardware::drm::V1_1::KeyRequestType; -using ::android::hardware::drm::V1_1::SecureStopRelease; -using ::android::hardware::drm::V1_1::SecurityLevel; -using ::android::hardware::drm::V1_1::SecurityLevel; - -using ::android::hardware::hidl_array; -using ::android::hardware::hidl_string; -using ::android::hardware::hidl_memory; -using ::android::hardware::hidl_vec; -using ::android::hardware::Return; -using ::android::hidl::allocator::V1_0::IAllocator; -using ::android::hidl::memory::V1_0::IMemory; -using ::android::sp; - -using std::string; -using std::unique_ptr; -using std::random_device; -using std::map; -using std::mt19937; -using std::vector; - -/** - * These clearkey tests use white box knowledge of the legacy clearkey - * plugin to verify that the HIDL HAL services and interfaces are working. - * It is not intended to verify any vendor's HAL implementation. If you - * are looking for vendor HAL tests, see drm_hal_vendor_test.cpp - */ -#define ASSERT_OK(ret) ASSERT_TRUE(ret.isOk()) -#define EXPECT_OK(ret) EXPECT_TRUE(ret.isOk()) - -// To be used in mpd to specify drm scheme for players -static const uint8_t kClearKeyUUID[16] = { +#include <vector> + +#include "android/hardware/drm/1.1/vts/drm_hal_clearkey_test.h" + +namespace android { +namespace hardware { +namespace drm { +namespace V1_1 { +namespace vts { + +const uint8_t kClearKeyUUID[16] = { 0xE2, 0x71, 0x9D, 0x58, 0xA9, 0x85, 0xB3, 0xC9, - 0x78, 0x1A, 0xB0, 0x30, 0xAF, 0x78, 0xD3, 0x0E}; - -class DrmHalClearkeyTest : public ::testing::TestWithParam<std::string> { - public: - void SetUp() override { - const ::testing::TestInfo* const test_info = - ::testing::UnitTest::GetInstance()->current_test_info(); - - ALOGD("DrmHalClearkeyTest: Running test %s.%s", test_info->test_case_name(), - test_info->name()); - - const std::string instance = GetParam(); - - sp<IDrmFactory> drmFactory = IDrmFactory::getService(instance); - drmPlugin = createDrmPlugin(drmFactory); - sp<ICryptoFactory> cryptoFactory = ICryptoFactory::getService(instance); - cryptoPlugin = createCryptoPlugin(cryptoFactory); - - if (drmPlugin == nullptr || cryptoPlugin == nullptr) { - if (instance == "clearkey") { - ASSERT_NE(nullptr, drmPlugin.get()) << "Can't get clearkey drm@1.1 plugin"; - ASSERT_NE(nullptr, cryptoPlugin.get()) << "Can't get clearkey crypto@1.1 plugin"; - } - GTEST_SKIP() << "Instance does not support clearkey"; - } - } - - SessionId openSession(); - SessionId openSession(SecurityLevel level); - void closeSession(const SessionId& sessionId); - hidl_vec<uint8_t> loadKeys(const SessionId& sessionId, const KeyType& type); - - private: - sp<IDrmPlugin> createDrmPlugin(sp<IDrmFactory> drmFactory) { - if (drmFactory == nullptr) { - return nullptr; - } - sp<IDrmPlugin> plugin = nullptr; - auto res = drmFactory->createPlugin( - kClearKeyUUID, "", [&](Status status, const sp<drm::V1_0::IDrmPlugin>& pluginV1_0) { - EXPECT_EQ(Status::OK == status, pluginV1_0 != nullptr); - plugin = IDrmPlugin::castFrom(pluginV1_0); - }); - - if (!res.isOk()) { - ALOGE("createDrmPlugin remote call failed"); - } - return plugin; - } - - sp<ICryptoPlugin> createCryptoPlugin(sp<ICryptoFactory> cryptoFactory) { - if (cryptoFactory == nullptr) { - return nullptr; - } - sp<ICryptoPlugin> plugin = nullptr; - hidl_vec<uint8_t> initVec; - auto res = cryptoFactory->createPlugin( - kClearKeyUUID, initVec, - [&](Status status, const sp<drm::V1_0::ICryptoPlugin>& pluginV1_0) { - EXPECT_EQ(Status::OK == status, pluginV1_0 != nullptr); - plugin = pluginV1_0; - }); - if (!res.isOk()) { - ALOGE("createCryptoPlugin remote call failed"); - } - return plugin; - } - -protected: - template <typename CT> - bool ValueEquals(DrmMetricGroup::ValueType type, const std::string& expected, const CT& actual) { - return type == DrmMetricGroup::ValueType::STRING_TYPE && expected == actual.stringValue; - } - - template <typename CT> - bool ValueEquals(DrmMetricGroup::ValueType type, const int64_t expected, const CT& actual) { - return type == DrmMetricGroup::ValueType::INT64_TYPE && expected == actual.int64Value; - } - - template <typename CT> - bool ValueEquals(DrmMetricGroup::ValueType type, const double expected, const CT& actual) { - return type == DrmMetricGroup::ValueType::DOUBLE_TYPE && expected == actual.doubleValue; - } - - template <typename AT, typename VT> - bool ValidateMetricAttributeAndValue(const DrmMetricGroup::Metric& metric, - const std::string& attributeName, const AT& attributeValue, - const std::string& componentName, const VT& componentValue) { - bool validAttribute = false; - bool validComponent = false; - for (const DrmMetricGroup::Attribute& attribute : metric.attributes) { - if (attribute.name == attributeName && - ValueEquals(attribute.type, attributeValue, attribute)) { - validAttribute = true; - } - } - for (const DrmMetricGroup::Value& value : metric.values) { - if (value.componentName == componentName && - ValueEquals(value.type, componentValue, value)) { - validComponent = true; - } - } - return validAttribute && validComponent; - } - - template <typename AT, typename VT> - bool ValidateMetricAttributeAndValue(const hidl_vec<DrmMetricGroup>& metricGroups, - const std::string& metricName, - const std::string& attributeName, const AT& attributeValue, - const std::string& componentName, const VT& componentValue) { - bool foundMetric = false; - for (const auto& group : metricGroups) { - for (const auto& metric : group.metrics) { - if (metric.name == metricName) { - foundMetric = foundMetric || ValidateMetricAttributeAndValue( - metric, attributeName, attributeValue, - componentName, componentValue); - } - } - } - return foundMetric; - } - - sp<IDrmPlugin> drmPlugin; - sp<ICryptoPlugin> cryptoPlugin; + 0x78, 0x1A, 0xB0, 0x30, 0xAF, 0x78, 0xD3, 0x0E }; /** @@ -812,16 +627,8 @@ TEST_P(DrmHalClearkeyTest, RemoveSecureStopById) { EXPECT_OK(res); } -static const std::set<std::string> kAllInstances = [] { - std::vector<std::string> drmInstances = - android::hardware::getAllHalInstanceNames(IDrmFactory::descriptor); - std::vector<std::string> cryptoInstances = - android::hardware::getAllHalInstanceNames(ICryptoFactory::descriptor); - std::set<std::string> allInstances; - allInstances.insert(drmInstances.begin(), drmInstances.end()); - allInstances.insert(cryptoInstances.begin(), cryptoInstances.end()); - return allInstances; -}(); - -INSTANTIATE_TEST_SUITE_P(PerInstance, DrmHalClearkeyTest, testing::ValuesIn(kAllInstances), - android::hardware::PrintInstanceNameToString); +} // namespace vts +} // namespace V1_1 +} // namespace drm +} // namespace hardware +} // namespace android diff --git a/drm/1.1/vts/functional/drm_hal_test_main.cpp b/drm/1.1/vts/functional/drm_hal_test_main.cpp new file mode 100644 index 0000000000..c6965bdc39 --- /dev/null +++ b/drm/1.1/vts/functional/drm_hal_test_main.cpp @@ -0,0 +1,52 @@ +/* + * Copyright (C) 2020 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include <gtest/gtest.h> +#include <hidl/HidlSupport.h> +#include <hidl/ServiceManagement.h> + +#include <algorithm> +#include <iterator> +#include <set> +#include <string> +#include <utility> +#include <vector> + +#include "android/hardware/drm/1.1/vts/drm_hal_clearkey_test.h" + +using android::hardware::drm::V1_1::ICryptoFactory; +using android::hardware::drm::V1_1::IDrmFactory; +using android::hardware::drm::V1_1::vts::DrmHalClearkeyTest; +using android::hardware::drm::V1_1::vts::kClearKeyUUID; + +static const std::vector<DrmHalTestParam> kAllInstances = [] { + std::vector<std::string> drmInstances = + android::hardware::getAllHalInstanceNames(IDrmFactory::descriptor); + std::vector<std::string> cryptoInstances = + android::hardware::getAllHalInstanceNames(ICryptoFactory::descriptor); + std::set<std::string> allInstances; + allInstances.insert(drmInstances.begin(), drmInstances.end()); + allInstances.insert(cryptoInstances.begin(), cryptoInstances.end()); + + std::vector<DrmHalTestParam> allInstancesWithClearKeyUuid; + std::transform(allInstances.begin(), allInstances.end(), + std::back_inserter(allInstancesWithClearKeyUuid), + [](std::string s) { return DrmHalTestParam(s, kClearKeyUUID); }); + return allInstancesWithClearKeyUuid; +}(); + +INSTANTIATE_TEST_SUITE_P(PerInstance, DrmHalClearkeyTest, testing::ValuesIn(kAllInstances), + PrintParamInstanceToString); diff --git a/drm/1.1/vts/functional/include/android/hardware/drm/1.1/vts/drm_hal_clearkey_test.h b/drm/1.1/vts/functional/include/android/hardware/drm/1.1/vts/drm_hal_clearkey_test.h new file mode 100644 index 0000000000..e21a2c135c --- /dev/null +++ b/drm/1.1/vts/functional/include/android/hardware/drm/1.1/vts/drm_hal_clearkey_test.h @@ -0,0 +1,209 @@ +/* + * Copyright (C) 2020 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef V1_1_DRM_HAL_CLEARKEY_TEST_H +#define V1_1_DRM_HAL_CLEARKEY_TEST_H + +#include <android/hardware/drm/1.0/ICryptoPlugin.h> +#include <android/hardware/drm/1.0/IDrmPlugin.h> +#include <android/hardware/drm/1.0/types.h> +#include <android/hardware/drm/1.1/ICryptoFactory.h> +#include <android/hardware/drm/1.1/IDrmFactory.h> +#include <android/hardware/drm/1.1/IDrmPlugin.h> +#include <android/hardware/drm/1.1/types.h> +#include <android/hidl/allocator/1.0/IAllocator.h> +#include <android/hidl/manager/1.2/IServiceManager.h> + +#include <gtest/gtest.h> +#include <hidl/HidlSupport.h> +#include <hidl/ServiceManagement.h> +#include <log/log.h> + +#include <string> + +#include "drm_vts_helper.h" + +namespace drm = ::android::hardware::drm; + +using ::android::hidl::allocator::V1_0::IAllocator; +using ::android::hidl::memory::V1_0::IMemory; + +using drm_vts::DrmHalTestParam; +using drm_vts::PrintParamInstanceToString; + +/** + * These clearkey tests use white box knowledge of the legacy clearkey + * plugin to verify that the HIDL HAL services and interfaces are working. + * It is not intended to verify any vendor's HAL implementation. If you + * are looking for vendor HAL tests, see drm_hal_vendor_test.cpp + */ +#define ASSERT_OK(ret) ASSERT_TRUE(ret.isOk()) +#define EXPECT_OK(ret) EXPECT_TRUE(ret.isOk()) + +namespace android { +namespace hardware { +namespace drm { +namespace V1_1 { +namespace vts { + +using ::android::hardware::drm::V1_0::ICryptoPlugin; +using ::android::hardware::drm::V1_0::KeyedVector; +using ::android::hardware::drm::V1_0::KeyType; +using ::android::hardware::drm::V1_0::Mode; +using ::android::hardware::drm::V1_0::Pattern; +using ::android::hardware::drm::V1_0::SecureStop; +using ::android::hardware::drm::V1_0::SecureStopId; +using ::android::hardware::drm::V1_0::SessionId; +using ::android::hardware::drm::V1_0::Status; + +// To be used in mpd to specify drm scheme for players +extern const uint8_t kClearKeyUUID[16]; + +class DrmHalClearkeyTest : public ::testing::TestWithParam<DrmHalTestParam> { + public: + void SetUp() override { + const ::testing::TestInfo* const test_info = + ::testing::UnitTest::GetInstance()->current_test_info(); + + ALOGD("DrmHalClearkeyTest: Running test %s.%s", test_info->test_case_name(), + test_info->name()); + + const std::string instance = GetParam().instance_; + + sp<IDrmFactory> drmFactory = IDrmFactory::getService(instance); + if (!drmFactory->isCryptoSchemeSupported(kClearKeyUUID)) { + GTEST_SKIP() << instance << " does not support clearkey"; + } + drmPlugin = createDrmPlugin(drmFactory); + sp<ICryptoFactory> cryptoFactory = ICryptoFactory::getService(instance); + cryptoPlugin = createCryptoPlugin(cryptoFactory); + + if (drmPlugin == nullptr || cryptoPlugin == nullptr) { + if (instance == "clearkey") { + ASSERT_NE(nullptr, drmPlugin.get()) << "Can't get clearkey drm@1.1 plugin"; + ASSERT_NE(nullptr, cryptoPlugin.get()) << "Can't get clearkey crypto@1.1 plugin"; + } + GTEST_SKIP() << "Instance does not support clearkey"; + } + } + + SessionId openSession(); + SessionId openSession(SecurityLevel level); + void closeSession(const SessionId& sessionId); + hidl_vec<uint8_t> loadKeys(const SessionId& sessionId, const KeyType& type); + + private: + sp<IDrmPlugin> createDrmPlugin(sp<IDrmFactory> drmFactory) { + if (drmFactory == nullptr) { + return nullptr; + } + sp<IDrmPlugin> plugin = nullptr; + auto res = drmFactory->createPlugin(GetParam().scheme_, "", + [&](Status status, const sp<drm::V1_0::IDrmPlugin>& pluginV1_0) { + EXPECT_EQ(Status::OK == status, pluginV1_0 != nullptr); + plugin = IDrmPlugin::castFrom(pluginV1_0); + }); + + if (!res.isOk()) { + ALOGE("createDrmPlugin remote call failed"); + } + return plugin; + } + + sp<ICryptoPlugin> createCryptoPlugin(sp<ICryptoFactory> cryptoFactory) { + if (cryptoFactory == nullptr) { + return nullptr; + } + sp<ICryptoPlugin> plugin = nullptr; + hidl_vec<uint8_t> initVec; + auto res = cryptoFactory->createPlugin( + GetParam().scheme_, initVec, + [&](Status status, const sp<drm::V1_0::ICryptoPlugin>& pluginV1_0) { + EXPECT_EQ(Status::OK == status, pluginV1_0 != nullptr); + plugin = pluginV1_0; + }); + if (!res.isOk()) { + ALOGE("createCryptoPlugin remote call failed"); + } + return plugin; + } + +protected: + template <typename CT> + bool ValueEquals(DrmMetricGroup::ValueType type, const std::string& expected, const CT& actual) { + return type == DrmMetricGroup::ValueType::STRING_TYPE && expected == actual.stringValue; + } + + template <typename CT> + bool ValueEquals(DrmMetricGroup::ValueType type, const int64_t expected, const CT& actual) { + return type == DrmMetricGroup::ValueType::INT64_TYPE && expected == actual.int64Value; + } + + template <typename CT> + bool ValueEquals(DrmMetricGroup::ValueType type, const double expected, const CT& actual) { + return type == DrmMetricGroup::ValueType::DOUBLE_TYPE && expected == actual.doubleValue; + } + + template <typename AT, typename VT> + bool ValidateMetricAttributeAndValue(const DrmMetricGroup::Metric& metric, + const std::string& attributeName, const AT& attributeValue, + const std::string& componentName, const VT& componentValue) { + bool validAttribute = false; + bool validComponent = false; + for (const DrmMetricGroup::Attribute& attribute : metric.attributes) { + if (attribute.name == attributeName && + ValueEquals(attribute.type, attributeValue, attribute)) { + validAttribute = true; + } + } + for (const DrmMetricGroup::Value& value : metric.values) { + if (value.componentName == componentName && + ValueEquals(value.type, componentValue, value)) { + validComponent = true; + } + } + return validAttribute && validComponent; + } + + template <typename AT, typename VT> + bool ValidateMetricAttributeAndValue(const hidl_vec<DrmMetricGroup>& metricGroups, + const std::string& metricName, + const std::string& attributeName, const AT& attributeValue, + const std::string& componentName, const VT& componentValue) { + bool foundMetric = false; + for (const auto& group : metricGroups) { + for (const auto& metric : group.metrics) { + if (metric.name == metricName) { + foundMetric = foundMetric || ValidateMetricAttributeAndValue( + metric, attributeName, attributeValue, + componentName, componentValue); + } + } + } + return foundMetric; + } + + sp<IDrmPlugin> drmPlugin; + sp<ICryptoPlugin> cryptoPlugin; +}; + +} // namespace vts +} // namespace V1_1 +} // namespace drm +} // namespace hardware +} // namespace android + +#endif // V1_1_DRM_HAL_CLEARKEY_TEST_H diff --git a/drm/1.2/vts/OWNERS b/drm/1.2/vts/OWNERS new file mode 100644 index 0000000000..ecb421cc1f --- /dev/null +++ b/drm/1.2/vts/OWNERS @@ -0,0 +1,6 @@ +edwinwong@google.com +fredgc@google.com +jtinker@google.com +kylealexander@google.com +rfrias@google.com +robertshih@google.com diff --git a/drm/1.2/vts/functional/Android.bp b/drm/1.2/vts/functional/Android.bp index 83ea104579..ecc7d6c592 100644 --- a/drm/1.2/vts/functional/Android.bp +++ b/drm/1.2/vts/functional/Android.bp @@ -14,27 +14,61 @@ // limitations under the License. // -cc_test { - name: "VtsHalDrmV1_2TargetTest", +cc_library_static { + name: "android.hardware.drm@1.2-vts", defaults: ["VtsHalTargetTestDefaults"], - include_dirs: ["hardware/interfaces/drm/1.0/vts/functional"], + local_include_dirs: [ + "include", + ], srcs: [ "drm_hal_clearkey_module.cpp", "drm_hal_common.cpp", "drm_hal_test.cpp", - "vendor_modules.cpp", ], - static_libs: [ + shared_libs: [ "android.hardware.drm@1.0", "android.hardware.drm@1.1", "android.hardware.drm@1.2", - "android.hardware.drm@1.0-helper", "android.hidl.allocator@1.0", "android.hidl.memory@1.0", "libhidlmemory", "libnativehelper", - "libssl", + ], + static_libs: [ + "android.hardware.drm@1.0-helper", + "libcrypto_static", + "libdrmvtshelper", + ], + export_shared_lib_headers: [ + "android.hardware.drm@1.2", + ], + export_static_lib_headers: [ + "android.hardware.drm@1.0-helper", + ], + export_include_dirs: [ + "include", + ], +} + +cc_test { + name: "VtsHalDrmV1_2TargetTest", + defaults: ["VtsHalTargetTestDefaults"], + srcs: [ + "drm_hal_test_main.cpp", + ], + whole_static_libs: [ + "android.hardware.drm@1.2-vts", + ], + shared_libs: [ + "android.hardware.drm@1.0", + "android.hardware.drm@1.2", + "android.hidl.allocator@1.0", + "libhidlmemory", + ], + static_libs: [ + "android.hardware.drm@1.0-helper", "libcrypto_static", + "libdrmvtshelper", ], test_suites: [ "general-tests", diff --git a/drm/1.2/vts/functional/drm_hal_common.cpp b/drm/1.2/vts/functional/drm_hal_common.cpp index 02aa3a99bc..d5e453c778 100644 --- a/drm/1.2/vts/functional/drm_hal_common.cpp +++ b/drm/1.2/vts/functional/drm_hal_common.cpp @@ -26,7 +26,7 @@ #include <random> #include "drm_hal_clearkey_module.h" -#include "drm_hal_common.h" +#include "android/hardware/drm/1.2/vts/drm_hal_common.h" using ::android::hardware::drm::V1_0::BufferType; using ::android::hardware::drm::V1_0::DestinationBuffer; @@ -87,14 +87,14 @@ static DrmHalVTSVendorModule_V1* getModuleForInstance(const std::string& instanc return new DrmHalVTSClearkeyModule(); } - return static_cast<DrmHalVTSVendorModule_V1*>(DrmHalTest::gVendorModules->getModule(instance)); + return static_cast<DrmHalVTSVendorModule_V1*>(DrmHalTest::gVendorModules->getModuleByName(instance)); } /** * DrmHalTest */ -DrmHalTest::DrmHalTest() : vendorModule(getModuleForInstance(GetParam())) {} +DrmHalTest::DrmHalTest() : vendorModule(getModuleForInstance(GetParamService())) {} void DrmHalTest::SetUp() { const ::testing::TestInfo* const test_info = @@ -102,9 +102,9 @@ void DrmHalTest::SetUp() { ALOGD("Running test %s.%s from (vendor) module %s", test_info->test_case_name(), test_info->name(), - GetParam().c_str()); + GetParamService().c_str()); - const string instance = GetParam(); + const string instance = GetParamService(); drmFactory = IDrmFactory::getService(instance); ASSERT_NE(drmFactory, nullptr); @@ -120,21 +120,16 @@ void DrmHalTest::SetUp() { GTEST_SKIP() << "No vendor module installed"; } - if (instance == "clearkey") { - // TODO(b/147449315) - // Only the clearkey plugged into the "default" instance supports - // this test. Currently the "clearkey" instance fails some tests - // here. - GTEST_SKIP() << "Clearkey tests don't work with 'clearkey' instance yet."; - } - ASSERT_EQ(instance, vendorModule->getServiceName()); contentConfigurations = vendorModule->getContentConfigurations(); // If drm scheme not installed skip subsequent tests - if (drmFactory.get() == nullptr || !drmFactory->isCryptoSchemeSupported(getVendorUUID())) { - vendorModule->setInstalled(false); - return; + if (!drmFactory->isCryptoSchemeSupported(getUUID())) { + if (GetParamUUID() == hidl_array<uint8_t, 16>()) { + GTEST_SKIP() << "vendor module drm scheme not supported"; + } else { + FAIL() << "param scheme must be supported: " << android::hardware::toString(GetParamUUID()); + } } ASSERT_NE(nullptr, drmPlugin.get()) << "Can't find " << vendorModule->getServiceName() << " drm@1.2 plugin"; @@ -149,7 +144,7 @@ sp<IDrmPlugin> DrmHalTest::createDrmPlugin() { sp<IDrmPlugin> plugin = nullptr; hidl_string packageName("android.hardware.drm.test"); auto res = - drmFactory->createPlugin(getVendorUUID(), packageName, + drmFactory->createPlugin(getUUID(), packageName, [&](StatusV1_0 status, const sp<IDrmPluginV1_0>& pluginV1_0) { EXPECT_EQ(StatusV1_0::OK == status, pluginV1_0 != nullptr); plugin = IDrmPlugin::castFrom(pluginV1_0); @@ -168,7 +163,7 @@ sp<ICryptoPlugin> DrmHalTest::createCryptoPlugin() { sp<ICryptoPlugin> plugin = nullptr; hidl_vec<uint8_t> initVec; auto res = cryptoFactory->createPlugin( - getVendorUUID(), initVec, + getUUID(), initVec, [&](StatusV1_0 status, const sp<ICryptoPluginV1_0>& pluginV1_0) { EXPECT_EQ(StatusV1_0::OK == status, pluginV1_0 != nullptr); plugin = ICryptoPlugin::castFrom(pluginV1_0); @@ -179,12 +174,63 @@ sp<ICryptoPlugin> DrmHalTest::createCryptoPlugin() { return plugin; } +hidl_array<uint8_t, 16> DrmHalTest::getUUID() { + if (GetParamUUID() == hidl_array<uint8_t, 16>()) { + return getVendorUUID(); + } + return GetParamUUID(); +} + hidl_array<uint8_t, 16> DrmHalTest::getVendorUUID() { if (vendorModule == nullptr) return {}; vector<uint8_t> uuid = vendorModule->getUUID(); return hidl_array<uint8_t, 16>(&uuid[0]); } +void DrmHalTest::provision() { + hidl_string certificateType; + hidl_string certificateAuthority; + hidl_vec<uint8_t> provisionRequest; + hidl_string defaultUrl; + auto res = drmPlugin->getProvisionRequest_1_2( + certificateType, certificateAuthority, + [&](StatusV1_2 status, const hidl_vec<uint8_t>& request, + const hidl_string& url) { + if (status == StatusV1_2::OK) { + EXPECT_NE(request.size(), 0u); + provisionRequest = request; + defaultUrl = url; + } else if (status == StatusV1_2::ERROR_DRM_CANNOT_HANDLE) { + EXPECT_EQ(0u, request.size()); + } + }); + EXPECT_OK(res); + + if (provisionRequest.size() > 0) { + vector<uint8_t> response = vendorModule->handleProvisioningRequest( + provisionRequest, defaultUrl); + ASSERT_NE(0u, response.size()); + + auto res = drmPlugin->provideProvisionResponse( + response, [&](StatusV1_0 status, const hidl_vec<uint8_t>&, + const hidl_vec<uint8_t>&) { + EXPECT_EQ(StatusV1_0::OK, status); + }); + EXPECT_OK(res); + } +} + +SessionId DrmHalTest::openSession(SecurityLevel level, StatusV1_0 *err) { + SessionId sessionId; + auto res = drmPlugin->openSession_1_1(level, + [&](StatusV1_0 status, const hidl_vec<unsigned char> &id) { + *err = status; + sessionId = id; + }); + EXPECT_OK(res); + return sessionId; +} + /** * Helper method to open a session and verify that a non-empty * session ID is returned @@ -474,7 +520,7 @@ void DrmHalTest::aes_cbc_decrypt(uint8_t* dest, uint8_t* src, /** * Helper method to test decryption with invalid keys is returned */ -void DrmHalClearkeyTest::decryptWithInvalidKeys( +void DrmHalClearkeyTestV1_2::decryptWithInvalidKeys( hidl_vec<uint8_t>& invalidResponse, vector<uint8_t>& iv, const Pattern& noPattern, diff --git a/drm/1.2/vts/functional/drm_hal_test.cpp b/drm/1.2/vts/functional/drm_hal_test.cpp index 54c5751eab..0dfff26528 100644 --- a/drm/1.2/vts/functional/drm_hal_test.cpp +++ b/drm/1.2/vts/functional/drm_hal_test.cpp @@ -17,13 +17,13 @@ #define LOG_TAG "drm_hal_test@1.2" #include <gtest/gtest.h> -#include <hidl/GtestPrinter.h> #include <hidl/HidlSupport.h> #include <hidl/ServiceManagement.h> #include <log/log.h> #include <openssl/aes.h> +#include <vector> -#include "drm_hal_common.h" +#include "android/hardware/drm/1.2/vts/drm_hal_common.h" using ::android::hardware::drm::V1_0::Status; using ::android::hardware::drm::V1_1::KeyRequestType; @@ -34,12 +34,13 @@ using ::android::hardware::drm::V1_2::KeyStatus; using ::android::hardware::drm::V1_2::KeyStatusType; using ::android::hardware::drm::V1_2::OfflineLicenseState; -using ::android::hardware::drm::V1_2::vts::DrmHalClearkeyTest; +using ::android::hardware::drm::V1_2::vts::DrmHalClearkeyTestV1_2; using ::android::hardware::drm::V1_2::vts::DrmHalPluginListener; using ::android::hardware::drm::V1_2::vts::DrmHalTest; using ::android::hardware::drm::V1_2::vts::kCallbackLostState; using ::android::hardware::drm::V1_2::vts::kCallbackKeysChange; +using ::android::hardware::hidl_array; using ::android::hardware::hidl_string; static const char* const kVideoMp4 = "video/mp4"; @@ -48,13 +49,13 @@ static const char* const kDrmErrorTestKey = "drmErrorTest"; static const char* const kDrmErrorInvalidState = "invalidState"; static const char* const kDrmErrorResourceContention = "resourceContention"; static const SecurityLevel kSwSecureCrypto = SecurityLevel::SW_SECURE_CRYPTO; +static const SecurityLevel kHwSecureAll = SecurityLevel::HW_SECURE_ALL; /** * Ensure drm factory supports module UUID Scheme */ TEST_P(DrmHalTest, VendorUuidSupported) { - RETURN_IF_SKIPPED; - auto res = drmFactory->isCryptoSchemeSupported_1_2(getVendorUUID(), kVideoMp4, kSwSecureCrypto); + auto res = drmFactory->isCryptoSchemeSupported_1_2(getUUID(), kVideoMp4, kSwSecureCrypto); ALOGI("kVideoMp4 = %s res %d", kVideoMp4, (bool)res); EXPECT_TRUE(res); } @@ -63,7 +64,6 @@ TEST_P(DrmHalTest, VendorUuidSupported) { * Ensure drm factory doesn't support an invalid scheme UUID */ TEST_P(DrmHalTest, InvalidPluginNotSupported) { - RETURN_IF_SKIPPED; const uint8_t kInvalidUUID[16] = { 0x10, 0x20, 0x30, 0x40, 0x50, 0x60, 0x70, 0x80, 0x10, 0x20, 0x30, 0x40, 0x50, 0x60, 0x70, 0x80}; @@ -74,7 +74,6 @@ TEST_P(DrmHalTest, InvalidPluginNotSupported) { * Ensure drm factory doesn't support an empty UUID */ TEST_P(DrmHalTest, EmptyPluginUUIDNotSupported) { - RETURN_IF_SKIPPED; hidl_array<uint8_t, 16> emptyUUID; memset(emptyUUID.data(), 0, 16); EXPECT_FALSE(drmFactory->isCryptoSchemeSupported_1_2(emptyUUID, kVideoMp4, kSwSecureCrypto)); @@ -84,8 +83,7 @@ TEST_P(DrmHalTest, EmptyPluginUUIDNotSupported) { * Ensure drm factory doesn't support an invalid mime type */ TEST_P(DrmHalTest, BadMimeNotSupported) { - RETURN_IF_SKIPPED; - EXPECT_FALSE(drmFactory->isCryptoSchemeSupported_1_2(getVendorUUID(), kBadMime, kSwSecureCrypto)); + EXPECT_FALSE(drmFactory->isCryptoSchemeSupported_1_2(getUUID(), kBadMime, kSwSecureCrypto)); } /** @@ -101,36 +99,17 @@ TEST_P(DrmHalTest, BadMimeNotSupported) { * that is delivered back to the HAL. */ TEST_P(DrmHalTest, DoProvisioning) { - RETURN_IF_SKIPPED; - hidl_string certificateType; - hidl_string certificateAuthority; - hidl_vec<uint8_t> provisionRequest; - hidl_string defaultUrl; - auto res = drmPlugin->getProvisionRequest_1_2( - certificateType, certificateAuthority, - [&](StatusV1_2 status, const hidl_vec<uint8_t>& request, - const hidl_string& url) { - if (status == StatusV1_2::OK) { - EXPECT_NE(request.size(), 0u); - provisionRequest = request; - defaultUrl = url; - } else if (status == StatusV1_2::ERROR_DRM_CANNOT_HANDLE) { - EXPECT_EQ(0u, request.size()); - } - }); - EXPECT_OK(res); - - if (provisionRequest.size() > 0) { - vector<uint8_t> response = vendorModule->handleProvisioningRequest( - provisionRequest, defaultUrl); - ASSERT_NE(0u, response.size()); - - auto res = drmPlugin->provideProvisionResponse( - response, [&](Status status, const hidl_vec<uint8_t>&, - const hidl_vec<uint8_t>&) { - EXPECT_EQ(Status::OK, status); - }); - EXPECT_OK(res); + for (auto level : {kHwSecureAll, kSwSecureCrypto}) { + StatusV1_0 err = StatusV1_0::OK; + auto sid = openSession(level, &err); + if (err == StatusV1_0::OK) { + closeSession(sid); + } else if (err == StatusV1_0::ERROR_DRM_CANNOT_HANDLE) { + continue; + } else { + EXPECT_EQ(StatusV1_0::ERROR_DRM_NOT_PROVISIONED, err); + provision(); + } } } @@ -138,7 +117,6 @@ TEST_P(DrmHalTest, DoProvisioning) { * A get key request should fail if no sessionId is provided */ TEST_P(DrmHalTest, GetKeyRequestNoSession) { - RETURN_IF_SKIPPED; SessionId invalidSessionId; hidl_vec<uint8_t> initData; KeyedVector optionalParameters; @@ -156,7 +134,6 @@ TEST_P(DrmHalTest, GetKeyRequestNoSession) { * invalid mime type */ TEST_P(DrmHalTest, GetKeyRequestBadMime) { - RETURN_IF_SKIPPED; auto sessionId = openSession(); hidl_vec<uint8_t> initData; KeyedVector optionalParameters; @@ -193,7 +170,6 @@ void checkKeySetIdState(Status status, OfflineLicenseState state) { * Test drm plugin offline key support */ TEST_P(DrmHalTest, OfflineLicenseTest) { - RETURN_IF_SKIPPED; auto sessionId = openSession(); hidl_vec<uint8_t> keySetId = loadKeys(sessionId, KeyType::OFFLINE); @@ -233,7 +209,6 @@ TEST_P(DrmHalTest, OfflineLicenseTest) { * Test drm plugin offline key state */ TEST_P(DrmHalTest, OfflineLicenseStateTest) { - RETURN_IF_SKIPPED; auto sessionId = openSession(); DrmHalVTSVendorModule_V1::ContentConfiguration content = getContent(KeyType::OFFLINE); hidl_vec<uint8_t> keySetId = loadKeys(sessionId, content, KeyType::OFFLINE); @@ -258,7 +233,6 @@ TEST_P(DrmHalTest, OfflineLicenseStateTest) { * Negative offline license test. Remove empty keySetId */ TEST_P(DrmHalTest, RemoveEmptyKeySetId) { - RETURN_IF_SKIPPED; KeySetId emptyKeySetId; Status err = drmPlugin->removeOfflineLicense(emptyKeySetId); EXPECT_EQ(Status::BAD_VALUE, err); @@ -268,7 +242,6 @@ TEST_P(DrmHalTest, RemoveEmptyKeySetId) { * Negative offline license test. Get empty keySetId state */ TEST_P(DrmHalTest, GetEmptyKeySetIdState) { - RETURN_IF_SKIPPED; KeySetId emptyKeySetId; auto res = drmPlugin->getOfflineLicenseState(emptyKeySetId, checkKeySetIdState<Status::BAD_VALUE, OfflineLicenseState::UNKNOWN>); EXPECT_OK(res); @@ -278,7 +251,6 @@ TEST_P(DrmHalTest, GetEmptyKeySetIdState) { * Test that the plugin returns valid connected and max HDCP levels */ TEST_P(DrmHalTest, GetHdcpLevels) { - RETURN_IF_SKIPPED; auto res = drmPlugin->getHdcpLevels_1_2( [&](StatusV1_2 status, const HdcpLevel &connectedLevel, const HdcpLevel &maxLevel) { @@ -294,7 +266,6 @@ TEST_P(DrmHalTest, GetHdcpLevels) { * the listener gets them. */ TEST_P(DrmHalTest, ListenerKeysChange) { - RETURN_IF_SKIPPED; sp<DrmHalPluginListener> listener = new DrmHalPluginListener(); auto res = drmPlugin->setListener(listener); EXPECT_OK(res); @@ -326,7 +297,6 @@ TEST_P(DrmHalTest, ListenerKeysChange) { * Positive decrypt test. "Decrypt" a single clear segment */ TEST_P(DrmHalTest, ClearSegmentTest) { - RETURN_IF_SKIPPED; for (const auto& config : contentConfigurations) { for (const auto& key : config.keys) { const size_t kSegmentSize = 1024; @@ -354,7 +324,6 @@ TEST_P(DrmHalTest, ClearSegmentTest) { * Verify data matches. */ TEST_P(DrmHalTest, EncryptedAesCtrSegmentTest) { - RETURN_IF_SKIPPED; for (const auto& config : contentConfigurations) { for (const auto& key : config.keys) { const size_t kSegmentSize = 1024; @@ -381,7 +350,6 @@ TEST_P(DrmHalTest, EncryptedAesCtrSegmentTest) { * Negative decrypt test. Decrypted frame too large to fit in output buffer */ TEST_P(DrmHalTest, ErrorFrameTooLarge) { - RETURN_IF_SKIPPED; for (const auto& config : contentConfigurations) { for (const auto& key : config.keys) { const size_t kSegmentSize = 1024; @@ -407,7 +375,6 @@ TEST_P(DrmHalTest, ErrorFrameTooLarge) { * Negative decrypt test. Decrypt without loading keys. */ TEST_P(DrmHalTest, EncryptedAesCtrSegmentTestNoKeys) { - RETURN_IF_SKIPPED; for (const auto& config : contentConfigurations) { for (const auto& key : config.keys) { vector<uint8_t> iv(AES_BLOCK_SIZE, 0); @@ -432,17 +399,14 @@ TEST_P(DrmHalTest, EncryptedAesCtrSegmentTestNoKeys) { /** * Ensure clearkey drm factory doesn't support security level higher than supported */ -TEST_P(DrmHalClearkeyTest, BadLevelNotSupported) { - RETURN_IF_SKIPPED; - const SecurityLevel kHwSecureAll = SecurityLevel::HW_SECURE_ALL; - EXPECT_FALSE(drmFactory->isCryptoSchemeSupported_1_2(getVendorUUID(), kVideoMp4, kHwSecureAll)); +TEST_P(DrmHalClearkeyTestV1_2, BadLevelNotSupported) { + EXPECT_FALSE(drmFactory->isCryptoSchemeSupported_1_2(getUUID(), kVideoMp4, kHwSecureAll)); } /** * Test resource contention during attempt to generate key request */ -TEST_P(DrmHalClearkeyTest, GetKeyRequestResourceContention) { - RETURN_IF_SKIPPED; +TEST_P(DrmHalClearkeyTestV1_2, GetKeyRequestResourceContention) { Status status = drmPlugin->setPropertyString(kDrmErrorTestKey, kDrmErrorResourceContention); EXPECT_EQ(Status::OK, status); auto sessionId = openSession(); @@ -463,8 +427,7 @@ TEST_P(DrmHalClearkeyTest, GetKeyRequestResourceContention) { /** * Test clearkey plugin offline key with mock error */ -TEST_P(DrmHalClearkeyTest, OfflineLicenseInvalidState) { - RETURN_IF_SKIPPED; +TEST_P(DrmHalClearkeyTestV1_2, OfflineLicenseInvalidState) { auto sessionId = openSession(); hidl_vec<uint8_t> keySetId = loadKeys(sessionId, KeyType::OFFLINE); Status status = drmPlugin->setPropertyString(kDrmErrorTestKey, kDrmErrorInvalidState); @@ -485,8 +448,7 @@ TEST_P(DrmHalClearkeyTest, OfflineLicenseInvalidState) { /** * Test SessionLostState is triggered on error */ -TEST_P(DrmHalClearkeyTest, SessionLostState) { - RETURN_IF_SKIPPED; +TEST_P(DrmHalClearkeyTestV1_2, SessionLostState) { sp<DrmHalPluginListener> listener = new DrmHalPluginListener(); auto res = drmPlugin->setListener(listener); EXPECT_OK(res); @@ -506,8 +468,7 @@ TEST_P(DrmHalClearkeyTest, SessionLostState) { /** * Negative decrypt test. Decrypt with invalid key. */ -TEST_P(DrmHalClearkeyTest, DecryptWithEmptyKey) { - RETURN_IF_SKIPPED; +TEST_P(DrmHalClearkeyTestV1_2, DecryptWithEmptyKey) { vector<uint8_t> iv(AES_BLOCK_SIZE, 0); const Pattern noPattern = {0, 0}; const uint32_t kClearBytes = 512; @@ -544,8 +505,7 @@ TEST_P(DrmHalClearkeyTest, DecryptWithEmptyKey) { /** * Negative decrypt test. Decrypt with a key exceeds AES_BLOCK_SIZE. */ -TEST_P(DrmHalClearkeyTest, DecryptWithKeyTooLong) { - RETURN_IF_SKIPPED; +TEST_P(DrmHalClearkeyTestV1_2, DecryptWithKeyTooLong) { vector<uint8_t> iv(AES_BLOCK_SIZE, 0); const Pattern noPattern = {0, 0}; const uint32_t kClearBytes = 512; @@ -572,43 +532,3 @@ TEST_P(DrmHalClearkeyTest, DecryptWithKeyTooLong) { memcpy(invalidResponse.data(), keyTooLongResponse.c_str(), kKeyTooLongResponseSize); decryptWithInvalidKeys(invalidResponse, iv, noPattern, subSamples); } - -/** - * Instantiate the set of test cases for each vendor module - */ - -static const std::set<std::string> kAllInstances = [] { - using ::android::hardware::drm::V1_2::ICryptoFactory; - using ::android::hardware::drm::V1_2::IDrmFactory; - - std::vector<std::string> drmInstances = - android::hardware::getAllHalInstanceNames(IDrmFactory::descriptor); - std::vector<std::string> cryptoInstances = - android::hardware::getAllHalInstanceNames(ICryptoFactory::descriptor); - std::set<std::string> allInstances; - allInstances.insert(drmInstances.begin(), drmInstances.end()); - allInstances.insert(cryptoInstances.begin(), cryptoInstances.end()); - return allInstances; -}(); - -INSTANTIATE_TEST_SUITE_P(PerInstance, DrmHalTest, testing::ValuesIn(kAllInstances), - android::hardware::PrintInstanceNameToString); -INSTANTIATE_TEST_SUITE_P(PerInstance, DrmHalClearkeyTest, testing::ValuesIn(kAllInstances), - android::hardware::PrintInstanceNameToString); - -int main(int argc, char** argv) { -#if defined(__LP64__) - const char* kModulePath = "/data/local/tmp/64/lib"; -#else - const char* kModulePath = "/data/local/tmp/32/lib"; -#endif - DrmHalTest::gVendorModules = new drm_vts::VendorModules(kModulePath); - if (DrmHalTest::gVendorModules->getPathList().size() == 0) { - std::cerr << "WARNING: No vendor modules found in " << kModulePath << - ", all vendor tests will be skipped" << std::endl; - } - ::testing::InitGoogleTest(&argc, argv); - int status = RUN_ALL_TESTS(); - ALOGI("Test result = %d", status); - return status; -} diff --git a/drm/1.2/vts/functional/drm_hal_test_main.cpp b/drm/1.2/vts/functional/drm_hal_test_main.cpp new file mode 100644 index 0000000000..ea6e63df6e --- /dev/null +++ b/drm/1.2/vts/functional/drm_hal_test_main.cpp @@ -0,0 +1,80 @@ +/* + * Copyright (C) 2020 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/** + * Instantiate the set of test cases for each vendor module + */ + +#define LOG_TAG "drm_hal_test@1.2" + +#include <gtest/gtest.h> +#include <hidl/HidlSupport.h> +#include <hidl/ServiceManagement.h> +#include <log/log.h> + +#include <algorithm> +#include <iterator> +#include <string> +#include <utility> +#include <vector> + +#include "android/hardware/drm/1.2/vts/drm_hal_common.h" + +using android::hardware::drm::V1_2::vts::DrmHalTest; +using android::hardware::drm::V1_2::vts::DrmHalClearkeyTestV1_2; +using drm_vts::DrmHalTestParam; +using drm_vts::PrintParamInstanceToString; + +static const std::vector<DrmHalTestParam> kAllInstances = [] { + using ::android::hardware::drm::V1_2::ICryptoFactory; + using ::android::hardware::drm::V1_2::IDrmFactory; + + std::vector<std::string> drmInstances = + android::hardware::getAllHalInstanceNames(IDrmFactory::descriptor); + std::vector<std::string> cryptoInstances = + android::hardware::getAllHalInstanceNames(ICryptoFactory::descriptor); + std::set<std::string> allInstances; + allInstances.insert(drmInstances.begin(), drmInstances.end()); + allInstances.insert(cryptoInstances.begin(), cryptoInstances.end()); + + std::vector<DrmHalTestParam> allInstanceUuidCombos; + auto noUUID = [](std::string s) { return DrmHalTestParam(s); }; + std::transform(allInstances.begin(), allInstances.end(), + std::back_inserter(allInstanceUuidCombos), noUUID); + return allInstanceUuidCombos; +}(); + +INSTANTIATE_TEST_SUITE_P(PerInstance, DrmHalTest, testing::ValuesIn(kAllInstances), + PrintParamInstanceToString); +INSTANTIATE_TEST_SUITE_P(PerInstance, DrmHalClearkeyTestV1_2, testing::ValuesIn(kAllInstances), + PrintParamInstanceToString); + +int main(int argc, char** argv) { +#if defined(__LP64__) + const char* kModulePath = "/data/local/tmp/64/lib"; +#else + const char* kModulePath = "/data/local/tmp/32/lib"; +#endif + DrmHalTest::gVendorModules = new drm_vts::VendorModules(kModulePath); + if (DrmHalTest::gVendorModules->getPathList().size() == 0) { + std::cerr << "WARNING: No vendor modules found in " << kModulePath << + ", all vendor tests will be skipped" << std::endl; + } + ::testing::InitGoogleTest(&argc, argv); + int status = RUN_ALL_TESTS(); + ALOGI("Test result = %d", status); + return status; +} diff --git a/drm/1.2/vts/functional/drm_hal_common.h b/drm/1.2/vts/functional/include/android/hardware/drm/1.2/vts/drm_hal_common.h index b2d654c604..6fee6f4631 100644 --- a/drm/1.2/vts/functional/drm_hal_common.h +++ b/drm/1.2/vts/functional/include/android/hardware/drm/1.2/vts/drm_hal_common.h @@ -23,28 +23,33 @@ #include <android/hardware/drm/1.2/IDrmPlugin.h> #include <android/hardware/drm/1.2/IDrmPluginListener.h> #include <android/hardware/drm/1.2/types.h> +#include <hidl/HidlSupport.h> +#include <array> #include <chrono> +# include <iostream> #include <map> #include <memory> #include <string> +#include <utility> #include <vector> #include "drm_hal_vendor_module_api.h" +#include "drm_vts_helper.h" #include "vendor_modules.h" #include "VtsHalHidlTargetCallbackBase.h" using ::android::hardware::drm::V1_0::EventType; using ::android::hardware::drm::V1_0::KeyedVector; -using KeyStatusV1_0 = ::android::hardware::drm::V1_0::KeyStatus; using ::android::hardware::drm::V1_0::KeyType; using ::android::hardware::drm::V1_0::Mode; using ::android::hardware::drm::V1_0::Pattern; using ::android::hardware::drm::V1_0::SessionId; using ::android::hardware::drm::V1_0::SubSample; +using ::android::hardware::drm::V1_1::SecurityLevel; -using ::android::hardware::drm::V1_1::ICryptoFactory; - +using KeyStatusV1_0 = ::android::hardware::drm::V1_0::KeyStatus; +using StatusV1_0 = ::android::hardware::drm::V1_0::Status; using StatusV1_2 = ::android::hardware::drm::V1_2::Status; using ::android::hardware::hidl_array; @@ -55,28 +60,24 @@ using ::android::hardware::Void; using ::android::hidl::memory::V1_0::IMemory; using ::android::sp; +using drm_vts::DrmHalTestParam; + +using std::array; using std::map; +using std::pair; using std::string; using std::unique_ptr; using std::vector; #define EXPECT_OK(ret) EXPECT_TRUE(ret.isOk()) -#define RETURN_IF_SKIPPED \ - if (!vendorModule->isInstalled()) { \ - GTEST_SKIP() << "This drm scheme not supported." \ - << " library:" << GetParam() \ - << " service-name:" << vendorModule->getServiceName() << std::endl; \ - return; \ - } - namespace android { namespace hardware { namespace drm { namespace V1_2 { namespace vts { -class DrmHalTest : public ::testing::TestWithParam<std::string> { +class DrmHalTest : public ::testing::TestWithParam<DrmHalTestParam> { public: static drm_vts::VendorModules* gVendorModules; DrmHalTest(); @@ -84,7 +85,12 @@ class DrmHalTest : public ::testing::TestWithParam<std::string> { virtual void TearDown() override {} protected: + hidl_array<uint8_t, 16> getUUID(); hidl_array<uint8_t, 16> getVendorUUID(); + hidl_array<uint8_t, 16> GetParamUUID() { return GetParam().scheme_; } + string GetParamService() { return GetParam().instance_; } + void provision(); + SessionId openSession(SecurityLevel level, StatusV1_0* err); SessionId openSession(); void closeSession(const SessionId& sessionId); hidl_vec<uint8_t> loadKeys(const SessionId& sessionId, @@ -127,13 +133,15 @@ class DrmHalTest : public ::testing::TestWithParam<std::string> { }; -class DrmHalClearkeyTest : public DrmHalTest { +class DrmHalClearkeyTestV1_2 : public DrmHalTest { public: virtual void SetUp() override { DrmHalTest::SetUp(); - - if (vendorModule == nullptr) { - GTEST_SKIP() << "Instance not supported"; + const uint8_t kClearKeyUUID[16] = { + 0xE2, 0x71, 0x9D, 0x58, 0xA9, 0x85, 0xB3, 0xC9, + 0x78, 0x1A, 0xB0, 0x30, 0xAF, 0x78, 0xD3, 0x0E}; + if (!drmFactory->isCryptoSchemeSupported(kClearKeyUUID)) { + GTEST_SKIP() << "ClearKey not supported by " << GetParamService(); } } virtual void TearDown() override {} diff --git a/drm/1.2/vts/functional/vendor_modules.cpp b/drm/1.2/vts/functional/vendor_modules.cpp deleted file mode 100644 index efcb90a91a..0000000000 --- a/drm/1.2/vts/functional/vendor_modules.cpp +++ /dev/null @@ -1,72 +0,0 @@ -/* - * Copyright (C) 2019 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#define LOG_TAG "drm-vts-vendor-modules" - -#include <dirent.h> -#include <dlfcn.h> -#include <log/log.h> -#include <memory> -#include <utils/String8.h> -#include <SharedLibrary.h> - -#include "vendor_modules.h" - -using std::string; -using std::vector; -using std::unique_ptr; -using ::android::String8; -using ::android::hardware::drm::V1_0::helper::SharedLibrary; - -namespace drm_vts { -void VendorModules::scanModules(const std::string &directory) { - DIR* dir = opendir(directory.c_str()); - if (dir == NULL) { - ALOGE("Unable to open drm VTS vendor directory %s", directory.c_str()); - } else { - struct dirent* entry; - while ((entry = readdir(dir))) { - ALOGD("checking file %s", entry->d_name); - string fullpath = directory + "/" + entry->d_name; - if (endsWith(fullpath, ".so")) { - mPathList.push_back(fullpath); - } - } - closedir(dir); - } -} - -DrmHalVTSVendorModule* VendorModules::getModule(const string& path) { - if (mOpenLibraries.find(path) == mOpenLibraries.end()) { - auto library = std::make_unique<SharedLibrary>(String8(path.c_str())); - if (!library) { - ALOGE("failed to map shared library %s", path.c_str()); - return NULL; - } - mOpenLibraries[path] = std::move(library); - } - const unique_ptr<SharedLibrary>& library = mOpenLibraries[path]; - void* symbol = library->lookup("vendorModuleFactory"); - if (symbol == NULL) { - ALOGE("getVendorModule failed to lookup 'vendorModuleFactory' in %s: " - "%s", path.c_str(), library->lastError()); - return NULL; - } - typedef DrmHalVTSVendorModule* (*ModuleFactory)(); - ModuleFactory moduleFactory = reinterpret_cast<ModuleFactory>(symbol); - return (*moduleFactory)(); -} -}; diff --git a/drm/1.2/vts/functional/vendor_modules.h b/drm/1.2/vts/functional/vendor_modules.h deleted file mode 100644 index 9b730adcd9..0000000000 --- a/drm/1.2/vts/functional/vendor_modules.h +++ /dev/null @@ -1,73 +0,0 @@ -/* - * Copyright (C) 2019 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#ifndef VENDOR_MODULES_H -#define VENDOR_MODULES_H - -#include <map> -#include <vector> -#include <string> - -#include <SharedLibrary.h> - -using ::android::hardware::drm::V1_0::helper::SharedLibrary; - -class DrmHalVTSVendorModule; - -namespace drm_vts { -class VendorModules { - public: - /** - * Initialize with a file system path where the shared libraries - * are to be found. - */ - explicit VendorModules(const std::string& dir) { - scanModules(dir); - } - ~VendorModules() {} - - /** - * Retrieve a DrmHalVTSVendorModule given its full path. The - * getAPIVersion method can be used to determine the versioned - * subclass type. - */ - DrmHalVTSVendorModule* getModule(const std::string& path); - - /** - * Return the list of paths to available vendor modules. - */ - std::vector<std::string> getPathList() const {return mPathList;} - - private: - std::vector<std::string> mPathList; - std::map<std::string, std::unique_ptr<SharedLibrary>> mOpenLibraries; - - /** - * Scan the list of paths to available vendor modules. - */ - void scanModules(const std::string& dir); - - inline bool endsWith(const std::string& str, const std::string& suffix) const { - if (suffix.size() > str.size()) return false; - return std::equal(suffix.rbegin(), suffix.rend(), str.rbegin()); - } - - VendorModules(const VendorModules&) = delete; - void operator=(const VendorModules&) = delete; -}; -}; - -#endif // VENDOR_MODULES_H diff --git a/drm/1.3/vts/OWNERS b/drm/1.3/vts/OWNERS new file mode 100644 index 0000000000..3a0672e5c6 --- /dev/null +++ b/drm/1.3/vts/OWNERS @@ -0,0 +1,9 @@ +conglin@google.com +edwinwong@google.com +fredgc@google.com +jtinker@google.com +juce@google.com +kylealexander@google.com +rfrias@google.com +robertshih@google.com +sigquit@google.com diff --git a/drm/1.3/vts/functional/Android.bp b/drm/1.3/vts/functional/Android.bp new file mode 100644 index 0000000000..4be157584b --- /dev/null +++ b/drm/1.3/vts/functional/Android.bp @@ -0,0 +1,78 @@ +// +// Copyright (C) 2020 The Android Open Source Project +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// + +cc_library_static { + name: "android.hardware.drm@1.3-vts", + defaults: ["VtsHalTargetTestDefaults"], + local_include_dirs: [ + "include", + ], + srcs: [ + "drm_hal_test.cpp", + ], + shared_libs: [ + "android.hardware.drm@1.0", + "android.hardware.drm@1.1", + "android.hardware.drm@1.2", + "android.hardware.drm@1.3", + "android.hidl.allocator@1.0", + "android.hidl.memory@1.0", + "libhidlmemory", + "libnativehelper", + ], + static_libs: [ + "android.hardware.drm@1.0-helper", + "libcrypto_static", + "libdrmvtshelper", + ], + export_include_dirs: [ + "include", + ], +} + +cc_test { + name: "VtsHalDrmV1_3TargetTest", + defaults: ["VtsHalTargetTestDefaults"], + include_dirs: ["hardware/interfaces/drm/1.0/vts/functional"], + srcs: [ + "drm_hal_test_main.cpp", + ], + whole_static_libs: [ + "android.hardware.drm@1.0-vts", + "android.hardware.drm@1.1-vts", + "android.hardware.drm@1.2-vts", + "android.hardware.drm@1.3-vts", + ], + shared_libs: [ + "android.hardware.drm@1.0", + "android.hardware.drm@1.1", + "android.hardware.drm@1.2", + "android.hardware.drm@1.3", + "android.hidl.allocator@1.0", + "android.hidl.memory@1.0", + "libhidlmemory", + "libnativehelper", + ], + static_libs: [ + "android.hardware.drm@1.0-helper", + "libcrypto_static", + "libdrmvtshelper", + ], + test_suites: [ + "general-tests", + "vts-core", + ], +} diff --git a/drm/1.3/vts/functional/drm_hal_test.cpp b/drm/1.3/vts/functional/drm_hal_test.cpp new file mode 100644 index 0000000000..738f5b28f6 --- /dev/null +++ b/drm/1.3/vts/functional/drm_hal_test.cpp @@ -0,0 +1,60 @@ +/* + * Copyright (C) 2020 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#define LOG_TAG "drm_hal_test@1.3" + +#include "android/hardware/drm/1.3/vts/drm_hal_test.h" + +namespace android { +namespace hardware { +namespace drm { +namespace V1_3 { +namespace vts { + +TEST_P(DrmHalTestV1_3, SchemeSupported) { + EXPECT_TRUE(drmFactory_->isCryptoSchemeSupported(GetParam().scheme_)); +} + +TEST_P(DrmHalTestV1_3, SignRsaNotAllowed) { + hidl_array<uint8_t, 16> kWidevineUUID ({ + 0xED,0xEF,0x8B,0xA9,0x79,0xD6,0x4A,0xCE, + 0xA3,0xC8,0x27,0xDC,0xD5,0x1D,0x21,0xED + }); + + if (!drmFactory_->isCryptoSchemeSupported(kWidevineUUID)) { + GTEST_SKIP() << "Widevine only test"; + } + + // signRSA + const hidl_vec<uint8_t>& sessionId{}; + const hidl_string& algorithm{}; + const hidl_vec<uint8_t>& message{}; + const hidl_vec<uint8_t>& wrappedKey{}; + auto res = drmPlugin_->signRSA( + sessionId, algorithm, message, wrappedKey, + [&](StatusV1_0 status, const hidl_vec<uint8_t>& signature) { + EXPECT_EQ(status, StatusV1_0::ERROR_DRM_UNKNOWN); + EXPECT_EQ(signature.size(), 0); + } + ); + EXPECT_TRUE(res.isOk()); +} + +} // namespace vts +} // namespace V1_3 +} // namespace drm +} // namespace hardware +} // namespace android diff --git a/drm/1.3/vts/functional/drm_hal_test_main.cpp b/drm/1.3/vts/functional/drm_hal_test_main.cpp new file mode 100644 index 0000000000..02b45ea6b9 --- /dev/null +++ b/drm/1.3/vts/functional/drm_hal_test_main.cpp @@ -0,0 +1,134 @@ +/* + * Copyright (C) 2020 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/** + * Instantiate the set of test cases for each vendor module + */ + +#define LOG_TAG "drm_hal_test@1.3" + +#include <android/hardware/drm/1.3/ICryptoFactory.h> +#include <android/hardware/drm/1.3/IDrmFactory.h> +#include <gtest/gtest.h> +#include <hidl/HidlSupport.h> +#include <hidl/ServiceManagement.h> +#include <log/log.h> + +#include <algorithm> +#include <iterator> +#include <string> +#include <utility> +#include <vector> + +#include "android/hardware/drm/1.0/vts/drm_hal_clearkey_test.h" // V1_0 tests +#include "android/hardware/drm/1.0/vts/drm_hal_vendor_test.h" // V1_0 tests +#include "android/hardware/drm/1.1/vts/drm_hal_clearkey_test.h" // V1_1 tests +#include "android/hardware/drm/1.2/vts/drm_hal_common.h" // V1_2 tests +#include "android/hardware/drm/1.3/vts/drm_hal_test.h" // V1_3 tests + +using drm_vts::DrmHalTestParam; +using drm_vts::PrintParamInstanceToString; + +using android::hardware::drm::V1_0::vts::DrmHalVendorFactoryTest; +using android::hardware::drm::V1_0::vts::DrmHalVendorPluginTest; +using android::hardware::drm::V1_0::vts::DrmHalVendorDecryptTest; +using android::hardware::drm::V1_0::vts::DrmHalClearkeyFactoryTest; +using android::hardware::drm::V1_0::vts::DrmHalClearkeyPluginTest; +using android::hardware::drm::V1_0::vts::DrmHalClearkeyDecryptTest; +using android::hardware::drm::V1_1::vts::DrmHalClearkeyTest; +using android::hardware::drm::V1_2::vts::DrmHalTest; +using android::hardware::drm::V1_2::vts::DrmHalClearkeyTestV1_2; +using android::hardware::drm::V1_3::vts::DrmHalTestV1_3; + +static const std::vector<DrmHalTestParam> kAllInstances = [] { + using ::android::hardware::drm::V1_3::ICryptoFactory; + using ::android::hardware::drm::V1_3::IDrmFactory; + + std::vector<std::string> drmInstances = + android::hardware::getAllHalInstanceNames(IDrmFactory::descriptor); + std::vector<std::string> cryptoInstances = + android::hardware::getAllHalInstanceNames(ICryptoFactory::descriptor); + std::set<std::string> allInstances; + allInstances.insert(drmInstances.begin(), drmInstances.end()); + allInstances.insert(cryptoInstances.begin(), cryptoInstances.end()); + + std::vector<DrmHalTestParam> allInstanceUuidCombos; + for (const auto &instance : allInstances) { + auto drmFactory = IDrmFactory::getService(instance); + if (drmFactory == nullptr) { + continue; + } + drmFactory->getSupportedCryptoSchemes( + [&](const hidl_vec<hidl_array<uint8_t, 16>>& schemes) { + for (const auto &scheme : schemes) { + allInstanceUuidCombos.push_back(DrmHalTestParam(instance, scheme)); + } + }); + } + return allInstanceUuidCombos; +}(); + +INSTANTIATE_TEST_CASE_P(PerInstanceUuidV1_0, DrmHalVendorFactoryTest, + testing::ValuesIn(kAllInstances), + drm_vts::PrintParamInstanceToString); +INSTANTIATE_TEST_CASE_P(PerInstanceUuidV1_0, DrmHalVendorPluginTest, + testing::ValuesIn(kAllInstances), + drm_vts::PrintParamInstanceToString); +INSTANTIATE_TEST_CASE_P(PerInstanceUuidV1_0, DrmHalVendorDecryptTest, + testing::ValuesIn(kAllInstances), + drm_vts::PrintParamInstanceToString); + +INSTANTIATE_TEST_SUITE_P(PerInstanceUuidV1_0, DrmHalClearkeyFactoryTest, + testing::ValuesIn(kAllInstances), + drm_vts::PrintParamInstanceToString); +INSTANTIATE_TEST_SUITE_P(PerInstanceUuidV1_0, DrmHalClearkeyPluginTest, + testing::ValuesIn(kAllInstances), + drm_vts::PrintParamInstanceToString); +INSTANTIATE_TEST_SUITE_P(PerInstanceUuidV1_0, DrmHalClearkeyDecryptTest, + testing::ValuesIn(kAllInstances), + drm_vts::PrintParamInstanceToString); + +INSTANTIATE_TEST_SUITE_P(PerInstanceUuidV1_1, DrmHalClearkeyTest, + testing::ValuesIn(kAllInstances), + PrintParamInstanceToString); + +INSTANTIATE_TEST_SUITE_P(PerInstanceUuidV1_2, DrmHalTest, + testing::ValuesIn(kAllInstances), + PrintParamInstanceToString); +INSTANTIATE_TEST_SUITE_P(PerInstanceUuidV1_2, DrmHalClearkeyTestV1_2, + testing::ValuesIn(kAllInstances), + PrintParamInstanceToString); + +INSTANTIATE_TEST_SUITE_P(PerInstanceUuidV1_3, DrmHalTestV1_3, + testing::ValuesIn(kAllInstances), + PrintParamInstanceToString); + +int main(int argc, char** argv) { +#if defined(__LP64__) + const char* kModulePath = "/data/local/tmp/64/lib"; +#else + const char* kModulePath = "/data/local/tmp/32/lib"; +#endif + DrmHalTest::gVendorModules = new drm_vts::VendorModules(kModulePath); + if (DrmHalTest::gVendorModules->getPathList().size() == 0) { + std::cerr << "WARNING: No vendor modules found in " << kModulePath << + ", all vendor tests will be skipped" << std::endl; + } + ::testing::InitGoogleTest(&argc, argv); + int status = RUN_ALL_TESTS(); + ALOGI("Test result = %d", status); + return status; +} diff --git a/drm/1.3/vts/functional/include/android/hardware/drm/1.3/vts/drm_hal_test.h b/drm/1.3/vts/functional/include/android/hardware/drm/1.3/vts/drm_hal_test.h new file mode 100644 index 0000000000..d8f527764b --- /dev/null +++ b/drm/1.3/vts/functional/include/android/hardware/drm/1.3/vts/drm_hal_test.h @@ -0,0 +1,86 @@ +/* + * Copyright (C) 2020 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef DRM_HAL_TEST_V1_3_H +#define DRM_HAL_TEST_V1_3_H + +#include <android/hardware/drm/1.3/ICryptoFactory.h> +#include <android/hardware/drm/1.3/IDrmFactory.h> +#include <gtest/gtest.h> +#include <hidl/HidlSupport.h> +#include <hidl/ServiceManagement.h> +#include <log/log.h> + +#include <algorithm> +#include <iterator> +#include <string> +#include <utility> +#include <vector> + +#include "drm_hal_vendor_module_api.h" +#include "drm_vts_helper.h" +#include "vendor_modules.h" +#include "VtsHalHidlTargetCallbackBase.h" + +namespace android { +namespace hardware { +namespace drm { +namespace V1_3 { +namespace vts { + +using android::hardware::hidl_array; +using android::hardware::hidl_string; + +using drm_vts::DrmHalTestParam; + +using IDrmFactoryV1_3 = android::hardware::drm::V1_3::IDrmFactory; +using IDrmPluginV1_0 = android::hardware::drm::V1_0::IDrmPlugin; +using StatusV1_0 = android::hardware::drm::V1_0::Status; + +class DrmHalTestV1_3 : public ::testing::TestWithParam<DrmHalTestParam> { +public: + DrmHalTestV1_3() + : drmFactory_(IDrmFactoryV1_3::getService(GetParam().instance_)) {} + + virtual void SetUp() override { + ASSERT_NE(drmFactory_, nullptr); + + // create plugin + hidl_string packageName("android.hardware.drm.V1_3.vts"); + auto res = drmFactory_->createPlugin( + GetParam().scheme_, packageName, + [&](StatusV1_0 status, const sp<IDrmPluginV1_0>& pluginV1_0) { + EXPECT_EQ(StatusV1_0::OK, status); + drmPlugin_ = pluginV1_0; + }); + EXPECT_TRUE(res.isOk()); + ASSERT_NE(drmPlugin_, nullptr); + } + + virtual void TearDown() override {} + +protected: + sp<IDrmFactoryV1_3> drmFactory_; + sp<IDrmPluginV1_0> drmPlugin_; +}; + +} // namespace vts +} // namespace V1_3 +} // namespace drm +} // namespace hardware +} // namespace android + +#endif // DRM_HAL_TEST_V1_3_H diff --git a/dumpstate/1.0/vts/functional/VtsHalDumpstateV1_0TargetTest.cpp b/dumpstate/1.0/vts/functional/VtsHalDumpstateV1_0TargetTest.cpp index 96b13c5c38..343d4c9bcf 100644 --- a/dumpstate/1.0/vts/functional/VtsHalDumpstateV1_0TargetTest.cpp +++ b/dumpstate/1.0/vts/functional/VtsHalDumpstateV1_0TargetTest.cpp @@ -78,6 +78,7 @@ TEST_P(DumpstateHidlTest, TestOk) { ASSERT_EQ(1, read(fds[0], &buff, 1)) << "dumped nothing"; native_handle_close(handle); + native_handle_delete(handle); } // Positive test: make sure dumpstateBoard() doesn't crash with two FDs. @@ -96,6 +97,7 @@ TEST_P(DumpstateHidlTest, TestHandleWithTwoFds) { ASSERT_TRUE(status.isOk()) << "Status should be ok: " << status.description(); native_handle_close(handle); + native_handle_delete(handle); } INSTANTIATE_TEST_SUITE_P( diff --git a/dumpstate/1.1/Android.bp b/dumpstate/1.1/Android.bp new file mode 100644 index 0000000000..2aa8c82dc7 --- /dev/null +++ b/dumpstate/1.1/Android.bp @@ -0,0 +1,18 @@ +// This file is autogenerated by hidl-gen -Landroidbp. + +hidl_interface { + name: "android.hardware.dumpstate@1.1", + root: "android.hardware", + vndk: { + enabled: true, + }, + srcs: [ + "types.hal", + "IDumpstateDevice.hal", + ], + interfaces: [ + "android.hardware.dumpstate@1.0", + "android.hidl.base@1.0", + ], + gen_java: true, +} diff --git a/dumpstate/1.1/IDumpstateDevice.hal b/dumpstate/1.1/IDumpstateDevice.hal new file mode 100644 index 0000000000..24831b37b2 --- /dev/null +++ b/dumpstate/1.1/IDumpstateDevice.hal @@ -0,0 +1,53 @@ +/* + * Copyright (C) 2020 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package android.hardware.dumpstate@1.1; + +import @1.0::IDumpstateDevice; + +interface IDumpstateDevice extends @1.0::IDumpstateDevice { + /** + * Extension of dumpstateBoard which also accepts a mode parameter to limit dumped data. + * + * For an example of when this is relevant, consider a bug report being generated with + * DumpstateMode::CONNECTIVITY - there is no reason to include camera or USB logs in this type + * of report. + * + * The 1.0 version of #dumpstateBoard(handle) should just delegate to this new method and pass + * DumpstateMode::DEFAULT and a timeout of 30,000ms (30 seconds). + * + * @param h A native handle with one or two valid file descriptors. The first FD is for text + * output, the second (if present) is for binary output. + * @param mode A mode value to restrict dumped content. + * @param timeoutMillis An approximate "budget" for how much time this call has been allotted. + * If execution runs longer than this, the IDumpstateDevice service may be killed and only + * partial information will be included in the report. + */ + dumpstateBoard_1_1(handle h, DumpstateMode mode, uint64_t timeoutMillis); + + /** + * Turns device vendor logging on or off. + * + * The setting should be persistent across reboots. Underlying implementations may need to start + * vendor logging daemons, set system properties, or change logging masks, for example. Given + * that many vendor logs contain significant amounts of private information and may come with + * memory/storage/battery impacts, calling this method on a user build should only be done after + * user consent has been obtained, e.g. from a toggle in developer settings. + * + * @param enable Whether to enable or disable device vendor logging. + * @return success Whether or not the change took effect. + */ + setDeviceLoggingEnabled(bool enable) generates (bool success); +}; diff --git a/dumpstate/1.1/types.hal b/dumpstate/1.1/types.hal new file mode 100644 index 0000000000..a6f391aded --- /dev/null +++ b/dumpstate/1.1/types.hal @@ -0,0 +1,62 @@ +/* + * Copyright (C) 2020 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package android.hardware.dumpstate@1.1; + +/** + * Constants that define the type of bug report being taken to restrict content appropriately. + */ +enum DumpstateMode : uint32_t { + /** + * Takes a bug report without user interference. + */ + FULL = 0, + + /** + * Interactive bug report, i.e. triggered by the user. + */ + INTERACTIVE = 1, + + /** + * Remote bug report triggered by DevicePolicyManager, for example. + */ + REMOTE = 2, + + /** + * Bug report triggered on a wear device. + */ + WEAR = 3, + + /** + * Bug report limited to only connectivity info (cellular, wifi, and networking). Sometimes + * called "telephony" in legacy contexts. + * + * All reported information MUST directly relate to connectivity debugging or customer support + * and MUST NOT contain unrelated private information. This information MUST NOT identify + * user-installed packages (UIDs are OK, package names are not), and MUST NOT contain logs of + * user application traffic. + */ + CONNECTIVITY = 4, + + /** + * Bug report limited to only wifi info. + */ + WIFI = 5, + + /** + * Default mode. + */ + DEFAULT = 6 +}; diff --git a/dumpstate/1.1/vts/functional/Android.bp b/dumpstate/1.1/vts/functional/Android.bp new file mode 100644 index 0000000000..5267706c0b --- /dev/null +++ b/dumpstate/1.1/vts/functional/Android.bp @@ -0,0 +1,29 @@ +// +// Copyright (C) 2020 The Android Open Source Project +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// + +cc_test { + name: "VtsHalDumpstateV1_1TargetTest", + defaults: ["VtsHalTargetTestDefaults"], + srcs: ["VtsHalDumpstateV1_1TargetTest.cpp"], + static_libs: [ + "android.hardware.dumpstate@1.0", + "android.hardware.dumpstate@1.1", + ], + test_suites: [ + "general-tests", + "vts-core", + ], +} diff --git a/dumpstate/1.1/vts/functional/VtsHalDumpstateV1_1TargetTest.cpp b/dumpstate/1.1/vts/functional/VtsHalDumpstateV1_1TargetTest.cpp new file mode 100644 index 0000000000..583efbf722 --- /dev/null +++ b/dumpstate/1.1/vts/functional/VtsHalDumpstateV1_1TargetTest.cpp @@ -0,0 +1,176 @@ +/* + * Copyright (C) 2020 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#define LOG_TAG "dumpstate_1_1_hidl_hal_test" + +#include <fcntl.h> +#include <unistd.h> + +#include <vector> + +#include <android/hardware/dumpstate/1.1/IDumpstateDevice.h> +#include <android/hardware/dumpstate/1.1/types.h> +#include <cutils/native_handle.h> +#include <gtest/gtest.h> +#include <hidl/GtestPrinter.h> +#include <hidl/ServiceManagement.h> +#include <log/log.h> + +namespace { + +using ::android::sp; +using ::android::hardware::Return; +using ::android::hardware::dumpstate::V1_1::DumpstateMode; +using ::android::hardware::dumpstate::V1_1::IDumpstateDevice; + +class DumpstateHidl1_1Test : public ::testing::TestWithParam<std::string> { + public: + virtual void SetUp() override { + dumpstate = IDumpstateDevice::getService(GetParam()); + ASSERT_NE(dumpstate, nullptr) << "Could not get HIDL instance"; + } + + sp<IDumpstateDevice> dumpstate; +}; + +#define TEST_FOR_DUMPSTATE_MODE(name, body, mode) \ + TEST_P(DumpstateHidl1_1Test, name##_##mode) { body(DumpstateMode::mode); } + +#define TEST_FOR_ALL_DUMPSTATE_MODES(name, body) \ + TEST_FOR_DUMPSTATE_MODE(name, body, FULL); \ + TEST_FOR_DUMPSTATE_MODE(name, body, INTERACTIVE); \ + TEST_FOR_DUMPSTATE_MODE(name, body, REMOTE); \ + TEST_FOR_DUMPSTATE_MODE(name, body, WEAR); \ + TEST_FOR_DUMPSTATE_MODE(name, body, CONNECTIVITY); \ + TEST_FOR_DUMPSTATE_MODE(name, body, WIFI); \ + TEST_FOR_DUMPSTATE_MODE(name, body, DEFAULT); + +constexpr uint64_t kDefaultTimeoutMillis = 30 * 1000; // 30 seconds + +// Negative test: make sure dumpstateBoard() doesn't crash when passed a null pointer. +TEST_FOR_ALL_DUMPSTATE_MODES(TestNullHandle, [this](DumpstateMode mode) { + Return<void> status = dumpstate->dumpstateBoard_1_1(nullptr, mode, kDefaultTimeoutMillis); + + ASSERT_TRUE(status.isOk()) << "Status should be ok: " << status.description(); +}); + +// Negative test: make sure dumpstateBoard() ignores a handle with no FD. +TEST_FOR_ALL_DUMPSTATE_MODES(TestHandleWithNoFd, [this](DumpstateMode mode) { + native_handle_t* handle = native_handle_create(0, 0); + ASSERT_NE(handle, nullptr) << "Could not create native_handle"; + + Return<void> status = dumpstate->dumpstateBoard_1_1(handle, mode, kDefaultTimeoutMillis); + + ASSERT_TRUE(status.isOk()) << "Status should be ok: " << status.description(); + + native_handle_close(handle); + native_handle_delete(handle); +}); + +// Positive test: make sure dumpstateBoard() writes something to the FD. +TEST_FOR_ALL_DUMPSTATE_MODES(TestOk, [this](DumpstateMode mode) { + // Index 0 corresponds to the read end of the pipe; 1 to the write end. + int fds[2]; + ASSERT_EQ(0, pipe2(fds, O_NONBLOCK)) << errno; + + native_handle_t* handle = native_handle_create(1, 0); + ASSERT_NE(handle, nullptr) << "Could not create native_handle"; + handle->data[0] = fds[1]; + + Return<void> status = dumpstate->dumpstateBoard_1_1(handle, mode, kDefaultTimeoutMillis); + ASSERT_TRUE(status.isOk()) << "Status should be ok: " << status.description(); + + // Check that at least one byte was written + char buff; + ASSERT_EQ(1, read(fds[0], &buff, 1)) << "dumped nothing"; + + native_handle_close(handle); + native_handle_delete(handle); +}); + +// Positive test: make sure dumpstateBoard() doesn't crash with two FDs. +TEST_FOR_ALL_DUMPSTATE_MODES(TestHandleWithTwoFds, [this](DumpstateMode mode) { + int fds1[2]; + int fds2[2]; + ASSERT_EQ(0, pipe2(fds1, O_NONBLOCK)) << errno; + ASSERT_EQ(0, pipe2(fds2, O_NONBLOCK)) << errno; + + native_handle_t* handle = native_handle_create(2, 0); + ASSERT_NE(handle, nullptr) << "Could not create native_handle"; + handle->data[0] = fds1[1]; + handle->data[1] = fds2[1]; + + Return<void> status = dumpstate->dumpstateBoard_1_1(handle, mode, kDefaultTimeoutMillis); + ASSERT_TRUE(status.isOk()) << "Status should be ok: " << status.description(); + + native_handle_close(handle); + native_handle_delete(handle); +}); + +// Make sure dumpstateBoard_1_1 actually validates its arguments. +TEST_P(DumpstateHidl1_1Test, TestInvalidModeArgument_Negative) { + int fds[2]; + ASSERT_EQ(0, pipe2(fds, O_NONBLOCK)) << errno; + + native_handle_t* handle = native_handle_create(1, 0); + ASSERT_NE(handle, nullptr) << "Could not create native_handle"; + handle->data[0] = fds[1]; + + Return<void> status = dumpstate->dumpstateBoard_1_1(handle, static_cast<DumpstateMode>(-100), + kDefaultTimeoutMillis); + ASSERT_FALSE(status.isOk()) << "Status should not be ok with invalid mode param: " + << status.description(); + + native_handle_close(handle); + native_handle_delete(handle); +} + +TEST_P(DumpstateHidl1_1Test, TestInvalidModeArgument_Undefined) { + int fds[2]; + ASSERT_EQ(0, pipe2(fds, O_NONBLOCK)) << errno; + + native_handle_t* handle = native_handle_create(1, 0); + ASSERT_NE(handle, nullptr) << "Could not create native_handle"; + handle->data[0] = fds[1]; + + Return<void> status = dumpstate->dumpstateBoard_1_1(handle, static_cast<DumpstateMode>(9001), + kDefaultTimeoutMillis); + ASSERT_FALSE(status.isOk()) << "Status should not be ok with invalid mode param: " + << status.description(); + + native_handle_close(handle); + native_handle_delete(handle); +} + +// Make sure toggling device logging doesn't crash. +TEST_P(DumpstateHidl1_1Test, TestEnableDeviceLogging) { + Return<bool> status = dumpstate->setDeviceLoggingEnabled(true); + + ASSERT_TRUE(status.isOk()) << "Status should be ok: " << status.description(); +} + +TEST_P(DumpstateHidl1_1Test, TestDisableDeviceLogging) { + Return<bool> status = dumpstate->setDeviceLoggingEnabled(false); + + ASSERT_TRUE(status.isOk()) << "Status should be ok: " << status.description(); +} + +INSTANTIATE_TEST_SUITE_P( + PerInstance, DumpstateHidl1_1Test, + testing::ValuesIn(android::hardware::getAllHalInstanceNames(IDumpstateDevice::descriptor)), + android::hardware::PrintInstanceNameToString); + +} // namespace diff --git a/gnss/1.0/IGnssCallback.hal b/gnss/1.0/IGnssCallback.hal index d62676fb5e..311ab2166c 100644 --- a/gnss/1.0/IGnssCallback.hal +++ b/gnss/1.0/IGnssCallback.hal @@ -90,6 +90,7 @@ interface IGnssCallback { * - QZSS: 193-200 * - Galileo: 1-36 * - Beidou: 1-37 + * - IRNSS: 1-14 */ int16_t svid; diff --git a/gnss/1.0/vts/functional/VtsHalGnssV1_0TargetTest.cpp b/gnss/1.0/vts/functional/VtsHalGnssV1_0TargetTest.cpp index 6183a1ae28..c04b4d0df2 100644 --- a/gnss/1.0/vts/functional/VtsHalGnssV1_0TargetTest.cpp +++ b/gnss/1.0/vts/functional/VtsHalGnssV1_0TargetTest.cpp @@ -26,6 +26,8 @@ #include <condition_variable> #include <mutex> +#include <cutils/properties.h> + using android::hardware::Return; using android::hardware::Void; @@ -37,6 +39,12 @@ using android::hardware::gnss::V1_0::IGnssDebug; using android::hardware::gnss::V1_0::IGnssMeasurement; using android::sp; +static bool IsAutomotiveDevice() { + char buffer[PROPERTY_VALUE_MAX] = {0}; + property_get("ro.hardware.type", buffer, ""); + return strncmp(buffer, "automotive", PROPERTY_VALUE_MAX) == 0; +} + #define TIMEOUT_SEC 2 // for basic commands/responses // for command line argument on how strictly to run the test @@ -473,9 +481,9 @@ TEST_P(GnssHalTest, GetAllExtensions) { auto gnssDebug = gnss_hal_->getExtensionGnssDebug(); ASSERT_TRUE(gnssDebug.isOk()); - if (info_called_count_ > 0 && last_info_.yearOfHw >= 2017) { - sp<IGnssDebug> iGnssDebug = gnssDebug; - EXPECT_NE(iGnssDebug, nullptr); + if (!IsAutomotiveDevice() && info_called_count_ > 0 && last_info_.yearOfHw >= 2017) { + sp<IGnssDebug> iGnssDebug = gnssDebug; + EXPECT_NE(iGnssDebug, nullptr); } } diff --git a/gnss/1.1/vts/functional/Android.bp b/gnss/1.1/vts/functional/Android.bp index bdd02d2e6e..369a89da39 100644 --- a/gnss/1.1/vts/functional/Android.bp +++ b/gnss/1.1/vts/functional/Android.bp @@ -29,6 +29,10 @@ cc_test { ], shared_libs: [ "android.hardware.gnss.measurement_corrections@1.0", + "android.hardware.gnss.measurement_corrections@1.1", + ], + test_suites: [ + "general-tests", + "vts-core", ], - test_suites: ["general-tests", "vts-core"], } diff --git a/gnss/1.1/vts/functional/gnss_hal_test_cases.cpp b/gnss/1.1/vts/functional/gnss_hal_test_cases.cpp index e0d8b54525..8530ffb08e 100644 --- a/gnss/1.1/vts/functional/gnss_hal_test_cases.cpp +++ b/gnss/1.1/vts/functional/gnss_hal_test_cases.cpp @@ -19,6 +19,7 @@ #include <gnss_hal_test.h> #include <android/hardware/gnss/1.1/IGnssConfiguration.h> +#include <cutils/properties.h> #include <gtest/gtest.h> using android::hardware::hidl_vec; @@ -32,6 +33,12 @@ using android::hardware::gnss::V1_0::IGnssDebug; using android::hardware::gnss::V1_1::IGnssConfiguration; using android::hardware::gnss::V1_1::IGnssMeasurement; +static bool IsAutomotiveDevice() { + char buffer[PROPERTY_VALUE_MAX] = {0}; + property_get("ro.hardware.type", buffer, ""); + return strncmp(buffer, "automotive", PROPERTY_VALUE_MAX) == 0; +} + /* * SetupTeardownCreateCleanup: * Requests the gnss HAL then calls cleanup @@ -524,7 +531,8 @@ TEST_P(GnssHalTest, InjectBestLocation) { TEST_P(GnssHalTest, GnssDebugValuesSanityTest) { auto gnssDebug = gnss_hal_->getExtensionGnssDebug(); ASSERT_TRUE(gnssDebug.isOk()); - if (gnss_cb_->info_cbq_.calledCount() > 0 && gnss_cb_->last_info_.yearOfHw >= 2017) { + if (!IsAutomotiveDevice() && gnss_cb_->info_cbq_.calledCount() > 0 && + gnss_cb_->last_info_.yearOfHw >= 2017) { sp<IGnssDebug> iGnssDebug = gnssDebug; EXPECT_NE(iGnssDebug, nullptr); diff --git a/gnss/2.0/vts/functional/gnss_hal_test_cases.cpp b/gnss/2.0/vts/functional/gnss_hal_test_cases.cpp index 0fa08b903e..53f5b9e283 100644 --- a/gnss/2.0/vts/functional/gnss_hal_test_cases.cpp +++ b/gnss/2.0/vts/functional/gnss_hal_test_cases.cpp @@ -296,6 +296,12 @@ TEST_P(GnssHalTest, TestGnssVisibilityControlExtension) { * capability flag is set. */ TEST_P(GnssHalTest, TestGnssMeasurementCorrectionsCapabilities) { + if (!IsGnssHalVersion_2_0()) { + ALOGI("Test GnssMeasurementCorrectionsCapabilities skipped. GNSS HAL version is greater " + "than 2.0."); + return; + } + if (!(gnss_cb_->last_capabilities_ & IGnssCallback::Capabilities::MEASUREMENT_CORRECTIONS)) { return; } diff --git a/gnss/2.1/Android.bp b/gnss/2.1/Android.bp index 8b0c374469..7efc4a60aa 100644 --- a/gnss/2.1/Android.bp +++ b/gnss/2.1/Android.bp @@ -7,6 +7,7 @@ hidl_interface { enabled: true, }, srcs: [ + "types.hal", "IGnss.hal", "IGnssCallback.hal", "IGnssMeasurement.hal", @@ -14,6 +15,7 @@ hidl_interface { "IGnssConfiguration.hal", ], interfaces: [ + "android.hardware.gnss.measurement_corrections@1.1", "android.hardware.gnss.measurement_corrections@1.0", "android.hardware.gnss.visibility_control@1.0", "android.hardware.gnss@1.0", diff --git a/gnss/2.1/IGnss.hal b/gnss/2.1/IGnss.hal index 2d633928dd..ce37647868 100644 --- a/gnss/2.1/IGnss.hal +++ b/gnss/2.1/IGnss.hal @@ -16,6 +16,7 @@ package android.hardware.gnss@2.1; +import android.hardware.gnss.measurement_corrections@1.1::IMeasurementCorrections; import @2.0::IGnss; import IGnssCallback; @@ -62,4 +63,14 @@ interface IGnss extends @2.0::IGnss { * @return gnssConfigurationIface Handle to the IGnssConfiguration interface. */ getExtensionGnssConfiguration_2_1() generates (IGnssConfiguration gnssConfigurationIface); + + /** + * This method returns the IMeasurementCorrections interface. + * + * Both getExtensionMeasurementCorrections and getExtensionMeasurementCorrections_1_1 must + * return non-null. Both methods can return the same V1.1 IMeasurementCorrections object. + * + * @return measurementCorrectionsIface Handle to the IMeasurementCorrections interface. + */ + getExtensionMeasurementCorrections_1_1() generates (IMeasurementCorrections measurementCorrectionsIface); };
\ No newline at end of file diff --git a/gnss/2.1/IGnssMeasurementCallback.hal b/gnss/2.1/IGnssMeasurementCallback.hal index ca6175f508..0385abd467 100644 --- a/gnss/2.1/IGnssMeasurementCallback.hal +++ b/gnss/2.1/IGnssMeasurementCallback.hal @@ -17,24 +17,115 @@ package android.hardware.gnss@2.1; import @1.0::IGnssMeasurementCallback; -import @1.1::IGnssMeasurementCallback; import @2.0::IGnssMeasurementCallback; import @2.0::ElapsedRealtime; +import GnssSignalType; /** The callback interface to report measurements from the HAL. */ interface IGnssMeasurementCallback extends @2.0::IGnssMeasurementCallback { /** - * Extends a GNSS Measurement, adding a basebandCN0DbHz. + * Flags to indicate what fields in GnssMeasurement are valid. + */ + enum GnssMeasurementFlags : uint32_t { + /** A valid 'snr' is stored in the data structure. */ + HAS_SNR = 1 << 0, + /** A valid 'carrier frequency' is stored in the data structure. */ + HAS_CARRIER_FREQUENCY = 1 << 9, + /** A valid 'carrier cycles' is stored in the data structure. */ + HAS_CARRIER_CYCLES = 1 << 10, + /** A valid 'carrier phase' is stored in the data structure. */ + HAS_CARRIER_PHASE = 1 << 11, + /** A valid 'carrier phase uncertainty' is stored in the data structure. */ + HAS_CARRIER_PHASE_UNCERTAINTY = 1 << 12, + /** A valid automatic gain control is stored in the data structure. */ + HAS_AUTOMATIC_GAIN_CONTROL = 1 << 13, + /** A valid receiver inter-signal bias is stored in the data structure. */ + HAS_RECEIVER_ISB = 1 << 16, + /** A valid receiver inter-signal bias uncertainty is stored in the data structure. */ + HAS_RECEIVER_ISB_UNCERTAINTY = 1 << 17, + /** A valid satellite inter-signal bias is stored in the data structure. */ + HAS_SATELLITE_ISB = 1 << 18, + /** A valid satellite inter-signal bias uncertainty is stored in the data structure. */ + HAS_SATELLITE_ISB_UNCERTAINTY = 1 << 19 + }; + + + /** + * Extends a GNSS Measurement, adding basebandCN0DbHz, GnssMeasurementFlags, + * receiverInterSignalBiasNs, receiverInterSignalBiasUncertaintyNs, satelliteInterSignalBiasNs + * and satelliteInterSignalBiasUncertaintyNs. */ struct GnssMeasurement { /** * GNSS measurement information for a single satellite and frequency, as in the 2.0 version * of the HAL. + * + * In this version of the HAL, the field 'flags' in the v2_0.v1_1.v1_0 struct is deprecated, + * and is no longer used by the framework. The GNSS measurement flags are instead reported + * in @2.1::IGnssMeasurementCallback.GnssMeasurement.flags. + * */ @2.0::IGnssMeasurementCallback.GnssMeasurement v2_0; /** + * A set of flags indicating the validity of the fields in this data + * structure. + * + * Fields for which there is no corresponding flag must be filled in + * with a valid value. For convenience, these are marked as mandatory. + * + * Others fields may have invalid information in them, if not marked as + * valid by the corresponding bit in flags. + */ + bitfield<GnssMeasurementFlags> flags; + + /** + * The receiver inter-signal bias (ISB) in nanoseconds. + * + * This value is the estimated receiver-side inter-system (different from the constellation + * in GnssClock.referenceSignalForIsb) bias and inter-frequency (different from the carrier + * frequency in GnssClock.referenceSignalForIsb) bias. The reported receiver ISB + * must include signal delays caused by + * + * - Receiver inter-constellation bias + * - Receiver inter-frequency bias + * - Receiver inter-code bias + * + * The value does not include the inter-frequency Ionospheric bias. + * + * The receiver ISB of GnssClock.referenceSignalForIsb is defined to be 0.0 nanoseconds. + */ + double receiverInterSignalBiasNs; + + /** + * 1-sigma uncertainty associated with the receiver inter-signal bias in nanoseconds. + */ + double receiverInterSignalBiasUncertaintyNs; + + /** + * The satellite inter-signal bias in nanoseconds. + * + * This value is the satellite-and-control-segment-side inter-system (different from the + * constellation in GnssClock.referenceSignalForIsb) bias and inter-frequency (different + * from the carrier frequency in GnssClock.referenceSignalForIsb) bias, including: + * + * - Master clock bias (e.g., GPS-GAL Time Offset (GGTO), GPT-UTC Time Offset (TauGps), + * BDS-GLO Time Offset (BGTO)) + * - Group delay (e.g., Total Group Delay (TGD)) + * - Satellite inter-signal bias, which includes satellite inter-frequency bias (GLO only), + * and satellite inter-code bias (e.g., Differential Code Bias (DCB)). + * + * The receiver ISB of GnssClock.referenceSignalForIsb is defined to be 0.0 nanoseconds. + */ + double satelliteInterSignalBiasNs; + + /** + * 1-sigma uncertainty associated with the satellite inter-signal bias in nanoseconds. + */ + double satelliteInterSignalBiasUncertaintyNs; + + /** * Baseband Carrier-to-noise density in dB-Hz, typically in the range [0, 63]. It contains * the measured C/N0 value for the signal measured at the baseband. * @@ -51,8 +142,22 @@ interface IGnssMeasurementCallback extends @2.0::IGnssMeasurementCallback { }; /** - * Complete set of GNSS Measurement data, same as 2.0 with additional double (i.e., - * basebandCN0DbHz) in measurements. + * Extends a GNSS clock time, adding a referenceSignalTypeForIsb. + */ + struct GnssClock { + /** + * GNSS clock time information, as in the 1.0 version of the HAL. + */ + @1.0::IGnssMeasurementCallback.GnssClock v1_0; + + /** + * Reference GNSS signal type for inter-signal bias. + */ + GnssSignalType referenceSignalTypeForIsb; + }; + + /** + * Complete set of GNSS Measurement data, same as 2.0 with additional fields in measurements. */ struct GnssData { /** The full set of satellite measurement observations. */ diff --git a/gnss/2.1/default/Android.bp b/gnss/2.1/default/Android.bp index 834847e91e..1f1078e398 100644 --- a/gnss/2.1/default/Android.bp +++ b/gnss/2.1/default/Android.bp @@ -32,6 +32,7 @@ cc_binary { "libhidlbase", "libutils", "liblog", + "android.hardware.gnss.measurement_corrections@1.1", "android.hardware.gnss.measurement_corrections@1.0", "android.hardware.gnss.visibility_control@1.0", "android.hardware.gnss@2.1", diff --git a/gnss/2.1/default/Gnss.cpp b/gnss/2.1/default/Gnss.cpp index 7db86897f9..679eb35804 100644 --- a/gnss/2.1/default/Gnss.cpp +++ b/gnss/2.1/default/Gnss.cpp @@ -25,7 +25,7 @@ #include <log/log.h> using ::android::hardware::gnss::common::Utils; -using ::android::hardware::gnss::measurement_corrections::V1_0::implementation:: +using ::android::hardware::gnss::measurement_corrections::V1_1::implementation:: GnssMeasurementCorrections; namespace android { @@ -368,6 +368,12 @@ Return<sp<V2_1::IGnssConfiguration>> Gnss::getExtensionGnssConfiguration_2_1() { return mGnssConfiguration; } +Return<sp<measurement_corrections::V1_1::IMeasurementCorrections>> +Gnss::getExtensionMeasurementCorrections_1_1() { + ALOGD("Gnss::getExtensionMeasurementCorrections_1_1()"); + return new GnssMeasurementCorrections(); +} + void Gnss::reportSvStatus(const hidl_vec<GnssSvInfo>& svInfoList) const { std::unique_lock<std::mutex> lock(mMutex); // TODO(skz): update this to call 2_0 callback if non-null diff --git a/gnss/2.1/default/Gnss.h b/gnss/2.1/default/Gnss.h index 7a2a2c953a..c47206a7a4 100644 --- a/gnss/2.1/default/Gnss.h +++ b/gnss/2.1/default/Gnss.h @@ -89,6 +89,8 @@ struct Gnss : public IGnss { Return<bool> setCallback_2_1(const sp<V2_1::IGnssCallback>& callback) override; Return<sp<V2_1::IGnssMeasurement>> getExtensionGnssMeasurement_2_1() override; Return<sp<V2_1::IGnssConfiguration>> getExtensionGnssConfiguration_2_1() override; + Return<sp<measurement_corrections::V1_1::IMeasurementCorrections>> + getExtensionMeasurementCorrections_1_1() override; private: void reportLocation(const V2_0::GnssLocation&) const; diff --git a/gnss/2.1/default/GnssMeasurementCorrections.cpp b/gnss/2.1/default/GnssMeasurementCorrections.cpp index 2bf5601820..9dedbf6747 100644 --- a/gnss/2.1/default/GnssMeasurementCorrections.cpp +++ b/gnss/2.1/default/GnssMeasurementCorrections.cpp @@ -23,11 +23,12 @@ namespace android { namespace hardware { namespace gnss { namespace measurement_corrections { -namespace V1_0 { +namespace V1_1 { namespace implementation { // Methods from V1_0::IMeasurementCorrections follow. -Return<bool> GnssMeasurementCorrections::setCorrections(const MeasurementCorrections& corrections) { +Return<bool> GnssMeasurementCorrections::setCorrections( + const V1_0::MeasurementCorrections& corrections) { ALOGD("setCorrections"); ALOGD("corrections = lat: %f, lng: %f, alt: %f, hUnc: %f, vUnc: %f, toa: %llu, " "satCorrections.size: %d", @@ -67,8 +68,40 @@ Return<bool> GnssMeasurementCorrections::setCallback( return true; } +// Methods from V1_1::IMeasurementCorrections follow. +Return<bool> GnssMeasurementCorrections::setCorrections_1_1( + const V1_1::MeasurementCorrections& corrections) { + ALOGD("setCorrections_1_1"); + ALOGD("corrections = lat: %f, lng: %f, alt: %f, hUnc: %f, vUnc: %f, toa: %llu," + "satCorrections.size: %d, hasEnvironmentBearing: %d, environmentBearingDeg: %f," + "environmentBearingUncDeg: %f", + corrections.v1_0.latitudeDegrees, corrections.v1_0.longitudeDegrees, + corrections.v1_0.altitudeMeters, corrections.v1_0.horizontalPositionUncertaintyMeters, + corrections.v1_0.verticalPositionUncertaintyMeters, + static_cast<unsigned long long>(corrections.v1_0.toaGpsNanosecondsOfWeek), + static_cast<int>(corrections.v1_0.satCorrections.size()), + corrections.hasEnvironmentBearing, corrections.environmentBearingDegrees, + corrections.environmentBearingUncertaintyDegrees); + for (auto singleSatCorrection : corrections.v1_0.satCorrections) { + ALOGD("singleSatCorrection = flags: %d, constellation: %d, svid: %d, cfHz: %f, probLos: %f," + " epl: %f, eplUnc: %f", + static_cast<int>(singleSatCorrection.singleSatCorrectionFlags), + static_cast<int>(singleSatCorrection.constellation), + static_cast<int>(singleSatCorrection.svid), singleSatCorrection.carrierFrequencyHz, + singleSatCorrection.probSatIsLos, singleSatCorrection.excessPathLengthMeters, + singleSatCorrection.excessPathLengthUncertaintyMeters); + ALOGD("reflecting plane = lat: %f, lng: %f, alt: %f, azm: %f", + singleSatCorrection.reflectingPlane.latitudeDegrees, + singleSatCorrection.reflectingPlane.longitudeDegrees, + singleSatCorrection.reflectingPlane.altitudeMeters, + singleSatCorrection.reflectingPlane.azimuthDegrees); + } + + return true; +} + } // namespace implementation -} // namespace V1_0 +} // namespace V1_1 } // namespace measurement_corrections } // namespace gnss } // namespace hardware diff --git a/gnss/2.1/default/GnssMeasurementCorrections.h b/gnss/2.1/default/GnssMeasurementCorrections.h index 4339bed55d..036e855586 100644 --- a/gnss/2.1/default/GnssMeasurementCorrections.h +++ b/gnss/2.1/default/GnssMeasurementCorrections.h @@ -16,7 +16,7 @@ #pragma once -#include <android/hardware/gnss/measurement_corrections/1.0/IMeasurementCorrections.h> +#include <android/hardware/gnss/measurement_corrections/1.1/IMeasurementCorrections.h> #include <hidl/MQDescriptor.h> #include <hidl/Status.h> @@ -24,7 +24,7 @@ namespace android { namespace hardware { namespace gnss { namespace measurement_corrections { -namespace V1_0 { +namespace V1_1 { namespace implementation { using ::android::sp; @@ -37,12 +37,15 @@ using ::android::hardware::Void; struct GnssMeasurementCorrections : public IMeasurementCorrections { // Methods from V1_0::IMeasurementCorrections follow. - Return<bool> setCorrections(const MeasurementCorrections& corrections) override; + Return<bool> setCorrections(const V1_0::MeasurementCorrections& corrections) override; Return<bool> setCallback(const sp<V1_0::IMeasurementCorrectionsCallback>& callback) override; + + // Methods from V1_1::IMeasurementCorrections follow. + Return<bool> setCorrections_1_1(const V1_1::MeasurementCorrections& corrections) override; }; } // namespace implementation -} // namespace V1_0 +} // namespace V1_1 } // namespace measurement_corrections } // namespace gnss } // namespace hardware diff --git a/gnss/2.1/types.hal b/gnss/2.1/types.hal new file mode 100644 index 0000000000..e4484c15f3 --- /dev/null +++ b/gnss/2.1/types.hal @@ -0,0 +1,46 @@ +/* + * Copyright (C) 2019 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.hardware.gnss@2.1; + +import @2.0::GnssConstellationType; + +/** + * Represents a GNSS signal type. + */ +struct GnssSignalType { + /** + * Constellation type of the SV that transmits the signal. + */ + GnssConstellationType constellation; + + /** + * Carrier frequency in Hz of the signal. + */ + double carrierFrequencyHz; + + /** + * The type of code of the GNSS signal. + * + * This is used to specify the observation descriptor defined in GNSS Observation Data File + * Header Section Description in the RINEX standard (Version 3.XX). In RINEX Version 3.03, + * in Appendix Table A2 Attributes are listed as uppercase letters (for instance, "A" for + * "A channel"). + * + * See the comment of @2.0::IGnssMeasurementCallback.GnssMeasurement.codeType for more details. + */ + string codeType; +}; diff --git a/gnss/2.1/vts/functional/Android.bp b/gnss/2.1/vts/functional/Android.bp index 83404992de..f008a26f1f 100644 --- a/gnss/2.1/vts/functional/Android.bp +++ b/gnss/2.1/vts/functional/Android.bp @@ -24,6 +24,7 @@ cc_test { ], static_libs: [ "android.hardware.gnss.measurement_corrections@1.0", + "android.hardware.gnss.measurement_corrections@1.1", "android.hardware.gnss.visibility_control@1.0", "android.hardware.gnss@1.0", "android.hardware.gnss@1.1", @@ -31,5 +32,8 @@ cc_test { "android.hardware.gnss@2.1", "android.hardware.gnss@common-vts-lib", ], - test_suites: ["general-tests", "vts-core"], + test_suites: [ + "general-tests", + "vts-core", + ], } diff --git a/gnss/2.1/vts/functional/gnss_hal_test.cpp b/gnss/2.1/vts/functional/gnss_hal_test.cpp index 22268f676d..93f89f54e0 100644 --- a/gnss/2.1/vts/functional/gnss_hal_test.cpp +++ b/gnss/2.1/vts/functional/gnss_hal_test.cpp @@ -256,3 +256,10 @@ Return<void> GnssHalTest::GnssMeasurementCallback::gnssMeasurementCb_2_1( measurement_cbq_.store(data); return Void(); } + +Return<void> GnssHalTest::GnssMeasurementCorrectionsCallback::setCapabilitiesCb( + uint32_t capabilities) { + ALOGI("GnssMeasurementCorrectionsCallback capabilities received %d", capabilities); + capabilities_cbq_.store(capabilities); + return Void(); +}
\ No newline at end of file diff --git a/gnss/2.1/vts/functional/gnss_hal_test.h b/gnss/2.1/vts/functional/gnss_hal_test.h index 6b67e139c3..b99cf2322a 100644 --- a/gnss/2.1/vts/functional/gnss_hal_test.h +++ b/gnss/2.1/vts/functional/gnss_hal_test.h @@ -27,6 +27,7 @@ using android::hardware::Return; using android::hardware::Void; using android::hardware::gnss::common::GnssCallbackEventQueue; +using android::hardware::gnss::measurement_corrections::V1_0::IMeasurementCorrectionsCallback; using android::hardware::gnss::V1_0::GnssLocationFlags; using android::hardware::gnss::V2_0::GnssConstellationType; using android::hardware::gnss::V2_1::IGnss; @@ -138,6 +139,19 @@ class GnssHalTest : public testing::TestWithParam<std::string> { Return<void> gnssMeasurementCb_2_1(const IGnssMeasurementCallback_2_1::GnssData&) override; }; + /* Callback class for GnssMeasurementCorrections. */ + class GnssMeasurementCorrectionsCallback : public IMeasurementCorrectionsCallback { + public: + uint32_t last_capabilities_; + GnssCallbackEventQueue<uint32_t> capabilities_cbq_; + + GnssMeasurementCorrectionsCallback() : capabilities_cbq_("capabilities"){}; + virtual ~GnssMeasurementCorrectionsCallback() = default; + + // Methods from V1_0::IMeasurementCorrectionsCallback follow. + Return<void> setCapabilitiesCb(uint32_t capabilities) override; + }; + /* * SetUpGnssCallback: * Set GnssCallback and verify the result. diff --git a/gnss/2.1/vts/functional/gnss_hal_test_cases.cpp b/gnss/2.1/vts/functional/gnss_hal_test_cases.cpp index 2c51717665..9ac9436b0d 100644 --- a/gnss/2.1/vts/functional/gnss_hal_test_cases.cpp +++ b/gnss/2.1/vts/functional/gnss_hal_test_cases.cpp @@ -17,6 +17,7 @@ #define LOG_TAG "GnssHalTestCases" #include <gnss_hal_test.h> +#include <cmath> #include "Utils.h" #include <gtest/gtest.h> @@ -30,6 +31,7 @@ using IGnssMeasurement_2_1 = android::hardware::gnss::V2_1::IGnssMeasurement; using IGnssMeasurement_2_0 = android::hardware::gnss::V2_0::IGnssMeasurement; using IGnssMeasurement_1_1 = android::hardware::gnss::V1_1::IGnssMeasurement; using IGnssMeasurement_1_0 = android::hardware::gnss::V1_0::IGnssMeasurement; + using IGnssConfiguration_2_1 = android::hardware::gnss::V2_1::IGnssConfiguration; using IGnssConfiguration_2_0 = android::hardware::gnss::V2_0::IGnssConfiguration; using IGnssConfiguration_1_1 = android::hardware::gnss::V1_1::IGnssConfiguration; @@ -38,6 +40,10 @@ using IGnssConfiguration_1_0 = android::hardware::gnss::V1_0::IGnssConfiguration using android::hardware::gnss::V2_0::GnssConstellationType; using android::hardware::gnss::V2_1::IGnssConfiguration; +using GnssMeasurementFlags = IGnssMeasurementCallback_2_1::GnssMeasurementFlags; +using IMeasurementCorrections_1_1 = + android::hardware::gnss::measurement_corrections::V1_1::IMeasurementCorrections; + /* * SetupTeardownCreateCleanup: * Requests the gnss HAL then calls cleanup @@ -92,6 +98,7 @@ TEST_P(GnssHalTest, TestGnssConfigurationExtension) { * TestGnssMeasurementFields: * Sets a GnssMeasurementCallback, waits for a measurement, and verifies * 1. basebandCN0DbHz is valid + * 2. ISB fields are valid if HAS_INTER_SIGNAL_BIAS is true. */ TEST_P(GnssHalTest, TestGnssMeasurementFields) { const int kFirstGnssMeasurementTimeoutSeconds = 10; @@ -118,6 +125,29 @@ TEST_P(GnssHalTest, TestGnssMeasurementFields) { for (auto measurement : lastMeasurement.measurements) { // Verify basebandCn0DbHz is valid. ASSERT_TRUE(measurement.basebandCN0DbHz > 0.0 && measurement.basebandCN0DbHz <= 65.0); + + if (((uint32_t)(measurement.flags & GnssMeasurementFlags::HAS_RECEIVER_ISB) > 0) && + ((uint32_t)(measurement.flags & GnssMeasurementFlags::HAS_RECEIVER_ISB_UNCERTAINTY) > + 0) && + ((uint32_t)(measurement.flags & GnssMeasurementFlags::HAS_SATELLITE_ISB) > 0) && + ((uint32_t)(measurement.flags & GnssMeasurementFlags::HAS_SATELLITE_ISB_UNCERTAINTY) > + 0)) { + GnssConstellationType referenceConstellation = + lastMeasurement.clock.referenceSignalTypeForIsb.constellation; + double carrierFrequencyHz = + lastMeasurement.clock.referenceSignalTypeForIsb.carrierFrequencyHz; + std::string codeType = lastMeasurement.clock.referenceSignalTypeForIsb.codeType; + + ASSERT_TRUE(referenceConstellation >= GnssConstellationType::UNKNOWN && + referenceConstellation >= GnssConstellationType::IRNSS); + ASSERT_TRUE(carrierFrequencyHz > 0); + ASSERT_TRUE(codeType != ""); + + ASSERT_TRUE(std::abs(measurement.receiverInterSignalBiasNs) < 1.0e6); + ASSERT_TRUE(measurement.receiverInterSignalBiasUncertaintyNs >= 0); + ASSERT_TRUE(std::abs(measurement.satelliteInterSignalBiasNs) < 1.0e6); + ASSERT_TRUE(measurement.satelliteInterSignalBiasUncertaintyNs >= 0); + } } iGnssMeasurement->close(); @@ -533,4 +563,36 @@ TEST_P(GnssHalTest, BlacklistConstellationLocationOn) { result = gnss_configuration_hal->setBlacklist_2_1(sources); ASSERT_TRUE(result.isOk()); EXPECT_TRUE(result); +} + +/* + * TestGnssMeasurementCorrections: + * If measurement corrections capability is supported, verifies that it supports the + * gnss.measurement_corrections@1.1::IMeasurementCorrections interface by invoking a method. + */ +TEST_P(GnssHalTest, TestGnssMeasurementCorrections) { + if (!(gnss_cb_->last_capabilities_ & + IGnssCallback_2_1::Capabilities::MEASUREMENT_CORRECTIONS)) { + return; + } + + // Verify IMeasurementCorrections is supported. + auto measurementCorrections = gnss_hal_->getExtensionMeasurementCorrections_1_1(); + ASSERT_TRUE(measurementCorrections.isOk()); + sp<IMeasurementCorrections_1_1> iMeasurementCorrections = measurementCorrections; + ASSERT_NE(iMeasurementCorrections, nullptr); + + sp<GnssMeasurementCorrectionsCallback> callback = new GnssMeasurementCorrectionsCallback(); + iMeasurementCorrections->setCallback(callback); + + const int kMeasurementCorrectionsCapabilitiesTimeoutSeconds = 5; + callback->capabilities_cbq_.retrieve(callback->last_capabilities_, + kMeasurementCorrectionsCapabilitiesTimeoutSeconds); + ASSERT_TRUE(callback->capabilities_cbq_.calledCount() > 0); + + // Set a mock MeasurementCorrections. + auto result = + iMeasurementCorrections->setCorrections_1_1(Utils::getMockMeasurementCorrections_1_1()); + ASSERT_TRUE(result.isOk()); + EXPECT_TRUE(result); }
\ No newline at end of file diff --git a/gnss/common/utils/default/Utils.cpp b/gnss/common/utils/default/Utils.cpp index ccb91b100f..0cdc865849 100644 --- a/gnss/common/utils/default/Utils.cpp +++ b/gnss/common/utils/default/Utils.cpp @@ -24,24 +24,45 @@ namespace gnss { namespace common { using GnssSvFlags = V1_0::IGnssCallback::GnssSvFlags; -using GnssMeasurementFlags = V1_0::IGnssMeasurementCallback::GnssMeasurementFlags; +using GnssMeasurementFlagsV1_0 = V1_0::IGnssMeasurementCallback::GnssMeasurementFlags; +using GnssMeasurementFlagsV2_1 = V2_1::IGnssMeasurementCallback::GnssMeasurementFlags; using GnssMeasurementStateV2_0 = V2_0::IGnssMeasurementCallback::GnssMeasurementState; using ElapsedRealtime = V2_0::ElapsedRealtime; using ElapsedRealtimeFlags = V2_0::ElapsedRealtimeFlags; using GnssConstellationTypeV2_0 = V2_0::GnssConstellationType; using IGnssMeasurementCallbackV2_0 = V2_0::IGnssMeasurementCallback; +using GnssSignalType = V2_1::GnssSignalType; GnssDataV2_1 Utils::getMockMeasurementV2_1() { GnssDataV2_0 gnssDataV2_0 = Utils::getMockMeasurementV2_0(); V2_1::IGnssMeasurementCallback::GnssMeasurement gnssMeasurementV2_1 = { .v2_0 = gnssDataV2_0.measurements[0], + .flags = (uint32_t)(GnssMeasurementFlagsV2_1::HAS_CARRIER_FREQUENCY | + GnssMeasurementFlagsV2_1::HAS_CARRIER_PHASE | + GnssMeasurementFlagsV2_1::HAS_RECEIVER_ISB | + GnssMeasurementFlagsV2_1::HAS_RECEIVER_ISB_UNCERTAINTY | + GnssMeasurementFlagsV2_1::HAS_SATELLITE_ISB | + GnssMeasurementFlagsV2_1::HAS_SATELLITE_ISB_UNCERTAINTY), + .receiverInterSignalBiasNs = 10.0, + .receiverInterSignalBiasUncertaintyNs = 100.0, + .satelliteInterSignalBiasNs = 20.0, + .satelliteInterSignalBiasUncertaintyNs = 150.0, .basebandCN0DbHz = 25.0, }; + GnssSignalType referenceSignalTypeForIsb = { + .constellation = GnssConstellationTypeV2_0::GPS, + .carrierFrequencyHz = 1.59975e+09, + .codeType = "C", + }; + V2_1::IGnssMeasurementCallback::GnssClock gnssClockV2_1 = { + .v1_0 = gnssDataV2_0.clock, + .referenceSignalTypeForIsb = referenceSignalTypeForIsb, + }; hidl_vec<V2_1::IGnssMeasurementCallback::GnssMeasurement> measurements(1); measurements[0] = gnssMeasurementV2_1; GnssDataV2_1 gnssDataV2_1 = { .measurements = measurements, - .clock = gnssDataV2_0.clock, + .clock = gnssClockV2_1, .elapsedRealtime = gnssDataV2_0.elapsedRealtime, }; return gnssDataV2_1; @@ -49,7 +70,7 @@ GnssDataV2_1 Utils::getMockMeasurementV2_1() { GnssDataV2_0 Utils::getMockMeasurementV2_0() { V1_0::IGnssMeasurementCallback::GnssMeasurement measurement_1_0 = { - .flags = (uint32_t)GnssMeasurementFlags::HAS_CARRIER_FREQUENCY, + .flags = (uint32_t)GnssMeasurementFlagsV1_0::HAS_CARRIER_FREQUENCY, .svid = (int16_t)6, .constellation = V1_0::GnssConstellationType::UNKNOWN, .timeOffsetNs = 0.0, diff --git a/gnss/common/utils/vts/Android.bp b/gnss/common/utils/vts/Android.bp index 1988171979..fd9613b02a 100644 --- a/gnss/common/utils/vts/Android.bp +++ b/gnss/common/utils/vts/Android.bp @@ -30,6 +30,7 @@ cc_library_static { shared_libs: [ "android.hardware.gnss@1.0", "android.hardware.gnss.measurement_corrections@1.0", + "android.hardware.gnss.measurement_corrections@1.1", ], static_libs: [ "libgtest", diff --git a/gnss/common/utils/vts/Utils.cpp b/gnss/common/utils/vts/Utils.cpp index 51d3ea18d5..b6c3f5eccf 100644 --- a/gnss/common/utils/vts/Utils.cpp +++ b/gnss/common/utils/vts/Utils.cpp @@ -92,7 +92,7 @@ void Utils::checkLocation(const GnssLocation& location, bool check_speed, EXPECT_GT(location.timestamp, 1.48e12); } -const MeasurementCorrections Utils::getMockMeasurementCorrections() { +const MeasurementCorrections_1_0 Utils::getMockMeasurementCorrections() { ReflectingPlane reflectingPlane = { .latitudeDegrees = 37.4220039, .longitudeDegrees = -122.0840991, @@ -127,7 +127,7 @@ const MeasurementCorrections Utils::getMockMeasurementCorrections() { hidl_vec<SingleSatCorrection> singleSatCorrections = {singleSatCorrection1, singleSatCorrection2}; - MeasurementCorrections mockCorrections = { + MeasurementCorrections_1_0 mockCorrections = { .latitudeDegrees = 37.4219999, .longitudeDegrees = -122.0840575, .altitudeMeters = 30.60062531, @@ -139,6 +139,18 @@ const MeasurementCorrections Utils::getMockMeasurementCorrections() { return mockCorrections; } +const MeasurementCorrections_1_1 Utils::getMockMeasurementCorrections_1_1() { + MeasurementCorrections_1_0 mockCorrections_1_0 = getMockMeasurementCorrections(); + + MeasurementCorrections_1_1 mockCorrections_1_1 = { + .v1_0 = mockCorrections_1_0, + .hasEnvironmentBearing = true, + .environmentBearingDegrees = 45.0, + .environmentBearingUncertaintyDegrees = 4.0, + }; + return mockCorrections_1_1; +} + } // namespace common } // namespace gnss } // namespace hardware diff --git a/gnss/common/utils/vts/include/Utils.h b/gnss/common/utils/vts/include/Utils.h index dce4c7b323..781ad428eb 100644 --- a/gnss/common/utils/vts/include/Utils.h +++ b/gnss/common/utils/vts/include/Utils.h @@ -19,10 +19,16 @@ #include <android/hardware/gnss/1.0/IGnss.h> #include <android/hardware/gnss/measurement_corrections/1.0/IMeasurementCorrections.h> +#include <android/hardware/gnss/measurement_corrections/1.1/IMeasurementCorrections.h> using GnssLocation = ::android::hardware::gnss::V1_0::GnssLocation; using namespace android::hardware::gnss::measurement_corrections::V1_0; +using MeasurementCorrections_1_0 = + android::hardware::gnss::measurement_corrections::V1_0::MeasurementCorrections; +using MeasurementCorrections_1_1 = + android::hardware::gnss::measurement_corrections::V1_1::MeasurementCorrections; + namespace android { namespace hardware { namespace gnss { @@ -31,7 +37,8 @@ namespace common { struct Utils { static void checkLocation(const GnssLocation& location, bool check_speed, bool check_more_accuracies); - static const MeasurementCorrections getMockMeasurementCorrections(); + static const MeasurementCorrections_1_0 getMockMeasurementCorrections(); + static const MeasurementCorrections_1_1 getMockMeasurementCorrections_1_1(); }; } // namespace common diff --git a/gnss/measurement_corrections/1.1/Android.bp b/gnss/measurement_corrections/1.1/Android.bp new file mode 100644 index 0000000000..1d69f20866 --- /dev/null +++ b/gnss/measurement_corrections/1.1/Android.bp @@ -0,0 +1,19 @@ +// This file is autogenerated by hidl-gen -Landroidbp. + +hidl_interface { + name: "android.hardware.gnss.measurement_corrections@1.1", + root: "android.hardware", + vndk: { + enabled: true, + }, + srcs: [ + "types.hal", + "IMeasurementCorrections.hal", + ], + interfaces: [ + "android.hardware.gnss.measurement_corrections@1.0", + "android.hardware.gnss@1.0", + "android.hidl.base@1.0", + ], + gen_java: true, +} diff --git a/gnss/measurement_corrections/1.1/IMeasurementCorrections.hal b/gnss/measurement_corrections/1.1/IMeasurementCorrections.hal new file mode 100644 index 0000000000..9461a5e445 --- /dev/null +++ b/gnss/measurement_corrections/1.1/IMeasurementCorrections.hal @@ -0,0 +1,40 @@ +/* + * Copyright (C) 2020 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.hardware.gnss.measurement_corrections@1.1; + +import @1.0::IMeasurementCorrections; + +/** + * Interface for measurement corrections support. + */ +interface IMeasurementCorrections extends @1.0::IMeasurementCorrections { + /** + * Injects measurement corrections to be used by the HAL to improve the GNSS location output. + * + * These are NOT to be used to adjust the IGnssMeasurementCallback output values - + * those remain raw, uncorrected measurements. + * + * In general, these are injected when conditions defined by the platform are met, such as when + * GNSS Location is being requested at a sufficiently high accuracy, based on the capabilities + * of the GNSS chipset as reported in the IGnssCallback. + * + * @param corrections The computed corrections to be used by the HAL. + * + * @return success Whether the HAL can accept & use these corrections. + */ + setCorrections_1_1(MeasurementCorrections corrections) generates (bool success); +};
\ No newline at end of file diff --git a/gnss/measurement_corrections/1.1/types.hal b/gnss/measurement_corrections/1.1/types.hal new file mode 100644 index 0000000000..40b6f52b7d --- /dev/null +++ b/gnss/measurement_corrections/1.1/types.hal @@ -0,0 +1,58 @@ +/* + * Copyright (C) 2020 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.hardware.gnss.measurement_corrections@1.1; + +import @1.0::MeasurementCorrections; + +/** + * A struct containing a set of measurement corrections for all used GNSS satellites at the location + * specified by latitudeDegrees, longitudeDegrees, altitudeMeters and at the time of week specified + * toaGpsNanosecondsOfWeek + */ +struct MeasurementCorrections { + @1.0::MeasurementCorrections v1_0; + + /** + * Boolean indicating if environment bearing is available. + */ + bool hasEnvironmentBearing; + + /** + * Environment bearing in degrees clockwise from true North (0.0 to 360.0], in direction of + * user motion. Environment bearing is provided when it is known with high probability that + * velocity is aligned with an environment feature, such as a building or road. + * + * If user speed is zero, environmentBearingDegrees represents bearing of most recent speed + * that was > 0. + * + * As position approaches another road, environmentBearingUncertaintyDegrees will grow, and at + * some stage hasEnvironmentBearing = false. + * + * As position moves towards an open area, environmentBearingUncertaintyDegrees will grow, and + * at some stage hasEnvironmentBearing = false. + * + * If the road is curved in the vicinity of the user location, then + * environmentBearingUncertaintyDegrees will include the amount by which the road direction + * changes in the area of position uncertainty. + */ + float environmentBearingDegrees; + + /** + * Bearing uncertainty [0 to 180]. + */ + float environmentBearingUncertaintyDegrees; +};
\ No newline at end of file diff --git a/graphics/common/aidl/android/hardware/graphics/common/StandardMetadataType.aidl b/graphics/common/aidl/android/hardware/graphics/common/StandardMetadataType.aidl index 43cf6722dd..7b46688d9f 100644 --- a/graphics/common/aidl/android/hardware/graphics/common/StandardMetadataType.aidl +++ b/graphics/common/aidl/android/hardware/graphics/common/StandardMetadataType.aidl @@ -280,7 +280,7 @@ enum StandardMetadataType { * * The default blend mode is INVALID. If the BlendMode is set to any * valid value other than INVALID, this BlendMode overrides all other - * dataspaces. For a longer description of this behavior see MetadataType::DATASPACE. + * blend modes. For a longer description of this behavior see MetadataType::DATASPACE. * * The blend mode is a stable aidl android.hardware.graphics.common.BlendMode. * diff --git a/graphics/composer/2.1/utils/vts/Android.bp b/graphics/composer/2.1/utils/vts/Android.bp index cdc0f35a41..3b0911f923 100644 --- a/graphics/composer/2.1/utils/vts/Android.bp +++ b/graphics/composer/2.1/utils/vts/Android.bp @@ -23,14 +23,13 @@ cc_library_static { "TestCommandReader.cpp", ], static_libs: [ - "VtsHalHidlTargetTestBase", "android.hardware.graphics.composer@2.1", "android.hardware.graphics.mapper@2.0-vts", "android.hardware.graphics.mapper@3.0-vts", "android.hardware.graphics.mapper@4.0-vts", + "libgtest", ], export_static_lib_headers: [ - "VtsHalHidlTargetTestBase", "android.hardware.graphics.composer@2.1", "android.hardware.graphics.mapper@2.0-vts", "android.hardware.graphics.mapper@3.0-vts", diff --git a/graphics/composer/2.1/utils/vts/ComposerVts.cpp b/graphics/composer/2.1/utils/vts/ComposerVts.cpp index a8e1480d4f..4b6b7c86b8 100644 --- a/graphics/composer/2.1/utils/vts/ComposerVts.cpp +++ b/graphics/composer/2.1/utils/vts/ComposerVts.cpp @@ -16,8 +16,6 @@ #include <composer-vts/2.1/ComposerVts.h> -#include <VtsHalHidlTargetTestBase.h> - namespace android { namespace hardware { namespace graphics { @@ -25,11 +23,6 @@ namespace composer { namespace V2_1 { namespace vts { -Composer::Composer() : Composer(::testing::VtsHalHidlTargetTestBase::getService<IComposer>()) {} - -Composer::Composer(const std::string& name) - : Composer(::testing::VtsHalHidlTargetTestBase::getService<IComposer>(name)) {} - Composer::Composer(const sp<IComposer>& composer) : mComposer(composer) { // ASSERT_* can only be used in functions returning void. [this] { diff --git a/graphics/composer/2.2/utils/vts/Android.bp b/graphics/composer/2.2/utils/vts/Android.bp index 5432882bea..a8bb1a22cb 100644 --- a/graphics/composer/2.2/utils/vts/Android.bp +++ b/graphics/composer/2.2/utils/vts/Android.bp @@ -26,11 +26,11 @@ cc_library_static { "libui", ], static_libs: [ - "VtsHalHidlTargetTestBase", "android.hardware.graphics.composer@2.1-vts", "android.hardware.graphics.composer@2.2", "android.hardware.graphics.mapper@2.1-vts", "libarect", + "libgtest", "libmath", "libnativewindow", "librenderengine", @@ -40,7 +40,6 @@ cc_library_static { "android.hardware.graphics.mapper@4.0-vts", ], export_static_lib_headers: [ - "VtsHalHidlTargetTestBase", "android.hardware.graphics.composer@2.1-vts", "android.hardware.graphics.composer@2.2", "android.hardware.graphics.mapper@2.1-vts", diff --git a/graphics/composer/2.2/utils/vts/ComposerVts.cpp b/graphics/composer/2.2/utils/vts/ComposerVts.cpp index 93b67f0fcc..a526137c41 100644 --- a/graphics/composer/2.2/utils/vts/ComposerVts.cpp +++ b/graphics/composer/2.2/utils/vts/ComposerVts.cpp @@ -16,7 +16,6 @@ #include <composer-vts/2.2/ComposerVts.h> -#include <VtsHalHidlTargetTestBase.h> #include <composer-command-buffer/2.2/ComposerCommandBuffer.h> #include <hidl/HidlTransportUtils.h> diff --git a/graphics/composer/2.2/utils/vts/RenderEngineVts.cpp b/graphics/composer/2.2/utils/vts/RenderEngineVts.cpp index e2f267012f..a23d72c949 100644 --- a/graphics/composer/2.2/utils/vts/RenderEngineVts.cpp +++ b/graphics/composer/2.2/utils/vts/RenderEngineVts.cpp @@ -57,7 +57,15 @@ void TestRenderEngine::initGraphicBuffer(uint32_t width, uint32_t height, uint32 void TestRenderEngine::drawLayers() { base::unique_fd bufferFence; base::unique_fd readyFence; - mRenderEngine->drawLayers(mDisplaySettings, mCompositionLayers, + + std::vector<const renderengine::LayerSettings*> compositionLayerPointers; + compositionLayerPointers.reserve(mCompositionLayers.size()); + std::transform(mCompositionLayers.begin(), mCompositionLayers.end(), + std::back_insert_iterator(compositionLayerPointers), + [](renderengine::LayerSettings& settings) -> renderengine::LayerSettings* { + return &settings; + }); + mRenderEngine->drawLayers(mDisplaySettings, compositionLayerPointers, mGraphicBuffer->getNativeBuffer(), true, std::move(bufferFence), &readyFence); int fd = readyFence.release(); diff --git a/graphics/composer/2.2/utils/vts/include/composer-vts/2.2/ComposerVts.h b/graphics/composer/2.2/utils/vts/include/composer-vts/2.2/ComposerVts.h index 5d22305020..6bc2732d7d 100644 --- a/graphics/composer/2.2/utils/vts/include/composer-vts/2.2/ComposerVts.h +++ b/graphics/composer/2.2/utils/vts/include/composer-vts/2.2/ComposerVts.h @@ -22,7 +22,6 @@ #include <unordered_set> #include <vector> -#include <VtsHalHidlTargetTestBase.h> #include <android/hardware/graphics/composer/2.2/IComposer.h> #include <android/hardware/graphics/composer/2.2/IComposerClient.h> #include <composer-command-buffer/2.2/ComposerCommandBuffer.h> diff --git a/graphics/composer/2.2/utils/vts/include/composer-vts/2.2/ReadbackVts.h b/graphics/composer/2.2/utils/vts/include/composer-vts/2.2/ReadbackVts.h index 7519a64b4f..d5eedf122c 100644 --- a/graphics/composer/2.2/utils/vts/include/composer-vts/2.2/ReadbackVts.h +++ b/graphics/composer/2.2/utils/vts/include/composer-vts/2.2/ReadbackVts.h @@ -142,7 +142,7 @@ class TestBufferLayer : public TestLayer { const native_handle_t* mBufferHandle = nullptr; }; -class ReadbackHelper : public ::testing::VtsHalHidlTargetTestBase { +class ReadbackHelper { public: static std::string getColorModeString(ColorMode mode); diff --git a/graphics/composer/2.2/utils/vts/include/composer-vts/2.2/RenderEngineVts.h b/graphics/composer/2.2/utils/vts/include/composer-vts/2.2/RenderEngineVts.h index b936cabb1e..4eb4bb7179 100644 --- a/graphics/composer/2.2/utils/vts/include/composer-vts/2.2/RenderEngineVts.h +++ b/graphics/composer/2.2/utils/vts/include/composer-vts/2.2/RenderEngineVts.h @@ -24,8 +24,6 @@ #include <ui/Rect.h> #include <ui/Region.h> -#include <VtsHalHidlTargetTestBase.h> - namespace android { namespace hardware { namespace graphics { diff --git a/graphics/composer/2.2/vts/functional/Android.bp b/graphics/composer/2.2/vts/functional/Android.bp index f987516a33..e38af00a38 100644 --- a/graphics/composer/2.2/vts/functional/Android.bp +++ b/graphics/composer/2.2/vts/functional/Android.bp @@ -19,7 +19,7 @@ cc_test { defaults: ["VtsHalTargetTestDefaults"], srcs: [ "VtsHalGraphicsComposerV2_2ReadbackTest.cpp", - "VtsHalGraphicsComposerV2_2TargetTest.cpp" + "VtsHalGraphicsComposerV2_2TargetTest.cpp", ], // TODO(b/64437680): Assume these libs are always available on the device. @@ -51,12 +51,16 @@ cc_test { "android.hardware.graphics.mapper@3.0-vts", "android.hardware.graphics.mapper@4.0", "android.hardware.graphics.mapper@4.0-vts", - "librenderengine" + "libgtest", + "librenderengine", ], header_libs: [ "android.hardware.graphics.composer@2.1-command-buffer", "android.hardware.graphics.composer@2.2-command-buffer", ], disable_framework: true, - test_suites: ["general-tests", "vts-core"], + test_suites: [ + "general-tests", + "vts-core", + ], } diff --git a/graphics/composer/2.2/vts/functional/VtsHalGraphicsComposerV2_2ReadbackTest.cpp b/graphics/composer/2.2/vts/functional/VtsHalGraphicsComposerV2_2ReadbackTest.cpp index 044bd9640e..cb43e64c81 100644 --- a/graphics/composer/2.2/vts/functional/VtsHalGraphicsComposerV2_2ReadbackTest.cpp +++ b/graphics/composer/2.2/vts/functional/VtsHalGraphicsComposerV2_2ReadbackTest.cpp @@ -932,7 +932,7 @@ TEST_P(GraphicsCompositionTest, SetLayerZOrder) { class GraphicsBlendModeCompositionTest : public GraphicsCompositionTestBase, - public testing::WithParamInterface<std::tuple<string, string>> { + public testing::WithParamInterface<std::tuple<std::string, std::string>> { public: void SetUp() override { SetUpBase(std::get<0>(GetParam())); diff --git a/graphics/composer/2.3/utils/vts/Android.bp b/graphics/composer/2.3/utils/vts/Android.bp index f65a9c41ae..3d81e8f647 100644 --- a/graphics/composer/2.3/utils/vts/Android.bp +++ b/graphics/composer/2.3/utils/vts/Android.bp @@ -23,6 +23,7 @@ cc_library_static { static_libs: [ "android.hardware.graphics.composer@2.2-vts", "android.hardware.graphics.composer@2.3", + "libgtest", ], export_static_lib_headers: [ "android.hardware.graphics.composer@2.2-vts", diff --git a/graphics/composer/2.3/utils/vts/ComposerVts.cpp b/graphics/composer/2.3/utils/vts/ComposerVts.cpp index d4f5b3a4de..d73a3b08b9 100644 --- a/graphics/composer/2.3/utils/vts/ComposerVts.cpp +++ b/graphics/composer/2.3/utils/vts/ComposerVts.cpp @@ -16,8 +16,6 @@ #include <composer-vts/2.3/ComposerVts.h> -#include <VtsHalHidlTargetTestBase.h> - namespace android { namespace hardware { namespace graphics { @@ -27,11 +25,6 @@ namespace vts { using V2_1::Error; -Composer::Composer() : Composer(::testing::VtsHalHidlTargetTestBase::getService<IComposer>()) {} - -Composer::Composer(const std::string& name) - : Composer(::testing::VtsHalHidlTargetTestBase::getService<IComposer>(name)) {} - Composer::Composer(const sp<IComposer>& composer) : V2_2::vts::Composer(composer), mComposer(composer) {} diff --git a/graphics/composer/2.3/utils/vts/include/composer-vts/2.3/ComposerVts.h b/graphics/composer/2.3/utils/vts/include/composer-vts/2.3/ComposerVts.h index e5ac842262..dae9ab42a2 100644 --- a/graphics/composer/2.3/utils/vts/include/composer-vts/2.3/ComposerVts.h +++ b/graphics/composer/2.3/utils/vts/include/composer-vts/2.3/ComposerVts.h @@ -19,7 +19,6 @@ #include <memory> #include <vector> -#include <VtsHalHidlTargetTestBase.h> #include <android/hardware/graphics/composer/2.3/IComposer.h> #include <android/hardware/graphics/composer/2.3/IComposerClient.h> #include <composer-vts/2.2/ComposerVts.h> diff --git a/graphics/composer/2.4/IComposerClient.hal b/graphics/composer/2.4/IComposerClient.hal index 7e0c33ce97..9e3cf0e083 100644 --- a/graphics/composer/2.4/IComposerClient.hal +++ b/graphics/composer/2.4/IComposerClient.hal @@ -71,6 +71,51 @@ interface IComposerClient extends @2.3::IComposerClient { * setClientTargetProperty(ClientTargetProperty clientTargetProperty); */ SET_CLIENT_TARGET_PROPERTY = 0x105 << @2.1::IComposerClient.Command:OPCODE_SHIFT, + + /** + * SET_LAYER_GENERIC_METADATA has this pseudo prototype + * + * setLayerGenericMetadata(string key, bool mandatory, vec<uint8_t> value); + * + * Sets a piece of generic metadata for the given layer. If this + * function is called twice with the same key but different values, the + * newer value must override the older one. Calling this function with a + * 0-length value must reset that key's metadata as if it had not been + * set. + * + * A given piece of metadata may either be mandatory or a hint + * (non-mandatory) as indicated by the second parameter. Mandatory + * metadata may affect the composition result, which is to say that it + * may cause a visible change in the final image. By contrast, hints may + * only affect the composition strategy, such as which layers are + * composited by the client, but must not cause a visible change in the + * final image. The value of the mandatory flag shall match the value + * returned from getLayerGenericMetadataKeys for the given key. + * + * Only keys which have been returned from getLayerGenericMetadataKeys() + * shall be accepted. Any other keys must result in an UNSUPPORTED error. + * + * The value passed into this function shall be the binary + * representation of a HIDL type corresponding to the given key. For + * example, a key of 'com.example.V1_3.Foo' shall be paired with a + * value of type com.example@1.3::Foo, which would be defined in a + * vendor HAL extension. + * + * This function will be encoded in the command buffer in this order: + * 1) The key length, stored as a uint32_t + * 2) The key itself, padded to a uint32_t boundary if necessary + * 3) The mandatory flag, stored as a uint32_t + * 4) The value length in bytes, stored as a uint32_t + * 5) The value itself, padded to a uint32_t boundary if necessary + * + * @param key indicates which metadata value should be set on this layer + * @param mandatory indicates whether this particular key represents + * mandatory metadata or a hint (non-mandatory metadata), as + * described above + * @param value is a binary representation of a HIDL struct + * corresponding to the key as described above + */ + SET_LAYER_GENERIC_METADATA = 0x40e << @2.1::IComposerClient.Command:OPCODE_SHIFT, }; /** @@ -271,4 +316,33 @@ interface IComposerClient extends @2.3::IComposerClient { */ setContentType(Display display, ContentType type) generates (Error error); + + struct LayerGenericMetadataKey { + /** + * Key names must comply with the requirements specified for + * getLayerGenericMetadataKeys below + */ + string name; + + /** + * The mandatory flag is defined in the description of + * setLayerGenericMetadata above + */ + bool mandatory; + }; + + /** + * Retrieves the set of keys that may be passed into setLayerGenericMetadata + * + * Key names must meet the following requirements: + * - Must be specified in reverse domain name notation + * - Must not start with 'com.android' or 'android' + * - Must be unique within the returned vector + * - Must correspond to a matching HIDL struct type, which defines the + * structure of its values. For example, the key 'com.example.V1-3.Foo' + * should correspond to a value of type com.example@1.3::Foo, which is + * defined in a vendor HAL extension + */ + getLayerGenericMetadataKeys() + generates(Error error, vec<LayerGenericMetadataKey> keys); }; diff --git a/graphics/composer/2.4/utils/command-buffer/include/composer-command-buffer/2.4/ComposerCommandBuffer.h b/graphics/composer/2.4/utils/command-buffer/include/composer-command-buffer/2.4/ComposerCommandBuffer.h index e84779ecbb..eb35e5cb9e 100644 --- a/graphics/composer/2.4/utils/command-buffer/include/composer-command-buffer/2.4/ComposerCommandBuffer.h +++ b/graphics/composer/2.4/utils/command-buffer/include/composer-command-buffer/2.4/ComposerCommandBuffer.h @@ -53,6 +53,27 @@ class CommandWriterBase : public V2_3::CommandWriterBase { writeSigned(static_cast<int32_t>(clientTargetProperty.dataspace)); endCommand(); } + + void setLayerGenericMetadata(const hidl_string& key, const bool mandatory, + const hidl_vec<uint8_t>& value) { + const size_t commandSize = 3 + sizeToElements(key.size()) + sizeToElements(value.size()); + if (commandSize > std::numeric_limits<uint16_t>::max()) { + LOG_FATAL("Too much generic metadata (%zu elements)", commandSize); + return; + } + + beginCommand(IComposerClient::Command::SET_LAYER_GENERIC_METADATA, + static_cast<uint16_t>(commandSize)); + write(key.size()); + writeBlob(key.size(), reinterpret_cast<const unsigned char*>(key.c_str())); + write(mandatory); + write(value.size()); + writeBlob(value.size(), value.data()); + endCommand(); + } + + protected: + uint32_t sizeToElements(uint32_t size) { return (size + 3) / 4; } }; // This class helps parse a command queue. Note that all sizes/lengths are in diff --git a/graphics/composer/2.4/utils/hal/include/composer-hal/2.4/ComposerClient.h b/graphics/composer/2.4/utils/hal/include/composer-hal/2.4/ComposerClient.h index c864ce6961..c889069143 100644 --- a/graphics/composer/2.4/utils/hal/include/composer-hal/2.4/ComposerClient.h +++ b/graphics/composer/2.4/utils/hal/include/composer-hal/2.4/ComposerClient.h @@ -164,6 +164,14 @@ class ComposerClientImpl : public V2_3::hal::detail::ComposerClientImpl<Interfac return mHal->setContentType(display, contentType); } + Return<void> getLayerGenericMetadataKeys( + IComposerClient::getLayerGenericMetadataKeys_cb hidl_cb) override { + std::vector<IComposerClient::LayerGenericMetadataKey> keys; + Error error = mHal->getLayerGenericMetadataKeys(&keys); + hidl_cb(error, keys); + return Void(); + } + static std::unique_ptr<ComposerClientImpl> create(Hal* hal) { auto client = std::make_unique<ComposerClientImpl>(hal); return client->init() ? std::move(client) : nullptr; diff --git a/graphics/composer/2.4/utils/hal/include/composer-hal/2.4/ComposerCommandEngine.h b/graphics/composer/2.4/utils/hal/include/composer-hal/2.4/ComposerCommandEngine.h index e0153ceb81..697d6b8a6b 100644 --- a/graphics/composer/2.4/utils/hal/include/composer-hal/2.4/ComposerCommandEngine.h +++ b/graphics/composer/2.4/utils/hal/include/composer-hal/2.4/ComposerCommandEngine.h @@ -74,6 +74,43 @@ class ComposerCommandEngine : public V2_3::hal::ComposerCommandEngine { CommandWriterBase* getWriter() { return static_cast<CommandWriterBase*>(mWriter.get()); } + bool executeCommand(V2_1::IComposerClient::Command command, uint16_t length) override { + switch (static_cast<IComposerClient::Command>(command)) { + case IComposerClient::Command::SET_LAYER_GENERIC_METADATA: + return executeSetLayerGenericMetadata(length); + default: + return BaseType2_3::executeCommand(command, length); + } + } + + bool executeSetLayerGenericMetadata(uint16_t length) { + // We expect at least two buffer lengths and a mandatory flag + if (length < 3) { + return false; + } + + const uint32_t keySize = read(); + std::string key; + key.resize(keySize); + readBlob(keySize, key.data()); + + const bool mandatory = read(); + + const uint32_t valueSize = read(); + std::vector<uint8_t> value(valueSize); + readBlob(valueSize, value.data()); + + auto error = mHal->setLayerGenericMetadata(mCurrentDisplay, mCurrentLayer, key, mandatory, + value); + if (error != Error::NONE) { + // The error cast is safe because setLayerGenericMetadata doesn't + // return any of the new values added in V2_4::Error + mWriter->setError(getCommandLoc(), static_cast<V2_1::Error>(error)); + } + + return true; + } + ComposerHal* mHal; }; diff --git a/graphics/composer/2.4/utils/hal/include/composer-hal/2.4/ComposerHal.h b/graphics/composer/2.4/utils/hal/include/composer-hal/2.4/ComposerHal.h index 277055c0c6..58991c1d8d 100644 --- a/graphics/composer/2.4/utils/hal/include/composer-hal/2.4/ComposerHal.h +++ b/graphics/composer/2.4/utils/hal/include/composer-hal/2.4/ComposerHal.h @@ -79,6 +79,10 @@ class ComposerHal : public V2_3::hal::ComposerHal { uint32_t* outDisplayRequestMask, std::vector<Layer>* outRequestedLayers, std::vector<uint32_t>* outRequestMasks, IComposerClient::ClientTargetProperty* outClientTargetProperty) = 0; + virtual Error setLayerGenericMetadata(Display display, Layer layer, const std::string& key, + bool mandatory, const std::vector<uint8_t>& value) = 0; + virtual Error getLayerGenericMetadataKeys( + std::vector<IComposerClient::LayerGenericMetadataKey>* outKeys) = 0; }; } // namespace hal diff --git a/graphics/composer/2.4/utils/passthrough/include/composer-passthrough/2.4/HwcHal.h b/graphics/composer/2.4/utils/passthrough/include/composer-passthrough/2.4/HwcHal.h index 616852a71b..d28e00625f 100644 --- a/graphics/composer/2.4/utils/passthrough/include/composer-passthrough/2.4/HwcHal.h +++ b/graphics/composer/2.4/utils/passthrough/include/composer-passthrough/2.4/HwcHal.h @@ -245,6 +245,61 @@ class HwcHalImpl : public V2_3::passthrough::detail::HwcHalImpl<Hal> { return err; } + Error setLayerGenericMetadata(Display display, Layer layer, const std::string& key, + bool mandatory, const std::vector<uint8_t>& value) override { + if (!mDispatch.setLayerGenericMetadata) { + return Error::UNSUPPORTED; + } + + if (key.size() > std::numeric_limits<uint32_t>::max()) { + return Error::BAD_PARAMETER; + } + + if (value.size() > std::numeric_limits<uint32_t>::max()) { + return Error::BAD_PARAMETER; + } + + int32_t error = mDispatch.setLayerGenericMetadata( + mDevice, display, layer, static_cast<uint32_t>(key.size()), key.c_str(), mandatory, + static_cast<uint32_t>(value.size()), value.data()); + return static_cast<Error>(error); + } + + Error getLayerGenericMetadataKeys( + std::vector<IComposerClient::LayerGenericMetadataKey>* outKeys) override { + if (!mDispatch.getLayerGenericMetadataKey) { + return Error::UNSUPPORTED; + } + + std::vector<IComposerClient::LayerGenericMetadataKey> keys; + + uint32_t index = 0; + uint32_t keyLength = 0; + while (true) { + mDispatch.getLayerGenericMetadataKey(mDevice, index, &keyLength, nullptr, nullptr); + if (keyLength == 0) { + break; + } + + IComposerClient::LayerGenericMetadataKey key; + std::string keyName; + keyName.resize(keyLength); + mDispatch.getLayerGenericMetadataKey(mDevice, index, &keyLength, keyName.data(), + &key.mandatory); + key.name = keyName; + keys.emplace_back(std::move(key)); + + // Only attempt to load the first 100 keys to avoid an infinite loop + // if something goes wrong + if (++index > 100) { + break; + } + } + + *outKeys = std::move(keys); + return Error::NONE; + } + protected: bool initDispatch() override { if (!BaseType2_3::initDispatch()) { @@ -267,6 +322,11 @@ class HwcHalImpl : public V2_3::passthrough::detail::HwcHalImpl<Hal> { this->initOptionalDispatch(HWC2_FUNCTION_SET_CONTENT_TYPE, &mDispatch.setContentType); this->initOptionalDispatch(HWC2_FUNCTION_GET_CLIENT_TARGET_PROPERTY, &mDispatch.getClientTargetProperty); + this->initOptionalDispatch(HWC2_FUNCTION_SET_LAYER_GENERIC_METADATA, + &mDispatch.setLayerGenericMetadata); + this->initOptionalDispatch(HWC2_FUNCTION_GET_LAYER_GENERIC_METADATA_KEY, + &mDispatch.getLayerGenericMetadataKey); + return true; } @@ -319,6 +379,8 @@ class HwcHalImpl : public V2_3::passthrough::detail::HwcHalImpl<Hal> { HWC2_PFN_GET_SUPPORTED_CONTENT_TYPES getSupportedContentTypes; HWC2_PFN_SET_CONTENT_TYPE setContentType; HWC2_PFN_GET_CLIENT_TARGET_PROPERTY getClientTargetProperty; + HWC2_PFN_SET_LAYER_GENERIC_METADATA setLayerGenericMetadata; + HWC2_PFN_GET_LAYER_GENERIC_METADATA_KEY getLayerGenericMetadataKey; } mDispatch = {}; hal::ComposerHal::EventCallback_2_4* mEventCallback_2_4 = nullptr; diff --git a/graphics/composer/2.4/utils/vts/Android.bp b/graphics/composer/2.4/utils/vts/Android.bp index 673c15eb28..e42223d6eb 100644 --- a/graphics/composer/2.4/utils/vts/Android.bp +++ b/graphics/composer/2.4/utils/vts/Android.bp @@ -22,12 +22,12 @@ cc_library_static { "GraphicsComposerCallback.cpp", ], static_libs: [ - "VtsHalHidlTargetTestBase", "android.hardware.graphics.composer@2.1", "android.hardware.graphics.composer@2.2", "android.hardware.graphics.composer@2.3-vts", "android.hardware.graphics.composer@2.3", "android.hardware.graphics.composer@2.4", + "libgtest", ], header_libs: [ "android.hardware.graphics.composer@2.1-command-buffer", diff --git a/graphics/composer/2.4/utils/vts/ComposerVts.cpp b/graphics/composer/2.4/utils/vts/ComposerVts.cpp index 8a9c00608d..c5f3b5ed2f 100644 --- a/graphics/composer/2.4/utils/vts/ComposerVts.cpp +++ b/graphics/composer/2.4/utils/vts/ComposerVts.cpp @@ -16,8 +16,6 @@ #include <composer-vts/2.4/ComposerVts.h> -#include <VtsHalHidlTargetTestBase.h> - namespace android { namespace hardware { namespace graphics { @@ -27,11 +25,6 @@ namespace vts { using V2_4::Error; -Composer::Composer() : Composer(::testing::VtsHalHidlTargetTestBase::getService<IComposer>()) {} - -Composer::Composer(const std::string& name) - : Composer(::testing::VtsHalHidlTargetTestBase::getService<IComposer>(name)) {} - Composer::Composer(const sp<IComposer>& composer) : V2_3::vts::Composer(composer), mComposer(composer) {} @@ -129,6 +122,16 @@ Error ComposerClient::setContentType(Display display, IComposerClient::ContentTy return mClient->setContentType(display, contentType); } +Error ComposerClient::getLayerGenericMetadataKeys( + std::vector<IComposerClient::LayerGenericMetadataKey>* outKeys) { + Error error = Error::NONE; + mClient->getLayerGenericMetadataKeys([&](const auto tmpError, const auto& tmpKeys) { + error = tmpError; + *outKeys = tmpKeys; + }); + return error; +} + } // namespace vts } // namespace V2_4 } // namespace composer diff --git a/graphics/composer/2.4/utils/vts/include/composer-vts/2.4/ComposerVts.h b/graphics/composer/2.4/utils/vts/include/composer-vts/2.4/ComposerVts.h index df75a48655..fd59eb93cd 100644 --- a/graphics/composer/2.4/utils/vts/include/composer-vts/2.4/ComposerVts.h +++ b/graphics/composer/2.4/utils/vts/include/composer-vts/2.4/ComposerVts.h @@ -19,7 +19,6 @@ #include <memory> #include <vector> -#include <VtsHalHidlTargetTestBase.h> #include <android/hardware/graphics/composer/2.4/IComposer.h> #include <android/hardware/graphics/composer/2.4/IComposerClient.h> #include <composer-vts/2.3/ComposerVts.h> @@ -93,6 +92,9 @@ class ComposerClient : public V2_3::vts::ComposerClient { Error setContentType(Display display, IComposerClient::ContentType contentType); + Error getLayerGenericMetadataKeys( + std::vector<IComposerClient::LayerGenericMetadataKey>* outKeys); + private: const sp<IComposerClient> mClient; }; diff --git a/graphics/composer/2.4/vts/functional/VtsHalGraphicsComposerV2_4TargetTest.cpp b/graphics/composer/2.4/vts/functional/VtsHalGraphicsComposerV2_4TargetTest.cpp index 800ff08a17..a7c3fd7b0c 100644 --- a/graphics/composer/2.4/vts/functional/VtsHalGraphicsComposerV2_4TargetTest.cpp +++ b/graphics/composer/2.4/vts/functional/VtsHalGraphicsComposerV2_4TargetTest.cpp @@ -17,6 +17,7 @@ #define LOG_TAG "graphics_composer_hidl_hal_test@2.4" #include <algorithm> +#include <regex> #include <thread> #include <android-base/logging.h> @@ -41,6 +42,8 @@ namespace V2_4 { namespace vts { namespace { +using namespace std::chrono_literals; + using common::V1_0::BufferUsage; using common::V1_1::RenderIntent; using common::V1_2::ColorMode; @@ -525,7 +528,7 @@ TEST_P(GraphicsComposerHidlTest, setAutoLowLatencyMode) { EXPECT_EQ(Error::UNSUPPORTED, mComposerClient->setAutoLowLatencyMode(mPrimaryDisplay, false)); GTEST_SUCCEED() << "Auto Low Latency Mode is not supported on display " - << to_string(display) << ", skipping test"; + << std::to_string(display) << ", skipping test"; return; } @@ -579,7 +582,7 @@ void GraphicsComposerHidlTest::Test_setContentTypeForDisplay( if (!contentTypeSupport) { EXPECT_EQ(Error::UNSUPPORTED, mComposerClient->setContentType(display, contentType)); GTEST_SUCCEED() << contentTypeStr << " content type is not supported on display " - << to_string(display) << ", skipping test"; + << std::to_string(display) << ", skipping test"; return; } @@ -625,6 +628,28 @@ INSTANTIATE_TEST_SUITE_P( testing::ValuesIn(android::hardware::getAllHalInstanceNames(IComposer::descriptor)), android::hardware::PrintInstanceNameToString); +TEST_P(GraphicsComposerHidlCommandTest, getLayerGenericMetadataKeys) { + std::vector<IComposerClient::LayerGenericMetadataKey> keys; + mComposerClient->getLayerGenericMetadataKeys(&keys); + + std::regex reverseDomainName("^[a-zA-Z-]{2,}(\\.[a-zA-Z0-9-]+)+$"); + std::unordered_set<std::string> uniqueNames; + for (const auto& key : keys) { + std::string name(key.name.c_str()); + + // Keys must not start with 'android' or 'com.android' + ASSERT_FALSE(name.find("android") == 0); + ASSERT_FALSE(name.find("com.android") == 0); + + // Keys must be in reverse domain name format + ASSERT_TRUE(std::regex_match(name, reverseDomainName)); + + // Keys must be unique within this list + const auto& [iter, inserted] = uniqueNames.insert(name); + ASSERT_TRUE(inserted); + } +} + } // namespace } // namespace vts } // namespace V2_4 diff --git a/health/2.0/vts/functional/VtsHalHealthV2_0TargetTest.cpp b/health/2.0/vts/functional/VtsHalHealthV2_0TargetTest.cpp index 49891b74cd..352a990af2 100644 --- a/health/2.0/vts/functional/VtsHalHealthV2_0TargetTest.cpp +++ b/health/2.0/vts/functional/VtsHalHealthV2_0TargetTest.cpp @@ -315,9 +315,7 @@ TEST_P(HealthHidlTest, getEnergyCounter) { TEST_P(HealthHidlTest, getChargeStatus) { SKIP_IF_SKIPPED(); EXPECT_OK(mHealth->getChargeStatus([](auto result, auto value) { - EXPECT_VALID_OR_UNSUPPORTED_PROP( - result, toString(value), - value != BatteryStatus::UNKNOWN && verifyEnum<BatteryStatus>(value)); + EXPECT_VALID_OR_UNSUPPORTED_PROP(result, toString(value), verifyEnum<BatteryStatus>(value)); })); } diff --git a/health/2.1/README.md b/health/2.1/README.md index bfcf13bc95..8390570d60 100644 --- a/health/2.1/README.md +++ b/health/2.1/README.md @@ -24,6 +24,9 @@ ```mk # Install default passthrough implementation to vendor. PRODUCT_PACKAGES += android.hardware.health@2.1-impl + + # For non-A/B devices, install default passthrough implementation to recovery. + PRODUCT_PACKAGES += android.hardware.health@2.1-impl.recovery ``` You are done. Otherwise, go to the next step. @@ -42,6 +45,8 @@ implementation, See [Upgrading from Health HAL 2.0](#update-from-2-0). + 1. [Install the implementation](#install). + 1. [Update necessary SELinux permissions](#selinux). 1. [Fix `/charger` symlink](#charger-symlink). @@ -95,15 +100,22 @@ and `update()`, with an additional `energyCounter()` function. `HealthImpl::getHealthInfo` or `HealthImpl::getHealthInfo_2_1` because they call `getDiskStats` and `getStorageInfo` to retrieve storage information. +# Install the implementation {#install} + +In `device.mk`: + +```mk +# Install the passthrough implementation to vendor. +PRODUCT_PACKAGES += android.hardware.health@2.1-impl-<device> + +# For non-A/B devices, also install the passthrough implementation to recovery. +PRODUCT_PACKAGES += android.hardware.health@2.1-impl-<device>.recovery +``` + # Update necessary SELinux permissions {#selinux} For example (replace `<device>` with the device name): ``` -# device/<manufacturer>/<device>/sepolicy/vendor/file_contexts -# Required for charger to open passthrough implementation. Replace <device> with the proper device -# name. File name must be consistent with `stem` of the implementation module. -/vendor/lib(64)?/hw/android\.hardware\.health@2\.0-impl-2\.1-<device>\.so u:object_r:same_process_hal_file:s0 - # device/<manufacturer>/<device>/sepolicy/vendor/hal_health_default.te # Add device specific permissions to hal_health_default domain, especially # if a device-specific libhealthd is used and/or device-specific storage related diff --git a/health/2.1/default/android.hardware.health@2.1-service.rc b/health/2.1/default/android.hardware.health@2.1-service.rc index 917f1c2f19..b6d9e3bcf3 100644 --- a/health/2.1/default/android.hardware.health@2.1-service.rc +++ b/health/2.1/default/android.hardware.health@2.1-service.rc @@ -1,5 +1,5 @@ service health-hal-2-1 /vendor/bin/hw/android.hardware.health@2.1-service - class hal + class hal charger user system group system capabilities WAKE_ALARM diff --git a/health/2.1/vts/functional/Android.bp b/health/2.1/vts/functional/Android.bp index 5aa873ab98..17472fa2fa 100644 --- a/health/2.1/vts/functional/Android.bp +++ b/health/2.1/vts/functional/Android.bp @@ -25,5 +25,8 @@ cc_test { "android.hardware.health@2.0", "android.hardware.health@2.1", ], - test_suites: ["general-tests"], + test_suites: [ + "general-tests", + "vts-core", + ], } diff --git a/health/2.1/vts/functional/VtsHalHealthV2_1TargetTest.cpp b/health/2.1/vts/functional/VtsHalHealthV2_1TargetTest.cpp index da9f5bbbbc..e75b299b0b 100644 --- a/health/2.1/vts/functional/VtsHalHealthV2_1TargetTest.cpp +++ b/health/2.1/vts/functional/VtsHalHealthV2_1TargetTest.cpp @@ -233,9 +233,13 @@ TEST_P(HealthHidlTest, getHealthInfo_2_1) { EXPECT_TRUE(IsEnum(value.batteryCapacityLevel)) << " BatteryCapacityLevel"; EXPECT_GE(value.batteryChargeTimeToFullNowSeconds, 0); - EXPECT_GE(value.batteryFullCapacityUah, 0) << "batteryFullCapacityUah is unknown"; - EXPECT_GE(value.batteryFullCapacityUah, legacy.batteryFullCharge * 0.50); - EXPECT_LE(value.batteryFullCapacityUah, legacy.batteryFullCharge * 1.20); + EXPECT_GE(value.batteryFullCapacityUah, 0) + << "batteryFullCapacityUah should not be negative"; + + if (value.batteryFullCapacityUah > 0) { + EXPECT_GE(value.batteryFullCapacityUah, legacy.batteryFullCharge * 0.50); + EXPECT_LE(value.batteryFullCapacityUah, legacy.batteryFullCharge * 1.20); + } }))); } diff --git a/health/utils/libhealthloop/utils.cpp b/health/utils/libhealthloop/utils.cpp index b0d153f583..ebfd8d8c6a 100644 --- a/health/utils/libhealthloop/utils.cpp +++ b/health/utils/libhealthloop/utils.cpp @@ -40,6 +40,8 @@ void InitHealthdConfig(struct healthd_config* healthd_config) { .batteryChargeCounterPath = String8(String8::kEmptyString), .batteryFullChargePath = String8(String8::kEmptyString), .batteryCycleCountPath = String8(String8::kEmptyString), + .batteryCapacityLevelPath = String8(String8::kEmptyString), + .batteryChargeTimeToFullNowPath = String8(String8::kEmptyString), .energyCounter = NULL, .boot_min_cap = 0, .screen_on = NULL, diff --git a/identity/1.0/Android.bp b/identity/1.0/Android.bp index a5cea90656..e0a6332f89 100644 --- a/identity/1.0/Android.bp +++ b/identity/1.0/Android.bp @@ -13,13 +13,8 @@ hidl_interface { "IWritableIdentityCredential.hal", ], interfaces: [ - "android.hidl.base@1.0", "android.hardware.keymaster@4.0", - ], - types: [ - "AuditLogEntry", - "ResultCode", - "SecureAccessControlProfile", + "android.hidl.base@1.0", ], gen_java: false, } diff --git a/keymaster/4.1/support/include/keymasterV4_1/keymaster_tags.h b/keymaster/4.1/support/include/keymasterV4_1/keymaster_tags.h index c5ce9505d3..6c186f6429 100644 --- a/keymaster/4.1/support/include/keymasterV4_1/keymaster_tags.h +++ b/keymaster/4.1/support/include/keymasterV4_1/keymaster_tags.h @@ -100,6 +100,7 @@ using V4_0::TAG_VENDOR_PATCHLEVEL; DECLARE_KM_4_1_TYPED_TAG(EARLY_BOOT_ONLY); DECLARE_KM_4_1_TYPED_TAG(DEVICE_UNIQUE_ATTESTATION); +DECLARE_KM_4_1_TYPED_TAG(STORAGE_KEY); } // namespace android::hardware::keymaster::V4_1 diff --git a/keymaster/4.1/types.hal b/keymaster/4.1/types.hal index 9e8b30e590..f3bdcc6404 100644 --- a/keymaster/4.1/types.hal +++ b/keymaster/4.1/types.hal @@ -50,10 +50,29 @@ enum Tag : @4.0::Tag { * HAL attests to Credential Keys. IIdentityCredential produces Keymaster-style attestations. */ IDENTITY_CREDENTIAL_KEY = TagType:BOOL | 721, + + /** + * To prevent keys from being compromised if an attacker acquires read access to system / kernel + * memory, some inline encryption hardware supports protecting storage encryption keys in hardware + * without software having access to or the ability to set the plaintext keys. Instead, software + * only sees wrapped version of these keys. + * + * STORAGE_KEY is used to denote that a key generated or imported is a key used for storage + * encryption. Keys of this type can either be generated or imported or secure imported using + * keymaster. exportKey() can be used to re-wrap storage key with a per-boot ephemeral key wrapped + * key once the key characteristics are enforced. + * + * Keys with this tag cannot be used for any operation within keymaster. + * ErrorCode::INVALID_OPERATION is returned when a key with Tag::STORAGE_KEY is provided to + * begin(). + */ + STORAGE_KEY = TagType:BOOL | 722, }; enum ErrorCode : @4.0::ErrorCode { EARLY_BOOT_ENDED = -73, ATTESTATION_KEYS_NOT_PROVISIONED = -74, ATTESTATION_IDS_NOT_PROVISIONED = -75, + INVALID_OPERATION = -76, + STORAGE_KEY_UNSUPPORTED = -77, }; diff --git a/light/aidl/Android.bp b/light/aidl/Android.bp new file mode 100644 index 0000000000..916a857b58 --- /dev/null +++ b/light/aidl/Android.bp @@ -0,0 +1,18 @@ +aidl_interface { + name: "android.hardware.light", + vendor_available: true, + srcs: [ + "android/hardware/light/*.aidl", + ], + stability: "vintf", + backend: { + java: { + platform_apis: true, + }, + ndk: { + vndk: { + enabled: true, + }, + }, + }, +} diff --git a/light/aidl/android/hardware/light/BrightnessMode.aidl b/light/aidl/android/hardware/light/BrightnessMode.aidl new file mode 100644 index 0000000000..bc296990ce --- /dev/null +++ b/light/aidl/android/hardware/light/BrightnessMode.aidl @@ -0,0 +1,58 @@ +/* + * Copyright (C) 2020 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package android.hardware.light; + +@VintfStability +enum BrightnessMode { + /** + * Light brightness is managed by a user setting. + */ + USER = 0, + + /** + * Light brightness is managed by a light sensor. This is typically used + * to control the display backlight, but not limited to it. HALs and + * hardware implementations are free to support sensor for other lights or + * none whatsoever. + */ + SENSOR = 1, + + /** + * Use a low-persistence mode for display backlights, where the pixel + * color transition times are lowered. + * + * When set, the device driver must switch to a mode optimized for low display + * persistence that is intended to be used when the device is being treated as a + * head mounted display (HMD). The actual display brightness in this mode is + * implementation dependent, and any value set for color in LightState may be + * overridden by the HAL implementation. + * + * For an optimal HMD viewing experience, the display must meet the following + * criteria in this mode: + * - Gray-to-Gray, White-to-Black, and Black-to-White switching time must be ≤ 3 ms. + * - The display must support low-persistence with ≤ 3.5 ms persistence. + * Persistence is defined as the amount of time for which a pixel is + * emitting light for a single frame. + * - Any "smart panel" or other frame buffering options that increase display + * latency are disabled. + * - Display brightness is set so that the display is still visible to the user + * under normal indoor lighting. + * - The display must update at 60 Hz at least, but higher refresh rates are + * recommended for low latency. + * + */ + LOW_PERSISTENCE = 2, +} diff --git a/light/aidl/android/hardware/light/FlashMode.aidl b/light/aidl/android/hardware/light/FlashMode.aidl new file mode 100644 index 0000000000..00c6b6a028 --- /dev/null +++ b/light/aidl/android/hardware/light/FlashMode.aidl @@ -0,0 +1,34 @@ +/* + * Copyright (C) 2020 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package android.hardware.light; + +@VintfStability +enum FlashMode { + /** + * Keep the light steady on or off. + */ + NONE = 0, + /** + * Flash the light at specified rate, potentially using a software-based + * implementation. + */ + TIMED = 1, + /** + * Flash the light using hardware flashing support. This may or may not + * support a user-defined flashing rate or other features. + */ + HARDWARE = 2, +} diff --git a/light/aidl/android/hardware/light/HwLight.aidl b/light/aidl/android/hardware/light/HwLight.aidl new file mode 100644 index 0000000000..43fdb4bf81 --- /dev/null +++ b/light/aidl/android/hardware/light/HwLight.aidl @@ -0,0 +1,42 @@ +/* + * Copyright (C) 2019 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.hardware.light; + +import android.hardware.light.LightType; + +/** + * A description of a single light. Multiple lights can map to the same physical + * LED. Separate physical LEDs are always represented by separate instances. + */ +@VintfStability +parcelable HwLight { + /** + * Integer ID used for controlling this light + */ + int id; + + /** + * For a group of lights of the same logical type, sorting by ordinal should + * be give their physical order. No other meaning is carried by it. + */ + int ordinal; + + /** + * Logical type use of this light. + */ + LightType type; +} diff --git a/light/aidl/android/hardware/light/HwLightState.aidl b/light/aidl/android/hardware/light/HwLightState.aidl new file mode 100644 index 0000000000..24d3250887 --- /dev/null +++ b/light/aidl/android/hardware/light/HwLightState.aidl @@ -0,0 +1,64 @@ +/* + * Copyright (C) 2020 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.hardware.light; + +import android.hardware.light.BrightnessMode; +import android.hardware.light.FlashMode; + +/** + * The parameters that can be set for a given light. + * + * Not all lights must support all parameters. If you + * can do something backward-compatible, do it. + */ +@VintfStability +parcelable HwLightState { + /** + * The color of the LED in ARGB. + * + * The implementation of this in the HAL and hardware is a best-effort one. + * - If a light can only do red or green and blue is requested, green + * should be shown. + * - If only a brightness ramp is supported, then this formula applies: + * unsigned char brightness = ((77*((color>>16)&0x00ff)) + * + (150*((color>>8)&0x00ff)) + (29*(color&0x00ff))) >> 8; + * - If only on and off are supported, 0 is off, anything else is on. + * + * The high byte should be ignored. Callers should set it to 0xff (which + * would correspond to 255 alpha). + */ + int color; + + /** + * To flash the light at a given rate, set flashMode to FLASH_TIMED. + */ + FlashMode flashMode; + + /** + * flashOnMs should be set to the number of milliseconds to turn the + * light on, before it's turned off. + */ + int flashOnMs; + + /** + * flashOfMs should be set to the number of milliseconds to turn the + * light off, before it's turned back on. + */ + int flashOffMs; + + BrightnessMode brightnessMode; +} diff --git a/light/aidl/android/hardware/light/ILights.aidl b/light/aidl/android/hardware/light/ILights.aidl new file mode 100644 index 0000000000..2253f73c36 --- /dev/null +++ b/light/aidl/android/hardware/light/ILights.aidl @@ -0,0 +1,47 @@ +/* + * Copyright (C) 2020 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.hardware.light; + +import android.hardware.light.HwLightState; +import android.hardware.light.HwLight; + +/** + * Allows controlling logical lights/indicators, mapped to LEDs in a + * hardware-specific manner by the HAL implementation. + */ +@VintfStability +interface ILights { + /** + * Set light identified by id to the provided state. + * + * If control over an invalid light is requested, this method exists with + * EX_UNSUPPORTED_OPERATION. Control over supported lights is done on a + * device-specific best-effort basis and unsupported sub-features will not + * be reported. + * + * @param id ID of logical light to set as returned by getLights() + * @param state describes what the light should look like. + */ + void setLightState(in int id, in HwLightState state); + + /** + * Discover what lights are supported by the HAL implementation. + * + * @return List of available lights + */ + HwLight[] getLights(); +} diff --git a/light/aidl/android/hardware/light/LightType.aidl b/light/aidl/android/hardware/light/LightType.aidl new file mode 100644 index 0000000000..9a7f65619d --- /dev/null +++ b/light/aidl/android/hardware/light/LightType.aidl @@ -0,0 +1,35 @@ +/* + * Copyright (C) 2020 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package android.hardware.light; + +/** + * These light IDs correspond to logical lights, not physical. + * So for example, if your INDICATOR light is in line with your + * BUTTONS, it might make sense to also light the INDICATOR + * light to a reasonable color when the BUTTONS are lit. + */ +@VintfStability +enum LightType { + BACKLIGHT = 0, + KEYBOARD = 1, + BUTTONS = 2, + BATTERY = 3, + NOTIFICATIONS = 4, + ATTENTION = 5, + BLUETOOTH = 6, + WIFI = 7, + MICROPHONE = 8, +} diff --git a/light/aidl/default/Android.bp b/light/aidl/default/Android.bp new file mode 100644 index 0000000000..ae3f4630de --- /dev/null +++ b/light/aidl/default/Android.bp @@ -0,0 +1,16 @@ +cc_binary { + name: "android.hardware.lights-service.example", + relative_install_path: "hw", + init_rc: ["lights-default.rc"], + vintf_fragments: ["lights-default.xml"], + vendor: true, + shared_libs: [ + "libbase", + "libbinder_ndk", + "android.hardware.light-ndk_platform", + ], + srcs: [ + "Lights.cpp", + "main.cpp", + ], +} diff --git a/light/aidl/default/Lights.cpp b/light/aidl/default/Lights.cpp new file mode 100644 index 0000000000..74747d57bd --- /dev/null +++ b/light/aidl/default/Lights.cpp @@ -0,0 +1,39 @@ +/* + * Copyright (C) 2019 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "Lights.h" + +#include <android-base/logging.h> + +namespace aidl { +namespace android { +namespace hardware { +namespace light { + +ndk::ScopedAStatus Lights::setLightState(int id, const HwLightState& state) { + LOG(INFO) << "Lights setting state for id=" << id << " to color " << std::hex << state.color; + return ndk::ScopedAStatus::fromExceptionCode(EX_UNSUPPORTED_OPERATION); +} + +ndk::ScopedAStatus Lights::getLights(std::vector<HwLight>* /*lights*/) { + LOG(INFO) << "Lights reporting supported lights"; + return ndk::ScopedAStatus::ok(); +} + +} // namespace light +} // namespace hardware +} // namespace android +} // namespace aidl diff --git a/light/aidl/default/Lights.h b/light/aidl/default/Lights.h new file mode 100644 index 0000000000..8cba5a1cfd --- /dev/null +++ b/light/aidl/default/Lights.h @@ -0,0 +1,35 @@ +/* + * Copyright (C) 2020 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#pragma once + +#include <aidl/android/hardware/light/BnLights.h> + +namespace aidl { +namespace android { +namespace hardware { +namespace light { + +// Default implementation that reports no supported lights. +class Lights : public BnLights { + ndk::ScopedAStatus setLightState(int id, const HwLightState& state) override; + ndk::ScopedAStatus getLights(std::vector<HwLight>* types) override; +}; + +} // namespace light +} // namespace hardware +} // namespace android +} // namespace aidl diff --git a/light/aidl/default/lights-default.rc b/light/aidl/default/lights-default.rc new file mode 100644 index 0000000000..687ec97dd1 --- /dev/null +++ b/light/aidl/default/lights-default.rc @@ -0,0 +1,5 @@ +service vendor.light-default /vendor/bin/hw/android.hardware.lights-service.example + class hal + user nobody + group nobody + shutdown critical diff --git a/light/aidl/default/lights-default.xml b/light/aidl/default/lights-default.xml new file mode 100644 index 0000000000..db604d61f0 --- /dev/null +++ b/light/aidl/default/lights-default.xml @@ -0,0 +1,6 @@ +<manifest version="1.0" type="device"> + <hal format="aidl"> + <name>android.hardware.light</name> + <fqname>ILights/default</fqname> + </hal> +</manifest> diff --git a/light/aidl/default/main.cpp b/light/aidl/default/main.cpp new file mode 100644 index 0000000000..a860bf4a98 --- /dev/null +++ b/light/aidl/default/main.cpp @@ -0,0 +1,35 @@ +/* + * Copyright (C) 2020 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "Lights.h" + +#include <android-base/logging.h> +#include <android/binder_manager.h> +#include <android/binder_process.h> + +using ::aidl::android::hardware::light::Lights; + +int main() { + ABinderProcess_setThreadPoolMaxThreadCount(0); + std::shared_ptr<Lights> lights = ndk::SharedRefBase::make<Lights>(); + + const std::string instance = std::string() + Lights::descriptor + "/default"; + binder_status_t status = AServiceManager_addService(lights->asBinder().get(), instance.c_str()); + CHECK(status == STATUS_OK); + + ABinderProcess_joinThreadPool(); + return EXIT_FAILURE; // should not reached +} diff --git a/light/aidl/vts/functional/Android.bp b/light/aidl/vts/functional/Android.bp new file mode 100644 index 0000000000..3dd8cf64ce --- /dev/null +++ b/light/aidl/vts/functional/Android.bp @@ -0,0 +1,35 @@ +// +// Copyright (C) 2020 The Android Open Source Project +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// + +cc_test { + name: "VtsHalLightTargetTest", + defaults: [ + "VtsHalTargetTestDefaults", + "use_libaidlvintf_gtest_helper_static", + ], + srcs: [ + "VtsHalLightTargetTest.cpp", + ], + shared_libs: [ + "libbinder", + ], + static_libs: [ + "android.hardware.light-cpp", + ], + test_suites: [ + "vts-core", + ], +} diff --git a/light/aidl/vts/functional/VtsHalLightTargetTest.cpp b/light/aidl/vts/functional/VtsHalLightTargetTest.cpp new file mode 100644 index 0000000000..3c26278a19 --- /dev/null +++ b/light/aidl/vts/functional/VtsHalLightTargetTest.cpp @@ -0,0 +1,171 @@ +/* + * Copyright (C) 2020 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#define LOG_TAG "light_aidl_hal_test" + +#include <aidl/Gtest.h> +#include <aidl/Vintf.h> + +#include <android-base/logging.h> +#include <android/hardware/light/ILights.h> +#include <binder/IServiceManager.h> +#include <binder/ProcessState.h> +#include <gtest/gtest.h> +#include <hidl/GtestPrinter.h> +#include <hidl/ServiceManagement.h> + +#include <unistd.h> +#include <set> + +using android::ProcessState; +using android::sp; +using android::String16; +using android::binder::Status; +using android::hardware::hidl_vec; +using android::hardware::Return; +using android::hardware::Void; +using android::hardware::light::BrightnessMode; +using android::hardware::light::FlashMode; +using android::hardware::light::HwLight; +using android::hardware::light::HwLightState; +using android::hardware::light::ILights; +using android::hardware::light::LightType; + +#define ASSERT_OK(ret) ASSERT_TRUE(ret.isOk()) +#define EXPECT_OK(ret) EXPECT_TRUE(ret.isOk()) + +const std::set<LightType> kAllTypes{android::enum_range<LightType>().begin(), + android::enum_range<LightType>().end()}; + +class LightsAidl : public testing::TestWithParam<std::string> { + public: + virtual void SetUp() override { + lights = android::waitForDeclaredService<ILights>(String16(GetParam().c_str())); + ASSERT_NE(lights, nullptr); + ASSERT_TRUE(lights->getLights(&supportedLights).isOk()); + } + + sp<ILights> lights; + std::vector<HwLight> supportedLights; + + virtual void TearDown() override { + for (const HwLight& light : supportedLights) { + HwLightState off; + off.color = 0x00000000; + off.flashMode = FlashMode::NONE; + off.brightnessMode = BrightnessMode::USER; + EXPECT_TRUE(lights->setLightState(light.id, off).isOk()); + } + + // must leave the device in a useable condition + for (const HwLight& light : supportedLights) { + if (light.type == LightType::BACKLIGHT) { + HwLightState backlightOn; + backlightOn.color = 0xFFFFFFFF; + backlightOn.flashMode = FlashMode::TIMED; + backlightOn.brightnessMode = BrightnessMode::USER; + EXPECT_TRUE(lights->setLightState(light.id, backlightOn).isOk()); + } + } + } +}; + +/** + * Ensure all reported lights actually work. + */ +TEST_P(LightsAidl, TestSupported) { + HwLightState whiteFlashing; + whiteFlashing.color = 0xFFFFFFFF; + whiteFlashing.flashMode = FlashMode::TIMED; + whiteFlashing.flashOnMs = 100; + whiteFlashing.flashOffMs = 50; + whiteFlashing.brightnessMode = BrightnessMode::USER; + for (const HwLight& light : supportedLights) { + EXPECT_TRUE(lights->setLightState(light.id, whiteFlashing).isOk()); + } +} + +/** + * Ensure all reported lights have one of the supported types. + */ +TEST_P(LightsAidl, TestSupportedLightTypes) { + for (const HwLight& light : supportedLights) { + EXPECT_TRUE(kAllTypes.find(light.type) != kAllTypes.end()); + } +} + +/** + * Ensure all lights have a unique id. + */ +TEST_P(LightsAidl, TestUniqueIds) { + std::set<int> ids; + for (const HwLight& light : supportedLights) { + EXPECT_TRUE(ids.find(light.id) == ids.end()); + ids.insert(light.id); + } +} + +/** + * Ensure all lights have a unique ordinal for a given type. + */ +TEST_P(LightsAidl, TestUniqueOrdinalsForType) { + std::map<int, std::set<int>> ordinalsByType; + for (const HwLight& light : supportedLights) { + auto& ordinals = ordinalsByType[(int)light.type]; + EXPECT_TRUE(ordinals.find(light.ordinal) == ordinals.end()); + ordinals.insert(light.ordinal); + } +} + +/** + * Ensure EX_UNSUPPORTED_OPERATION is returned if LOW_PERSISTENCE is not supported. + */ +TEST_P(LightsAidl, TestLowPersistence) { + HwLightState lowPersistence; + lowPersistence.color = 0xFF123456; + lowPersistence.flashMode = FlashMode::TIMED; + lowPersistence.flashOnMs = 100; + lowPersistence.flashOffMs = 50; + lowPersistence.brightnessMode = BrightnessMode::LOW_PERSISTENCE; + for (const HwLight& light : supportedLights) { + Status status = lights->setLightState(light.id, lowPersistence); + EXPECT_TRUE(status.isOk() || Status::EX_UNSUPPORTED_OPERATION == status.exceptionCode()); + } +} + +/** + * Ensure EX_UNSUPPORTED_OPERATION is returns for an invalid light id. + */ +TEST_P(LightsAidl, TestInvalidLightIdUnsupported) { + int maxId = INT_MIN; + for (const HwLight& light : supportedLights) { + maxId = std::max(maxId, light.id); + } + + Status status = lights->setLightState(maxId + 1, HwLightState()); + EXPECT_TRUE(status.exceptionCode() == Status::EX_UNSUPPORTED_OPERATION); +} + +INSTANTIATE_TEST_SUITE_P(Lights, LightsAidl, + testing::ValuesIn(android::getAidlHalInstanceNames(ILights::descriptor)), + android::PrintInstanceNameToString); + +int main(int argc, char** argv) { + ::testing::InitGoogleTest(&argc, argv); + ProcessState::self()->setThreadPoolMaxThreadCount(1); + ProcessState::self()->startThreadPool(); + return RUN_ALL_TESTS(); +} diff --git a/light/utils/Android.bp b/light/utils/Android.bp index 4c287e4620..e901129d1f 100644 --- a/light/utils/Android.bp +++ b/light/utils/Android.bp @@ -23,7 +23,11 @@ cc_binary { shared_libs: [ "android.hardware.light@2.0", "libbase", + "libbinder", "libhidlbase", "libutils", ], + static_libs: [ + "android.hardware.light-cpp", + ], } diff --git a/light/utils/main.cpp b/light/utils/main.cpp index b83413246b..b9b6489eec 100644 --- a/light/utils/main.cpp +++ b/light/utils/main.cpp @@ -19,34 +19,23 @@ #include <android-base/logging.h> #include <android/hardware/light/2.0/ILight.h> +#include <android/hardware/light/ILights.h> +#include <binder/IServiceManager.h> + +using android::sp; +using android::waitForVintfService; +using android::binder::Status; +using android::hardware::hidl_vec; + +namespace V2_0 = android::hardware::light::V2_0; +namespace aidl = android::hardware::light; void error(const std::string& msg) { LOG(ERROR) << msg; std::cerr << msg << std::endl; } -int main(int argc, char* argv[]) { - using ::android::hardware::hidl_vec; - using ::android::hardware::light::V2_0::Brightness; - using ::android::hardware::light::V2_0::Flash; - using ::android::hardware::light::V2_0::ILight; - using ::android::hardware::light::V2_0::LightState; - using ::android::hardware::light::V2_0::Status; - using ::android::hardware::light::V2_0::Type; - using ::android::sp; - - sp<ILight> service = ILight::getService(); - if (service == nullptr) { - error("Could not retrieve light service."); - return -1; - } - - static LightState off = { - .color = 0u, - .flashMode = Flash::NONE, - .brightnessMode = Brightness::USER, - }; - +int parseArgs(int argc, char* argv[], unsigned int* color) { if (argc > 2) { error("Usage: blank_screen [color]"); return -1; @@ -54,25 +43,78 @@ int main(int argc, char* argv[]) { if (argc > 1) { char* col_ptr; - unsigned int col_new; - col_new = strtoul(argv[1], &col_ptr, 0); + *color = strtoul(argv[1], &col_ptr, 0); if (*col_ptr != '\0') { error("Failed to convert " + std::string(argv[1]) + " to number"); return -1; } - off.color = col_new; + + return 0; + } + + *color = 0u; + return 0; +} + +void setToColorAidl(sp<aidl::ILights> hal, unsigned int color) { + static aidl::HwLightState off; + off.color = color; + off.flashMode = aidl::FlashMode::NONE; + off.brightnessMode = aidl::BrightnessMode::USER; + + std::vector<aidl::HwLight> lights; + Status status = hal->getLights(&lights); + if (!status.isOk()) { + error("Failed to list lights"); + return; + } + + for (auto light : lights) { + Status setStatus = hal->setLightState(light.id, off); + if (!setStatus.isOk()) { + error("Failed to shut off light id " + std::to_string(light.id)); + } } +} - service->getSupportedTypes([&](const hidl_vec<Type>& types) { - for (Type type : types) { - Status ret = service->setLight(type, off); - if (ret != Status::SUCCESS) { - error("Failed to shut off screen for type " + +void setToColorHidl(sp<V2_0::ILight> hal, unsigned int color) { + static V2_0::LightState off = { + .color = color, + .flashMode = V2_0::Flash::NONE, + .brightnessMode = V2_0::Brightness::USER, + }; + + hal->getSupportedTypes([&](const hidl_vec<V2_0::Type>& types) { + for (auto type : types) { + V2_0::Status ret = hal->setLight(type, off); + if (ret != V2_0::Status::SUCCESS) { + error("Failed to shut off light for type " + std::to_string(static_cast<int>(type))); } } }); +} - return 0; +int main(int argc, char* argv[]) { + unsigned int inputColor; + int result = parseArgs(argc, argv, &inputColor); + if (result != 0) { + return result; + } + + auto aidlHal = waitForVintfService<aidl::ILights>(); + if (aidlHal != nullptr) { + setToColorAidl(aidlHal, inputColor); + return 0; + } + + sp<V2_0::ILight> hidlHal = V2_0::ILight::getService(); + if (hidlHal != nullptr) { + setToColorHidl(hidlHal, inputColor); + return 0; + } + + error("Could not retrieve light service."); + return -1; } diff --git a/memtrack/1.0/default/Memtrack.cpp b/memtrack/1.0/default/Memtrack.cpp index 33a6906694..0bbf83d2b3 100644 --- a/memtrack/1.0/default/Memtrack.cpp +++ b/memtrack/1.0/default/Memtrack.cpp @@ -34,9 +34,7 @@ Memtrack::Memtrack(const memtrack_module_t *module) : mModule(module) { mModule->init(mModule); } -Memtrack::~Memtrack() { - delete(mModule); -} +Memtrack::~Memtrack() {} Return<void> Memtrack::getMemory(int32_t pid, MemtrackType type, getMemory_cb _hidl_cb) { diff --git a/neuralnetworks/1.3/Android.bp b/neuralnetworks/1.3/Android.bp index 56011e227d..7b02cc510f 100644 --- a/neuralnetworks/1.3/Android.bp +++ b/neuralnetworks/1.3/Android.bp @@ -11,6 +11,7 @@ hidl_interface { "IBuffer.hal", "IDevice.hal", "IExecutionCallback.hal", + "IFencedExecutionCallback.hal", "IPreparedModel.hal", "IPreparedModelCallback.hal", ], diff --git a/neuralnetworks/1.3/IFencedExecutionCallback.hal b/neuralnetworks/1.3/IFencedExecutionCallback.hal new file mode 100644 index 0000000000..6030809406 --- /dev/null +++ b/neuralnetworks/1.3/IFencedExecutionCallback.hal @@ -0,0 +1,61 @@ +/* + * Copyright (C) 2020 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.hardware.neuralnetworks@1.3; + +import @1.2::Timing; +import ErrorStatus; + +/** + * IFencedExecutionCallback can be used to query the error status result + * and duration information from an IPreparedModel::executeFenced call. + */ +interface IFencedExecutionCallback { + + /** + * The getExecutionInfo method is used by the clients to query error status + * result and duration information. The method must only be called after the actual + * evaluation has finished or resulted in an runtime error, as indicated by the status + * of the sync fence returned by the IPreparedModel::executeFenced call, otherwise + * GENERAL_FAILURE must be returned. + * + * @return status Error status returned from the asynchronously dispatched execution + * must be: + * - NONE if the asynchronous execution was successful + * - DEVICE_UNAVAILABLE if driver is offline or busy + * - GENERAL_FAILURE if the asynchronous task resulted in an + * unspecified error + * - MISSED_DEADLINE_* if the deadline for executing a model + * cannot be met + * - RESOURCE_EXHAUSTED_* if the task was aborted by the + * driver + * @return timingLaunched The duration starts when executeFenced is called and ends when + * executeFenced signals the returned syncFence. + * Unless MeasureTiming::YES was passed when + * launching the execution and status is NONE, all times + * must be reported as UINT64_MAX. A driver may choose to + * report any time as UINT64_MAX, indicating that particular + * measurement is not available. + * @return timingFenced The duration starts when all waitFor sync fences have been signaled + * and ends when executeFenced signals the returned syncFence. + * Unless MeasureTiming::YES was passed when + * launching the execution and status is NONE, all times + * must be reported as UINT64_MAX. A driver may choose to + * report any time as UINT64_MAX, indicating that particular + * measurement is not available. + */ + getExecutionInfo() generates (ErrorStatus status, Timing timingLaunched, Timing timingFenced); +}; diff --git a/neuralnetworks/1.3/IPreparedModel.hal b/neuralnetworks/1.3/IPreparedModel.hal index bce6ee227a..d645de789c 100644 --- a/neuralnetworks/1.3/IPreparedModel.hal +++ b/neuralnetworks/1.3/IPreparedModel.hal @@ -21,9 +21,11 @@ import @1.2::MeasureTiming; import @1.2::OutputShape; import @1.2::Timing; import ErrorStatus; +import OptionalTimeoutDuration; import OptionalTimePoint; import Request; import IExecutionCallback; +import IFencedExecutionCallback; /** * IPreparedModel describes a model that has been prepared for execution and @@ -67,7 +69,7 @@ interface IPreparedModel extends @1.2::IPreparedModel { * There must be no failure unless the device itself is in a bad state. * * execute_1_3 can be called with an optional deadline. If the execution - * is not able to completed before the provided deadline, the execution + * is not able to be completed before the provided deadline, the execution * must be aborted, and either {@link * ErrorStatus::MISSED_DEADLINE_TRANSIENT} or {@link * ErrorStatus::MISSED_DEADLINE_PERSISTENT} must be returned. The error due @@ -87,11 +89,12 @@ interface IPreparedModel extends @1.2::IPreparedModel { * The duration runs from the time the driver sees the call * to the execute_1_3 function to the time the driver invokes * the callback. - * @param deadline The time by which execution must complete. If the + * @param deadline The time by which the execution must complete. If the * execution cannot be finished by the deadline, the * execution must be aborted. * @param callback A callback object used to return the error status of - * the execution. The callback object's notify function must + * the execution, shape information of model output operands, and + * duration of execution. The callback object's notify function must * be called exactly once, even if the execution was * unsuccessful. * @return status Error status of the call, must be: @@ -137,7 +140,7 @@ interface IPreparedModel extends @1.2::IPreparedModel { * in a bad state. * * executeSynchronously_1_3 can be called with an optional deadline. If the - * execution is not able to completed before the provided deadline, the + * execution is not able to be completed before the provided deadline, the * execution must be aborted, and either {@link * ErrorStatus::MISSED_DEADLINE_TRANSIENT} or {@link * ErrorStatus::MISSED_DEADLINE_PERSISTENT} must be returned. The error due @@ -157,7 +160,7 @@ interface IPreparedModel extends @1.2::IPreparedModel { * The duration runs from the time the driver sees the call * to the executeSynchronously_1_3 function to the time the driver * returns from the function. - * @param deadline The time by which execution must complete. If the + * @param deadline The time by which the execution must complete. If the * execution cannot be finished by the deadline, the * execution must be aborted. * @return status Error status of the execution, must be: @@ -187,4 +190,80 @@ interface IPreparedModel extends @1.2::IPreparedModel { OptionalTimePoint deadline) generates (ErrorStatus status, vec<OutputShape> outputShapes, Timing timing); + + /** + * Launch a fenced asynchronous execution on a prepared model. + * + * The execution is performed asynchronously with respect to the caller. + * executeFenced must verify the inputs to the function are correct, and the usages + * of memory pools allocated by IDevice::allocate are valid. If there is an error, + * executeFenced must immediately return with the corresponding ErrorStatus, an empty + * handle for syncFence, and nullptr for callback. If the inputs to the function + * are valid and there is no error, executeFenced must dispatch an asynchronous task + * to perform the execution in the background, and immediately return with + * ErrorStatus::NONE, a sync fence that will be signaled once the execution is completed, + * and a callback that can be used by the client to query the duration and runtime error + * status. If the task has finished before the call returns, an empty handle may be returned + * for syncFence. The execution must wait for all the sync fences (if any) in waitFor + * to be signaled before starting the actual execution. + * + * When the asynchronous task has finished its execution, it must + * immediately signal the syncFence returned from the executeFenced call. After + * the syncFence is signaled, the task must not modify the content of + * any data object referenced by 'request' (described by the + * {@link @1.0::DataLocation} of a {@link @1.0::RequestArgument}). + * + * executeFenced can be called with an optional deadline and an optional duration. + * If the execution is not able to be completed before the provided deadline or + * within the timeout duration (measured from when all sync fences in waitFor are + * signaled), whichever comes earlier, the execution must be aborted, and either + * {@link ErrorStatus::MISSED_DEADLINE_TRANSIENT} or {@link + * ErrorStatus::MISSED_DEADLINE_PERSISTENT} must be returned. The error due + * to an abort must be sent the same way as other errors, described above. + * If the service reports that it does not support execution deadlines via + * IDevice::supportsDeadlines, and executeFenced is called with a + * deadline or duration, then the argument is invalid, and + * {@link ErrorStatus::INVALID_ARGUMENT} must be returned. + * + * If any of the sync fences in waitFor changes to error status after the executeFenced + * call succeeds, or the execution is aborted because it cannot finish before the deadline + * has been reached or the duration has elapsed, the driver must immediately set the returned + * syncFence to error status. + * + * Any number of calls to the executeFenced, execute* and executeSynchronously* + * functions, in any combination, may be made concurrently, even on the same + * IPreparedModel object. + * + * @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 measure Specifies whether or not to measure duration of the execution. + * @param deadline The time by which the execution must complete. If the + * execution cannot be finished by the deadline, the + * execution must be aborted. + * @param duration The length of time within which the execution must + * complete after all sync fences in waitFor are signaled. If the + * execution cannot be finished within the duration, the execution + * must be aborted. + * @return status Error status of the call, must be: + * - NONE if task is successfully launched + * - 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 deadline for executing a model + * cannot be met + * - RESOURCE_EXHAUSTED_* if the task was aborted by the + * driver + * @return syncFence The sync fence that will be signaled when the task is completed. + * The sync fence will be set to error if a critical error, + * e.g. hardware failure or kernel panic, occurs when doing execution. + * @return callback The IFencedExecutionCallback can be used to query information like duration + * and error status when the execution is completed. + */ + executeFenced(Request request, vec<handle> waitFor, MeasureTiming measure, + OptionalTimePoint deadline, OptionalTimeoutDuration duration) + generates (ErrorStatus status, handle syncFence, IFencedExecutionCallback callback); }; diff --git a/neuralnetworks/1.3/types.hal b/neuralnetworks/1.3/types.hal index b330b50084..ed577e4d9d 100644 --- a/neuralnetworks/1.3/types.hal +++ b/neuralnetworks/1.3/types.hal @@ -1415,6 +1415,7 @@ enum OperationType : int32_t { * * {@link OperandType::TENSOR_FLOAT32} * * {@link OperandType::TENSOR_QUANT8_ASYMM} * * {@link OperandType::TENSOR_QUANT8_ASYMM_SIGNED} (since HAL version 1.3) + * * {@link OperandType::TENSOR_INT32} (since HAL version 1.3) * * Supported tensor rank: up to 4 * @@ -1425,6 +1426,8 @@ enum OperationType : int32_t { * * 2: An {@link OperandType::INT32} scalar, and has to be one of the * {@link FusedActivationFunc} values. Specifies the activation to * invoke on the result. + * For a {@link OperandType::TENSOR_INT32} tensor, + * the {@link FusedActivationFunc} must be "NONE". * * Outputs: * * 0: The product, a tensor of the same {@link OperandType} as input0. @@ -1905,6 +1908,11 @@ enum OperationType : int32_t { * dimensions. The output is the result of dividing the first input tensor * by the second, optionally modified by an activation function. * + * For inputs of {@link OperandType::TENSOR_INT32}, performs + * "floor division" ("//" in Python). For example, + * 5 // 2 = 2 + * -5 // 2 = -3 + * * Two dimensions are compatible when: * 1. they are equal, or * 2. one of them is 1 @@ -1925,6 +1933,7 @@ enum OperationType : int32_t { * Supported tensor {@link OperandType}: * * {@link OperandType::TENSOR_FLOAT16} (since HAL version 1.2) * * {@link OperandType::TENSOR_FLOAT32} + * * {@link OperandType::TENSOR_INT32} (since HAL version 1.3) * * Supported tensor rank: up to 4 * @@ -1935,6 +1944,8 @@ enum OperationType : int32_t { * * 2: An {@link OperandType::INT32} scalar, and has to be one of the * {@link FusedActivationFunc} values. Specifies the activation to * invoke on the result. + * For a {@link OperandType::TENSOR_INT32} tensor, + * the {@link FusedActivationFunc} must be "NONE". * * Outputs: * * 0: A tensor of the same {@link OperandType} as input0. @@ -2186,6 +2197,7 @@ enum OperationType : int32_t { * * {@link OperandType::TENSOR_FLOAT32} * * {@link OperandType::TENSOR_QUANT8_ASYMM} (since HAL version 1.2) * * {@link OperandType::TENSOR_QUANT8_ASYMM_SIGNED} (since HAL version 1.3) + * * {@link OperandType::TENSOR_INT32} (since HAL version 1.3) * * Supported tensor rank: up to 4 * @@ -2196,6 +2208,8 @@ enum OperationType : int32_t { * * 2: An {@link OperandType::INT32} scalar, and has to be one of the * {@link FusedActivationFunc} values. Specifies the activation to * invoke on the result. + * For a {@link OperandType::TENSOR_INT32} tensor, + * the {@link FusedActivationFunc} must be "NONE". * * Outputs: * * 0: A tensor of the same {@link OperandType} as input0. @@ -2242,6 +2256,7 @@ enum OperationType : int32_t { * Supported tensor {@link OperandType}: * * {@link OperandType::TENSOR_FLOAT16} * * {@link OperandType::TENSOR_FLOAT32} + * * {@link OperandType::TENSOR_INT32} (since HAL version 1.3) * * Supported tensor rank: from 1. * @@ -4972,6 +4987,106 @@ enum OperationType : int32_t { WHILE = 97, /** + * Computes exponential linear activation on the input tensor element-wise. + * + * The output is calculated using the following formula: + * + * ELU(x) = max(0, x) + min(0, alpha * (exp(x) - 1)) + * + * Supported tensor {@link OperandType}: + * * {@link OperandType::TENSOR_FLOAT16} + * * {@link OperandType::TENSOR_FLOAT32} + * + * Inputs: + * * 0: A tensor, specifying the input. May be zero-sized. + * * 1: A scalar, specifying the alpha parameter. + * For input tensor of {@link OperandType::TENSOR_FLOAT16}, + * the alpha value must be of {@link OperandType::FLOAT16}. + * For input tensor of {@link OperandType::TENSOR_FLOAT32}, + * the alpha value must be of {@link OperandType::FLOAT32}. + * + * Outputs: + * * 0: The output tensor of same shape and type as input0. + */ + ELU = 98, + + /** + * Computes hard-swish activation on the input tensor element-wise. + * + * Hard swish activation is introduced in + * https://arxiv.org/pdf/1905.02244.pdf + * + * The output is calculated using the following formula: + * + * h-swish(x) = x * max(0, min(6, (x + 3))) / 6 + + * Supported tensor {@link OperandType}: + * * {@link OperandType::TENSOR_FLOAT16} + * * {@link OperandType::TENSOR_FLOAT32} + * * {@link OperandType::TENSOR_QUANT8_ASYMM} + * * {@link OperandType::TENSOR_QUANT8_ASYMM_SIGNED} + * + * Inputs: + * * 0: A tensor, specifying the input. May be zero-sized. + * + * Outputs: + * * 0: The output tensor of same shape and type as input0. + * Scale and zero point of this tensor may be different from the input + * tensor's parameters. + */ + HARD_SWISH = 99, + + /** + * Creates a tensor filled with a scalar value. + * + * Supported output tensor {@link OperandType}: + * * {@link OperandType::TENSOR_FLOAT16} + * * {@link OperandType::TENSOR_FLOAT32} + * * {@link OperandType::TENSOR_INT32} + * + * Inputs: + * * 0: A 1-D tensor, specifying the desired output tensor shape. + * * 1: A scalar, specifying the value to fill the output tensors with. + * For output tensor of {@link OperandType::TENSOR_FLOAT16}, + * the scalar must be of {@link OperandType::FLOAT16}. + * For output tensor of {@link OperandType::TENSOR_FLOAT32}, + * the scalar must be of {@link OperandType::FLOAT32}. + * For output tensor of {@link OperandType::TENSOR_INT32}, + * the scalar must be of {@link OperandType::INT32}. + * + * Outputs: + * * 0: The output tensor. + */ + FILL = 100, + + /** + * Returns the rank of a tensor. + * + * The rank of a tensor is the number of dimensions in it. Also known as + * "order", "degree", "ndims". + * + * Supported tensor {@link OperandType}: + * * {@link OperandType::TENSOR_FLOAT16} + * * {@link OperandType::TENSOR_FLOAT32} + * * {@link OperandType::TENSOR_INT32} + * * {@link OperandType::TENSOR_QUANT8_ASYMM} + * * {@link OperandType::TENSOR_QUANT16_SYMM} + * * {@link OperandType::TENSOR_BOOL8} + * * {@link OperandType::TENSOR_QUANT8_SYMM_PER_CHANNEL} + * * {@link OperandType::TENSOR_QUANT16_ASYMM} + * * {@link OperandType::TENSOR_QUANT8_SYMM} + * * {@link OperandType::TENSOR_QUANT8_ASYMM_SIGNED} + * + * Inputs: + * * 0: The input tensor. + * + * Outputs: + * * 0: A scalar of {@link OperandType::INT32}, specifying the rank + * of the input tensor. + */ + RANK = 101, + + /** * DEPRECATED. Since NNAPI 1.2, extensions are the preferred alternative to * OEM operation and data types. * @@ -4993,7 +5108,7 @@ enum OperationType : int32_t { enum OperationTypeRange : uint32_t { BASE_MIN = 0, FUNDAMENTAL_MIN = 0, - FUNDAMENTAL_MAX = 97, + FUNDAMENTAL_MAX = 101, OEM_MIN = 10000, OEM_MAX = 10000, BASE_MAX = 0xFFFF, @@ -5462,6 +5577,19 @@ safe_union OptionalTimePoint { }; /** + * Optional timeout duration measured in nanoseconds. + */ +safe_union OptionalTimeoutDuration { + /** No time point provided. */ + Monostate none; + + /** + * Timeout duration measured in nanoseconds. + */ + uint64_t nanoseconds; +}; + +/** * Return status of a function. */ enum ErrorStatus : @1.0::ErrorStatus { diff --git a/neuralnetworks/1.3/types.t b/neuralnetworks/1.3/types.t index a973923d8d..d4351ec8d7 100644 --- a/neuralnetworks/1.3/types.t +++ b/neuralnetworks/1.3/types.t @@ -553,6 +553,19 @@ safe_union OptionalTimePoint { }; /** + * Optional timeout duration measured in nanoseconds. + */ +safe_union OptionalTimeoutDuration { + /** No time point provided. */ + Monostate none; + + /** + * Timeout duration measured in nanoseconds. + */ + uint64_t nanoseconds; +}; + +/** * Return status of a function. */ enum ErrorStatus : @1.0::ErrorStatus { diff --git a/neuralnetworks/1.3/vts/functional/Android.bp b/neuralnetworks/1.3/vts/functional/Android.bp index ce2d3a917a..8e7e9b9d62 100644 --- a/neuralnetworks/1.3/vts/functional/Android.bp +++ b/neuralnetworks/1.3/vts/functional/Android.bp @@ -65,6 +65,7 @@ cc_test { "libhidlmemory", "libneuralnetworks_generated_test_harness", "libneuralnetworks_utils", + "libsync", ], whole_static_libs: [ "neuralnetworks_generated_V1_0_example", diff --git a/neuralnetworks/1.3/vts/functional/GeneratedTestHarness.cpp b/neuralnetworks/1.3/vts/functional/GeneratedTestHarness.cpp index a2c0c4efa0..8ea0b7eb47 100644 --- a/neuralnetworks/1.3/vts/functional/GeneratedTestHarness.cpp +++ b/neuralnetworks/1.3/vts/functional/GeneratedTestHarness.cpp @@ -29,11 +29,13 @@ #include <android/hardware/neuralnetworks/1.2/IPreparedModelCallback.h> #include <android/hardware/neuralnetworks/1.2/types.h> #include <android/hardware/neuralnetworks/1.3/IDevice.h> +#include <android/hardware/neuralnetworks/1.3/IFencedExecutionCallback.h> #include <android/hardware/neuralnetworks/1.3/IPreparedModel.h> #include <android/hardware/neuralnetworks/1.3/IPreparedModelCallback.h> #include <android/hardware/neuralnetworks/1.3/types.h> #include <android/hidl/allocator/1.0/IAllocator.h> #include <android/hidl/memory/1.0/IMemory.h> +#include <android/sync.h> #include <gtest/gtest.h> #include <hidlmemory/mapping.h> @@ -70,7 +72,7 @@ using HidlToken = hidl_array<uint8_t, static_cast<uint32_t>(Constant::BYTE_SIZE_ namespace { -enum class Executor { ASYNC, SYNC, BURST }; +enum class Executor { ASYNC, SYNC, BURST, FENCED }; enum class OutputType { FULLY_SPECIFIED, UNSPECIFIED, INSUFFICIENT }; @@ -78,6 +80,13 @@ enum class MemoryType { SHARED, DEVICE }; enum class IOType { INPUT, OUTPUT }; +static void waitForSyncFence(int syncFd) { + constexpr int kInfiniteTimeout = -1; + ASSERT_GT(syncFd, 0); + int r = sync_wait(syncFd, kInfiniteTimeout); + ASSERT_GE(r, 0); +} + struct TestConfig { Executor executor; MeasureTiming measureTiming; @@ -562,9 +571,44 @@ void EvaluatePreparedModel(const sp<IDevice>& device, const sp<IPreparedModel>& break; } + case Executor::FENCED: { + SCOPED_TRACE("fenced"); + ErrorStatus result; + hidl_handle syncFenceHandle; + sp<IFencedExecutionCallback> fencedCallback; + Return<void> ret = preparedModel->executeFenced( + request, {}, testConfig.measureTiming, {}, {}, + [&result, &syncFenceHandle, &fencedCallback]( + ErrorStatus error, const hidl_handle& handle, + const sp<IFencedExecutionCallback>& callback) { + result = error; + syncFenceHandle = handle; + fencedCallback = callback; + }); + ASSERT_TRUE(ret.isOk()); + if (result != ErrorStatus::NONE) { + ASSERT_EQ(syncFenceHandle.getNativeHandle(), nullptr); + ASSERT_EQ(fencedCallback, nullptr); + executionStatus = ErrorStatus::GENERAL_FAILURE; + } else if (syncFenceHandle.getNativeHandle()) { + waitForSyncFence(syncFenceHandle.getNativeHandle()->data[0]); + } + if (result == ErrorStatus::NONE) { + ASSERT_NE(fencedCallback, nullptr); + Return<void> ret = fencedCallback->getExecutionInfo( + [&executionStatus, &timing](ErrorStatus error, Timing t, Timing) { + executionStatus = error; + timing = t; + }); + ASSERT_TRUE(ret.isOk()); + } + break; + } } - if (testConfig.outputType != OutputType::FULLY_SPECIFIED && + // The driver is allowed to reject executeFenced, and if they do, we should skip. + if ((testConfig.outputType != OutputType::FULLY_SPECIFIED || + testConfig.executor == Executor::FENCED) && executionStatus == ErrorStatus::GENERAL_FAILURE) { if (skipped != nullptr) { *skipped = true; @@ -648,6 +692,11 @@ void EvaluatePreparedModel(const sp<IDevice>& device, const sp<IPreparedModel>& executorList = {Executor::ASYNC, Executor::SYNC}; memoryType = MemoryType::DEVICE; } break; + case TestKind::FENCED_COMPUTE: { + outputTypesList = {OutputType::FULLY_SPECIFIED}; + measureTimingList = {MeasureTiming::NO, MeasureTiming::YES}; + executorList = {Executor::FENCED}; + } break; case TestKind::QUANTIZATION_COUPLING: { LOG(FATAL) << "Wrong TestKind for EvaluatePreparedModel"; return; @@ -671,7 +720,8 @@ void EvaluatePreparedCoupledModels(const sp<IDevice>& device, const TestModel& coupledModel) { const std::vector<OutputType> outputTypesList = {OutputType::FULLY_SPECIFIED}; const std::vector<MeasureTiming> measureTimingList = {MeasureTiming::NO, MeasureTiming::YES}; - const std::vector<Executor> executorList = {Executor::ASYNC, Executor::SYNC, Executor::BURST}; + const std::vector<Executor> executorList = {Executor::ASYNC, Executor::SYNC, Executor::BURST, + Executor::FENCED}; for (const OutputType outputType : outputTypesList) { for (const MeasureTiming measureTiming : measureTimingList) { @@ -708,7 +758,8 @@ void Execute(const sp<IDevice>& device, const TestModel& testModel, TestKind tes switch (testKind) { case TestKind::GENERAL: case TestKind::DYNAMIC_SHAPE: - case TestKind::MEMORY_DOMAIN: { + case TestKind::MEMORY_DOMAIN: + case TestKind::FENCED_COMPUTE: { createPreparedModel(device, model, &preparedModel); if (preparedModel == nullptr) return; EvaluatePreparedModel(device, preparedModel, testModel, testKind); @@ -771,6 +822,9 @@ class DynamicOutputShapeTest : public GeneratedTest {}; // Tag for the memory domain tests class MemoryDomainTest : public GeneratedTest {}; +// Tag for the fenced compute tests +class FencedComputeTest : public GeneratedTest {}; + // Tag for the dynamic output shape tests class QuantizationCouplingTest : public GeneratedTest {}; @@ -786,6 +840,10 @@ TEST_P(MemoryDomainTest, Test) { Execute(kDevice, kTestModel, /*testKind=*/TestKind::MEMORY_DOMAIN); } +TEST_P(FencedComputeTest, Test) { + Execute(kDevice, kTestModel, /*testKind=*/TestKind::FENCED_COMPUTE); +} + TEST_P(QuantizationCouplingTest, Test) { Execute(kDevice, kTestModel, /*testKind=*/TestKind::QUANTIZATION_COUPLING); } @@ -793,12 +851,16 @@ TEST_P(QuantizationCouplingTest, Test) { INSTANTIATE_GENERATED_TEST(GeneratedTest, [](const TestModel& testModel) { return !testModel.expectFailure; }); -INSTANTIATE_GENERATED_TEST(DynamicOutputShapeTest, - [](const TestModel& testModel) { return !testModel.expectFailure; }); +INSTANTIATE_GENERATED_TEST(DynamicOutputShapeTest, [](const TestModel& testModel) { + return !testModel.expectFailure && !testModel.hasScalarOutputs(); +}); INSTANTIATE_GENERATED_TEST(MemoryDomainTest, [](const TestModel& testModel) { return !testModel.expectFailure; }); +INSTANTIATE_GENERATED_TEST(FencedComputeTest, + [](const TestModel& testModel) { return !testModel.expectFailure; }); + INSTANTIATE_GENERATED_TEST(QuantizationCouplingTest, [](const TestModel& testModel) { return testModel.hasQuant8CoupledOperands() && testModel.operations.size() == 1; }); diff --git a/neuralnetworks/1.3/vts/functional/GeneratedTestHarness.h b/neuralnetworks/1.3/vts/functional/GeneratedTestHarness.h index fe695b471d..e597fac7cf 100644 --- a/neuralnetworks/1.3/vts/functional/GeneratedTestHarness.h +++ b/neuralnetworks/1.3/vts/functional/GeneratedTestHarness.h @@ -65,6 +65,8 @@ enum class TestKind { DYNAMIC_SHAPE, // Same as GENERAL but use device memories for inputs and outputs MEMORY_DOMAIN, + // Same as GENERAL but use executeFenced for exeuction + FENCED_COMPUTE, // Tests if quantized model with TENSOR_QUANT8_ASYMM produces the same result // (OK/SKIPPED/FAILED) as the model with all such tensors converted to // TENSOR_QUANT8_ASYMM_SIGNED. diff --git a/neuralnetworks/1.3/vts/functional/ValidateModel.cpp b/neuralnetworks/1.3/vts/functional/ValidateModel.cpp index a21142880e..1245432307 100644 --- a/neuralnetworks/1.3/vts/functional/ValidateModel.cpp +++ b/neuralnetworks/1.3/vts/functional/ValidateModel.cpp @@ -337,6 +337,7 @@ static bool mutateOperationOperandTypeSkip(size_t operand, OperandType type, con // - TRANSPOSE_CONV_2D filter type (arg 1) can be QUANT8_ASYMM or QUANT8_SYMM_PER_CHANNEL // - AXIS_ALIGNED_BBOX_TRANSFORM bounding boxes (arg 1) can be of // TENSOR_QUANT8_ASYMM or TENSOR_QUANT8_ASYMM_SIGNED. + // - RANK's input can have any TENSOR_* type. switch (operation.type) { case OperationType::LSH_PROJECTION: { if (operand == operation.inputs[1]) { @@ -399,6 +400,20 @@ static bool mutateOperationOperandTypeSkip(size_t operand, OperandType type, con return true; } } break; + case OperationType::RANK: { + if (operand == operation.inputs[0] && + (type == OperandType::TENSOR_FLOAT16 || type == OperandType::TENSOR_FLOAT32 || + type == OperandType::TENSOR_INT32 || + type == OperandType::TENSOR_QUANT8_ASYMM || + type == OperandType::TENSOR_QUANT16_SYMM || + type == OperandType::TENSOR_BOOL8 || + type == OperandType::TENSOR_QUANT8_SYMM_PER_CHANNEL || + type == OperandType::TENSOR_QUANT16_ASYMM || + type == OperandType::TENSOR_QUANT8_SYMM || + type == OperandType::TENSOR_QUANT8_ASYMM_SIGNED)) { + return true; + } + } break; default: break; } diff --git a/neuralnetworks/1.3/vts/functional/ValidateRequest.cpp b/neuralnetworks/1.3/vts/functional/ValidateRequest.cpp index be4112ac2d..2fd9b647f1 100644 --- a/neuralnetworks/1.3/vts/functional/ValidateRequest.cpp +++ b/neuralnetworks/1.3/vts/functional/ValidateRequest.cpp @@ -16,7 +16,9 @@ #define LOG_TAG "neuralnetworks_hidl_hal_test" +#include <android/hardware/neuralnetworks/1.3/IFencedExecutionCallback.h> #include <chrono> + #include "1.0/Utils.h" #include "1.3/Callbacks.h" #include "ExecutionBurstController.h" @@ -136,6 +138,22 @@ static void validate(const sp<IPreparedModel>& preparedModel, const std::string& burst->freeMemory(keys.front()); } } + + // dispatch + { + SCOPED_TRACE(message + " [executeFenced]"); + Return<void> ret = preparedModel->executeFenced( + request, {}, MeasureTiming::NO, {}, {}, + [](ErrorStatus error, const hidl_handle& handle, + const sp<IFencedExecutionCallback>& callback) { + if (error != ErrorStatus::DEVICE_UNAVAILABLE) { + ASSERT_EQ(ErrorStatus::INVALID_ARGUMENT, error); + } + ASSERT_EQ(handle.getNativeHandle(), nullptr); + ASSERT_EQ(callback, nullptr); + }); + ASSERT_TRUE(ret.isOk()); + } } ///////////////////////// REMOVE INPUT //////////////////////////////////// diff --git a/neuralnetworks/1.3/vts/functional/VtsHalNeuralnetworks.cpp b/neuralnetworks/1.3/vts/functional/VtsHalNeuralnetworks.cpp index 93c8f13c17..896ace65b9 100644 --- a/neuralnetworks/1.3/vts/functional/VtsHalNeuralnetworks.cpp +++ b/neuralnetworks/1.3/vts/functional/VtsHalNeuralnetworks.cpp @@ -133,6 +133,23 @@ void validateRequestFailure(const sp<IPreparedModel>& preparedModel, const Reque // Forward declaration from ValidateBurst.cpp void validateBurst(const sp<IPreparedModel>& preparedModel, const V1_0::Request& request); +// Validate sync_fence handles for dispatch with valid input +void validateExecuteFenced(const sp<IPreparedModel>& preparedModel, const Request& request) { + SCOPED_TRACE("Expecting request to fail [executeFenced]"); + Return<void> ret_null = preparedModel->executeFenced( + request, {hidl_handle(nullptr)}, V1_2::MeasureTiming::NO, {}, {}, + [](ErrorStatus error, const hidl_handle& handle, + const sp<IFencedExecutionCallback>& callback) { + // TODO: fix this once sample driver impl is merged. + if (error != ErrorStatus::DEVICE_UNAVAILABLE) { + ASSERT_EQ(ErrorStatus::INVALID_ARGUMENT, error); + } + ASSERT_EQ(handle.getNativeHandle(), nullptr); + ASSERT_EQ(callback, nullptr); + }); + ASSERT_TRUE(ret_null.isOk()); +} + void validateEverything(const sp<IDevice>& device, const Model& model, const Request& request, std::pair<bool, bool> supportsDeadlines) { const auto [prepareModelDeadlineSupported, executionDeadlineSupported] = supportsDeadlines; @@ -144,6 +161,7 @@ void validateEverything(const sp<IDevice>& device, const Model& model, const Req if (preparedModel == nullptr) return; validateRequest(preparedModel, request, executionDeadlineSupported); + validateExecuteFenced(preparedModel, request); // TODO(butlermichael): Check if we need to test burst in V1_3 if the interface remains V1_2. ASSERT_TRUE(nn::compliantWithV1_0(request)); diff --git a/radio/1.1/vts/functional/radio_hidl_hal_api.cpp b/radio/1.1/vts/functional/radio_hidl_hal_api.cpp index 75abbbf107..02dcbab20d 100644 --- a/radio/1.1/vts/functional/radio_hidl_hal_api.cpp +++ b/radio/1.1/vts/functional/radio_hidl_hal_api.cpp @@ -24,6 +24,9 @@ TEST_P(RadioHidlTest_v1_1, setSimCardPower_1_1) { /* Record the sim card state for the testing environment */ CardState cardStateForTest = cardStatus.cardState; +#if 0 + /* This test has to be removed for Japan Model. + * After "setSimCardPower power down", Japan model can not "setSimCardPower power up" */ /* Test setSimCardPower power down */ serial = GetRandomSerialNumber(); radio_v1_1->setSimCardPower_1_1(serial, CardPowerState::POWER_DOWN); @@ -46,6 +49,7 @@ TEST_P(RadioHidlTest_v1_1, setSimCardPower_1_1) { } EXPECT_EQ(CardState::ABSENT, cardStatus.cardState); } +#endif /* Test setSimCardPower power up */ serial = GetRandomSerialNumber(); diff --git a/radio/1.5/IRadio.hal b/radio/1.5/IRadio.hal index ee4438d08d..2ec92e54d1 100644 --- a/radio/1.5/IRadio.hal +++ b/radio/1.5/IRadio.hal @@ -16,15 +16,15 @@ package android.hardware.radio@1.5; +import @1.0::CdmaSmsMessage; import @1.2::DataRequestReason; import @1.4::IRadio; -import @1.4::DataProfileInfo; import @1.5::AccessNetwork; -import @1.5::BarringInfo; import @1.5::DataProfileInfo; import @1.5::IndicationFilter; import @1.5::LinkAddress; import @1.5::NetworkScanRequest; +import @1.5::PersoSubstate; import @1.5::RadioAccessNetworks; import @1.5::RadioAccessSpecifier; import @1.5::SignalThresholdInfo; @@ -116,13 +116,13 @@ interface IRadio extends @1.4::IRadio { vec<RadioAccessSpecifier> specifiers); /** - * Starts a network scan + * Starts a network scan. * * @param serial Serial number of request. * @param request Defines the radio networks/bands/channels which need to be scanned. * - * Same API as @1.4::IRadio.startNetworkScan_1_4, except using - * 1.5 version of NetworkScanRequest + * Same API as @1.4::IRadio.startNetworkScan_1_4, except using the + * 1.5 NetworkScanRequest as the input param. */ oneway startNetworkScan_1_5(int32_t serial, NetworkScanRequest request); @@ -164,14 +164,14 @@ interface IRadio extends @1.4::IRadio { * Response function is IRadioResponse.setupDataCallResponse_1_5() * * Note this API is the same as the 1.4 version except using the - * 1.5 AccessNetwork, DataProfileInto, and link addresses as the input param. + * 1.5 AccessNetwork, DataProfileInto, and LinkAddress as the input param. */ oneway setupDataCall_1_5(int32_t serial, AccessNetwork accessNetwork, DataProfileInfo dataProfileInfo, bool roamingAllowed, DataRequestReason reason, vec<LinkAddress> addresses, vec<string> dnses); /** - * Set an apn to initial attach network + * Set an APN to initial attach network. * * @param serial Serial number of request. * @param dataProfileInfo data profile containing APN settings @@ -187,7 +187,7 @@ interface IRadio extends @1.4::IRadio { * Send data profiles of the current carrier to the modem. * * @param serial Serial number of request. - * @param profiles Array of DataProfile to set. + * @param profiles Array of DataProfileInfo to set. * * Response callback is IRadioResponse.setDataProfileResponse_1_5() * @@ -228,7 +228,7 @@ interface IRadio extends @1.4::IRadio { * * Prevents the reporting of specified unsolicited indications from the radio. This is used * for power saving in instances when those indications are not needed. If unset, defaults to - * @1.2::IndicationFilter:ALL. + * @1.5::IndicationFilter:ALL. * * @param serial Serial number of request. * @param indicationFilter 32-bit bitmap of IndicationFilter. Bits set to 1 indicate the @@ -248,7 +248,7 @@ interface IRadio extends @1.4::IRadio { oneway getBarringInfo(int32_t serial); /** - * Request current voice registration state + * Request current voice registration state. * * @param serial Serial number of request. * @@ -257,7 +257,7 @@ interface IRadio extends @1.4::IRadio { oneway getVoiceRegistrationState_1_5(int32_t serial); /** - * Request current data registration state + * Request current data registration state. * * @param serial Serial number of request. * @@ -282,4 +282,26 @@ interface IRadio extends @1.4::IRadio { */ oneway setNetworkSelectionModeManual_1_5(int32_t serial, string operatorNumeric, RadioAccessNetworks ran); + + /** + * Send an SMS message. Identical to sendCdmaSms, + * except that more messages are expected to be sent soon. + * + * @param serial Serial number of request. + * @param sms Cdma Sms to be sent described by CdmaSmsMessage in types.hal + * + * Response callback is IRadioResponse.sendCdmaSMSExpectMoreResponse() + */ + oneway sendCdmaSmsExpectMore(int32_t serial, CdmaSmsMessage sms); + + /** + * Requests that deactivates one category of the device personalization. + * + * @param serial Serial number of request. + * @param persoType SIM personalization type. + * @param controlKey depersonalization code corresponding to persoType + * + * Response function is IRadioResponse.supplySimDepersonalizationResponse() + */ + oneway supplySimDepersonalization(int32_t serial, PersoSubstate persoType, string controlKey); }; diff --git a/radio/1.5/IRadioResponse.hal b/radio/1.5/IRadioResponse.hal index e66e00b1ea..aa8b526ffa 100644 --- a/radio/1.5/IRadioResponse.hal +++ b/radio/1.5/IRadioResponse.hal @@ -17,11 +17,13 @@ package android.hardware.radio@1.5; import @1.0::RadioResponseInfo; +import @1.0::SendSmsResult; import @1.4::IRadioResponse; import @1.5::BarringInfo; import @1.5::CellInfo; -import @1.5::SetupDataCallResult; +import @1.5::PersoSubstate; import @1.5::RegStateResult; +import @1.5::SetupDataCallResult; /** * Interface declaring response functions to solicited radio requests. @@ -213,7 +215,6 @@ interface IRadioResponse extends @1.4::IRadioResponse { */ oneway getCellInfoListResponse_1_5(RadioResponseInfo info, vec<CellInfo> cellInfo); - /** * @param info Response info struct containing response type, serial no. and error * @@ -236,4 +237,57 @@ interface IRadioResponse extends @1.4::IRadioResponse { * no retries needed, such as illegal SIM or ME. */ oneway setNetworkSelectionModeManualResponse_1_5(RadioResponseInfo info); + + /** + * @param info Response info struct containing response type, serial no. and error + * @param sms Response to sms sent as defined by SendSmsResult in types.hal + * + * Valid errors returned: + * RadioError:NONE + * RadioError:RADIO_NOT_AVAILABLE + * RadioError:SMS_SEND_FAIL_RETRY + * RadioError:NETWORK_REJECT + * RadioError:INVALID_STATE + * RadioError:INVALID_ARGUMENTS + * RadioError:NO_MEMORY + * RadioError:REQUEST_RATE_LIMITED + * RadioError:INVALID_SMS_FORMAT + * RadioError:SYSTEM_ERR + * RadioError:FDN_CHECK_FAILURE + * RadioError:ENCODING_ERR + * RadioError:INVALID_SMSC_ADDRESS + * RadioError:MODEM_ERR + * RadioError:NETWORK_ERR + * RadioError:INTERNAL_ERR + * RadioError:REQUEST_NOT_SUPPORTED + * RadioError:INVALID_MODEM_STATE + * RadioError:NETWORK_NOT_READY + * RadioError:OPERATION_NOT_ALLOWED + * RadioError:NO_RESOURCES + * RadioError:CANCELLED + * RadioError:SIM_ABSENT + */ + oneway sendCdmaSmsExpectMoreResponse(RadioResponseInfo info, SendSmsResult sms); + + /** + * @param info Response info struct contatining response type, serial no. and error + * @param persoType SIM Personalisation type + * @param remainingRetries postiive values indicates number of retries remaining, + * must be equal to -1 if number of retries is infinite. + * + * Valid errors returned: + * RadioError:NONE + * RadioError:RADIO_NOT_AVAILABLE + * RadioError:PASSWORD_INCORRECT (code is invalid) + * RadioError:NO_MEMORY + * RadioError:INVALID_SIM_STATE + * RadioError:INTERNAL_ERR + * RadioError:SYSTEM_ERR + * RadioError:MODEM_ERR + * RadioError:INVALID_ARGUMENTS + * RadioError:NO_RESOURCES + * RadioError:REQUEST_NOT_SUPPORTED + */ + oneway supplySimDepersonalizationResponse(RadioResponseInfo info, + PersoSubstate persoType, int32_t remainingRetries); }; diff --git a/radio/1.5/types.hal b/radio/1.5/types.hal index 5482acaf27..a0868337dd 100644 --- a/radio/1.5/types.hal +++ b/radio/1.5/types.hal @@ -19,18 +19,15 @@ package android.hardware.radio@1.5; import @1.0::ApnAuthType; import @1.0::DataProfileId; import @1.0::DataProfileInfoType; -import @1.0::CdmaSignalStrength; -import @1.0::EvdoSignalStrength; import @1.0::GsmSignalStrength; import @1.0::LteSignalStrength; -import @1.0::RadioAccessFamily; +import @1.0::PersoSubstate; import @1.0::RadioError; import @1.0::RegState; import @1.0::TimeStampType; import @1.1::EutranBands; import @1.1::GeranBands; import @1.1::RadioAccessNetworks; -import @1.1::RadioAccessSpecifier; import @1.1::ScanStatus; import @1.1::ScanType; import @1.1::UtranBands; @@ -42,7 +39,6 @@ import @1.2::CellIdentityTdscdma; import @1.2::CellIdentityLte; import @1.2::CellInfoCdma; import @1.2::IndicationFilter; -import @1.2::NetworkScanRequest; import @1.2::TdscdmaSignalStrength; import @1.2::WcdmaSignalStrength; import @1.4::AccessNetwork; @@ -50,11 +46,11 @@ import @1.4::ApnTypes; import @1.4::CellIdentityNr; import @1.4::DataCallFailCause; import @1.4::DataConnActiveStatus; -import @1.4::DataProfileInfo; import @1.4::LteVopsInfo; import @1.4::NrIndicators; import @1.4::NrSignalStrength; import @1.4::PdpProtocolType; +import @1.4::RadioAccessFamily; import @1.4::RadioTechnology; import android.hidl.safe_union@1.0::Monostate; @@ -130,7 +126,7 @@ struct SignalThresholdInfo { /** Signal Measurement Type */ SignalMeasurementType signalMeasurement; - /** A hysteresis time in milliseconds to prevent flapping. A value of 0 disables hysteresis */ + /** A hysteresis time in milliseconds to prevent flapping. A value of 0 disables hysteresis. */ int32_t hysteresisMs; /** @@ -174,7 +170,7 @@ enum RadioAccessNetworks : @1.1::RadioAccessNetworks { }; /** - * Overwritten from @1.1::RadioAccessSpecifier to add NGRAN and NgranBands + * Overwritten from @1.1::RadioAccessSpecifier to add NGRAN and NgranBands. */ struct RadioAccessSpecifier { /** @@ -263,8 +259,7 @@ enum UtranBands : @1.1::UtranBands { }; /** - * Overwritten from @1.2::NetworkScanRequest to update - * RadioAccessSpecifier to 1.5 version + * Overwritten from @1.2::NetworkScanRequest to update RadioAccessSpecifier to 1.5 version. */ struct NetworkScanRequest { ScanType type; @@ -327,11 +322,10 @@ enum ApnTypes : @1.4::ApnTypes { }; /** - * Extended from @1.4::DataProfileInfo to update ApnTypes to 1.5 version and replace mtu with + * Overwritten from @1.4::DataProfileInfo to update ApnTypes to 1.5 version and replace mtu with * mtuV4 and mtuV6. In the future, this must be extended instead of overwritten. */ struct DataProfileInfo { - /** ID of the data profile. */ DataProfileId profileId; @@ -427,21 +421,25 @@ struct LinkAddress { bitfield<AddressProperty> properties; /** - * The UTC time that this link address will be deprecated. 0 indicates this information is not - * available. + * The time, as reported by SystemClock.elapsedRealtime(), when this link address will be or + * was deprecated. -1 indicates this information is not available. At the time existing + * connections can still use this address until it expires, but new connections should use the + * new address. LONG_MAX(0x7FFFFFFFFFFFFFFF) indicates this link address will never be + * deprecated. */ - uint64_t deprecatedTime; + uint64_t deprecationTime; /** - * The UTC time that this link address will expire and no longer valid. 0 indicates this - * information is not available. + * The time, as reported by SystemClock.elapsedRealtime(), when this link address will expire + * and be removed from the interface. -1 indicates this information is not available. + * LONG_MAX(0x7FFFFFFFFFFFFFFF) indicates this link address will never expire. */ - uint64_t expiredTime; + uint64_t expirationTime; }; /** - * Overwritten from @1.4::SetupDataCallResult in order to update the addresses to 1.5 - * version. In 1.5 the type of addresses changes to vector of LinkAddress, and mtu is replaced by + * Overwritten from @1.4::SetupDataCallResult in order to update the addresses to 1.5 version. + * In 1.5 the type of addresses changes to vector of LinkAddress, and mtu is replaced by * mtuV4 and mtuV6. */ struct SetupDataCallResult { @@ -600,6 +598,9 @@ struct CellIdentityLte { /** Information about any closed subscriber group ID for this cell */ OptionalCsgInfo optionalCsgInfo; + + /** Bands used by the cell. */ + vec<EutranBands> bands; }; /** @@ -615,8 +616,8 @@ struct CellIdentityNr { /** Additional PLMN-IDs beyond the primary PLMN broadcast for this cell */ vec<string> additionalPlmns; - /** Band used by the cell */ - NgranBands band; + /** Bands used by the cell. */ + vec<NgranBands> bands; }; struct CellInfoGsm { @@ -679,7 +680,7 @@ struct CellInfo { } ratSpecificInfo; }; -/** A union representing the CellIdentity of a single cell */ +/** A union representing the CellIdentity of a single cell. */ safe_union CellIdentity { Monostate noinit; @@ -700,7 +701,7 @@ safe_union CellIdentity { * -NGRAN - 3gpp 38.331 Sec 6.3.2 UAC-BarringInfo and 22.261 Sec 6.22.2.[2-3] */ enum BarringServiceType : int32_t { - /** Applicabe to UTRAN */ + /** Applicable to UTRAN */ /** Barring for all CS services, including registration */ CS_SERVICE, /** Barring for all PS services, including registration */ @@ -955,14 +956,14 @@ struct RegStateResult { string registeredPlmn; /** - * Access-technology-specific registration information, such as for Cdma2000. + * Access-technology-specific registration information, such as for CDMA2000. */ safe_union AccessTechnologySpecificInfo { Monostate noinit; struct Cdma2000RegistrationInfo { /** - * concurrent services support indicator. if registered on a CDMA system. + * Concurrent services support indicator. if registered on a CDMA system. * false - Concurrent services not supported, * true - Concurrent services supported */ @@ -991,10 +992,10 @@ struct RegStateResult { struct EutranRegistrationInfo { /** * Network capabilities for voice over PS services. This info is valid only on LTE - * network and must be present when device is camped on LTE. vopsInfo must be empty when + * network and must be present when device is camped on LTE. VopsInfo must be empty when * device is camped only on 2G/3G. */ - LteVopsInfo lteVopsInfo; // LTE network capability + LteVopsInfo lteVopsInfo; /** * The parameters of NR 5G Non-Standalone. This value is only valid on E-UTRAN, @@ -1022,3 +1023,22 @@ struct NetworkScanResult { */ vec<CellInfo> networkInfos; }; + +/** + * Additional personalization categories in addition to those specified in 3GPP TS 22.022 and + * 3GPP2 C.S0068-0. + */ +enum PersoSubstate : @1.0::PersoSubstate { + SIM_SPN, + SIM_SPN_PUK, + /** Equivalent Home PLMN */ + SIM_SP_EHPLMN, + SIM_SP_EHPLMN_PUK, + SIM_ICCID, + SIM_ICCID_PUK, + SIM_IMPI, + SIM_IMPI_PUK, + /** Network subset service provider */ + SIM_NS_SP, + SIM_NS_SP_PUK, +}; diff --git a/radio/1.5/vts/functional/radio_hidl_hal_api.cpp b/radio/1.5/vts/functional/radio_hidl_hal_api.cpp index a4095b7a91..621825fa52 100644 --- a/radio/1.5/vts/functional/radio_hidl_hal_api.cpp +++ b/radio/1.5/vts/functional/radio_hidl_hal_api.cpp @@ -969,7 +969,10 @@ TEST_F(RadioHidlTest_v1_5, setDataProfile_1_5) { } } -TEST_F(RadioHidlTest_v1_5, setRadioPower_1_5_emergencyCall_cancalled) { +/* + * Test IRadio.setRadioPower_1_5() for the response returned. + */ +TEST_F(RadioHidlTest_v1_5, setRadioPower_1_5_emergencyCall_cancelled) { // Set radio power to off. serial = GetRandomSerialNumber(); radio_v1_5->setRadioPower_1_5(serial, false, false, false); @@ -1022,3 +1025,47 @@ TEST_F(RadioHidlTest_v1_5, setNetworkSelectionModeManual_1_5) { CHECK_GENERAL_ERROR)); } } + +/* + * Test IRadio.sendCdmaSmsExpectMore() for the response returned. + */ +TEST_F(RadioHidlTest_v1_5, sendCdmaSmsExpectMore) { + serial = GetRandomSerialNumber(); + + // Create a CdmaSmsAddress + CdmaSmsAddress cdmaSmsAddress; + cdmaSmsAddress.digitMode = CdmaSmsDigitMode::FOUR_BIT; + cdmaSmsAddress.numberMode = CdmaSmsNumberMode::NOT_DATA_NETWORK; + cdmaSmsAddress.numberType = CdmaSmsNumberType::UNKNOWN; + cdmaSmsAddress.numberPlan = CdmaSmsNumberPlan::UNKNOWN; + cdmaSmsAddress.digits = (std::vector<uint8_t>){11, 1, 6, 5, 10, 7, 7, 2, 10, 3, 10, 3}; + + // Create a CdmaSmsSubAddress + CdmaSmsSubaddress cdmaSmsSubaddress; + cdmaSmsSubaddress.subaddressType = CdmaSmsSubaddressType::NSAP; + cdmaSmsSubaddress.odd = false; + cdmaSmsSubaddress.digits = (std::vector<uint8_t>){}; + + // Create a CdmaSmsMessage + android::hardware::radio::V1_0::CdmaSmsMessage cdmaSmsMessage; + cdmaSmsMessage.teleserviceId = 4098; + cdmaSmsMessage.isServicePresent = false; + cdmaSmsMessage.serviceCategory = 0; + cdmaSmsMessage.address = cdmaSmsAddress; + cdmaSmsMessage.subAddress = cdmaSmsSubaddress; + cdmaSmsMessage.bearerData = + (std::vector<uint8_t>){15, 0, 3, 32, 3, 16, 1, 8, 16, 53, 76, 68, 6, 51, 106, 0}; + + radio_v1_5->sendCdmaSmsExpectMore(serial, cdmaSmsMessage); + + EXPECT_EQ(std::cv_status::no_timeout, wait()); + EXPECT_EQ(RadioResponseType::SOLICITED, radioRsp_v1_5->rspInfo.type); + EXPECT_EQ(serial, radioRsp_v1_5->rspInfo.serial); + + if (cardStatus.base.base.cardState == CardState::ABSENT) { + ASSERT_TRUE(CheckAnyOfErrors( + radioRsp_v1_5->rspInfo.error, + {RadioError::INVALID_ARGUMENTS, RadioError::INVALID_STATE, RadioError::SIM_ABSENT}, + CHECK_GENERAL_ERROR)); + } +} diff --git a/radio/1.5/vts/functional/radio_hidl_hal_utils_v1_5.h b/radio/1.5/vts/functional/radio_hidl_hal_utils_v1_5.h index abab452915..ce7b1ab8e7 100644 --- a/radio/1.5/vts/functional/radio_hidl_hal_utils_v1_5.h +++ b/radio/1.5/vts/functional/radio_hidl_hal_utils_v1_5.h @@ -573,6 +573,13 @@ class RadioResponse_v1_5 : public ::android::hardware::radio::V1_5::IRadioRespon cellInfo); Return<void> setNetworkSelectionModeManualResponse_1_5(const RadioResponseInfo& info); + + Return<void> sendCdmaSmsExpectMoreResponse(const RadioResponseInfo& info, + const SendSmsResult& sms); + + Return<void> supplySimDepersonalizationResponse( + const RadioResponseInfo& info, + ::android::hardware::radio::V1_5::PersoSubstate persoType, int32_t remainingRetries); }; /* Callback class for radio indication */ diff --git a/radio/1.5/vts/functional/radio_response.cpp b/radio/1.5/vts/functional/radio_response.cpp index d7197d5eb1..26401eb800 100644 --- a/radio/1.5/vts/functional/radio_response.cpp +++ b/radio/1.5/vts/functional/radio_response.cpp @@ -999,3 +999,15 @@ Return<void> RadioResponse_v1_5::setNetworkSelectionModeManualResponse_1_5( parent_v1_5.notify(info.serial); return Void(); } + +Return<void> RadioResponse_v1_5::sendCdmaSmsExpectMoreResponse(const RadioResponseInfo& /*info*/, + const SendSmsResult& /*sms*/) { + return Void(); +} + +Return<void> RadioResponse_v1_5::supplySimDepersonalizationResponse( + const RadioResponseInfo& /*info*/, + ::android::hardware::radio::V1_5::PersoSubstate /*persoType*/, + int32_t /*remainingRetries*/) { + return Void(); +} diff --git a/radio/config/1.3/types.hal b/radio/config/1.3/types.hal index 3414534138..aef0ff8a21 100644 --- a/radio/config/1.3/types.hal +++ b/radio/config/1.3/types.hal @@ -17,10 +17,10 @@ package android.hardware.radio.config@1.3; import android.hardware.radio@1.1::GeranBands; -import android.hardware.radio@1.1::UtranBands; import android.hardware.radio@1.1::EutranBands; import android.hardware.radio@1.4::RadioAccessFamily; import android.hardware.radio@1.5::NgranBands; +import android.hardware.radio@1.5::UtranBands; /** Type for the SIM slot. */ enum SlotType : int32_t { @@ -50,17 +50,17 @@ enum ModemFeatures : int32_t { /** 3GPP capability. */ THREE_GPP_REG = 1 << 1, /** CDMA 2000 with EHRPD capability. */ - CDMA2000_EHRPD_REG = 1 << 2, - /** GSM capability. */ - GERAN_REG = 1 << 3, + CDMA2000_EHRPD = 1 << 2, + /** GSM/EDGE capability. */ + GERAN = 1 << 3, /** UMTS capability. */ - UTRAN_REG = 1 << 4, + UTRAN = 1 << 4, /** LTE capability. */ - EUTRAN_REG = 1 << 5, + EUTRAN = 1 << 5, /** 5G capability. */ - NGRAN_REG = 1 << 6, - /** Dual Connectivity capability. */ - EN_DC_REG = 1 << 7, + NGRAN = 1 << 6, + /** 5G dual connectivity capability. */ + EN_DC = 1 << 7, /** VoLTE capability (IMS registered). */ PS_VOICE_REG = 1 << 8, /** CS voice call capability. */ @@ -72,7 +72,7 @@ enum ModemFeatures : int32_t { /** Network scanning capability. */ NETWORK_SCAN = 1 << 12, /** CDMA capability for SIM associated with modem. */ - CSIM = 1 << 13, + CSIM_APP = 1 << 13, }; struct ConcurrentModemFeatures { diff --git a/rebootescrow/aidl/default/RebootEscrow.cpp b/rebootescrow/aidl/default/RebootEscrow.cpp index 94d09010d9..dbc09215b3 100644 --- a/rebootescrow/aidl/default/RebootEscrow.cpp +++ b/rebootescrow/aidl/default/RebootEscrow.cpp @@ -29,7 +29,7 @@ namespace rebootescrow { using ::android::base::unique_fd; ndk::ScopedAStatus RebootEscrow::storeKey(const std::vector<int8_t>& kek) { - int rawFd = TEMP_FAILURE_RETRY(::open(REBOOT_ESCROW_DEVICE, O_WRONLY | O_NOFOLLOW | O_CLOEXEC)); + int rawFd = TEMP_FAILURE_RETRY(::open(devicePath_.c_str(), O_WRONLY | O_NOFOLLOW | O_CLOEXEC)); unique_fd fd(rawFd); if (fd.get() < 0) { LOG(WARNING) << "Could not open reboot escrow device"; @@ -48,20 +48,19 @@ ndk::ScopedAStatus RebootEscrow::storeKey(const std::vector<int8_t>& kek) { } ndk::ScopedAStatus RebootEscrow::retrieveKey(std::vector<int8_t>* _aidl_return) { - int rawFd = TEMP_FAILURE_RETRY(::open(REBOOT_ESCROW_DEVICE, O_RDONLY | O_NOFOLLOW | O_CLOEXEC)); + int rawFd = TEMP_FAILURE_RETRY(::open(devicePath_.c_str(), O_RDONLY | O_NOFOLLOW | O_CLOEXEC)); unique_fd fd(rawFd); if (fd.get() < 0) { LOG(WARNING) << "Could not open reboot escrow device"; return ndk::ScopedAStatus(AStatus_fromExceptionCode(EX_UNSUPPORTED_OPERATION)); } - std::string encodedString; - if (!::android::base::ReadFdToString(fd, &encodedString)) { - LOG(WARNING) << "Could not read device to string"; + std::vector<uint8_t> encodedBytes(hadamard::OUTPUT_SIZE_BYTES); + if (!::android::base::ReadFully(fd, &encodedBytes[0], encodedBytes.size())) { + LOG(WARNING) << "Could not read device"; return ndk::ScopedAStatus(AStatus_fromExceptionCode(EX_UNSUPPORTED_OPERATION)); } - std::vector<uint8_t> encodedBytes(encodedString.begin(), encodedString.end()); auto keyBytes = hadamard::DecodeKey(encodedBytes); std::vector<int8_t> signedKeyBytes(keyBytes.begin(), keyBytes.end()); diff --git a/rebootescrow/aidl/default/include/rebootescrow-impl/RebootEscrow.h b/rebootescrow/aidl/default/include/rebootescrow-impl/RebootEscrow.h index 1ed73978d9..00ff16b2ea 100644 --- a/rebootescrow/aidl/default/include/rebootescrow-impl/RebootEscrow.h +++ b/rebootescrow/aidl/default/include/rebootescrow-impl/RebootEscrow.h @@ -23,11 +23,14 @@ namespace android { namespace hardware { namespace rebootescrow { -static const char* REBOOT_ESCROW_DEVICE = "/dev/access-kregistry"; - class RebootEscrow : public BnRebootEscrow { + public: + explicit RebootEscrow(const std::string& devicePath) : devicePath_(devicePath) {} ndk::ScopedAStatus storeKey(const std::vector<int8_t>& kek) override; ndk::ScopedAStatus retrieveKey(std::vector<int8_t>* _aidl_return) override; + + private: + const std::string devicePath_; }; } // namespace rebootescrow diff --git a/rebootescrow/aidl/default/rebootescrow-default.rc b/rebootescrow/aidl/default/rebootescrow-default.rc index e7a9cfcb70..ad90465aa3 100644 --- a/rebootescrow/aidl/default/rebootescrow-default.rc +++ b/rebootescrow/aidl/default/rebootescrow-default.rc @@ -3,7 +3,3 @@ service vendor.rebootescrow-default /vendor/bin/hw/android.hardware.rebootescrow class hal user system group system - -on boot - chmod 770 /dev/access-kregistry - chown system system /dev/access-kregistry diff --git a/rebootescrow/aidl/default/service.cpp b/rebootescrow/aidl/default/service.cpp index bd2378e513..8a8086b498 100644 --- a/rebootescrow/aidl/default/service.cpp +++ b/rebootescrow/aidl/default/service.cpp @@ -17,15 +17,21 @@ #include "rebootescrow-impl/RebootEscrow.h" #include <android-base/logging.h> +#include <android-base/properties.h> #include <android/binder_manager.h> #include <android/binder_process.h> using aidl::android::hardware::rebootescrow::RebootEscrow; +constexpr auto kRebootEscrowDeviceProperty = "ro.rebootescrow.device"; +constexpr auto kRebootEscrowDeviceDefault = "/dev/access-kregistry"; + int main() { ABinderProcess_setThreadPoolMaxThreadCount(0); - auto re = ndk::SharedRefBase::make<RebootEscrow>(); + auto rebootEscrowDevicePath = + android::base::GetProperty(kRebootEscrowDeviceProperty, kRebootEscrowDeviceDefault); + auto re = ndk::SharedRefBase::make<RebootEscrow>(rebootEscrowDevicePath); const std::string instance = std::string() + RebootEscrow::descriptor + "/default"; binder_status_t status = AServiceManager_addService(re->asBinder().get(), instance.c_str()); CHECK(status == STATUS_OK); diff --git a/rebootescrow/aidl/vts/functional/VtsHalRebootEscrowTargetTest.cpp b/rebootescrow/aidl/vts/functional/VtsHalRebootEscrowTargetTest.cpp index f69cf877ad..cd8cc3eaa1 100644 --- a/rebootescrow/aidl/vts/functional/VtsHalRebootEscrowTargetTest.cpp +++ b/rebootescrow/aidl/vts/functional/VtsHalRebootEscrowTargetTest.cpp @@ -26,6 +26,9 @@ using android::sp; using android::String16; using android::hardware::rebootescrow::IRebootEscrow; +#define SKIP_UNSUPPORTED \ + if (rebootescrow == nullptr) GTEST_SKIP() << "Not supported on this device" + /** * This tests that the key can be written, read, and removed. It does not test * that the key survives a reboot. That needs a host-based test. @@ -36,7 +39,6 @@ class RebootEscrowAidlTest : public testing::TestWithParam<std::string> { public: virtual void SetUp() override { rebootescrow = android::waitForDeclaredService<IRebootEscrow>(String16(GetParam().c_str())); - ASSERT_NE(rebootescrow, nullptr); } sp<IRebootEscrow> rebootescrow; @@ -59,6 +61,8 @@ class RebootEscrowAidlTest : public testing::TestWithParam<std::string> { }; TEST_P(RebootEscrowAidlTest, StoreAndRetrieve_Success) { + SKIP_UNSUPPORTED; + ASSERT_TRUE(rebootescrow->storeKey(KEY_1).isOk()); std::vector<uint8_t> actualKey; @@ -67,6 +71,8 @@ TEST_P(RebootEscrowAidlTest, StoreAndRetrieve_Success) { } TEST_P(RebootEscrowAidlTest, StoreAndRetrieve_SecondRetrieveSucceeds) { + SKIP_UNSUPPORTED; + ASSERT_TRUE(rebootescrow->storeKey(KEY_1).isOk()); std::vector<uint8_t> actualKey; @@ -78,6 +84,8 @@ TEST_P(RebootEscrowAidlTest, StoreAndRetrieve_SecondRetrieveSucceeds) { } TEST_P(RebootEscrowAidlTest, StoreTwiceOverwrites_Success) { + SKIP_UNSUPPORTED; + ASSERT_TRUE(rebootescrow->storeKey(KEY_1).isOk()); ASSERT_TRUE(rebootescrow->storeKey(KEY_2).isOk()); @@ -87,6 +95,8 @@ TEST_P(RebootEscrowAidlTest, StoreTwiceOverwrites_Success) { } TEST_P(RebootEscrowAidlTest, StoreEmpty_AfterGetEmptyKey_Success) { + SKIP_UNSUPPORTED; + rebootescrow->storeKey(KEY_1); rebootescrow->storeKey(EMPTY_KEY); diff --git a/secure_element/1.2/Android.bp b/secure_element/1.2/Android.bp new file mode 100644 index 0000000000..e134771880 --- /dev/null +++ b/secure_element/1.2/Android.bp @@ -0,0 +1,18 @@ +// This file is autogenerated by hidl-gen -Landroidbp. + +hidl_interface { + name: "android.hardware.secure_element@1.2", + root: "android.hardware", + vndk: { + enabled: true, + }, + srcs: [ + "ISecureElement.hal", + ], + interfaces: [ + "android.hardware.secure_element@1.0", + "android.hardware.secure_element@1.1", + "android.hidl.base@1.0", + ], + gen_java: true, +} diff --git a/secure_element/1.2/ISecureElement.hal b/secure_element/1.2/ISecureElement.hal new file mode 100644 index 0000000000..16cc5775b5 --- /dev/null +++ b/secure_element/1.2/ISecureElement.hal @@ -0,0 +1,35 @@ +/* + * Copyright (C) 2019 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.hardware.secure_element@1.2; + +import @1.1::ISecureElementHalCallback; +import @1.1::ISecureElement; +import @1.0::SecureElementStatus; + +interface ISecureElement extends @1.1::ISecureElement { + /** + * Reset the Secure Element. + * + * HAL should trigger reset to the secure element. It could hardware power cycle or + * a soft reset depends on hardware design. + * HAL service must send onStateChange() with connected equal to true + * after resetting and all the re-initialization has been successfully completed. + * + * @return SecureElementStatus::SUCCESS on success and SecureElementStatus::FAILED on error. + */ + reset() generates (SecureElementStatus status); +}; diff --git a/secure_element/1.2/vts/functional/Android.bp b/secure_element/1.2/vts/functional/Android.bp new file mode 100644 index 0000000000..a1732108ab --- /dev/null +++ b/secure_element/1.2/vts/functional/Android.bp @@ -0,0 +1,27 @@ +// +// Copyright (C) 2019 The Android Open Source Project +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// + +cc_test { + name: "VtsHalSecureElementV1_2TargetTest", + defaults: ["VtsHalTargetTestDefaults"], + srcs: ["VtsHalSecureElementV1_2TargetTest.cpp"], + static_libs: [ + "android.hardware.secure_element@1.0", + "android.hardware.secure_element@1.1", + "android.hardware.secure_element@1.2", + ], + test_suites: ["general-tests", "vts-core"], +} diff --git a/secure_element/1.2/vts/functional/VtsHalSecureElementV1_2TargetTest.cpp b/secure_element/1.2/vts/functional/VtsHalSecureElementV1_2TargetTest.cpp new file mode 100644 index 0000000000..98e45021d9 --- /dev/null +++ b/secure_element/1.2/vts/functional/VtsHalSecureElementV1_2TargetTest.cpp @@ -0,0 +1,101 @@ +/* + * Copyright (C) 2019 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include <string> + +#define LOG_TAG "secure_element_hidl_hal_test" +#include <android-base/logging.h> + +#include <android/hardware/secure_element/1.0/types.h> +#include <android/hardware/secure_element/1.1/ISecureElementHalCallback.h> +#include <android/hardware/secure_element/1.2/ISecureElement.h> +#include <gtest/gtest.h> +#include <hidl/GtestPrinter.h> +#include <hidl/ServiceManagement.h> + +#include <VtsHalHidlTargetCallbackBase.h> + +using ::android::sp; +using ::android::hardware::hidl_string; +using ::android::hardware::Return; +using ::android::hardware::Void; +using ::android::hardware::secure_element::V1_0::SecureElementStatus; +using ::android::hardware::secure_element::V1_1::ISecureElementHalCallback; +using ::android::hardware::secure_element::V1_2::ISecureElement; + +constexpr char kCallbackNameOnStateChange[] = "onStateChange"; + +class SecureElementCallbackArgs { + public: + bool state_; + hidl_string reason_; +}; + +class SecureElementHalCallback + : public ::testing::VtsHalHidlTargetCallbackBase<SecureElementCallbackArgs>, + public ISecureElementHalCallback { + public: + virtual ~SecureElementHalCallback() = default; + + Return<void> onStateChange_1_1(bool state, const hidl_string& reason) override { + SecureElementCallbackArgs args; + args.state_ = state; + args.reason_ = reason; + NotifyFromCallback(kCallbackNameOnStateChange, args); + return Void(); + }; + + Return<void> onStateChange(__attribute__((unused)) bool state) override { return Void(); } +}; + +class SecureElementHidlTest : public ::testing::TestWithParam<std::string> { + public: + virtual void SetUp() override { + LOG(INFO) << "get service with name:" << GetParam(); + se_ = ISecureElement::getService(GetParam()); + ASSERT_NE(se_, nullptr); + + se_cb_ = new SecureElementHalCallback(); + ASSERT_NE(se_cb_, nullptr); + se_->init_1_1(se_cb_); + auto res = se_cb_->WaitForCallback(kCallbackNameOnStateChange); + EXPECT_TRUE(res.no_timeout); + EXPECT_TRUE(res.args->state_); + EXPECT_NE(res.args->reason_, ""); + } + + sp<ISecureElement> se_; + sp<SecureElementHalCallback> se_cb_; +}; + +/* + * Reset: + * Calls reset() + * Checks status + * Check onStateChange is received with connected state set to true + */ +TEST_P(SecureElementHidlTest, Reset) { + EXPECT_EQ(SecureElementStatus::SUCCESS, se_->reset()); + + auto res = se_cb_->WaitForCallback(kCallbackNameOnStateChange); + EXPECT_TRUE(res.no_timeout); + EXPECT_TRUE(res.args->state_); +} + +INSTANTIATE_TEST_SUITE_P( + PerInstance, SecureElementHidlTest, + testing::ValuesIn(android::hardware::getAllHalInstanceNames(ISecureElement::descriptor)), + android::hardware::PrintInstanceNameToString); diff --git a/sensors/2.0/multihal/HalProxy.cpp b/sensors/2.0/multihal/HalProxy.cpp index fd76bda726..7c52661601 100644 --- a/sensors/2.0/multihal/HalProxy.cpp +++ b/sensors/2.0/multihal/HalProxy.cpp @@ -651,12 +651,12 @@ void HalProxyCallback::postEvents(const std::vector<Event>& events, ScopedWakelo if (numWakeupEvents > 0) { ALOG_ASSERT(wakelock.isLocked(), "Wakeup events posted while wakelock unlocked for subhal" - " w/ index %zu.", + " w/ index %" PRId32 ".", mSubHalIndex); } else { ALOG_ASSERT(!wakelock.isLocked(), "No Wakeup events posted but wakelock locked for subhal" - " w/ index %zu.", + " w/ index %" PRId32 ".", mSubHalIndex); } mHalProxy->postEventsToMessageQueue(processedEvents, numWakeupEvents, std::move(wakelock)); diff --git a/soundtrigger/2.3/types.hal b/soundtrigger/2.3/types.hal index 730f9695af..10fc34e312 100644 --- a/soundtrigger/2.3/types.hal +++ b/soundtrigger/2.3/types.hal @@ -66,7 +66,7 @@ struct RecognitionConfig { * Bit field encoding of the AudioCapabilities * supported by the firmware. */ - uint32_t audioCapabilities; + bitfield<AudioCapabilities> audioCapabilities; }; /** diff --git a/tv/tuner/1.0/TEST_MAPPING b/tv/tuner/1.0/TEST_MAPPING new file mode 100644 index 0000000000..1979887eea --- /dev/null +++ b/tv/tuner/1.0/TEST_MAPPING @@ -0,0 +1,7 @@ +{ + "presubmit": [ + { + "name": "VtsHalTvTunerV1_0TargetTest" + } + ] +}
\ No newline at end of file diff --git a/tv/tuner/1.0/vts/functional/Android.bp b/tv/tuner/1.0/vts/functional/Android.bp index 7d6b990a7b..3637708a6a 100644 --- a/tv/tuner/1.0/vts/functional/Android.bp +++ b/tv/tuner/1.0/vts/functional/Android.bp @@ -30,5 +30,10 @@ cc_test { shared_libs: [ "libbinder", ], - test_suites: ["general-tests"], + test_suites: [ + "general-tests", + "vts-core", + ], + + require_root: true, } diff --git a/tv/tuner/1.0/vts/functional/VtsHalTvTunerV1_0TargetTest.cpp b/tv/tuner/1.0/vts/functional/VtsHalTvTunerV1_0TargetTest.cpp index 4e7dcc3ae4..820c58c4ce 100644 --- a/tv/tuner/1.0/vts/functional/VtsHalTvTunerV1_0TargetTest.cpp +++ b/tv/tuner/1.0/vts/functional/VtsHalTvTunerV1_0TargetTest.cpp @@ -31,8 +31,11 @@ #include <android/hardware/tv/tuner/1.0/types.h> #include <binder/MemoryDealer.h> #include <fmq/MessageQueue.h> +#include <gtest/gtest.h> +#include <hidl/GtestPrinter.h> #include <hidl/HidlSupport.h> #include <hidl/HidlTransportSupport.h> +#include <hidl/ServiceManagement.h> #include <hidl/Status.h> #include <hidlmemory/FrameworkUtils.h> #include <utils/Condition.h> @@ -633,23 +636,10 @@ void DvrCallback::stopRecordThread() { android::Mutex::Autolock autoLock(mRecordThreadLock); } -// Test environment for Tuner HIDL HAL. -class TunerHidlEnvironment : public ::testing::VtsHalHidlTargetTestEnvBase { - public: - // get the test environment singleton - static TunerHidlEnvironment* Instance() { - static TunerHidlEnvironment* instance = new TunerHidlEnvironment; - return instance; - } - - virtual void registerTestServices() override { registerTestService<ITuner>(); } -}; - -class TunerHidlTest : public ::testing::VtsHalHidlTargetTestBase { +class TunerHidlTest : public testing::TestWithParam<std::string> { public: virtual void SetUp() override { - mService = ::testing::VtsHalHidlTargetTestBase::getService<ITuner>( - TunerHidlEnvironment::Instance()->getServiceName<ITuner>()); + mService = ITuner::getService(GetParam()); ASSERT_NE(mService, nullptr); } @@ -1242,7 +1232,7 @@ class TunerHidlTest : public ::testing::VtsHalHidlTargetTestBase { /* * API STATUS TESTS */ -TEST_F(TunerHidlTest, CreateFrontend) { +TEST_P(TunerHidlTest, CreateFrontend) { Result status; hidl_vec<FrontendId> feIds; @@ -1262,7 +1252,7 @@ TEST_F(TunerHidlTest, CreateFrontend) { } } -TEST_F(TunerHidlTest, TuneFrontend) { +TEST_P(TunerHidlTest, TuneFrontend) { Result status; hidl_vec<FrontendId> feIds; @@ -1282,7 +1272,7 @@ TEST_F(TunerHidlTest, TuneFrontend) { } } -TEST_F(TunerHidlTest, StopTuneFrontend) { +TEST_P(TunerHidlTest, StopTuneFrontend) { Result status; hidl_vec<FrontendId> feIds; @@ -1302,7 +1292,7 @@ TEST_F(TunerHidlTest, StopTuneFrontend) { } } -TEST_F(TunerHidlTest, CloseFrontend) { +TEST_P(TunerHidlTest, CloseFrontend) { Result status; hidl_vec<FrontendId> feIds; @@ -1322,7 +1312,7 @@ TEST_F(TunerHidlTest, CloseFrontend) { } } -TEST_F(TunerHidlTest, CreateDemuxWithFrontend) { +TEST_P(TunerHidlTest, CreateDemuxWithFrontend) { Result status; hidl_vec<FrontendId> feIds; @@ -1349,22 +1339,22 @@ TEST_F(TunerHidlTest, CreateDemuxWithFrontend) { } } -TEST_F(TunerHidlTest, CreateDemux) { +TEST_P(TunerHidlTest, CreateDemux) { description("Create Demux"); ASSERT_TRUE(createDemux()); } -TEST_F(TunerHidlTest, CloseDemux) { +TEST_P(TunerHidlTest, CloseDemux) { description("Close Demux"); ASSERT_TRUE(closeDemux()); } -TEST_F(TunerHidlTest, CreateDescrambler) { +TEST_P(TunerHidlTest, CreateDescrambler) { description("Create Descrambler"); ASSERT_TRUE(createDescrambler()); } -TEST_F(TunerHidlTest, CloseDescrambler) { +TEST_P(TunerHidlTest, CloseDescrambler) { description("Close Descrambler"); ASSERT_TRUE(closeDescrambler()); } @@ -1374,7 +1364,7 @@ TEST_F(TunerHidlTest, CloseDescrambler) { * * TODO: re-enable the tests after finalizing the testing stream. */ -/*TEST_F(TunerHidlTest, PlaybackDataFlowWithSectionFilterTest) { +/*TEST_P(TunerHidlTest, PlaybackDataFlowWithSectionFilterTest) { description("Feed ts data from playback and configure pes filter to get output"); // todo modulize the filter conf parser @@ -1417,7 +1407,7 @@ TEST_F(TunerHidlTest, CloseDescrambler) { ASSERT_TRUE(playbackDataFlowTest(filterConf, playbackConf, goldenOutputFiles)); } -TEST_F(TunerHidlTest, BroadcastDataFlowWithPesFilterTest) { +TEST_P(TunerHidlTest, BroadcastDataFlowWithPesFilterTest) { description("Feed ts data from frontend and test with PES filter"); // todo modulize the filter conf parser @@ -1447,7 +1437,7 @@ TEST_F(TunerHidlTest, BroadcastDataFlowWithPesFilterTest) { ASSERT_TRUE(broadcastDataFlowTest(filterConf, goldenOutputFiles)); } -TEST_F(TunerHidlTest, RecordDataFlowWithTsRecordFilterTest) { +TEST_P(TunerHidlTest, RecordDataFlowWithTsRecordFilterTest) { description("Feed ts data from frontend to recording and test with ts record filter"); // todo modulize the filter conf parser @@ -1487,11 +1477,7 @@ TEST_F(TunerHidlTest, RecordDataFlowWithTsRecordFilterTest) { } // namespace -int main(int argc, char** argv) { - ::testing::AddGlobalTestEnvironment(TunerHidlEnvironment::Instance()); - ::testing::InitGoogleTest(&argc, argv); - TunerHidlEnvironment::Instance()->init(&argc, argv); - int status = RUN_ALL_TESTS(); - LOG(INFO) << "Test result = " << status; - return status; -} +INSTANTIATE_TEST_SUITE_P( + PerInstance, TunerHidlTest, + testing::ValuesIn(android::hardware::getAllHalInstanceNames(ITuner::descriptor)), + android::hardware::PrintInstanceNameToString); diff --git a/vibrator/aidl/android/hardware/vibrator/CompositePrimitive.aidl b/vibrator/aidl/android/hardware/vibrator/CompositePrimitive.aidl index b9a80ec116..0fdfa5d5c2 100644 --- a/vibrator/aidl/android/hardware/vibrator/CompositePrimitive.aidl +++ b/vibrator/aidl/android/hardware/vibrator/CompositePrimitive.aidl @@ -49,4 +49,9 @@ enum CompositePrimitive { * A haptic effect that simulates quick downwards movement with gravity. */ QUICK_FALL, + /** + * This very short effect should produce a light crisp sensation intended + * to be used repetitively for dynamic feedback. + */ + LIGHT_TICK, } diff --git a/vibrator/aidl/android/hardware/vibrator/IVibrator.aidl b/vibrator/aidl/android/hardware/vibrator/IVibrator.aidl index f553664eeb..06a8bf5c17 100644 --- a/vibrator/aidl/android/hardware/vibrator/IVibrator.aidl +++ b/vibrator/aidl/android/hardware/vibrator/IVibrator.aidl @@ -158,12 +158,31 @@ interface IVibrator { int getCompositionSizeMax(); /** + * List of supported effect primitive. + * + * Return the effect primitives which are supported by the compose API. + * Implementations are expected to support all primitives of the interface + * version that they implement. + */ + CompositePrimitive[] getSupportedPrimitives(); + + /** + * Retrieve effect primitive's duration in milliseconds. + * + * Support is reflected in getCapabilities (CAP_COMPOSE_EFFECTS). + * + * @return Best effort estimation of effect primitive's duration. + * @param primitive Effect primitive being queried. + */ + int getPrimitiveDuration(CompositePrimitive primitive); + + /** * Fire off a string of effect primitives, combined to perform richer effects. * * Support is reflected in getCapabilities (CAP_COMPOSE_EFFECTS). * * Doing this operation while the vibrator is already on is undefined behavior. Clients should - * explicitly call off. + * explicitly call off. IVibratorCallback.onComplete() support is required for this API. * * @param composite Array of composition parameters. */ diff --git a/vibrator/aidl/default/Vibrator.cpp b/vibrator/aidl/default/Vibrator.cpp index cedd9cb89c..9236b95ddc 100644 --- a/vibrator/aidl/default/Vibrator.cpp +++ b/vibrator/aidl/default/Vibrator.cpp @@ -113,12 +113,35 @@ ndk::ScopedAStatus Vibrator::getCompositionSizeMax(int32_t* maxSize) { return ndk::ScopedAStatus::ok(); } +ndk::ScopedAStatus Vibrator::getSupportedPrimitives(std::vector<CompositePrimitive>* supported) { + *supported = { + CompositePrimitive::NOOP, CompositePrimitive::CLICK, + CompositePrimitive::THUD, CompositePrimitive::SPIN, + CompositePrimitive::QUICK_RISE, CompositePrimitive::SLOW_RISE, + CompositePrimitive::QUICK_FALL, CompositePrimitive::LIGHT_TICK, + }; + return ndk::ScopedAStatus::ok(); +} + +ndk::ScopedAStatus Vibrator::getPrimitiveDuration(CompositePrimitive primitive, + int32_t* durationMs) { + if (primitive != CompositePrimitive::NOOP) { + *durationMs = 100; + } else { + *durationMs = 0; + } + return ndk::ScopedAStatus::ok(); +} + ndk::ScopedAStatus Vibrator::compose(const std::vector<CompositeEffect>& composite, const std::shared_ptr<IVibratorCallback>& callback) { if (composite.size() > kComposeSizeMax) { return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_ARGUMENT); } + std::vector<CompositePrimitive> supported; + getSupportedPrimitives(&supported); + for (auto& e : composite) { if (e.delayMs > kComposeDelayMaxMs) { return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_ARGUMENT); @@ -126,8 +149,7 @@ ndk::ScopedAStatus Vibrator::compose(const std::vector<CompositeEffect>& composi if (e.scale <= 0.0f || e.scale > 1.0f) { return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_ARGUMENT); } - if (e.primitive < CompositePrimitive::NOOP || - e.primitive > CompositePrimitive::QUICK_FALL) { + if (std::find(supported.begin(), supported.end(), e.primitive) == supported.end()) { return ndk::ScopedAStatus::fromExceptionCode(EX_UNSUPPORTED_OPERATION); } } diff --git a/vibrator/aidl/default/include/vibrator-impl/Vibrator.h b/vibrator/aidl/default/include/vibrator-impl/Vibrator.h index 0eb957d9fa..c3f3616876 100644 --- a/vibrator/aidl/default/include/vibrator-impl/Vibrator.h +++ b/vibrator/aidl/default/include/vibrator-impl/Vibrator.h @@ -36,6 +36,9 @@ class Vibrator : public BnVibrator { ndk::ScopedAStatus setExternalControl(bool enabled) override; ndk::ScopedAStatus getCompositionDelayMax(int32_t* maxDelayMs); ndk::ScopedAStatus getCompositionSizeMax(int32_t* maxSize); + ndk::ScopedAStatus getSupportedPrimitives(std::vector<CompositePrimitive>* supported) override; + ndk::ScopedAStatus getPrimitiveDuration(CompositePrimitive primitive, + int32_t* durationMs) override; ndk::ScopedAStatus compose(const std::vector<CompositeEffect>& composite, const std::shared_ptr<IVibratorCallback>& callback) override; ndk::ScopedAStatus getSupportedAlwaysOnEffects(std::vector<Effect>* _aidl_return) override; diff --git a/vibrator/aidl/vts/VtsHalVibratorTargetTest.cpp b/vibrator/aidl/vts/VtsHalVibratorTargetTest.cpp index f197763be3..411fe7a2fe 100644 --- a/vibrator/aidl/vts/VtsHalVibratorTargetTest.cpp +++ b/vibrator/aidl/vts/VtsHalVibratorTargetTest.cpp @@ -261,6 +261,29 @@ TEST_P(VibratorAidl, ExternalControlUnsupportedMatchingCapabilities) { } } +TEST_P(VibratorAidl, GetSupportedPrimitives) { + if (capabilities & IVibrator::CAP_COMPOSE_EFFECTS) { + std::vector<CompositePrimitive> supported; + + EXPECT_EQ(Status::EX_NONE, vibrator->getSupportedPrimitives(&supported).exceptionCode()); + + std::sort(supported.begin(), supported.end()); + + EXPECT_EQ(kCompositePrimitives, supported); + } +} + +TEST_P(VibratorAidl, GetPrimitiveDuration) { + if (capabilities & IVibrator::CAP_COMPOSE_EFFECTS) { + int32_t duration; + + for (auto primitive : kCompositePrimitives) { + EXPECT_EQ(Status::EX_NONE, + vibrator->getPrimitiveDuration(primitive, &duration).exceptionCode()); + } + } +} + TEST_P(VibratorAidl, ComposeValidPrimitives) { if (capabilities & IVibrator::CAP_COMPOSE_EFFECTS) { int32_t maxDelay, maxSize; @@ -357,6 +380,30 @@ TEST_P(VibratorAidl, CompseSizeBoundary) { } } +TEST_P(VibratorAidl, ComposeCallback) { + if (capabilities & IVibrator::CAP_COMPOSE_EFFECTS) { + std::promise<void> completionPromise; + std::future<void> completionFuture{completionPromise.get_future()}; + sp<CompletionCallback> callback = + new CompletionCallback([&completionPromise] { completionPromise.set_value(); }); + CompositePrimitive primitive = CompositePrimitive::CLICK; + CompositeEffect effect; + std::vector<CompositeEffect> composite; + int32_t duration; + + effect.delayMs = 0; + effect.primitive = primitive; + effect.scale = 1.0f; + composite.emplace_back(effect); + + EXPECT_EQ(Status::EX_NONE, + vibrator->getPrimitiveDuration(primitive, &duration).exceptionCode()); + EXPECT_EQ(Status::EX_NONE, vibrator->compose(composite, callback).exceptionCode()); + EXPECT_EQ(completionFuture.wait_for(std::chrono::milliseconds(duration * 2)), + std::future_status::ready); + } +} + TEST_P(VibratorAidl, AlwaysOn) { if (capabilities & IVibrator::CAP_ALWAYS_ON_CONTROL) { std::vector<Effect> supported; diff --git a/wifi/1.2/IWifiStaIface.hal b/wifi/1.2/IWifiStaIface.hal index 3a7f7772c8..d65b33bb2b 100644 --- a/wifi/1.2/IWifiStaIface.hal +++ b/wifi/1.2/IWifiStaIface.hal @@ -23,7 +23,7 @@ import @1.0::IWifiStaIface; /** * Interface used to represent a single STA iface. * - * IWifiChip.createStaIface() may return a @1.2::IWifiStaIface when supported. + * IWifiChip.createStaIface() must return a @1.2::IWifiStaIface when supported. */ interface IWifiStaIface extends @1.0::IWifiStaIface { /** diff --git a/wifi/1.3/IWifiStaIface.hal b/wifi/1.3/IWifiStaIface.hal index 81c0c3869b..3a755090ae 100644 --- a/wifi/1.3/IWifiStaIface.hal +++ b/wifi/1.3/IWifiStaIface.hal @@ -23,7 +23,7 @@ import @1.2::IWifiStaIface; /** * Interface used to represent a single STA iface. * - * IWifiChip.createStaIface() may return a @1.3::IWifiStaIface when supported. + * IWifiChip.createStaIface() must return a @1.3::IWifiStaIface when supported. */ interface IWifiStaIface extends @1.2::IWifiStaIface { /** diff --git a/wifi/1.4/default/wifi.cpp b/wifi/1.4/default/wifi.cpp index 4f48d7e7a6..9c6b0f0f16 100644 --- a/wifi/1.4/default/wifi.cpp +++ b/wifi/1.4/default/wifi.cpp @@ -124,6 +124,8 @@ WifiStatus Wifi::startInternal() { } } LOG(ERROR) << "Wifi HAL start failed"; + // Clear the event callback objects since the HAL start failed. + event_cb_handler_.invalidate(); } return wifi_status; } @@ -158,6 +160,8 @@ WifiStatus Wifi::stopInternal( } LOG(ERROR) << "Wifi HAL stop failed"; } + // Clear the event callback objects since the HAL is now stopped. + event_cb_handler_.invalidate(); return wifi_status; } diff --git a/wifi/1.4/default/wifi_legacy_hal.cpp b/wifi/1.4/default/wifi_legacy_hal.cpp index 3117473345..4f03fbb489 100644 --- a/wifi/1.4/default/wifi_legacy_hal.cpp +++ b/wifi/1.4/default/wifi_legacy_hal.cpp @@ -824,11 +824,10 @@ wifi_error WifiLegacyHal::setLatencyMode(const std::string& iface_name, mode); } -wifi_error WifiLegacyHal::setThermalMitigationMode( - const std::string& iface_name, wifi_thermal_mode mode, - uint32_t completion_window) { +wifi_error WifiLegacyHal::setThermalMitigationMode(wifi_thermal_mode mode, + uint32_t completion_window) { return global_func_table_.wifi_set_thermal_mitigation_mode( - getIfaceHandle(iface_name), mode, completion_window); + global_handle_, mode, completion_window); } std::pair<wifi_error, uint32_t> WifiLegacyHal::getLoggerSupportedFeatureSet( diff --git a/wifi/1.4/default/wifi_legacy_hal.h b/wifi/1.4/default/wifi_legacy_hal.h index 9df227b34b..67bf6c2d76 100644 --- a/wifi/1.4/default/wifi_legacy_hal.h +++ b/wifi/1.4/default/wifi_legacy_hal.h @@ -259,8 +259,7 @@ class WifiLegacyHal { virtual wifi_error resetTxPowerScenario(const std::string& iface_name); wifi_error setLatencyMode(const std::string& iface_name, wifi_latency_mode mode); - wifi_error setThermalMitigationMode(const std::string& iface_name, - wifi_thermal_mode mode, + wifi_error setThermalMitigationMode(wifi_thermal_mode mode, uint32_t completion_window); // Logger/debug functions. std::pair<wifi_error, uint32_t> getLoggerSupportedFeatureSet( |