Merge "Cleanup IRadio/IRadioConfig HAL"
diff --git a/audio/common/all-versions/default/service/Android.bp b/audio/common/all-versions/default/service/Android.bp
index 4565730..3e8b715 100644
--- a/audio/common/all-versions/default/service/Android.bp
+++ b/audio/common/all-versions/default/service/Android.bp
@@ -24,23 +24,6 @@
"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",
],
}
diff --git a/audio/common/all-versions/default/service/service.cpp b/audio/common/all-versions/default/service/service.cpp
index 2730f3b..7331b0a 100644
--- a/audio/common/all-versions/default/service/service.cpp
+++ b/audio/common/all-versions/default/service/service.cpp
@@ -16,19 +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 <string>
+#include <vector>
+
#include <binder/ProcessState.h>
#include <cutils/properties.h>
#include <hidl/HidlTransportSupport.h>
@@ -38,13 +28,20 @@
using namespace android::hardware;
using android::OK;
+using InterfacesList = std::vector<std::string>;
+
/** Try to register the provided factories in the provided order.
* 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 */ []) {
@@ -61,36 +58,57 @@
}
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.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_2::ISoundTriggerHw,
- soundtrigger::V2_1::ISoundTriggerHw,
- soundtrigger::V2_0::ISoundTriggerHw>()),
- "Could not register soundtrigger 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());
+ }
- ALOGW_IF(registerPassthroughServiceImplementations<
- bluetooth::audio::V2_0::IBluetoothAudioProvidersFactory>(),
- "Could not register Bluetooth audio API");
-
- // 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/effect/6.0/xml/api/current.txt b/audio/effect/6.0/xml/api/current.txt
index c9c59b6..072cf8f 100644
--- a/audio/effect/6.0/xml/api/current.txt
+++ b/audio/effect/6.0/xml/api/current.txt
@@ -138,6 +138,8 @@
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 89387bc..fcfecda 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">
diff --git a/compatibility_matrices/compatibility_matrix.current.xml b/compatibility_matrices/compatibility_matrix.current.xml
index 395848c..bb057fb 100644
--- a/compatibility_matrices/compatibility_matrix.current.xml
+++ b/compatibility_matrices/compatibility_matrix.current.xml
@@ -165,7 +165,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>
diff --git a/current.txt b/current.txt
index 26362d2..b58e466 100644
--- a/current.txt
+++ b/current.txt
@@ -612,6 +612,8 @@
40ab2c6866c18d32baf6e49e3053949e79601f56963a791e93e68b9ee18f718d android.hardware.bluetooth@1.1::IBluetoothHciCallbacks
07d0a252b2d8fa35887908a996ba395cf392968395fc30afab791f46e0c22a52 android.hardware.boot@1.1::IBootControl
74049a402be913963edfdd80828a53736570e9d8124a1bf18166b6ed46a6b0ab android.hardware.boot@1.1::types
+881aa8720fb1d69aa9843bfab69d810ab7654a61d2f5ab5e2626cbf240f24eaf android.hardware.dumpstate@1.1::types
+13b33f623521ded51a6c0f7ea5b77e97066d0aa1e38a83c2873f08ad67294f89 android.hardware.dumpstate@1.1::IDumpstateDevice
ce8dbe76eb9ee94b46ef98f725be992e760a5751073d4f4912484026541371f3 android.hardware.health@2.1::IHealth
26f04510a0b57aba5167c5c0a7c2f077c2acbb98b81902a072517829fd9fd67f android.hardware.health@2.1::IHealthInfoCallback
db47f4ceceb1f06c656f39caa70c557b0f8471ef59fd58611bea667ffca20101 android.hardware.health@2.1::types
@@ -622,11 +624,13 @@
27ae3724053940462114228872b3ffaf0b8e6177d5ba97f5a76339d12b8a99dd android.hardware.keymaster@4.1::IKeymasterDevice
adb0efdf1462e9b2e742c0dcadd598666aac551f178be06e755bfcdf5797abd0 android.hardware.keymaster@4.1::IOperation
ac429fca0da4ce91218768ec31b64ded88251f8a26d8c4f27c06abdc5b1926d9 android.hardware.keymaster@4.1::types
-4b5c8546533db9412fec6d32c0ef42b22e5e68dbf390c775ec3c22bb2d501102 android.hardware.neuralnetworks@1.3::IBuffer
-5a6b75f13f0e010a4268defa4f627b862ab2899fb04f9d985194a25bd8f9fe0d android.hardware.neuralnetworks@1.3::IDevice
-058b48f0e2e725bb2b3fa2b7917b0f0a696383d03a4c57afe26f0eadb6a7af28 android.hardware.neuralnetworks@1.3::IPreparedModel
-94e803236398bed1febb11cc21051bc42ec003700139b099d6c479e02a7ca3c3 android.hardware.neuralnetworks@1.3::IPreparedModelCallback
-12c51f9d04a52324510419aeee3e37bb3607e6900556cdde79774d80ed989855 android.hardware.neuralnetworks@1.3::types
+65c16331e57f6dd68b3971f06f78fe9e3209afb60630c31705aa355f9a52bf0d android.hardware.neuralnetworks@1.3::IBuffer
+d1f382d14e1384b907d5bb5780df7f01934650d556fedbed2f15a90773c657d6 android.hardware.neuralnetworks@1.3::IDevice
+4167dc3ad35e9cd0d2057d4868c7675ae2c3c9d05bbd614c1f5dccfa5fd68797 android.hardware.neuralnetworks@1.3::IExecutionCallback
+2fa3679ad7c94b5e88724adcd560c561041068a4ca565c63830e68101988746a android.hardware.neuralnetworks@1.3::IFencedExecutionCallback
+237b23b126a66f3432658020fed78cdd06ba6297459436fe6bae0ba753370833 android.hardware.neuralnetworks@1.3::IPreparedModel
+0439a1fbbec7f16e5e4c653d85ac685d51bfafbae15b8f8cca530acdd7d6a8ce android.hardware.neuralnetworks@1.3::IPreparedModelCallback
+3646950b10f7cacdafca13609b0e18496cea942f3bdfe920494661856eff48bb android.hardware.neuralnetworks@1.3::types
3e01d4446cd69fd1c48f8572efd97487bc179564b32bd795800b97bbe10be37b android.hardware.wifi@1.4::IWifi
a64467bae843569f0d465c5be7f0c7a5b987985b55a3ef4794dd5afc68538650 android.hardware.wifi.supplicant@1.3::ISupplicant
44445b8a03d7b9e68b2fbd954672c18a8fce9e32851b0692f4f4ab3407f86ecb android.hardware.wifi.supplicant@1.3::ISupplicantStaIface
diff --git a/drm/1.2/vts/functional/Android.bp b/drm/1.2/vts/functional/Android.bp
index 83ea104..ecc7d6c 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 8a68608..d5e453c 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;
@@ -94,7 +94,7 @@
* DrmHalTest
*/
-DrmHalTest::DrmHalTest() : vendorModule(getModuleForInstance(GetParam())) {}
+DrmHalTest::DrmHalTest() : vendorModule(getModuleForInstance(GetParamService())) {}
void DrmHalTest::SetUp() {
const ::testing::TestInfo* const test_info =
@@ -102,9 +102,9 @@
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);
@@ -124,8 +124,12 @@
contentConfigurations = vendorModule->getContentConfigurations();
// If drm scheme not installed skip subsequent tests
- if (!drmFactory->isCryptoSchemeSupported(getVendorUUID())) {
- GTEST_SKIP() << "vendor module drm scheme not supported";
+ 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";
@@ -140,7 +144,7 @@
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);
@@ -159,7 +163,7 @@
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);
@@ -170,6 +174,13 @@
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();
@@ -509,7 +520,7 @@
/**
* 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 71296dc..0dfff26 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::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";
@@ -54,7 +55,7 @@
* Ensure drm factory supports module UUID Scheme
*/
TEST_P(DrmHalTest, VendorUuidSupported) {
- 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);
}
@@ -82,7 +83,7 @@
* Ensure drm factory doesn't support an invalid mime type
*/
TEST_P(DrmHalTest, BadMimeNotSupported) {
- EXPECT_FALSE(drmFactory->isCryptoSchemeSupported_1_2(getVendorUUID(), kBadMime, kSwSecureCrypto));
+ EXPECT_FALSE(drmFactory->isCryptoSchemeSupported_1_2(getUUID(), kBadMime, kSwSecureCrypto));
}
/**
@@ -398,14 +399,14 @@
/**
* Ensure clearkey drm factory doesn't support security level higher than supported
*/
-TEST_P(DrmHalClearkeyTest, BadLevelNotSupported) {
- 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) {
+TEST_P(DrmHalClearkeyTestV1_2, GetKeyRequestResourceContention) {
Status status = drmPlugin->setPropertyString(kDrmErrorTestKey, kDrmErrorResourceContention);
EXPECT_EQ(Status::OK, status);
auto sessionId = openSession();
@@ -426,7 +427,7 @@
/**
* Test clearkey plugin offline key with mock error
*/
-TEST_P(DrmHalClearkeyTest, OfflineLicenseInvalidState) {
+TEST_P(DrmHalClearkeyTestV1_2, OfflineLicenseInvalidState) {
auto sessionId = openSession();
hidl_vec<uint8_t> keySetId = loadKeys(sessionId, KeyType::OFFLINE);
Status status = drmPlugin->setPropertyString(kDrmErrorTestKey, kDrmErrorInvalidState);
@@ -447,7 +448,7 @@
/**
* Test SessionLostState is triggered on error
*/
-TEST_P(DrmHalClearkeyTest, SessionLostState) {
+TEST_P(DrmHalClearkeyTestV1_2, SessionLostState) {
sp<DrmHalPluginListener> listener = new DrmHalPluginListener();
auto res = drmPlugin->setListener(listener);
EXPECT_OK(res);
@@ -467,7 +468,7 @@
/**
* Negative decrypt test. Decrypt with invalid key.
*/
-TEST_P(DrmHalClearkeyTest, DecryptWithEmptyKey) {
+TEST_P(DrmHalClearkeyTestV1_2, DecryptWithEmptyKey) {
vector<uint8_t> iv(AES_BLOCK_SIZE, 0);
const Pattern noPattern = {0, 0};
const uint32_t kClearBytes = 512;
@@ -504,7 +505,7 @@
/**
* Negative decrypt test. Decrypt with a key exceeds AES_BLOCK_SIZE.
*/
-TEST_P(DrmHalClearkeyTest, DecryptWithKeyTooLong) {
+TEST_P(DrmHalClearkeyTestV1_2, DecryptWithKeyTooLong) {
vector<uint8_t> iv(AES_BLOCK_SIZE, 0);
const Pattern noPattern = {0, 0};
const uint32_t kClearBytes = 512;
@@ -531,43 +532,3 @@
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 0000000..ea6e63d
--- /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
similarity index 92%
rename from drm/1.2/vts/functional/drm_hal_common.h
rename to drm/1.2/vts/functional/include/android/hardware/drm/1.2/vts/drm_hal_common.h
index 6b71aa4..6fee6f4 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,14 +23,19 @@
#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"
@@ -41,13 +46,10 @@
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 KeyStatusV1_0 = ::android::hardware::drm::V1_0::KeyStatus;
using StatusV1_0 = ::android::hardware::drm::V1_0::Status;
-
-using ::android::hardware::drm::V1_1::ICryptoFactory;
-using ::android::hardware::drm::V1_1::SecurityLevel;
-
using StatusV1_2 = ::android::hardware::drm::V1_2::Status;
using ::android::hardware::hidl_array;
@@ -58,7 +60,11 @@
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;
@@ -71,7 +77,7 @@
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();
@@ -79,7 +85,10 @@
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();
@@ -124,7 +133,7 @@
};
-class DrmHalClearkeyTest : public DrmHalTest {
+class DrmHalClearkeyTestV1_2 : public DrmHalTest {
public:
virtual void SetUp() override {
DrmHalTest::SetUp();
@@ -132,7 +141,7 @@
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 " << GetParam();
+ 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 08131cf..0000000
--- a/drm/1.2/vts/functional/vendor_modules.cpp
+++ /dev/null
@@ -1,83 +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 "drm_hal_vendor_module_api.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)();
-}
-
-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.2/vts/functional/vendor_modules.h b/drm/1.2/vts/functional/vendor_modules.h
deleted file mode 100644
index eacd2d1..0000000
--- a/drm/1.2/vts/functional/vendor_modules.h
+++ /dev/null
@@ -1,78 +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);
-
- /**
- * Retrieve a DrmHalVTSVendorModule given a service name.
- */
- DrmHalVTSVendorModule* getModuleByName(const std::string& name);
-
- /**
- * 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/dumpstate/1.0/vts/functional/VtsHalDumpstateV1_0TargetTest.cpp b/dumpstate/1.0/vts/functional/VtsHalDumpstateV1_0TargetTest.cpp
index 96b13c5..343d4c9 100644
--- a/dumpstate/1.0/vts/functional/VtsHalDumpstateV1_0TargetTest.cpp
+++ b/dumpstate/1.0/vts/functional/VtsHalDumpstateV1_0TargetTest.cpp
@@ -78,6 +78,7 @@
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 @@
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 0000000..2aa8c82
--- /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 0000000..24831b3
--- /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 0000000..a6f391a
--- /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 0000000..5267706
--- /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 0000000..583efbf
--- /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/health/2.1/README.md b/health/2.1/README.md
index bfcf13b..8390570 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 @@
`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 917f1c2..b6d9e3b 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/VtsHalHealthV2_1TargetTest.cpp b/health/2.1/vts/functional/VtsHalHealthV2_1TargetTest.cpp
index da9f5bb..e75b299 100644
--- a/health/2.1/vts/functional/VtsHalHealthV2_1TargetTest.cpp
+++ b/health/2.1/vts/functional/VtsHalHealthV2_1TargetTest.cpp
@@ -233,9 +233,13 @@
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/neuralnetworks/1.2/vts/functional/GeneratedTestHarness.cpp b/neuralnetworks/1.2/vts/functional/GeneratedTestHarness.cpp
index 4909214..599fd1d 100644
--- a/neuralnetworks/1.2/vts/functional/GeneratedTestHarness.cpp
+++ b/neuralnetworks/1.2/vts/functional/GeneratedTestHarness.cpp
@@ -272,7 +272,7 @@
int n;
std::tie(n, outputShapes, timing, std::ignore) =
controller->compute(request, testConfig.measureTiming, keys);
- executionStatus = nn::convertResultCodeToErrorStatus(n);
+ executionStatus = nn::convertToV1_0(nn::convertResultCodeToErrorStatus(n));
break;
}
diff --git a/neuralnetworks/1.2/vts/functional/ValidateBurst.cpp b/neuralnetworks/1.2/vts/functional/ValidateBurst.cpp
index 416744f..ec9629b 100644
--- a/neuralnetworks/1.2/vts/functional/ValidateBurst.cpp
+++ b/neuralnetworks/1.2/vts/functional/ValidateBurst.cpp
@@ -296,7 +296,8 @@
// collect serialized result by running regular burst
const auto [nRegular, outputShapesRegular, timingRegular, fallbackRegular] =
controllerRegular->compute(request, MeasureTiming::NO, keys);
- const ErrorStatus statusRegular = nn::convertResultCodeToErrorStatus(nRegular);
+ const ErrorStatus statusRegular =
+ nn::convertToV1_0(nn::convertResultCodeToErrorStatus(nRegular));
EXPECT_FALSE(fallbackRegular);
// skip test if regular burst output isn't useful for testing a failure
@@ -312,7 +313,7 @@
// large enough to return the serialized result
const auto [nSmall, outputShapesSmall, timingSmall, fallbackSmall] =
controllerSmall->compute(request, MeasureTiming::NO, keys);
- const ErrorStatus statusSmall = nn::convertResultCodeToErrorStatus(nSmall);
+ const ErrorStatus statusSmall = nn::convertToV1_0(nn::convertResultCodeToErrorStatus(nSmall));
EXPECT_NE(ErrorStatus::NONE, statusSmall);
EXPECT_EQ(0u, outputShapesSmall.size());
EXPECT_TRUE(badTiming(timingSmall));
diff --git a/neuralnetworks/1.2/vts/functional/ValidateRequest.cpp b/neuralnetworks/1.2/vts/functional/ValidateRequest.cpp
index 2d83b81..7b5ff9b 100644
--- a/neuralnetworks/1.2/vts/functional/ValidateRequest.cpp
+++ b/neuralnetworks/1.2/vts/functional/ValidateRequest.cpp
@@ -107,7 +107,7 @@
// execute and verify
const auto [n, outputShapes, timing, fallback] = burst->compute(request, measure, keys);
- const ErrorStatus status = nn::convertResultCodeToErrorStatus(n);
+ const ErrorStatus status = nn::convertToV1_0(nn::convertResultCodeToErrorStatus(n));
EXPECT_EQ(ErrorStatus::INVALID_ARGUMENT, status);
EXPECT_EQ(outputShapes.size(), 0);
EXPECT_TRUE(badTiming(timing));
diff --git a/neuralnetworks/1.3/Android.bp b/neuralnetworks/1.3/Android.bp
index 08e824d..7b02cc5 100644
--- a/neuralnetworks/1.3/Android.bp
+++ b/neuralnetworks/1.3/Android.bp
@@ -10,6 +10,8 @@
"types.hal",
"IBuffer.hal",
"IDevice.hal",
+ "IExecutionCallback.hal",
+ "IFencedExecutionCallback.hal",
"IPreparedModel.hal",
"IPreparedModelCallback.hal",
],
diff --git a/neuralnetworks/1.3/IBuffer.hal b/neuralnetworks/1.3/IBuffer.hal
index 84241c5..dfc57fe 100644
--- a/neuralnetworks/1.3/IBuffer.hal
+++ b/neuralnetworks/1.3/IBuffer.hal
@@ -16,7 +16,7 @@
package android.hardware.neuralnetworks@1.3;
-import @1.0::ErrorStatus;
+import ErrorStatus;
/**
* This interface represents a device memory buffer.
diff --git a/neuralnetworks/1.3/IDevice.hal b/neuralnetworks/1.3/IDevice.hal
index 8dc41f7..610db79 100644
--- a/neuralnetworks/1.3/IDevice.hal
+++ b/neuralnetworks/1.3/IDevice.hal
@@ -16,7 +16,6 @@
package android.hardware.neuralnetworks@1.3;
-import @1.0::ErrorStatus;
import @1.1::ExecutionPreference;
import @1.2::Constant;
import @1.2::DeviceType;
@@ -25,7 +24,10 @@
import BufferDesc;
import BufferRole;
import Capabilities;
+import ErrorStatus;
import Model;
+import OptionalTimePoint;
+import Priority;
import IBuffer;
import IPreparedModel;
import IPreparedModelCallback;
@@ -46,6 +48,19 @@
getCapabilities_1_3() generates (ErrorStatus status, Capabilities capabilities);
/**
+ * Returns whether the device is able to complete or abort a task within a
+ * specified duration.
+ *
+ * @return prepareModelDeadline 'true' if the device supports completing or
+ * aborting model preparation by the deadline when the deadline is supplied,
+ * 'false' otherwise.
+ * @return executionDeadline 'true' if the device supports completing or
+ * aborting an execution by the deadline when the deadline is supplied,
+ * 'false' otherwise.
+ */
+ supportsDeadlines() generates (bool prepareModelDeadline, bool executionDeadline);
+
+ /**
* Gets the supported operations in a model.
*
* getSupportedOperations indicates which operations of the top-level
@@ -118,6 +133,22 @@
* the callback object must be invoked with the appropriate ErrorStatus
* value and nullptr for the IPreparedModel.
*
+ * The model is prepared with a priority. This priority is relative to other
+ * prepared models owned by the same client. Higher priority executions may
+ * use more compute resources than lower priority executions, and may
+ * preempt or starve lower priority executions.
+ *
+ * prepareModel_1_3 can be called with an optional deadline. If the model
+ * is not able to be prepared before the provided deadline, the model
+ * preparation 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 preparation deadlines via
+ * IDevice::supportsDeadlines, and prepareModel_1_3 is called with a
+ * deadline, then the argument is invalid, and {@link
+ * ErrorStatus::INVALID_ARGUMENT} must be returned.
+ *
* Optionally, the driver may save the prepared model to cache during the
* asynchronous preparation. Any error that occurs when saving to cache must
* not affect the status of preparing the model. Even if the input arguments
@@ -139,6 +170,11 @@
* @param model The model to be prepared for execution.
* @param preference Indicates the intended execution behavior of a prepared
* model.
+ * @param priority The priority of the prepared model relative to other
+ * prepared models owned by the client.
+ * @param deadline The time by which the model must be prepared. If the
+ * model cannot be prepared by the deadline, the preparation must be
+ * aborted.
* @param modelCache A vector of handles with each entry holding exactly one
* cache file descriptor for the security-sensitive cache. The length of
* the vector must either be 0 indicating that caching information is
@@ -173,8 +209,12 @@
* - GENERAL_FAILURE if there is an unspecified error
* - INVALID_ARGUMENT if one of the input arguments related to preparing
* the model is invalid
+ * - MISSED_DEADLINE_* if the deadline for preparing a model cannot be
+ * met
+ * - RESOURCE_EXHAUSTED_* if the task was aborted by the driver
*/
prepareModel_1_3(Model model, ExecutionPreference preference,
+ Priority priority, OptionalTimePoint deadline,
vec<handle> modelCache, vec<handle> dataCache,
uint8_t[Constant:BYTE_SIZE_OF_CACHE_TOKEN] token,
IPreparedModelCallback callback)
@@ -220,6 +260,22 @@
* the model, the callback object must be invoked with the appropriate
* ErrorStatus value and nullptr for the IPreparedModel.
*
+ * The model is prepared with a priority. This priority is relative to other
+ * prepared models owned by the same client. Higher priority executions may
+ * use more compute resources than lower priority executions, and may
+ * preempt or starve lower priority executions.
+ *
+ * prepareModelFromCache_1_3 can be called with an optional deadline. If the
+ * model is not able to prepared before the provided deadline, the model
+ * preparation 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
+ * preparation deadlines via IDevice::supportsDeadlines, and
+ * prepareModelFromCache_1_3 is called with a deadline, then the argument is
+ * invalid, and {@link ErrorStatus::INVALID_ARGUMENT} must be returned.
+ *
* The only information that may be unknown to the model at this stage is
* the shape of the tensors, which may only be known at execution time. As
* such, some driver services may return partially prepared models, where
@@ -228,6 +284,11 @@
* used with different shapes of inputs on different (possibly concurrent)
* executions.
*
+ * @param priority The priority of the prepared model relative to other
+ * prepared models owned by the client.
+ * @param deadline The time by which the model must be prepared. If the
+ * model cannot be prepared by the deadline, the preparation must be
+ * aborted.
* @param modelCache A vector of handles with each entry holding exactly one
* cache file descriptor for the security-sensitive cache. The length of
* the vector must match the numModelCache returned from getNumberOfCacheFilesNeeded.
@@ -253,8 +314,12 @@
* - GENERAL_FAILURE if caching is not supported or if there is an
* unspecified error
* - INVALID_ARGUMENT if one of the input arguments is invalid
+ * - MISSED_DEADLINE_* if the deadline for preparing a model cannot be
+ * met
+ * - RESOURCE_EXHAUSTED_* if the task was aborted by the driver
*/
- prepareModelFromCache_1_3(vec<handle> modelCache, vec<handle> dataCache,
+ prepareModelFromCache_1_3(Priority priority, OptionalTimePoint deadline,
+ vec<handle> modelCache, vec<handle> dataCache,
uint8_t[Constant:BYTE_SIZE_OF_CACHE_TOKEN] token,
IPreparedModelCallback callback)
generates (ErrorStatus status);
diff --git a/neuralnetworks/1.3/IExecutionCallback.hal b/neuralnetworks/1.3/IExecutionCallback.hal
new file mode 100644
index 0000000..439428a
--- /dev/null
+++ b/neuralnetworks/1.3/IExecutionCallback.hal
@@ -0,0 +1,64 @@
+/*
+ * 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.neuralnetworks@1.3;
+
+import @1.2::IExecutionCallback;
+import @1.2::OutputShape;
+import @1.2::Timing;
+
+/**
+ * IExecutionCallback must be used to return the error status result from an
+ * execution asynchronously launched from IPreparedModel::execute*.
+ */
+interface IExecutionCallback extends @1.2::IExecutionCallback {
+
+ /**
+ * There are three notify methods declared for the IExecutionCallback
+ * interface: notify_1_3, notify_1_2, and notify. One of the three notify
+ * methods must be invoked immediately after the asynchronous task has
+ * finished performing the execution. One of the notify methods must be
+ * provided with the ErrorStatus from the execution. If the asynchronous
+ * task is not launched, one of the notify methods must be invoked with the
+ * appropriate error.
+ *
+ * @param status Error status returned from launching the asynchronous task
+ * (if the launch fails) or from the asynchronous task itself
+ * (if the launch succeeds). 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
+ * - OUTPUT_INSUFFICIENT_SIZE if at least one output
+ * operand buffer is not large enough to store the
+ * corresponding output
+ * - INVALID_ARGUMENT if one of the input arguments to
+ * prepareModel is invalid
+ * - MISSED_DEADLINE_* if the deadline could not be met
+ * - RESOURCE_EXHAUSTED_* if the task was aborted by the driver
+ * @param outputShapes A list of shape information of model output operands.
+ * The index into "outputShapes" corresponds with to index
+ * of the output operand in the Request outputs vector.
+ * outputShapes must be empty unless the status is either
+ * NONE or OUTPUT_INSUFFICIENT_SIZE.
+ * @param timing Duration of execution. 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.
+ */
+ oneway notify_1_3(ErrorStatus status, vec<OutputShape> outputShapes, Timing timing);
+};
diff --git a/neuralnetworks/1.3/IFencedExecutionCallback.hal b/neuralnetworks/1.3/IFencedExecutionCallback.hal
new file mode 100644
index 0000000..6030809
--- /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 00adc1f..d645de7 100644
--- a/neuralnetworks/1.3/IPreparedModel.hal
+++ b/neuralnetworks/1.3/IPreparedModel.hal
@@ -16,13 +16,16 @@
package android.hardware.neuralnetworks@1.3;
-import @1.0::ErrorStatus;
-import @1.2::IExecutionCallback;
import @1.2::IPreparedModel;
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
@@ -65,6 +68,17 @@
* values, the execution should complete successfully (ErrorStatus::NONE):
* 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 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
+ * 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 execute_1_3 is called with a deadline,
+ * then the argument is invalid, and {@link ErrorStatus::INVALID_ARGUMENT}
+ * must be returned.
+ *
* Any number of calls to the execute* and executeSynchronously* functions,
* in any combination, may be made concurrently, even on the same
* IPreparedModel object.
@@ -75,8 +89,12 @@
* 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 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:
@@ -87,8 +105,13 @@
* not large enough to store the resultant values
* - INVALID_ARGUMENT if one of the input arguments is
* invalid
+ * - MISSED_DEADLINE_* if the deadline for executing a model
+ * cannot be met
+ * - RESOURCE_EXHAUSTED_* if the task was aborted by the
+ * driver
*/
- execute_1_3(Request request, MeasureTiming measure, IExecutionCallback callback)
+ execute_1_3(Request request, MeasureTiming measure, OptionalTimePoint deadline,
+ IExecutionCallback callback)
generates (ErrorStatus status);
/**
@@ -116,6 +139,17 @@
* (ErrorStatus::NONE): There must be no failure unless the device itself is
* in a bad state.
*
+ * executeSynchronously_1_3 can be called with an optional deadline. If 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
+ * 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 executeSynchronously_1_3 is called with a
+ * deadline, then the argument is invalid, and
+ * {@link ErrorStatus::INVALID_ARGUMENT} must be returned.
+ *
* Any number of calls to the execute* and executeSynchronously* functions,
* in any combination, may be made concurrently, even on the same
* IPreparedModel object.
@@ -126,6 +160,9 @@
* 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 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:
* - NONE if execution is performed successfully
* - DEVICE_UNAVAILABLE if driver is offline or busy
@@ -135,16 +172,98 @@
* corresponding output
* - INVALID_ARGUMENT if one of the input arguments is
* invalid
+ * - MISSED_DEADLINE_* if the deadline for executing a model
+ * cannot be met
+ * - RESOURCE_EXHAUSTED_* if the task was aborted by the
+ * driver
* @return outputShapes A list of shape information of model output operands.
* The index into "outputShapes" corresponds to the index
* of the output operand in the Request outputs vector.
* outputShapes must be empty unless the status is either
* NONE or OUTPUT_INSUFFICIENT_SIZE.
- * @return Timing Duration of execution. Unless measure is YES and status is
+ * @return timing Duration of execution. Unless measure is YES 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
* measurement is not available.
*/
- executeSynchronously_1_3(Request request, MeasureTiming measure)
- generates (ErrorStatus status, vec<OutputShape> outputShapes, Timing timing);
+ executeSynchronously_1_3(Request request, MeasureTiming measure,
+ 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/IPreparedModelCallback.hal b/neuralnetworks/1.3/IPreparedModelCallback.hal
index ff295a2..11ebbf4 100644
--- a/neuralnetworks/1.3/IPreparedModelCallback.hal
+++ b/neuralnetworks/1.3/IPreparedModelCallback.hal
@@ -16,7 +16,6 @@
package android.hardware.neuralnetworks@1.3;
-import @1.0::ErrorStatus;
import @1.2::IPreparedModelCallback;
import IPreparedModel;
@@ -48,6 +47,10 @@
* unspecified error
* - INVALID_ARGUMENT if one of the input arguments to
* prepareModel is invalid
+ * - MISSED_DEADLINE_* if the deadline for executing a model
+ * cannot be met
+ * - RESOURCE_EXHAUSTED_* if the task was aborted by the
+ * driver
* @param preparedModel A model that has been asynchronously prepared for
* execution. If the model was unable to be prepared
* due to an error, nullptr must be passed in place of
diff --git a/neuralnetworks/1.3/types.hal b/neuralnetworks/1.3/types.hal
index a6d274a..ed577e4 100644
--- a/neuralnetworks/1.3/types.hal
+++ b/neuralnetworks/1.3/types.hal
@@ -17,6 +17,7 @@
package android.hardware.neuralnetworks@1.3;
import @1.0::DataLocation;
+import @1.0::ErrorStatus;
import @1.0::PerformanceInfo;
import @1.0::RequestArgument;
import @1.2::Model.ExtensionNameAndPrefix;
@@ -1414,6 +1415,7 @@
* * {@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
*
@@ -1424,6 +1426,8 @@
* * 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.
@@ -1904,6 +1908,11 @@
* 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
@@ -1924,6 +1933,7 @@
* 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
*
@@ -1934,6 +1944,8 @@
* * 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.
@@ -2185,6 +2197,7 @@
* * {@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
*
@@ -2195,6 +2208,8 @@
* * 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.
@@ -2241,6 +2256,7 @@
* 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.
*
@@ -4971,6 +4987,106 @@
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.
*
@@ -4992,13 +5108,23 @@
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,
};
/**
+ * Priority given to a prepared model for execution.
+ */
+enum Priority : int32_t {
+ LOW,
+ MEDIUM,
+ HIGH,
+};
+
+
+/**
* The capabilities of a driver.
*
* Performance of an operation comes from the type of its first operand.
@@ -5434,3 +5560,62 @@
*/
vec<MemoryPool> pools;
};
+
+/**
+ * Optional time point of the steady clock (as from std::chrono::steady_clock)
+ * measured in nanoseconds.
+ */
+safe_union OptionalTimePoint {
+ /** No time point provided. */
+ Monostate none;
+
+ /**
+ * Time point of the steady clock (as from std::chrono::steady_clock)
+ * measured in nanoseconds.
+ */
+ uint64_t nanoseconds;
+};
+
+/**
+ * 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 {
+ /**
+ * Failure because a deadline could not be met for a task, but future
+ * deadlines may still be met for the same task after a short delay.
+ */
+ MISSED_DEADLINE_TRANSIENT,
+
+ /**
+ * Failure because a deadline could not be met for a task, and future
+ * deadlines will likely also not be met for the same task even after a
+ * short delay.
+ */
+ MISSED_DEADLINE_PERSISTENT,
+
+ /**
+ * Failure because of a resource limitation within the driver, but future
+ * calls for the same task may still succeed after a short delay.
+ */
+ RESOURCE_EXHAUSTED_TRANSIENT,
+
+ /**
+ * Failure because of a resource limitation within the driver, and future
+ * calls for the same task will likely also fail even after a short
+ * delay.
+ */
+ RESOURCE_EXHAUSTED_PERSISTENT,
+};
diff --git a/neuralnetworks/1.3/types.t b/neuralnetworks/1.3/types.t
index f3319e5..a973923 100644
--- a/neuralnetworks/1.3/types.t
+++ b/neuralnetworks/1.3/types.t
@@ -19,6 +19,7 @@
package android.hardware.neuralnetworks@1.3;
import @1.0::DataLocation;
+import @1.0::ErrorStatus;
import @1.0::PerformanceInfo;
import @1.0::RequestArgument;
import @1.2::Model.ExtensionNameAndPrefix;
@@ -90,6 +91,16 @@
};
/**
+ * Priority given to a prepared model for execution.
+ */
+enum Priority : int32_t {
+ LOW,
+ MEDIUM,
+ HIGH,
+};
+
+
+/**
* The capabilities of a driver.
*
* Performance of an operation comes from the type of its first operand.
@@ -525,3 +536,49 @@
*/
vec<MemoryPool> pools;
};
+
+/**
+ * Optional time point of the steady clock (as from std::chrono::steady_clock)
+ * measured in nanoseconds.
+ */
+safe_union OptionalTimePoint {
+ /** No time point provided. */
+ Monostate none;
+
+ /**
+ * Time point of the steady clock (as from std::chrono::steady_clock)
+ * measured in nanoseconds.
+ */
+ uint64_t nanoseconds;
+};
+
+/**
+ * Return status of a function.
+ */
+enum ErrorStatus : @1.0::ErrorStatus {
+ /**
+ * Failure because a deadline could not be met for a task, but future
+ * deadlines may still be met for the same task after a short delay.
+ */
+ MISSED_DEADLINE_TRANSIENT,
+
+ /**
+ * Failure because a deadline could not be met for a task, and future
+ * deadlines will likely also not be met for the same task even after a
+ * short delay.
+ */
+ MISSED_DEADLINE_PERSISTENT,
+
+ /**
+ * Failure because of a resource limitation within the driver, but future
+ * calls for the same task may still succeed after a short delay.
+ */
+ RESOURCE_EXHAUSTED_TRANSIENT,
+
+ /**
+ * Failure because of a resource limitation within the driver, and future
+ * calls for the same task will likely also fail even after a short
+ * delay.
+ */
+ RESOURCE_EXHAUSTED_PERSISTENT,
+};
diff --git a/neuralnetworks/1.3/vts/functional/Android.bp b/neuralnetworks/1.3/vts/functional/Android.bp
index e2795de..8e7e9b9 100644
--- a/neuralnetworks/1.3/vts/functional/Android.bp
+++ b/neuralnetworks/1.3/vts/functional/Android.bp
@@ -15,11 +15,12 @@
//
cc_library_static {
- name: "VtsHalNeuralNetworksV1_3Callbacks",
+ name: "VtsHalNeuralNetworksV1_3_utils",
defaults: ["VtsHalTargetTestDefaults"],
export_include_dirs: ["include"],
srcs: [
"Callbacks.cpp",
+ "Utils.cpp",
],
static_libs: [
"android.hardware.neuralnetworks@1.0",
@@ -29,7 +30,7 @@
],
header_libs: [
"libbase_headers",
- ]
+ ],
}
cc_test {
@@ -39,6 +40,7 @@
"BasicTests.cpp",
"CompilationCachingTests.cpp",
"GeneratedTestHarness.cpp",
+ "QualityOfServiceTests.cpp",
"TestAssertions.cpp",
"ValidateBurst.cpp",
"ValidateModel.cpp",
@@ -50,6 +52,9 @@
"libnativewindow",
],
static_libs: [
+ "VtsHalNeuralNetworksV1_0_utils",
+ "VtsHalNeuralNetworksV1_2Callbacks",
+ "VtsHalNeuralNetworksV1_3_utils",
"android.hardware.neuralnetworks@1.0",
"android.hardware.neuralnetworks@1.1",
"android.hardware.neuralnetworks@1.2",
@@ -60,9 +65,7 @@
"libhidlmemory",
"libneuralnetworks_generated_test_harness",
"libneuralnetworks_utils",
- "VtsHalNeuralNetworksV1_0_utils",
- "VtsHalNeuralNetworksV1_2Callbacks",
- "VtsHalNeuralNetworksV1_3Callbacks",
+ "libsync",
],
whole_static_libs: [
"neuralnetworks_generated_V1_0_example",
diff --git a/neuralnetworks/1.3/vts/functional/BasicTests.cpp b/neuralnetworks/1.3/vts/functional/BasicTests.cpp
index b64dc2f..891850c 100644
--- a/neuralnetworks/1.3/vts/functional/BasicTests.cpp
+++ b/neuralnetworks/1.3/vts/functional/BasicTests.cpp
@@ -21,7 +21,6 @@
namespace android::hardware::neuralnetworks::V1_3::vts::functional {
using V1_0::DeviceStatus;
-using V1_0::ErrorStatus;
using V1_0::PerformanceInfo;
using V1_2::Constant;
using V1_2::DeviceType;
diff --git a/neuralnetworks/1.3/vts/functional/Callbacks.cpp b/neuralnetworks/1.3/vts/functional/Callbacks.cpp
index 4f08e72..5768e37 100644
--- a/neuralnetworks/1.3/vts/functional/Callbacks.cpp
+++ b/neuralnetworks/1.3/vts/functional/Callbacks.cpp
@@ -24,12 +24,16 @@
namespace android::hardware::neuralnetworks::V1_3::implementation {
-using V1_0::ErrorStatus;
+using V1_2::OutputShape;
+using V1_2::Timing;
+
+constexpr Timing kNoTiming = {.timeOnDevice = std::numeric_limits<uint64_t>::max(),
+ .timeInDriver = std::numeric_limits<uint64_t>::max()};
// PreparedModelCallback methods begin here
-Return<void> PreparedModelCallback::notify(ErrorStatus errorStatus,
- const sp<V1_0::IPreparedModel>& preparedModel) {
+Return<void> PreparedModelCallback::notifyInternal(ErrorStatus errorStatus,
+ const sp<V1_0::IPreparedModel>& preparedModel) {
{
std::lock_guard<std::mutex> hold(mMutex);
@@ -48,14 +52,19 @@
return Void();
}
-Return<void> PreparedModelCallback::notify_1_2(ErrorStatus errorStatus,
- const sp<V1_2::IPreparedModel>& preparedModel) {
- return notify(errorStatus, preparedModel);
+Return<void> PreparedModelCallback::notify(V1_0::ErrorStatus errorStatus,
+ const sp<V1_0::IPreparedModel>& preparedModel) {
+ return notifyInternal(static_cast<ErrorStatus>(errorStatus), preparedModel);
}
-Return<void> PreparedModelCallback::notify_1_3(ErrorStatus errorStatus,
+Return<void> PreparedModelCallback::notify_1_2(V1_0::ErrorStatus errorStatus,
+ const sp<V1_2::IPreparedModel>& preparedModel) {
+ return notifyInternal(static_cast<ErrorStatus>(errorStatus), preparedModel);
+}
+
+Return<void> PreparedModelCallback::notify_1_3(V1_3::ErrorStatus errorStatus,
const sp<V1_3::IPreparedModel>& preparedModel) {
- return notify(errorStatus, preparedModel);
+ return notifyInternal(errorStatus, preparedModel);
}
void PreparedModelCallback::wait() const {
@@ -73,4 +82,82 @@
return mPreparedModel;
}
+// ExecutionCallback methods begin here
+
+Return<void> ExecutionCallback::notify(V1_0::ErrorStatus errorStatus) {
+ return notifyInternal(static_cast<ErrorStatus>(errorStatus), {}, kNoTiming);
+}
+
+Return<void> ExecutionCallback::notify_1_2(V1_0::ErrorStatus errorStatus,
+ const hidl_vec<OutputShape>& outputShapes,
+ const Timing& timing) {
+ return notifyInternal(static_cast<ErrorStatus>(errorStatus), outputShapes, timing);
+}
+
+Return<void> ExecutionCallback::notify_1_3(V1_3::ErrorStatus errorStatus,
+ const hidl_vec<OutputShape>& outputShapes,
+ const Timing& timing) {
+ return notifyInternal(errorStatus, outputShapes, timing);
+}
+
+void ExecutionCallback::wait() const {
+ std::unique_lock<std::mutex> lock(mMutex);
+ mCondition.wait(lock, [this] { return mNotified; });
+}
+
+ErrorStatus ExecutionCallback::getStatus() const {
+ wait();
+ return mErrorStatus;
+}
+
+const std::vector<OutputShape>& ExecutionCallback::getOutputShapes() const {
+ wait();
+ return mOutputShapes;
+}
+
+Timing ExecutionCallback::getTiming() const {
+ wait();
+ return mTiming;
+}
+
+Return<void> ExecutionCallback::notifyInternal(ErrorStatus errorStatus,
+ hidl_vec<OutputShape> outputShapes, Timing timing) {
+ // check results
+ if (errorStatus == ErrorStatus::OUTPUT_INSUFFICIENT_SIZE) {
+ // outputShapes must not be empty if OUTPUT_INSUFFICIENT_SIZE.
+ if (outputShapes.size() == 0) {
+ LOG(ERROR) << "Notifid with empty output shape vector when OUTPUT_INSUFFICIENT_SIZE";
+ errorStatus = ErrorStatus::GENERAL_FAILURE;
+ outputShapes = {};
+ timing = kNoTiming;
+ }
+ } else if (errorStatus != ErrorStatus::NONE) {
+ // outputShapes must be empty if errorStatus is neither NONE nor OUTPUT_INSUFFICIENT_SIZE.
+ if (outputShapes.size() != 0) {
+ LOG(ERROR) << "Notified with non-empty output shape vector when error status is "
+ "neither NONE nor OUTPUT_INSUFFICIENT_SIZE";
+ errorStatus = ErrorStatus::GENERAL_FAILURE;
+ outputShapes = {};
+ timing = kNoTiming;
+ }
+ }
+
+ // store results
+ {
+ std::lock_guard<std::mutex> hold(mMutex);
+
+ // quick-return if object has already been notified
+ if (mNotified) {
+ return Void();
+ }
+
+ mErrorStatus = errorStatus;
+ mOutputShapes = std::move(outputShapes);
+ mTiming = timing;
+ mNotified = true;
+ }
+ mCondition.notify_all();
+ return Void();
+}
+
} // namespace android::hardware::neuralnetworks::V1_3::implementation
diff --git a/neuralnetworks/1.3/vts/functional/CompilationCachingTests.cpp b/neuralnetworks/1.3/vts/functional/CompilationCachingTests.cpp
index 5cb466f..576e524 100644
--- a/neuralnetworks/1.3/vts/functional/CompilationCachingTests.cpp
+++ b/neuralnetworks/1.3/vts/functional/CompilationCachingTests.cpp
@@ -29,6 +29,7 @@
#include <thread>
#include "1.3/Callbacks.h"
+#include "1.3/Utils.h"
#include "GeneratedTestHarness.h"
#include "MemoryUtils.h"
#include "TestHarness.h"
@@ -49,7 +50,6 @@
using namespace test_helper;
using implementation::PreparedModelCallback;
-using V1_0::ErrorStatus;
using V1_1::ExecutionPreference;
using V1_2::Constant;
using V1_2::OperationType;
@@ -238,8 +238,8 @@
mCacheDir.push_back('/');
Return<void> ret = kDevice->getNumberOfCacheFilesNeeded(
- [this](ErrorStatus status, uint32_t numModelCache, uint32_t numDataCache) {
- EXPECT_EQ(ErrorStatus::NONE, status);
+ [this](V1_0::ErrorStatus status, uint32_t numModelCache, uint32_t numDataCache) {
+ EXPECT_EQ(V1_0::ErrorStatus::NONE, status);
mNumModelCache = numModelCache;
mNumDataCache = numDataCache;
});
@@ -324,9 +324,9 @@
// Launch prepare model.
sp<PreparedModelCallback> preparedModelCallback = new PreparedModelCallback();
hidl_array<uint8_t, sizeof(mToken)> cacheToken(mToken);
- Return<ErrorStatus> prepareLaunchStatus =
- kDevice->prepareModel_1_3(model, ExecutionPreference::FAST_SINGLE_ANSWER,
- modelCache, dataCache, cacheToken, preparedModelCallback);
+ Return<ErrorStatus> prepareLaunchStatus = kDevice->prepareModel_1_3(
+ model, ExecutionPreference::FAST_SINGLE_ANSWER, kDefaultPriority, {}, modelCache,
+ dataCache, cacheToken, preparedModelCallback);
ASSERT_TRUE(prepareLaunchStatus.isOk());
ASSERT_EQ(static_cast<ErrorStatus>(prepareLaunchStatus), ErrorStatus::NONE);
@@ -370,7 +370,7 @@
sp<PreparedModelCallback> preparedModelCallback = new PreparedModelCallback();
hidl_array<uint8_t, sizeof(mToken)> cacheToken(mToken);
Return<ErrorStatus> prepareLaunchStatus = kDevice->prepareModelFromCache_1_3(
- modelCache, dataCache, cacheToken, preparedModelCallback);
+ kDefaultPriority, {}, modelCache, dataCache, cacheToken, preparedModelCallback);
ASSERT_TRUE(prepareLaunchStatus.isOk());
if (static_cast<ErrorStatus>(prepareLaunchStatus) != ErrorStatus::NONE) {
*preparedModel = nullptr;
diff --git a/neuralnetworks/1.3/vts/functional/GeneratedTestHarness.cpp b/neuralnetworks/1.3/vts/functional/GeneratedTestHarness.cpp
index 805d5b5..8ea0b7e 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>
@@ -44,8 +46,8 @@
#include <vector>
#include "1.0/Utils.h"
-#include "1.2/Callbacks.h"
#include "1.3/Callbacks.h"
+#include "1.3/Utils.h"
#include "ExecutionBurstController.h"
#include "MemoryUtils.h"
#include "TestHarness.h"
@@ -56,9 +58,9 @@
using namespace test_helper;
using hidl::memory::V1_0::IMemory;
+using implementation::ExecutionCallback;
using implementation::PreparedModelCallback;
using V1_0::DataLocation;
-using V1_0::ErrorStatus;
using V1_0::RequestArgument;
using V1_1::ExecutionPreference;
using V1_2::Constant;
@@ -66,12 +68,11 @@
using V1_2::OutputShape;
using V1_2::SymmPerChannelQuantParams;
using V1_2::Timing;
-using V1_2::implementation::ExecutionCallback;
using HidlToken = hidl_array<uint8_t, static_cast<uint32_t>(Constant::BYTE_SIZE_OF_CACHE_TOKEN)>;
namespace {
-enum class Executor { ASYNC, SYNC, BURST };
+enum class Executor { ASYNC, SYNC, BURST, FENCED };
enum class OutputType { FULLY_SPECIFIED, UNSPECIFIED, INSUFFICIENT };
@@ -79,6 +80,13 @@
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;
@@ -453,7 +461,7 @@
static Return<ErrorStatus> ExecutePreparedModel(const sp<IPreparedModel>& preparedModel,
const Request& request, MeasureTiming measure,
sp<ExecutionCallback>& callback) {
- return preparedModel->execute_1_3(request, measure, callback);
+ return preparedModel->execute_1_3(request, measure, {}, callback);
}
static Return<ErrorStatus> ExecutePreparedModel(const sp<IPreparedModel>& preparedModel,
const Request& request, MeasureTiming measure,
@@ -461,7 +469,7 @@
Timing* timing) {
ErrorStatus result;
Return<void> ret = preparedModel->executeSynchronously_1_3(
- request, measure,
+ request, measure, {},
[&result, outputShapes, timing](ErrorStatus error, const hidl_vec<OutputShape>& shapes,
const Timing& time) {
result = error;
@@ -563,9 +571,44 @@
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;
@@ -649,6 +692,11 @@
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;
@@ -672,7 +720,8 @@
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) {
@@ -709,14 +758,16 @@
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);
} break;
case TestKind::QUANTIZATION_COUPLING: {
ASSERT_TRUE(testModel.hasQuant8CoupledOperands());
- createPreparedModel(device, model, &preparedModel, /*reportSkipping*/ false);
+ createPreparedModel(device, model, &preparedModel,
+ /*reportSkipping*/ false);
TestModel signedQuantizedModel = convertQuant8AsymmOperandsToSigned(testModel);
sp<IPreparedModel> preparedCoupledModel;
createPreparedModel(device, createModel(signedQuantizedModel), &preparedCoupledModel,
@@ -745,6 +796,12 @@
void GeneratedTestBase::SetUp() {
testing::TestWithParam<GeneratedTestParam>::SetUp();
ASSERT_NE(kDevice, nullptr);
+
+ const Return<void> ret =
+ kDevice->supportsDeadlines([this](bool prepareModelDeadline, bool executionDeadline) {
+ mSupportsDeadlines = {prepareModelDeadline, executionDeadline};
+ });
+ ASSERT_TRUE(ret.isOk());
}
std::vector<NamedModel> getNamedModels(const FilterFn& filter) {
@@ -765,6 +822,9 @@
// 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 {};
@@ -780,6 +840,10 @@
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);
}
@@ -787,12 +851,16 @@
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 2273e3b..e597fac 100644
--- a/neuralnetworks/1.3/vts/functional/GeneratedTestHarness.h
+++ b/neuralnetworks/1.3/vts/functional/GeneratedTestHarness.h
@@ -36,6 +36,7 @@
void SetUp() override;
const sp<IDevice> kDevice = getData(std::get<NamedDevice>(GetParam()));
const test_helper::TestModel& kTestModel = *getData(std::get<NamedModel>(GetParam()));
+ std::pair<bool, bool> mSupportsDeadlines;
};
using FilterFn = std::function<bool(const test_helper::TestModel&)>;
@@ -64,6 +65,8 @@
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/QualityOfServiceTests.cpp b/neuralnetworks/1.3/vts/functional/QualityOfServiceTests.cpp
new file mode 100644
index 0000000..62ffcda
--- /dev/null
+++ b/neuralnetworks/1.3/vts/functional/QualityOfServiceTests.cpp
@@ -0,0 +1,299 @@
+/*
+ * 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 "1.0/Utils.h"
+#include "1.3/Callbacks.h"
+#include "1.3/Utils.h"
+#include "GeneratedTestHarness.h"
+#include "Utils.h"
+
+namespace android::hardware::neuralnetworks::V1_3::vts::functional {
+
+using implementation::ExecutionCallback;
+using implementation::PreparedModelCallback;
+using test_helper::TestBuffer;
+using test_helper::TestModel;
+using V1_1::ExecutionPreference;
+using V1_2::MeasureTiming;
+using V1_2::OutputShape;
+using V1_2::Timing;
+
+using HidlToken =
+ hidl_array<uint8_t, static_cast<uint32_t>(V1_2::Constant::BYTE_SIZE_OF_CACHE_TOKEN)>;
+
+enum class DeadlineBoundType { NOW, UNLIMITED };
+constexpr std::array<DeadlineBoundType, 2> deadlineBounds = {DeadlineBoundType::NOW,
+ DeadlineBoundType::UNLIMITED};
+std::string toString(DeadlineBoundType type) {
+ switch (type) {
+ case DeadlineBoundType::NOW:
+ return "NOW";
+ case DeadlineBoundType::UNLIMITED:
+ return "UNLIMITED";
+ }
+ LOG(FATAL) << "Unrecognized DeadlineBoundType: " << static_cast<int>(type);
+ return {};
+}
+
+using Results = std::tuple<ErrorStatus, hidl_vec<OutputShape>, Timing>;
+using MaybeResults = std::optional<Results>;
+
+using ExecutionFunction =
+ std::function<MaybeResults(const sp<IPreparedModel>& preparedModel, const Request& request,
+ DeadlineBoundType deadlineBound)>;
+
+static OptionalTimePoint makeOptionalTimePoint(DeadlineBoundType deadlineBoundType) {
+ OptionalTimePoint deadline;
+ switch (deadlineBoundType) {
+ case DeadlineBoundType::NOW: {
+ const auto currentTime = std::chrono::steady_clock::now();
+ const auto currentTimeInNanoseconds =
+ std::chrono::time_point_cast<std::chrono::nanoseconds>(currentTime);
+ const uint64_t nanosecondsSinceEpoch =
+ currentTimeInNanoseconds.time_since_epoch().count();
+ deadline.nanoseconds(nanosecondsSinceEpoch);
+ } break;
+ case DeadlineBoundType::UNLIMITED: {
+ uint64_t unlimited = std::numeric_limits<uint64_t>::max();
+ deadline.nanoseconds(unlimited);
+ } break;
+ }
+ return deadline;
+}
+
+void runPrepareModelTest(const sp<IDevice>& device, const Model& model, Priority priority,
+ std::optional<DeadlineBoundType> deadlineBound) {
+ OptionalTimePoint deadline;
+ if (deadlineBound.has_value()) {
+ deadline = makeOptionalTimePoint(deadlineBound.value());
+ }
+
+ // see if service can handle model
+ bool fullySupportsModel = false;
+ const Return<void> supportedCall = device->getSupportedOperations_1_3(
+ model, [&fullySupportsModel](ErrorStatus status, const hidl_vec<bool>& supported) {
+ ASSERT_EQ(ErrorStatus::NONE, status);
+ ASSERT_NE(0ul, supported.size());
+ fullySupportsModel = std::all_of(supported.begin(), supported.end(),
+ [](bool valid) { return valid; });
+ });
+ ASSERT_TRUE(supportedCall.isOk());
+
+ // launch prepare model
+ const sp<PreparedModelCallback> preparedModelCallback = new PreparedModelCallback();
+ const Return<ErrorStatus> prepareLaunchStatus = device->prepareModel_1_3(
+ model, ExecutionPreference::FAST_SINGLE_ANSWER, priority, deadline,
+ hidl_vec<hidl_handle>(), hidl_vec<hidl_handle>(), HidlToken(), preparedModelCallback);
+ ASSERT_TRUE(prepareLaunchStatus.isOk());
+ ASSERT_EQ(ErrorStatus::NONE, static_cast<ErrorStatus>(prepareLaunchStatus));
+
+ // retrieve prepared model
+ preparedModelCallback->wait();
+ const ErrorStatus prepareReturnStatus = preparedModelCallback->getStatus();
+ const sp<V1_0::IPreparedModel> preparedModelV1_0 = preparedModelCallback->getPreparedModel();
+ const sp<IPreparedModel> preparedModel =
+ IPreparedModel::castFrom(preparedModelV1_0).withDefault(nullptr);
+
+ // The getSupportedOperations_1_3 call returns a list of operations that are
+ // guaranteed not to fail if prepareModel_1_3 is called, and
+ // 'fullySupportsModel' is true i.f.f. the entire model is guaranteed.
+ // If a driver has any doubt that it can prepare an operation, it must
+ // return false. So here, if a driver isn't sure if it can support an
+ // operation, but reports that it successfully prepared the model, the test
+ // can continue.
+ if (!fullySupportsModel && prepareReturnStatus != ErrorStatus::NONE) {
+ ASSERT_EQ(nullptr, preparedModel.get());
+ return;
+ }
+
+ // verify return status
+ if (!deadlineBound.has_value()) {
+ EXPECT_EQ(ErrorStatus::NONE, prepareReturnStatus);
+ } else {
+ switch (deadlineBound.value()) {
+ case DeadlineBoundType::NOW:
+ // If the execution was launched with a deadline of NOW, the
+ // deadline has already passed when the driver would launch the
+ // execution. In this case, the driver must return
+ // MISSED_DEADLINE_*.
+ EXPECT_TRUE(prepareReturnStatus == ErrorStatus::MISSED_DEADLINE_TRANSIENT ||
+ prepareReturnStatus == ErrorStatus::MISSED_DEADLINE_PERSISTENT);
+ break;
+ case DeadlineBoundType::UNLIMITED:
+ // If an unlimited deadline is supplied, we expect the execution to
+ // proceed normally. In this case, check it normally by breaking out
+ // of the switch statement.
+ EXPECT_EQ(ErrorStatus::NONE, prepareReturnStatus);
+ break;
+ }
+ }
+ ASSERT_EQ(prepareReturnStatus == ErrorStatus::NONE, preparedModel.get() != nullptr);
+}
+
+void runPrepareModelTests(const sp<IDevice>& device, const Model& model,
+ bool supportsPrepareModelDeadline) {
+ // test priority
+ for (auto priority : hidl_enum_range<Priority>{}) {
+ SCOPED_TRACE("priority: " + toString(priority));
+ if (priority == kDefaultPriority) continue;
+ runPrepareModelTest(device, model, priority, {});
+ }
+
+ // test deadline
+ if (supportsPrepareModelDeadline) {
+ for (auto deadlineBound : deadlineBounds) {
+ SCOPED_TRACE("deadlineBound: " + toString(deadlineBound));
+ runPrepareModelTest(device, model, kDefaultPriority, deadlineBound);
+ }
+ }
+}
+
+static MaybeResults executeAsynchronously(const sp<IPreparedModel>& preparedModel,
+ const Request& request, DeadlineBoundType deadlineBound) {
+ SCOPED_TRACE("asynchronous");
+ const MeasureTiming measure = MeasureTiming::NO;
+ const OptionalTimePoint deadline = makeOptionalTimePoint(deadlineBound);
+
+ // launch execution
+ const sp<ExecutionCallback> callback = new ExecutionCallback();
+ Return<ErrorStatus> ret = preparedModel->execute_1_3(request, measure, deadline, callback);
+ EXPECT_TRUE(ret.isOk());
+ EXPECT_EQ(ErrorStatus::NONE, ret.withDefault(ErrorStatus::GENERAL_FAILURE));
+ if (!ret.isOk() || ret != ErrorStatus::NONE) return std::nullopt;
+
+ // retrieve execution results
+ callback->wait();
+ const ErrorStatus status = callback->getStatus();
+ hidl_vec<OutputShape> outputShapes = callback->getOutputShapes();
+ const Timing timing = callback->getTiming();
+
+ // return results
+ return Results{status, std::move(outputShapes), timing};
+}
+
+static MaybeResults executeSynchronously(const sp<IPreparedModel>& preparedModel,
+ const Request& request, DeadlineBoundType deadlineBound) {
+ SCOPED_TRACE("synchronous");
+ const MeasureTiming measure = MeasureTiming::NO;
+ const OptionalTimePoint deadline = makeOptionalTimePoint(deadlineBound);
+
+ // configure results callback
+ MaybeResults results;
+ const auto cb = [&results](const auto&... args) { *results = {args...}; };
+
+ // run execution
+ const Return<void> ret =
+ preparedModel->executeSynchronously_1_3(request, measure, deadline, cb);
+ EXPECT_TRUE(ret.isOk());
+ if (!ret.isOk()) return std::nullopt;
+
+ // return results
+ return results;
+}
+
+void runExecutionTest(const sp<IPreparedModel>& preparedModel, const TestModel& testModel,
+ const Request& request, bool synchronous, DeadlineBoundType deadlineBound) {
+ const ExecutionFunction execute = synchronous ? executeSynchronously : executeAsynchronously;
+
+ // Perform execution and unpack results.
+ const auto results = execute(preparedModel, request, deadlineBound);
+ if (!results.has_value()) return;
+ const auto& [status, outputShapes, timing] = results.value();
+
+ // Verify no timing information was returned
+ EXPECT_EQ(UINT64_MAX, timing.timeOnDevice);
+ EXPECT_EQ(UINT64_MAX, timing.timeInDriver);
+
+ // Validate deadline information if applicable.
+ switch (deadlineBound) {
+ case DeadlineBoundType::NOW:
+ // If the execution was launched with a deadline of NOW, the
+ // deadline has already passed when the driver would launch the
+ // execution. In this case, the driver must return
+ // MISSED_DEADLINE_*.
+ ASSERT_TRUE(status == ErrorStatus::MISSED_DEADLINE_TRANSIENT ||
+ status == ErrorStatus::MISSED_DEADLINE_PERSISTENT);
+ return;
+ case DeadlineBoundType::UNLIMITED:
+ // If an unlimited deadline is supplied, we expect the execution to
+ // proceed normally. In this case, check it normally by breaking out
+ // of the switch statement.
+ ASSERT_EQ(ErrorStatus::NONE, status);
+ break;
+ }
+
+ // If the model output operands are fully specified, outputShapes must be either
+ // either empty, or have the same number of elements as the number of outputs.
+ ASSERT_TRUE(outputShapes.size() == 0 || outputShapes.size() == testModel.outputIndexes.size());
+
+ // Go through all outputs, check returned output shapes.
+ for (uint32_t i = 0; i < outputShapes.size(); i++) {
+ EXPECT_TRUE(outputShapes[i].isSufficient);
+ const auto& expect = testModel.operands[testModel.outputIndexes[i]].dimensions;
+ const std::vector<uint32_t> actual = outputShapes[i].dimensions;
+ EXPECT_EQ(expect, actual);
+ }
+
+ // Retrieve execution results.
+ ASSERT_TRUE(nn::compliantWithV1_0(request));
+ const V1_0::Request request10 = nn::convertToV1_0(request);
+ const std::vector<TestBuffer> outputs = getOutputBuffers(request10);
+
+ // We want "close-enough" results.
+ checkResults(testModel, outputs);
+}
+
+void runExecutionTests(const sp<IPreparedModel>& preparedModel, const TestModel& testModel,
+ const Request& request) {
+ for (bool synchronous : {false, true}) {
+ for (auto deadlineBound : deadlineBounds) {
+ runExecutionTest(preparedModel, testModel, request, synchronous, deadlineBound);
+ }
+ }
+}
+
+void runTests(const sp<IDevice>& device, const TestModel& testModel,
+ std::pair<bool, bool> supportsDeadlines) {
+ // setup
+ const auto [supportsPrepareModelDeadline, supportsExecutionDeadline] = supportsDeadlines;
+ if (!supportsPrepareModelDeadline && !supportsExecutionDeadline) return;
+ const Model model = createModel(testModel);
+
+ // run prepare model tests
+ runPrepareModelTests(device, model, supportsPrepareModelDeadline);
+
+ if (supportsExecutionDeadline) {
+ // prepare model
+ sp<IPreparedModel> preparedModel;
+ createPreparedModel(device, model, &preparedModel);
+ if (preparedModel == nullptr) return;
+
+ // run execution tests
+ const Request request = nn::convertToV1_3(createRequest(testModel));
+ runExecutionTests(preparedModel, testModel, request);
+ }
+}
+
+class DeadlineTest : public GeneratedTestBase {};
+
+TEST_P(DeadlineTest, Test) {
+ runTests(kDevice, kTestModel, mSupportsDeadlines);
+}
+
+INSTANTIATE_GENERATED_TEST(DeadlineTest,
+ [](const TestModel& testModel) { return !testModel.expectFailure; });
+
+} // namespace android::hardware::neuralnetworks::V1_3::vts::functional
diff --git a/neuralnetworks/1.3/vts/functional/Utils.cpp b/neuralnetworks/1.3/vts/functional/Utils.cpp
new file mode 100644
index 0000000..23e2af8
--- /dev/null
+++ b/neuralnetworks/1.3/vts/functional/Utils.cpp
@@ -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.
+ */
+
+#include "1.3/Utils.h"
+
+#include <iostream>
+
+namespace android::hardware::neuralnetworks::V1_3 {
+
+::std::ostream& operator<<(::std::ostream& os, ErrorStatus errorStatus) {
+ return os << toString(errorStatus);
+}
+
+} // namespace android::hardware::neuralnetworks::V1_3
diff --git a/neuralnetworks/1.3/vts/functional/ValidateBurst.cpp b/neuralnetworks/1.3/vts/functional/ValidateBurst.cpp
index 7df8046..6ff9dfd 100644
--- a/neuralnetworks/1.3/vts/functional/ValidateBurst.cpp
+++ b/neuralnetworks/1.3/vts/functional/ValidateBurst.cpp
@@ -34,7 +34,6 @@
using nn::ExecutionBurstController;
using nn::RequestChannelSender;
using nn::ResultChannelReceiver;
-using V1_0::ErrorStatus;
using V1_0::Request;
using V1_2::FmqRequestDatum;
using V1_2::FmqResultDatum;
@@ -80,16 +79,17 @@
ASSERT_NE(nullptr, fmqResultDescriptor);
// configure burst
- ErrorStatus errorStatus;
+ V1_0::ErrorStatus errorStatus;
sp<IBurstContext> burstContext;
const Return<void> ret = preparedModel->configureExecutionBurst(
callback, *fmqRequestDescriptor, *fmqResultDescriptor,
- [&errorStatus, &burstContext](ErrorStatus status, const sp<IBurstContext>& context) {
+ [&errorStatus, &burstContext](V1_0::ErrorStatus status,
+ const sp<IBurstContext>& context) {
errorStatus = status;
burstContext = context;
});
ASSERT_TRUE(ret.isOk());
- ASSERT_EQ(ErrorStatus::NONE, errorStatus);
+ ASSERT_EQ(V1_0::ErrorStatus::NONE, errorStatus);
ASSERT_NE(nullptr, burstContext.get());
// return values
@@ -144,7 +144,7 @@
auto results = receiver->getBlocking();
ASSERT_TRUE(results.has_value());
const auto [status, outputShapes, timing] = std::move(*results);
- EXPECT_NE(ErrorStatus::NONE, status);
+ EXPECT_NE(V1_0::ErrorStatus::NONE, status);
EXPECT_EQ(0u, outputShapes.size());
EXPECT_TRUE(badTiming(timing));
}
@@ -302,14 +302,15 @@
// collect serialized result by running regular burst
const auto [nRegular, outputShapesRegular, timingRegular, fallbackRegular] =
controllerRegular->compute(request, MeasureTiming::NO, keys);
- const ErrorStatus statusRegular = nn::convertResultCodeToErrorStatus(nRegular);
+ const V1_0::ErrorStatus statusRegular =
+ nn::convertToV1_0(nn::convertResultCodeToErrorStatus(nRegular));
EXPECT_FALSE(fallbackRegular);
// skip test if regular burst output isn't useful for testing a failure
// caused by having too small of a length for the result FMQ
const std::vector<FmqResultDatum> serialized =
android::nn::serialize(statusRegular, outputShapesRegular, timingRegular);
- if (statusRegular != ErrorStatus::NONE ||
+ if (statusRegular != V1_0::ErrorStatus::NONE ||
serialized.size() <= kExecutionBurstChannelSmallLength) {
return;
}
@@ -318,8 +319,9 @@
// large enough to return the serialized result
const auto [nSmall, outputShapesSmall, timingSmall, fallbackSmall] =
controllerSmall->compute(request, MeasureTiming::NO, keys);
- const ErrorStatus statusSmall = nn::convertResultCodeToErrorStatus(nSmall);
- EXPECT_NE(ErrorStatus::NONE, statusSmall);
+ const V1_0::ErrorStatus statusSmall =
+ nn::convertToV1_0(nn::convertResultCodeToErrorStatus(nSmall));
+ EXPECT_NE(V1_0::ErrorStatus::NONE, statusSmall);
EXPECT_EQ(0u, outputShapesSmall.size());
EXPECT_TRUE(badTiming(timingSmall));
EXPECT_FALSE(fallbackSmall);
diff --git a/neuralnetworks/1.3/vts/functional/ValidateModel.cpp b/neuralnetworks/1.3/vts/functional/ValidateModel.cpp
index cc86264..1245432 100644
--- a/neuralnetworks/1.3/vts/functional/ValidateModel.cpp
+++ b/neuralnetworks/1.3/vts/functional/ValidateModel.cpp
@@ -18,13 +18,13 @@
#include "1.0/Utils.h"
#include "1.3/Callbacks.h"
+#include "1.3/Utils.h"
#include "GeneratedTestHarness.h"
#include "VtsHalNeuralnetworks.h"
namespace android::hardware::neuralnetworks::V1_3::vts::functional {
using implementation::PreparedModelCallback;
-using V1_0::ErrorStatus;
using V1_1::ExecutionPreference;
using V1_2::SymmPerChannelQuantParams;
using HidlToken =
@@ -44,13 +44,19 @@
}
static void validatePrepareModel(const sp<IDevice>& device, const std::string& message,
- const Model& model, ExecutionPreference preference) {
+ const Model& model, ExecutionPreference preference,
+ bool testDeadline) {
SCOPED_TRACE(message + " [prepareModel_1_3]");
+ OptionalTimePoint deadline;
+ if (testDeadline) {
+ deadline.nanoseconds(std::numeric_limits<uint64_t>::max());
+ }
+
sp<PreparedModelCallback> preparedModelCallback = new PreparedModelCallback();
- Return<ErrorStatus> prepareLaunchStatus =
- device->prepareModel_1_3(model, preference, hidl_vec<hidl_handle>(),
- hidl_vec<hidl_handle>(), HidlToken(), preparedModelCallback);
+ Return<ErrorStatus> prepareLaunchStatus = device->prepareModel_1_3(
+ model, preference, kDefaultPriority, deadline, hidl_vec<hidl_handle>(),
+ hidl_vec<hidl_handle>(), HidlToken(), preparedModelCallback);
ASSERT_TRUE(prepareLaunchStatus.isOk());
ASSERT_EQ(ErrorStatus::INVALID_ARGUMENT, static_cast<ErrorStatus>(prepareLaunchStatus));
@@ -73,12 +79,13 @@
// to the model does not leave this function.
static void validate(const sp<IDevice>& device, const std::string& message, Model model,
const std::function<void(Model*)>& mutation,
- ExecutionPreference preference = ExecutionPreference::FAST_SINGLE_ANSWER) {
+ ExecutionPreference preference = ExecutionPreference::FAST_SINGLE_ANSWER,
+ bool testDeadline = false) {
mutation(&model);
- if (validExecutionPreference(preference)) {
+ if (validExecutionPreference(preference) && !testDeadline) {
validateGetSupportedOperations(device, message, model);
}
- validatePrepareModel(device, message, model, preference);
+ validatePrepareModel(device, message, model, preference, testDeadline);
}
static uint32_t addOperand(Model* model) {
@@ -330,6 +337,7 @@
// - 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]) {
@@ -392,6 +400,20 @@
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;
}
@@ -714,9 +736,19 @@
}
}
+///////////////////////// DEADLINE /////////////////////////
+
+static void deadlineTest(const sp<IDevice>& device, const Model& model) {
+ const std::string message = "deadlineTest: deadline not supported";
+ const auto noop = [](Model*) {};
+ validate(device, message, model, noop, ExecutionPreference::FAST_SINGLE_ANSWER,
+ /*testDeadline=*/true);
+}
+
////////////////////////// ENTRY POINT //////////////////////////////
-void validateModel(const sp<IDevice>& device, const Model& model) {
+void validateModel(const sp<IDevice>& device, const Model& model,
+ bool prepareModelDeadlineSupported) {
mutateOperandTypeTest(device, model);
mutateOperandRankTest(device, model);
mutateOperandScaleTest(device, model);
@@ -732,6 +764,9 @@
addOperationInputTest(device, model);
addOperationOutputTest(device, model);
mutateExecutionPreferenceTest(device, model);
+ if (!prepareModelDeadlineSupported) {
+ deadlineTest(device, model);
+ }
}
} // namespace android::hardware::neuralnetworks::V1_3::vts::functional
diff --git a/neuralnetworks/1.3/vts/functional/ValidateRequest.cpp b/neuralnetworks/1.3/vts/functional/ValidateRequest.cpp
index 96dc589..2fd9b64 100644
--- a/neuralnetworks/1.3/vts/functional/ValidateRequest.cpp
+++ b/neuralnetworks/1.3/vts/functional/ValidateRequest.cpp
@@ -16,9 +16,11 @@
#define LOG_TAG "neuralnetworks_hidl_hal_test"
+#include <android/hardware/neuralnetworks/1.3/IFencedExecutionCallback.h>
#include <chrono>
+
#include "1.0/Utils.h"
-#include "1.2/Callbacks.h"
+#include "1.3/Callbacks.h"
#include "ExecutionBurstController.h"
#include "GeneratedTestHarness.h"
#include "TestHarness.h"
@@ -27,11 +29,10 @@
namespace android::hardware::neuralnetworks::V1_3::vts::functional {
-using V1_0::ErrorStatus;
+using implementation::ExecutionCallback;
using V1_2::MeasureTiming;
using V1_2::OutputShape;
using V1_2::Timing;
-using V1_2::implementation::ExecutionCallback;
///////////////////////// UTILITY FUNCTIONS /////////////////////////
@@ -44,7 +45,8 @@
// that use the request. Note that the request here is passed by value, and any
// mutation to the request does not leave this function.
static void validate(const sp<IPreparedModel>& preparedModel, const std::string& message,
- Request request, const std::function<void(Request*)>& mutation) {
+ Request request, const std::function<void(Request*)>& mutation,
+ bool testDeadline = false) {
mutation(&request);
// We'd like to test both with timing requested and without timing
@@ -57,13 +59,18 @@
};
MeasureTiming measure = (hash & 1) ? MeasureTiming::YES : MeasureTiming::NO;
+ OptionalTimePoint deadline;
+ if (testDeadline) {
+ deadline.nanoseconds(std::numeric_limits<uint64_t>::max());
+ }
+
// asynchronous
{
SCOPED_TRACE(message + " [execute_1_3]");
sp<ExecutionCallback> executionCallback = new ExecutionCallback();
Return<ErrorStatus> executeLaunchStatus =
- preparedModel->execute_1_3(request, measure, executionCallback);
+ preparedModel->execute_1_3(request, measure, deadline, executionCallback);
ASSERT_TRUE(executeLaunchStatus.isOk());
ASSERT_EQ(ErrorStatus::INVALID_ARGUMENT, static_cast<ErrorStatus>(executeLaunchStatus));
@@ -81,7 +88,7 @@
SCOPED_TRACE(message + " [executeSynchronously_1_3]");
Return<void> executeStatus = preparedModel->executeSynchronously_1_3(
- request, measure,
+ request, measure, deadline,
[](ErrorStatus error, const hidl_vec<OutputShape>& outputShapes,
const Timing& timing) {
ASSERT_EQ(ErrorStatus::INVALID_ARGUMENT, error);
@@ -93,7 +100,7 @@
// burst
// TODO(butlermichael): Check if we need to test burst in V1_3 if the interface remains V1_2.
- {
+ if (!testDeadline) {
SCOPED_TRACE(message + " [burst]");
ASSERT_TRUE(nn::compliantWithV1_0(request));
@@ -131,6 +138,22 @@
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 ////////////////////////////////////
@@ -153,17 +176,29 @@
}
}
+///////////////////////// DEADLINE ////////////////////////////////////
+
+static void deadlineTest(const sp<IPreparedModel>& preparedModel, const Request& request) {
+ const std::string message = "deadlineTest: deadline not supported";
+ const auto noop = [](Request*) {};
+ validate(preparedModel, message, request, noop, /*testDeadline=*/true);
+}
+
///////////////////////////// ENTRY POINT //////////////////////////////////
-void validateRequest(const sp<IPreparedModel>& preparedModel, const Request& request) {
+void validateRequest(const sp<IPreparedModel>& preparedModel, const Request& request,
+ bool executionDeadlineSupported) {
removeInputTest(preparedModel, request);
removeOutputTest(preparedModel, request);
+ if (!executionDeadlineSupported) {
+ deadlineTest(preparedModel, request);
+ }
}
void validateRequestFailure(const sp<IPreparedModel>& preparedModel, const Request& request) {
SCOPED_TRACE("Expecting request to fail [executeSynchronously_1_3]");
Return<void> executeStatus = preparedModel->executeSynchronously_1_3(
- request, MeasureTiming::NO,
+ request, MeasureTiming::NO, {},
[](ErrorStatus error, const hidl_vec<OutputShape>& outputShapes, const Timing& timing) {
ASSERT_NE(ErrorStatus::NONE, error);
EXPECT_EQ(outputShapes.size(), 0);
diff --git a/neuralnetworks/1.3/vts/functional/VtsHalNeuralnetworks.cpp b/neuralnetworks/1.3/vts/functional/VtsHalNeuralnetworks.cpp
index 1140b68..896ace6 100644
--- a/neuralnetworks/1.3/vts/functional/VtsHalNeuralnetworks.cpp
+++ b/neuralnetworks/1.3/vts/functional/VtsHalNeuralnetworks.cpp
@@ -23,6 +23,7 @@
#include <utility>
#include "1.0/Utils.h"
#include "1.3/Callbacks.h"
+#include "1.3/Utils.h"
#include "GeneratedTestHarness.h"
#include "TestHarness.h"
#include "Utils.h"
@@ -32,7 +33,6 @@
using HidlToken =
hidl_array<uint8_t, static_cast<uint32_t>(V1_2::Constant::BYTE_SIZE_OF_CACHE_TOKEN)>;
using implementation::PreparedModelCallback;
-using V1_0::ErrorStatus;
using V1_1::ExecutionPreference;
// internal helper function
@@ -55,8 +55,8 @@
// launch prepare model
const sp<PreparedModelCallback> preparedModelCallback = new PreparedModelCallback();
const Return<ErrorStatus> prepareLaunchStatus = device->prepareModel_1_3(
- model, ExecutionPreference::FAST_SINGLE_ANSWER, hidl_vec<hidl_handle>(),
- hidl_vec<hidl_handle>(), HidlToken(), preparedModelCallback);
+ model, ExecutionPreference::FAST_SINGLE_ANSWER, kDefaultPriority, {},
+ hidl_vec<hidl_handle>(), hidl_vec<hidl_handle>(), HidlToken(), preparedModelCallback);
ASSERT_TRUE(prepareLaunchStatus.isOk());
ASSERT_EQ(ErrorStatus::NONE, static_cast<ErrorStatus>(prepareLaunchStatus));
@@ -84,6 +84,7 @@
<< std::endl;
GTEST_SKIP();
}
+
ASSERT_EQ(ErrorStatus::NONE, prepareReturnStatus);
ASSERT_NE(nullptr, preparedModel->get());
}
@@ -122,23 +123,45 @@
INSTANTIATE_DEVICE_TEST(NeuralnetworksHidlTest);
// Forward declaration from ValidateModel.cpp
-void validateModel(const sp<IDevice>& device, const Model& model);
+void validateModel(const sp<IDevice>& device, const Model& model,
+ bool prepareModelDeadlineSupported);
// Forward declaration from ValidateRequest.cpp
-void validateRequest(const sp<IPreparedModel>& preparedModel, const Request& request);
+void validateRequest(const sp<IPreparedModel>& preparedModel, const Request& request,
+ bool executionDeadlineSupported);
// Forward declaration from ValidateRequest.cpp
void validateRequestFailure(const sp<IPreparedModel>& preparedModel, const Request& request);
// Forward declaration from ValidateBurst.cpp
void validateBurst(const sp<IPreparedModel>& preparedModel, const V1_0::Request& request);
-void validateEverything(const sp<IDevice>& device, const Model& model, const Request& request) {
- validateModel(device, model);
+// 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;
+ validateModel(device, model, prepareModelDeadlineSupported);
// Create IPreparedModel.
sp<IPreparedModel> preparedModel;
createPreparedModel(device, model, &preparedModel);
if (preparedModel == nullptr) return;
- validateRequest(preparedModel, request);
+ 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));
@@ -146,10 +169,12 @@
validateBurst(preparedModel, request10);
}
-void validateFailure(const sp<IDevice>& device, const Model& model, const Request& request) {
+void validateFailure(const sp<IDevice>& device, const Model& model, const Request& request,
+ std::pair<bool, bool> supportsDeadlines) {
+ const bool prepareModelDeadlineSupported = supportsDeadlines.first;
// TODO: Should this always succeed?
// What if the invalid input is part of the model (i.e., a parameter).
- validateModel(device, model);
+ validateModel(device, model, prepareModelDeadlineSupported);
// Create IPreparedModel.
sp<IPreparedModel> preparedModel;
@@ -163,9 +188,9 @@
const Model model = createModel(kTestModel);
const Request request = nn::convertToV1_3(createRequest(kTestModel));
if (kTestModel.expectFailure) {
- validateFailure(kDevice, model, request);
+ validateFailure(kDevice, model, request, mSupportsDeadlines);
} else {
- validateEverything(kDevice, model, request);
+ validateEverything(kDevice, model, request, mSupportsDeadlines);
}
}
diff --git a/neuralnetworks/1.3/vts/functional/include/1.3/Callbacks.h b/neuralnetworks/1.3/vts/functional/include/1.3/Callbacks.h
index fb19a84..e9dec2d 100644
--- a/neuralnetworks/1.3/vts/functional/include/1.3/Callbacks.h
+++ b/neuralnetworks/1.3/vts/functional/include/1.3/Callbacks.h
@@ -18,8 +18,11 @@
#define ANDROID_HARDWARE_NEURALNETWORKS_V1_3_CALLBACKS_H
#include <android-base/thread_annotations.h>
+#include <android/hardware/neuralnetworks/1.0/IExecutionCallback.h>
#include <android/hardware/neuralnetworks/1.0/IPreparedModelCallback.h>
+#include <android/hardware/neuralnetworks/1.2/IExecutionCallback.h>
#include <android/hardware/neuralnetworks/1.2/IPreparedModelCallback.h>
+#include <android/hardware/neuralnetworks/1.3/IExecutionCallback.h>
#include <android/hardware/neuralnetworks/1.3/IPreparedModelCallback.h>
#include <hidl/Status.h>
#include <condition_variable>
@@ -136,7 +139,7 @@
* @param preparedModel Returned model that has been prepared for execution,
* nullptr if the model was unable to be prepared.
*/
- Return<void> notify_1_3(V1_0::ErrorStatus status,
+ Return<void> notify_1_3(V1_3::ErrorStatus status,
const sp<V1_3::IPreparedModel>& preparedModel) override;
/**
@@ -158,7 +161,7 @@
* - GENERAL_FAILURE if there is an unspecified error
* - INVALID_ARGUMENT if the input model is invalid
*/
- V1_0::ErrorStatus getStatus() const;
+ ErrorStatus getStatus() const;
/**
* Retrieves the model that has been prepared for execution from the
@@ -173,13 +176,216 @@
sp<V1_0::IPreparedModel> getPreparedModel() const;
private:
+ Return<void> notifyInternal(ErrorStatus status, const sp<V1_0::IPreparedModel>& preparedModel);
+
mutable std::mutex mMutex;
mutable std::condition_variable mCondition;
bool mNotified GUARDED_BY(mMutex) = false;
- V1_0::ErrorStatus mErrorStatus = V1_0::ErrorStatus::GENERAL_FAILURE;
+ ErrorStatus mErrorStatus = ErrorStatus::GENERAL_FAILURE;
sp<V1_0::IPreparedModel> mPreparedModel;
};
+/**
+ * The ExecutionCallback class is used to receive the results of the execution
+ * from a task executing asynchronously with respect to the runtime. If a
+ * calling thread calls wait or get* on a ExecutionCallback object and the
+ * corresponding asynchronous task has not finished the execution, the calling
+ * thread will block until the asynchronous task has either called one of the
+ * notify* methods.
+ *
+ * If the callback object is notified more than once, only the results of the
+ * first call to notify* are used, and the results from subsequent calls are
+ * discarded.
+ *
+ * This callback object is passed as an argument to IPreparedModel::execute*.
+ */
+class ExecutionCallback : public IExecutionCallback {
+ public:
+ /**
+ * IExecutionCallback::notify marks the callback object with the return
+ * status of the asynchronous execution that held this callback and enables
+ * all prior and future wait calls on the ExecutionCallback object to
+ * proceed.
+ *
+ * One of the IExecutionCallback::notify* methods must be called on a given
+ * ExecutionCallback object.
+ *
+ * If the callback object is notified more than once, only the results of
+ * the first call to notify* are used, and the results from subsequent calls
+ * are discarded.
+ *
+ * @param status Error status returned from launching the asynchronous task
+ * (if the launch fails) or from the asynchronous task itself (if the
+ * launch succeeds). Must be:
+ * - NONE if the asynchronous execution was successful
+ * - DEVICE_UNAVAILABLE if driver is offline or busy
+ * - GENERAL_FAILURE if there is an unspecified error
+ * - OUTPUT_INSUFFICIENT_SIZE if provided output buffer is not large
+ * enough to store the resultant values
+ * - INVALID_ARGUMENT if the input request is invalid
+ */
+ Return<void> notify(V1_0::ErrorStatus status) override;
+
+ /**
+ * IExecutionCallback::notify_1_2 marks the callback object with the results
+ * (error status, dynamic output shapes, and timing information) of the
+ * asynchronous execution that held this callback and enables all prior and
+ * future wait calls on the ExecutionCallback object to proceed.
+ *
+ * One of the IExecutionCallback::notify* methods must be called on a given
+ * ExecutionCallback object.
+ *
+ * If the callback object is notified more than once, only the results of
+ * the first call to notify* are used, and the results from subsequent calls
+ * are discarded.
+ *
+ * @param status Error status returned from launching the asynchronous task
+ * (if the launch fails) or from the asynchronous task itself (if the
+ * launch succeeds). 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
+ * - OUTPUT_INSUFFICIENT_SIZE if at least one output operand buffer is
+ * not large enough to store the corresponding output
+ * - INVALID_ARGUMENT if one of the input arguments to prepareModel is
+ * invalid
+ * @param outputShapes A list of shape information of model output operands.
+ * The index into "outputShapes" corresponds to the index of the output
+ * operand in the Request outputs vector. outputShapes must be empty
+ * unless the status is either NONE or OUTPUT_INSUFFICIENT_SIZE.
+ * @param Timing Duration of execution. 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<void> notify_1_2(V1_0::ErrorStatus status,
+ const hidl_vec<V1_2::OutputShape>& outputShapes,
+ const V1_2::Timing& timing) override;
+
+ /**
+ * IExecutionCallback::notify_1_3 marks the callback object with the results
+ * (error status, dynamic output shapes, and timing information) of the
+ * asynchronous execution that held this callback and enables all prior and
+ * future wait calls on the ExecutionCallback object to proceed.
+ *
+ * One of the IExecutionCallback::notify* methods must be called on a given
+ * ExecutionCallback object.
+ *
+ * If the callback object is notified more than once, only the results of
+ * the first call to notify* are used, and the results from subsequent calls
+ * are discarded.
+ *
+ * @param status Error status returned from launching the asynchronous task
+ * (if the launch fails) or from the asynchronous task itself (if the
+ * launch succeeds). 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
+ * - OUTPUT_INSUFFICIENT_SIZE if at least one output operand buffer is
+ * not large enough to store the corresponding output
+ * - INVALID_ARGUMENT if one of the input arguments to prepareModel is
+ * invalid
+ * - MISSED_DEADLINE_* if the deadline was not met
+ * @param outputShapes A list of shape information of model output operands.
+ * The index into "outputShapes" corresponds to the index of the output
+ * operand in the Request outputs vector. outputShapes must be empty
+ * unless the status is either NONE or OUTPUT_INSUFFICIENT_SIZE.
+ * @param Timing Duration of execution. 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<void> notify_1_3(V1_3::ErrorStatus status,
+ const hidl_vec<V1_2::OutputShape>& outputShapes,
+ const V1_2::Timing& timing) override;
+
+ /**
+ * ExecutionCallback::wait blocks until notify* has been called on the
+ * callback object.
+ */
+ void wait() const;
+
+ /**
+ * Retrieves the error status returned from the asynchronous task launched
+ * by one of the IPreparedModel::execute* methods. If
+ * IPreparedModel::execute* (but not IPreparedModel::executeSynchronously*)
+ * has not finished asynchronously executing, this call will block until the
+ * asynchronous task notifies the object.
+ *
+ * @return status Error status returned from launching the asynchronous task
+ * (if the launch fails) or from the asynchronous task itself (if the
+ * launch succeeds). 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
+ * - OUTPUT_INSUFFICIENT_SIZE if at least one output operand buffer is
+ * not large enough to store the corresponding output
+ * - INVALID_ARGUMENT if one of the input arguments to prepareModel is
+ * invalid
+ * - MISSED_DEADLINE_* if the deadline could not be met
+ */
+ V1_3::ErrorStatus getStatus() const;
+
+ /**
+ * Retrieves the error status returned from the asynchronous task launched
+ * by one of the IPreparedModel::execute* methods. If
+ * IPreparedModel::execute* (but not IPreparedModel::executeSynchronously*)
+ * has not finished asynchronously executing, this call will block until the
+ * asynchronous task notifies the object.
+ *
+ * If the asynchronous task was launched by IPreparedModel::execute, an
+ * empty vector will be returned.
+ *
+ * @return outputShapes A list of shape information of model output
+ * operands. The index into "outputShapes" corresponds to the index of
+ * the output operand in the Request outputs vector. outputShapes must
+ * be empty unless the status is either NONE or
+ * OUTPUT_INSUFFICIENT_SIZE. outputShaps may be empty if the status is
+ * NONE and all model output operands are fully-specified at execution
+ * time. outputShapes must have the same number of elements as the
+ * number of model output operands if the status is
+ * OUTPUT_INSUFFICIENT_SIZE, or if the status is NONE and the model has
+ * at least one output operand that is not fully-specified.
+ */
+ const std::vector<V1_2::OutputShape>& getOutputShapes() const;
+
+ /**
+ * Retrieves the error status returned from the asynchronous task launched
+ * by one of the IPreparedModel::execute* methods. If
+ * IPreparedModel::execute* (but not IPreparedModel::executeSynchronously*)
+ * has not finished asynchronously executing, this call will block until the
+ * asynchronous task notifies the object.
+ *
+ * If the asynchronous task was launched by IPreparedModel::execute, every
+ * time must be UINT64_MAX.
+ *
+ * @return timing Duration of the execution. Every time must be UINT64_MAX
+ * unless the status is NONE.
+ */
+ V1_2::Timing getTiming() const;
+
+ private:
+ /*
+ * ExecutionCallback::notifyInternal stores the results of the execution
+ * (status, output shapes, and timing information) in the ExecutionCallback
+ * object before any call to wait or get* return. It then enables all prior
+ * and future wait calls on the ExecutionCallback object to proceed.
+ */
+ Return<void> notifyInternal(V1_3::ErrorStatus errorStatus,
+ hidl_vec<V1_2::OutputShape> outputShapes, V1_2::Timing timing);
+
+ // members
+ mutable std::mutex mMutex;
+ mutable std::condition_variable mCondition;
+ bool mNotified GUARDED_BY(mMutex) = false;
+ V1_3::ErrorStatus mErrorStatus = V1_3::ErrorStatus::GENERAL_FAILURE;
+ std::vector<V1_2::OutputShape> mOutputShapes = {};
+ V1_2::Timing mTiming = {};
+};
+
} // namespace android::hardware::neuralnetworks::V1_3::implementation
#endif // ANDROID_HARDWARE_NEURALNETWORKS_V1_3_CALLBACKS_H
diff --git a/neuralnetworks/1.3/vts/functional/include/1.3/Utils.h b/neuralnetworks/1.3/vts/functional/include/1.3/Utils.h
new file mode 100644
index 0000000..3661b66
--- /dev/null
+++ b/neuralnetworks/1.3/vts/functional/include/1.3/Utils.h
@@ -0,0 +1,36 @@
+/*
+ * 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_NEURALNETWORKS_V1_3_UTILS_H
+#define ANDROID_HARDWARE_NEURALNETWORKS_V1_3_UTILS_H
+
+#include <android/hardware/neuralnetworks/1.3/types.h>
+#include <iosfwd>
+
+namespace android::hardware::neuralnetworks {
+
+inline constexpr V1_3::Priority kDefaultPriority = V1_3::Priority::MEDIUM;
+
+} // namespace android::hardware::neuralnetworks
+
+namespace android::hardware::neuralnetworks::V1_3 {
+
+// pretty-print values for error messages
+::std::ostream& operator<<(::std::ostream& os, ErrorStatus errorStatus);
+
+} // namespace android::hardware::neuralnetworks::V1_3
+
+#endif // ANDROID_HARDWARE_NEURALNETWORKS_V1_3_UTILS_H
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 75abbbf..02dcbab 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 @@
/* 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 @@
}
EXPECT_EQ(CardState::ABSENT, cardStatus.cardState);
}
+#endif
/* Test setSimCardPower power up */
serial = GetRandomSerialNumber();
diff --git a/sensors/2.0/multihal/HalProxy.cpp b/sensors/2.0/multihal/HalProxy.cpp
index 7c52661..ac6f17a 100644
--- a/sensors/2.0/multihal/HalProxy.cpp
+++ b/sensors/2.0/multihal/HalProxy.cpp
@@ -290,6 +290,8 @@
stream << " Wakelock ref count: " << mWakelockRefCount << std::endl;
stream << " # of events on pending write writes queue: " << mSizePendingWriteEventsQueue
<< std::endl;
+ stream << " Most events seen on pending write events queue: "
+ << mMostEventsObservedPendingWriteEventsQueue << std::endl;
if (!mPendingWriteEventsQueue.empty()) {
stream << " Size of events list on front of pending writes queue: "
<< mPendingWriteEventsQueue.front().first.size() << std::endl;
@@ -571,6 +573,8 @@
std::vector<Event> eventsLeft(events.begin() + numToWrite, events.end());
mPendingWriteEventsQueue.push({eventsLeft, numWakeupEvents});
mSizePendingWriteEventsQueue += numLeft;
+ mMostEventsObservedPendingWriteEventsQueue =
+ std::max(mMostEventsObservedPendingWriteEventsQueue, mSizePendingWriteEventsQueue);
mEventQueueWriteCV.notify_one();
}
}
diff --git a/sensors/2.0/multihal/include/HalProxy.h b/sensors/2.0/multihal/include/HalProxy.h
index ce28e67..978f7cf 100644
--- a/sensors/2.0/multihal/include/HalProxy.h
+++ b/sensors/2.0/multihal/include/HalProxy.h
@@ -200,6 +200,9 @@
*/
std::queue<std::pair<std::vector<Event>, size_t>> mPendingWriteEventsQueue;
+ //! The most events observed on the pending write events queue for debug purposes.
+ size_t mMostEventsObservedPendingWriteEventsQueue = 0;
+
//! The max number of events allowed in the pending write events queue
static constexpr size_t kMaxSizePendingWriteEventsQueue = 100000;
diff --git a/vibrator/aidl/default/Vibrator.cpp b/vibrator/aidl/default/Vibrator.cpp
index 0d7131a..9236b95 100644
--- a/vibrator/aidl/default/Vibrator.cpp
+++ b/vibrator/aidl/default/Vibrator.cpp
@@ -139,6 +139,9 @@
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);
@@ -146,8 +149,7 @@
if (e.scale <= 0.0f || e.scale > 1.0f) {
return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_ARGUMENT);
}
- if (e.primitive < CompositePrimitive::NOOP ||
- e.primitive > CompositePrimitive::LIGHT_TICK) {
+ if (std::find(supported.begin(), supported.end(), e.primitive) == supported.end()) {
return ndk::ScopedAStatus::fromExceptionCode(EX_UNSUPPORTED_OPERATION);
}
}