Merge "Add radio config 1.2 VTS tests"
diff --git a/current.txt b/current.txt
index f4aa57e..ce3d7b5 100644
--- a/current.txt
+++ b/current.txt
@@ -478,4 +478,6 @@
# ABI preserving changes to HALs during Android R
b69a7615c508acf5c5201efd1bfa3262167874fc3594e2db5a3ff93addd8ac75 android.hardware.keymaster@4.0::IKeymasterDevice
+ad431c8de51c07934a068e3043d8dd0537ac4d3158627706628b123f42df48dc android.hardware.neuralnetworks@1.0::IPreparedModel
+aafcc10cf04ab247e86d4582586c71c6b4c2b8c479241ffa7fe37deb659fc942 android.hardware.neuralnetworks@1.2::IPreparedModel
1a6e2bd289f22931c526b21916910f1d4c436b7acb9556e4243de4ce8e6cc2e4 android.hardware.soundtrigger@2.0::ISoundTriggerHwCallback
diff --git a/gatekeeper/1.0/default/OWNERS b/gatekeeper/1.0/default/OWNERS
new file mode 100644
index 0000000..335660d
--- /dev/null
+++ b/gatekeeper/1.0/default/OWNERS
@@ -0,0 +1,2 @@
+jdanis@google.com
+swillden@google.com
diff --git a/gatekeeper/1.0/software/Android.bp b/gatekeeper/1.0/software/Android.bp
new file mode 100644
index 0000000..148c989
--- /dev/null
+++ b/gatekeeper/1.0/software/Android.bp
@@ -0,0 +1,28 @@
+cc_binary {
+ name: "android.hardware.gatekeeper@1.0-service.software",
+ defaults: ["hidl_defaults"],
+ relative_install_path: "hw",
+ vendor: true,
+ init_rc: ["android.hardware.gatekeeper@1.0-service.software.rc"],
+
+ srcs: [
+ "service.cpp",
+ "SoftGateKeeperDevice.cpp",
+ ],
+
+ shared_libs: [
+ "android.hardware.gatekeeper@1.0",
+ "libbase",
+ "libhardware",
+ "libhidlbase",
+ "libhidltransport",
+ "libutils",
+ "liblog",
+ "libcrypto",
+ "libgatekeeper",
+ ],
+
+ static_libs: ["libscrypt_static"],
+
+ vintf_fragments: ["android.hardware.gatekeeper@1.0-service.software.xml"],
+}
diff --git a/gatekeeper/1.0/software/OWNERS b/gatekeeper/1.0/software/OWNERS
new file mode 100644
index 0000000..335660d
--- /dev/null
+++ b/gatekeeper/1.0/software/OWNERS
@@ -0,0 +1,2 @@
+jdanis@google.com
+swillden@google.com
diff --git a/gatekeeper/1.0/software/SoftGateKeeper.h b/gatekeeper/1.0/software/SoftGateKeeper.h
new file mode 100644
index 0000000..3276d1e
--- /dev/null
+++ b/gatekeeper/1.0/software/SoftGateKeeper.h
@@ -0,0 +1,171 @@
+/*
+ * Copyright 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+#ifndef SOFT_GATEKEEPER_H_
+#define SOFT_GATEKEEPER_H_
+
+extern "C" {
+#include <openssl/rand.h>
+#include <openssl/sha.h>
+
+#include <crypto_scrypt.h>
+}
+
+#include <android-base/memory.h>
+#include <gatekeeper/gatekeeper.h>
+
+#include <iostream>
+#include <memory>
+#include <unordered_map>
+
+namespace gatekeeper {
+
+struct fast_hash_t {
+ uint64_t salt;
+ uint8_t digest[SHA256_DIGEST_LENGTH];
+};
+
+class SoftGateKeeper : public GateKeeper {
+ public:
+ static const uint32_t SIGNATURE_LENGTH_BYTES = 32;
+
+ // scrypt params
+ static const uint64_t N = 16384;
+ static const uint32_t r = 8;
+ static const uint32_t p = 1;
+
+ static const int MAX_UINT_32_CHARS = 11;
+
+ SoftGateKeeper() {
+ key_.reset(new uint8_t[SIGNATURE_LENGTH_BYTES]);
+ memset(key_.get(), 0, SIGNATURE_LENGTH_BYTES);
+ }
+
+ virtual ~SoftGateKeeper() {}
+
+ virtual bool GetAuthTokenKey(const uint8_t** auth_token_key, uint32_t* length) const {
+ if (auth_token_key == NULL || length == NULL) return false;
+ *auth_token_key = key_.get();
+ *length = SIGNATURE_LENGTH_BYTES;
+ return true;
+ }
+
+ virtual void GetPasswordKey(const uint8_t** password_key, uint32_t* length) {
+ if (password_key == NULL || length == NULL) return;
+ *password_key = key_.get();
+ *length = SIGNATURE_LENGTH_BYTES;
+ }
+
+ virtual void ComputePasswordSignature(uint8_t* signature, uint32_t signature_length,
+ const uint8_t*, uint32_t, const uint8_t* password,
+ uint32_t password_length, salt_t salt) const {
+ if (signature == NULL) return;
+ crypto_scrypt(password, password_length, reinterpret_cast<uint8_t*>(&salt), sizeof(salt), N,
+ r, p, signature, signature_length);
+ }
+
+ virtual void GetRandom(void* random, uint32_t requested_length) const {
+ if (random == NULL) return;
+ RAND_pseudo_bytes((uint8_t*)random, requested_length);
+ }
+
+ virtual void ComputeSignature(uint8_t* signature, uint32_t signature_length, const uint8_t*,
+ uint32_t, const uint8_t*, const uint32_t) const {
+ if (signature == NULL) return;
+ memset(signature, 0, signature_length);
+ }
+
+ virtual uint64_t GetMillisecondsSinceBoot() const {
+ struct timespec time;
+ int res = clock_gettime(CLOCK_BOOTTIME, &time);
+ if (res < 0) return 0;
+ return (time.tv_sec * 1000) + (time.tv_nsec / 1000 / 1000);
+ }
+
+ virtual bool IsHardwareBacked() const { return false; }
+
+ virtual bool GetFailureRecord(uint32_t uid, secure_id_t user_id, failure_record_t* record,
+ bool /* secure */) {
+ failure_record_t* stored = &failure_map_[uid];
+ if (user_id != stored->secure_user_id) {
+ stored->secure_user_id = user_id;
+ stored->last_checked_timestamp = 0;
+ stored->failure_counter = 0;
+ }
+ memcpy(record, stored, sizeof(*record));
+ return true;
+ }
+
+ virtual bool ClearFailureRecord(uint32_t uid, secure_id_t user_id, bool /* secure */) {
+ failure_record_t* stored = &failure_map_[uid];
+ stored->secure_user_id = user_id;
+ stored->last_checked_timestamp = 0;
+ stored->failure_counter = 0;
+ return true;
+ }
+
+ virtual bool WriteFailureRecord(uint32_t uid, failure_record_t* record, bool /* secure */) {
+ failure_map_[uid] = *record;
+ return true;
+ }
+
+ fast_hash_t ComputeFastHash(const SizedBuffer& password, uint64_t salt) {
+ fast_hash_t fast_hash;
+ size_t digest_size = password.size() + sizeof(salt);
+ std::unique_ptr<uint8_t[]> digest(new uint8_t[digest_size]);
+ memcpy(digest.get(), &salt, sizeof(salt));
+ memcpy(digest.get() + sizeof(salt), password.Data<uint8_t>(), password.size());
+
+ SHA256(digest.get(), digest_size, (uint8_t*)&fast_hash.digest);
+
+ fast_hash.salt = salt;
+ return fast_hash;
+ }
+
+ bool VerifyFast(const fast_hash_t& fast_hash, const SizedBuffer& password) {
+ fast_hash_t computed = ComputeFastHash(password, fast_hash.salt);
+ return memcmp(computed.digest, fast_hash.digest, SHA256_DIGEST_LENGTH) == 0;
+ }
+
+ bool DoVerify(const password_handle_t* expected_handle, const SizedBuffer& password) {
+ uint64_t user_id = android::base::get_unaligned<secure_id_t>(&expected_handle->user_id);
+ FastHashMap::const_iterator it = fast_hash_map_.find(user_id);
+ if (it != fast_hash_map_.end() && VerifyFast(it->second, password)) {
+ return true;
+ } else {
+ if (GateKeeper::DoVerify(expected_handle, password)) {
+ uint64_t salt;
+ GetRandom(&salt, sizeof(salt));
+ fast_hash_map_[user_id] = ComputeFastHash(password, salt);
+ return true;
+ }
+ }
+
+ return false;
+ }
+
+ private:
+ typedef std::unordered_map<uint32_t, failure_record_t> FailureRecordMap;
+ typedef std::unordered_map<uint64_t, fast_hash_t> FastHashMap;
+
+ std::unique_ptr<uint8_t[]> key_;
+ FailureRecordMap failure_map_;
+ FastHashMap fast_hash_map_;
+};
+} // namespace gatekeeper
+
+#endif // SOFT_GATEKEEPER_H_
diff --git a/gatekeeper/1.0/software/SoftGateKeeperDevice.cpp b/gatekeeper/1.0/software/SoftGateKeeperDevice.cpp
new file mode 100644
index 0000000..d7a0b46
--- /dev/null
+++ b/gatekeeper/1.0/software/SoftGateKeeperDevice.cpp
@@ -0,0 +1,114 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "SoftGateKeeperDevice.h"
+#include "SoftGateKeeper.h"
+
+using ::android::hardware::hidl_vec;
+using ::android::hardware::Return;
+using ::android::hardware::gatekeeper::V1_0::GatekeeperStatusCode;
+using ::gatekeeper::EnrollRequest;
+using ::gatekeeper::EnrollResponse;
+using ::gatekeeper::ERROR_INVALID;
+using ::gatekeeper::ERROR_MEMORY_ALLOCATION_FAILED;
+using ::gatekeeper::ERROR_NONE;
+using ::gatekeeper::ERROR_RETRY;
+using ::gatekeeper::SizedBuffer;
+using ::gatekeeper::VerifyRequest;
+using ::gatekeeper::VerifyResponse;
+
+#include <limits>
+
+namespace android {
+
+inline SizedBuffer hidl_vec2sized_buffer(const hidl_vec<uint8_t>& vec) {
+ if (vec.size() == 0 || vec.size() > std::numeric_limits<uint32_t>::max()) return {};
+ auto dummy = new uint8_t[vec.size()];
+ std::copy(vec.begin(), vec.end(), dummy);
+ return {dummy, static_cast<uint32_t>(vec.size())};
+}
+
+Return<void> SoftGateKeeperDevice::enroll(uint32_t uid,
+ const hidl_vec<uint8_t>& currentPasswordHandle,
+ const hidl_vec<uint8_t>& currentPassword,
+ const hidl_vec<uint8_t>& desiredPassword,
+ enroll_cb _hidl_cb) {
+ if (desiredPassword.size() == 0) {
+ _hidl_cb({GatekeeperStatusCode::ERROR_GENERAL_FAILURE, 0, {}});
+ return {};
+ }
+
+ EnrollRequest request(uid, hidl_vec2sized_buffer(currentPasswordHandle),
+ hidl_vec2sized_buffer(desiredPassword),
+ hidl_vec2sized_buffer(currentPassword));
+ EnrollResponse response;
+ impl_->Enroll(request, &response);
+
+ if (response.error == ERROR_RETRY) {
+ _hidl_cb({GatekeeperStatusCode::ERROR_RETRY_TIMEOUT, response.retry_timeout, {}});
+ } else if (response.error != ERROR_NONE) {
+ _hidl_cb({GatekeeperStatusCode::ERROR_GENERAL_FAILURE, 0, {}});
+ } else {
+ hidl_vec<uint8_t> new_handle(response.enrolled_password_handle.Data<uint8_t>(),
+ response.enrolled_password_handle.Data<uint8_t>() +
+ response.enrolled_password_handle.size());
+ _hidl_cb({GatekeeperStatusCode::STATUS_OK, response.retry_timeout, new_handle});
+ }
+ return {};
+}
+
+Return<void> SoftGateKeeperDevice::verify(
+ uint32_t uid, uint64_t challenge,
+ const ::android::hardware::hidl_vec<uint8_t>& enrolledPasswordHandle,
+ const ::android::hardware::hidl_vec<uint8_t>& providedPassword, verify_cb _hidl_cb) {
+ if (enrolledPasswordHandle.size() == 0) {
+ _hidl_cb({GatekeeperStatusCode::ERROR_GENERAL_FAILURE, 0, {}});
+ return {};
+ }
+
+ VerifyRequest request(uid, challenge, hidl_vec2sized_buffer(enrolledPasswordHandle),
+ hidl_vec2sized_buffer(providedPassword));
+ VerifyResponse response;
+
+ impl_->Verify(request, &response);
+
+ if (response.error == ERROR_RETRY) {
+ _hidl_cb({GatekeeperStatusCode::ERROR_RETRY_TIMEOUT, response.retry_timeout, {}});
+ } else if (response.error != ERROR_NONE) {
+ _hidl_cb({GatekeeperStatusCode::ERROR_GENERAL_FAILURE, 0, {}});
+ } else {
+ hidl_vec<uint8_t> auth_token(
+ response.auth_token.Data<uint8_t>(),
+ response.auth_token.Data<uint8_t>() + response.auth_token.size());
+
+ _hidl_cb({response.request_reenroll ? GatekeeperStatusCode::STATUS_REENROLL
+ : GatekeeperStatusCode::STATUS_OK,
+ response.retry_timeout, auth_token});
+ }
+ return {};
+}
+
+Return<void> SoftGateKeeperDevice::deleteUser(uint32_t /*uid*/, deleteUser_cb _hidl_cb) {
+ _hidl_cb({GatekeeperStatusCode::ERROR_NOT_IMPLEMENTED, 0, {}});
+ return {};
+}
+
+Return<void> SoftGateKeeperDevice::deleteAllUsers(deleteAllUsers_cb _hidl_cb) {
+ _hidl_cb({GatekeeperStatusCode::ERROR_NOT_IMPLEMENTED, 0, {}});
+ return {};
+}
+
+} // namespace android
diff --git a/gatekeeper/1.0/software/SoftGateKeeperDevice.h b/gatekeeper/1.0/software/SoftGateKeeperDevice.h
new file mode 100644
index 0000000..17b474e
--- /dev/null
+++ b/gatekeeper/1.0/software/SoftGateKeeperDevice.h
@@ -0,0 +1,80 @@
+/*
+ * Copyright 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef SOFT_GATEKEEPER_DEVICE_H_
+#define SOFT_GATEKEEPER_DEVICE_H_
+
+#include <android/hardware/gatekeeper/1.0/IGatekeeper.h>
+#include <hidl/Status.h>
+
+#include <memory>
+#include "SoftGateKeeper.h"
+
+namespace android {
+
+/**
+ * Software based GateKeeper implementation
+ */
+class SoftGateKeeperDevice : public ::android::hardware::gatekeeper::V1_0::IGatekeeper {
+ public:
+ SoftGateKeeperDevice() { impl_.reset(new ::gatekeeper::SoftGateKeeper()); }
+
+ // Wrappers to translate the gatekeeper HAL API to the Kegyuard Messages API.
+
+ /**
+ * Enrolls password_payload, which should be derived from a user selected pin or password,
+ * with the authentication factor private key used only for enrolling authentication
+ * factor data.
+ *
+ * Returns: 0 on success or an error code less than 0 on error.
+ * On error, enrolled_password_handle will not be allocated.
+ */
+ ::android::hardware::Return<void> enroll(
+ uint32_t uid, const ::android::hardware::hidl_vec<uint8_t>& currentPasswordHandle,
+ const ::android::hardware::hidl_vec<uint8_t>& currentPassword,
+ const ::android::hardware::hidl_vec<uint8_t>& desiredPassword,
+ enroll_cb _hidl_cb) override;
+
+ /**
+ * Verifies provided_password matches enrolled_password_handle.
+ *
+ * Implementations of this module may retain the result of this call
+ * to attest to the recency of authentication.
+ *
+ * On success, writes the address of a verification token to auth_token,
+ * usable to attest password verification to other trusted services. Clients
+ * may pass NULL for this value.
+ *
+ * Returns: 0 on success or an error code less than 0 on error
+ * On error, verification token will not be allocated
+ */
+ ::android::hardware::Return<void> verify(
+ uint32_t uid, uint64_t challenge,
+ const ::android::hardware::hidl_vec<uint8_t>& enrolledPasswordHandle,
+ const ::android::hardware::hidl_vec<uint8_t>& providedPassword,
+ verify_cb _hidl_cb) override;
+
+ ::android::hardware::Return<void> deleteUser(uint32_t uid, deleteUser_cb _hidl_cb) override;
+
+ ::android::hardware::Return<void> deleteAllUsers(deleteAllUsers_cb _hidl_cb) override;
+
+ private:
+ std::unique_ptr<::gatekeeper::SoftGateKeeper> impl_;
+};
+
+} // namespace android
+
+#endif // SOFT_GATEKEEPER_DEVICE_H_
diff --git a/gatekeeper/1.0/software/android.hardware.gatekeeper@1.0-service.software.rc b/gatekeeper/1.0/software/android.hardware.gatekeeper@1.0-service.software.rc
new file mode 100644
index 0000000..60cb96c
--- /dev/null
+++ b/gatekeeper/1.0/software/android.hardware.gatekeeper@1.0-service.software.rc
@@ -0,0 +1,4 @@
+service vendor.gatekeeper-1-0 /vendor/bin/hw/android.hardware.gatekeeper@1.0-service.software
+ class hal
+ user system
+ group system
diff --git a/gatekeeper/1.0/software/android.hardware.gatekeeper@1.0-service.software.xml b/gatekeeper/1.0/software/android.hardware.gatekeeper@1.0-service.software.xml
new file mode 100644
index 0000000..19714a8
--- /dev/null
+++ b/gatekeeper/1.0/software/android.hardware.gatekeeper@1.0-service.software.xml
@@ -0,0 +1,11 @@
+<manifest version="1.0" type="device">
+ <hal format="hidl">
+ <name>android.hardware.gatekeeper</name>
+ <transport>hwbinder</transport>
+ <version>1.0</version>
+ <interface>
+ <name>IGatekeeper</name>
+ <instance>default</instance>
+ </interface>
+ </hal>
+</manifest>
diff --git a/gatekeeper/1.0/software/service.cpp b/gatekeeper/1.0/software/service.cpp
new file mode 100644
index 0000000..a48a964
--- /dev/null
+++ b/gatekeeper/1.0/software/service.cpp
@@ -0,0 +1,39 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+#define LOG_TAG "android.hardware.gatekeeper@1.0-service"
+
+#include <android-base/logging.h>
+#include <android/hardware/gatekeeper/1.0/IGatekeeper.h>
+
+#include <hidl/LegacySupport.h>
+
+#include "SoftGateKeeperDevice.h"
+
+// Generated HIDL files
+using android::SoftGateKeeperDevice;
+using android::hardware::gatekeeper::V1_0::IGatekeeper;
+
+int main() {
+ ::android::hardware::configureRpcThreadpool(1, true /* willJoinThreadpool */);
+ android::sp<SoftGateKeeperDevice> gatekeeper(new SoftGateKeeperDevice());
+ auto status = gatekeeper->registerAsService();
+ if (status != android::OK) {
+ LOG(FATAL) << "Could not register service for Gatekeeper 1.0 (software) (" << status << ")";
+ }
+
+ android::hardware::joinRpcThreadpool();
+ return -1; // Should never get here.
+}
diff --git a/gatekeeper/1.0/software/tests/Android.bp b/gatekeeper/1.0/software/tests/Android.bp
new file mode 100644
index 0000000..3f0300d
--- /dev/null
+++ b/gatekeeper/1.0/software/tests/Android.bp
@@ -0,0 +1,34 @@
+//
+// Copyright (C) 2015 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//
+
+cc_test {
+ name: "gatekeeper-software-device-unit-tests",
+
+ cflags: [
+ "-g",
+ "-Wall",
+ "-Werror",
+ "-Wno-missing-field-initializers",
+ ],
+ shared_libs: [
+ "libgatekeeper",
+ "libcrypto",
+ "libbase",
+ ],
+ static_libs: ["libscrypt_static"],
+
+ srcs: ["gatekeeper_test.cpp"],
+}
diff --git a/gatekeeper/1.0/software/tests/gatekeeper_test.cpp b/gatekeeper/1.0/software/tests/gatekeeper_test.cpp
new file mode 100644
index 0000000..bf4a8bc
--- /dev/null
+++ b/gatekeeper/1.0/software/tests/gatekeeper_test.cpp
@@ -0,0 +1,178 @@
+/*
+ * Copyright 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <arpa/inet.h>
+#include <iostream>
+
+#include <gtest/gtest.h>
+#include <hardware/hw_auth_token.h>
+
+#include "../SoftGateKeeper.h"
+
+using ::gatekeeper::EnrollRequest;
+using ::gatekeeper::EnrollResponse;
+using ::gatekeeper::secure_id_t;
+using ::gatekeeper::SizedBuffer;
+using ::gatekeeper::SoftGateKeeper;
+using ::gatekeeper::VerifyRequest;
+using ::gatekeeper::VerifyResponse;
+using ::testing::Test;
+
+static SizedBuffer makePasswordBuffer(int init = 0) {
+ constexpr const uint32_t pw_buffer_size = 16;
+ auto pw_buffer = new uint8_t[pw_buffer_size];
+ memset(pw_buffer, init, pw_buffer_size);
+
+ return {pw_buffer, pw_buffer_size};
+}
+
+static SizedBuffer makeAndInitializeSizedBuffer(const uint8_t* data, uint32_t size) {
+ auto buffer = new uint8_t[size];
+ memcpy(buffer, data, size);
+ return {buffer, size};
+}
+
+static SizedBuffer copySizedBuffer(const SizedBuffer& rhs) {
+ return makeAndInitializeSizedBuffer(rhs.Data<uint8_t>(), rhs.size());
+}
+
+static void do_enroll(SoftGateKeeper& gatekeeper, EnrollResponse* response) {
+ EnrollRequest request(0, {}, makePasswordBuffer(), {});
+
+ gatekeeper.Enroll(request, response);
+}
+
+TEST(GateKeeperTest, EnrollSuccess) {
+ SoftGateKeeper gatekeeper;
+ EnrollResponse response;
+ do_enroll(gatekeeper, &response);
+ ASSERT_EQ(::gatekeeper::gatekeeper_error_t::ERROR_NONE, response.error);
+}
+
+TEST(GateKeeperTest, EnrollBogusData) {
+ SoftGateKeeper gatekeeper;
+ EnrollResponse response;
+
+ EnrollRequest request(0, {}, {}, {});
+
+ gatekeeper.Enroll(request, &response);
+
+ ASSERT_EQ(::gatekeeper::gatekeeper_error_t::ERROR_INVALID, response.error);
+}
+
+TEST(GateKeeperTest, VerifySuccess) {
+ SoftGateKeeper gatekeeper;
+ EnrollResponse enroll_response;
+
+ do_enroll(gatekeeper, &enroll_response);
+ ASSERT_EQ(::gatekeeper::gatekeeper_error_t::ERROR_NONE, enroll_response.error);
+ VerifyRequest request(0, 1, std::move(enroll_response.enrolled_password_handle),
+ makePasswordBuffer());
+ VerifyResponse response;
+
+ gatekeeper.Verify(request, &response);
+
+ ASSERT_EQ(::gatekeeper::gatekeeper_error_t::ERROR_NONE, response.error);
+
+ auto auth_token = response.auth_token.Data<hw_auth_token_t>();
+
+ ASSERT_NE(nullptr, auth_token);
+ ASSERT_EQ((uint32_t)HW_AUTH_PASSWORD, ntohl(auth_token->authenticator_type));
+ ASSERT_EQ((uint64_t)1, auth_token->challenge);
+ ASSERT_NE(~((uint32_t)0), auth_token->timestamp);
+ ASSERT_NE((uint64_t)0, auth_token->user_id);
+ ASSERT_NE((uint64_t)0, auth_token->authenticator_id);
+}
+
+TEST(GateKeeperTest, TrustedReEnroll) {
+ SoftGateKeeper gatekeeper;
+ EnrollResponse enroll_response;
+
+ // do_enroll enrolls an all 0 password
+ do_enroll(gatekeeper, &enroll_response);
+ ASSERT_EQ(::gatekeeper::gatekeeper_error_t::ERROR_NONE, enroll_response.error);
+
+ // verify first password
+ VerifyRequest request(0, 0, copySizedBuffer(enroll_response.enrolled_password_handle),
+ makePasswordBuffer());
+ VerifyResponse response;
+ gatekeeper.Verify(request, &response);
+ ASSERT_EQ(::gatekeeper::gatekeeper_error_t::ERROR_NONE, response.error);
+ auto auth_token = response.auth_token.Data<hw_auth_token_t>();
+ ASSERT_NE(nullptr, auth_token);
+
+ secure_id_t secure_id = auth_token->user_id;
+
+ // enroll new password
+ EnrollRequest enroll_request(0, std::move(enroll_response.enrolled_password_handle),
+ makePasswordBuffer(1) /* new password */,
+ makePasswordBuffer() /* old password */);
+ gatekeeper.Enroll(enroll_request, &enroll_response);
+ ASSERT_EQ(::gatekeeper::gatekeeper_error_t::ERROR_NONE, enroll_response.error);
+
+ // verify new password
+ VerifyRequest new_request(0, 0, std::move(enroll_response.enrolled_password_handle),
+ makePasswordBuffer(1));
+ gatekeeper.Verify(new_request, &response);
+ ASSERT_EQ(::gatekeeper::gatekeeper_error_t::ERROR_NONE, response.error);
+ ASSERT_NE(nullptr, response.auth_token.Data<hw_auth_token_t>());
+ ASSERT_EQ(secure_id, response.auth_token.Data<hw_auth_token_t>()->user_id);
+}
+
+TEST(GateKeeperTest, UntrustedReEnroll) {
+ SoftGateKeeper gatekeeper;
+ SizedBuffer provided_password;
+ EnrollResponse enroll_response;
+
+ // do_enroll enrolls an all 0 password
+ provided_password = makePasswordBuffer();
+ do_enroll(gatekeeper, &enroll_response);
+ ASSERT_EQ(::gatekeeper::gatekeeper_error_t::ERROR_NONE, enroll_response.error);
+
+ // verify first password
+ VerifyRequest request(0, 0, std::move(enroll_response.enrolled_password_handle),
+ std::move(provided_password));
+ VerifyResponse response;
+ gatekeeper.Verify(request, &response);
+ ASSERT_EQ(::gatekeeper::gatekeeper_error_t::ERROR_NONE, response.error);
+ auto auth_token = response.auth_token.Data<hw_auth_token_t>();
+ ASSERT_NE(nullptr, auth_token);
+
+ secure_id_t secure_id = auth_token->user_id;
+
+ EnrollRequest enroll_request(0, {}, makePasswordBuffer(1), {});
+ gatekeeper.Enroll(enroll_request, &enroll_response);
+ ASSERT_EQ(::gatekeeper::gatekeeper_error_t::ERROR_NONE, enroll_response.error);
+
+ // verify new password
+ VerifyRequest new_request(0, 0, std::move(enroll_response.enrolled_password_handle),
+ makePasswordBuffer(1));
+ gatekeeper.Verify(new_request, &response);
+ ASSERT_EQ(::gatekeeper::gatekeeper_error_t::ERROR_NONE, response.error);
+ ASSERT_NE(nullptr, response.auth_token.Data<hw_auth_token_t>());
+ ASSERT_NE(secure_id, response.auth_token.Data<hw_auth_token_t>()->user_id);
+}
+
+TEST(GateKeeperTest, VerifyBogusData) {
+ SoftGateKeeper gatekeeper;
+ VerifyResponse response;
+
+ VerifyRequest request(0, 0, {}, {});
+
+ gatekeeper.Verify(request, &response);
+
+ ASSERT_EQ(::gatekeeper::gatekeeper_error_t::ERROR_INVALID, response.error);
+}
diff --git a/gatekeeper/1.0/vts/OWNERS b/gatekeeper/1.0/vts/OWNERS
new file mode 100644
index 0000000..738c710
--- /dev/null
+++ b/gatekeeper/1.0/vts/OWNERS
@@ -0,0 +1,3 @@
+jdanis@google.com
+swillden@google.com
+guangzhu@google.com
diff --git a/health/storage/1.0/vts/functional/VtsHalHealthStorageV1_0TargetTest.cpp b/health/storage/1.0/vts/functional/VtsHalHealthStorageV1_0TargetTest.cpp
index 946e5f2..2365124 100644
--- a/health/storage/1.0/vts/functional/VtsHalHealthStorageV1_0TargetTest.cpp
+++ b/health/storage/1.0/vts/functional/VtsHalHealthStorageV1_0TargetTest.cpp
@@ -35,6 +35,9 @@
// Dev GC timeout. This is the timeout used by vold.
const uint64_t kDevGcTimeoutSec = 120;
const std::chrono::seconds kDevGcTimeout{kDevGcTimeoutSec};
+// Dev GC timeout tolerance. The HAL may not immediately return after the
+// timeout, so include an acceptable tolerance.
+const std::chrono::seconds kDevGcTolerance{3};
// Time accounted for RPC calls.
const std::chrono::milliseconds kRpcTime{1000};
@@ -156,8 +159,9 @@
ASSERT_OK(ret);
// Hold test process because HAL can be single-threaded and doing GC.
- ASSERT_TRUE(ping(kDevGcTimeout + kRpcTime))
- << "Service must be available after " << toString(kDevGcTimeout + kRpcTime);
+ ASSERT_TRUE(ping(kDevGcTimeout + kDevGcTolerance + kRpcTime))
+ << "Service must be available after "
+ << toString(kDevGcTimeout + kDevGcTolerance + kRpcTime);
}
/**
@@ -167,7 +171,7 @@
sp<GcCallback> cb = new GcCallback();
auto ret = fs->garbageCollect(kDevGcTimeoutSec, cb);
ASSERT_OK(ret);
- cb->waitForResult(kDevGcTimeout + kRpcTime, Result::SUCCESS);
+ cb->waitForResult(kDevGcTimeout + kDevGcTolerance + kRpcTime, Result::SUCCESS);
}
} // namespace V1_0
diff --git a/neuralnetworks/1.0/IPreparedModel.hal b/neuralnetworks/1.0/IPreparedModel.hal
index ecaa7f8..5320050 100644
--- a/neuralnetworks/1.0/IPreparedModel.hal
+++ b/neuralnetworks/1.0/IPreparedModel.hal
@@ -42,6 +42,13 @@
* execute function. This callback must be provided with the ErrorStatus of
* the execution.
*
+ * If the launch is successful, the caller must not change the content of
+ * any data object referenced by 'request' (described by the
+ * {@link DataLocation} of a {@link RequestArgument}) until the
+ * asynchronous task has invoked the callback object. The asynchronous task
+ * must not change the content of any of the data objects corresponding to
+ * 'request' inputs.
+ *
* If the prepared model was prepared from a model wherein all
* tensor operands have fully specified dimensions, and the inputs
* to the function are valid, then the execution should launch
diff --git a/neuralnetworks/1.2/IPreparedModel.hal b/neuralnetworks/1.2/IPreparedModel.hal
index 5d2d80f..ba16334 100644
--- a/neuralnetworks/1.2/IPreparedModel.hal
+++ b/neuralnetworks/1.2/IPreparedModel.hal
@@ -47,6 +47,13 @@
* execute_1_2 function. This callback must be provided with the ErrorStatus of
* the execution.
*
+ * If the launch is successful, the caller must not change the content of
+ * any data object referenced by 'request' (described by the
+ * {@link @1.0::DataLocation} of a {@link @1.0::RequestArgument}) until the
+ * asynchronous task has invoked the callback object. The asynchronous task
+ * must not change the content of any of the data objects corresponding to
+ * 'request' inputs.
+ *
* If the prepared model was prepared from a model wherein all
* tensor operands have fully specified dimensions, and the inputs
* to the function are valid, then the execution should launch
@@ -90,6 +97,12 @@
* perform the execution, and must not return until the execution is
* complete.
*
+ * The caller must not change the content of any data object referenced by
+ * 'request' (described by the {@link @1.0::DataLocation} of a
+ * {@link @1.0::RequestArgument}) until executeSynchronously
+ * returns. executeSynchronously must not change the content of any of the
+ * data objects corresponding to 'request' inputs.
+ *
* If the prepared model was prepared from a model wherein all tensor
* operands have fully specified dimensions, and the inputs to the function
* are valid, then the execution should complete successfully
@@ -135,9 +148,15 @@
* @param callback A callback object used to retrieve memory resources
* corresponding to a unique identifiers ("slots").
* @param requestChannel Used by the client to send a serialized Request to
- * the Burst for execution. requestChannel must not be
- * used to pass a second Request object until a result
- * has been received from resultChannel.
+ * the Burst for execution. The client must not change
+ * the content of any data object referenced by the
+ * Request (described by the {@link @1.0::DataLocation}
+ * of an {@link OperandInformation}) until a result
+ * has been received from resultChannel. Execution
+ * must not change the content of any of the data
+ * objects corresponding to Request inputs. requestChannel
+ * must not be used to pass a second Request object
+ * until a result has been received from resultChannel.
* @param resultChannel Used by the service to return the results of an
* execution to the client: the status of the execution
* and OutputShape of all output tensors. resultChannel
diff --git a/neuralnetworks/1.2/vts/functional/GeneratedTests.cpp b/neuralnetworks/1.2/vts/functional/GeneratedTests.cpp
index 2c3287a..5af3255 100644
--- a/neuralnetworks/1.2/vts/functional/GeneratedTests.cpp
+++ b/neuralnetworks/1.2/vts/functional/GeneratedTests.cpp
@@ -44,6 +44,21 @@
// in frameworks/ml/nn/runtime/tests/generated/
#include "all_generated_V1_2_vts_tests.cpp"
+// Generated from spec/strided_slice_invalid_output_dims.mod.py.
+// TODO(b/132155416): Make this part of all_generated_V1_2_vts_tests.cpp.
+namespace strided_slice_invalid_output_dims {
+#include "generated/strided_slice_invalid_output_dims.example.cpp"
+#include "generated/strided_slice_invalid_output_dims.model.cpp"
+} // namespace strided_slice_invalid_output_dims
+
+// TODO(b/132155416): Make this part of all_generated_V1_2_vts_tests.cpp.
+TEST_F(ValidationTest, strided_slice_invalid_output_dims) {
+ const Model model = strided_slice_invalid_output_dims::createTestModel();
+ const std::vector<Request> requests =
+ createRequests(strided_slice_invalid_output_dims::get_examples());
+ validateFailure(model, requests);
+}
+
} // namespace functional
} // namespace vts
} // namespace V1_2
diff --git a/neuralnetworks/1.2/vts/functional/ValidateRequest.cpp b/neuralnetworks/1.2/vts/functional/ValidateRequest.cpp
index 9703c2d..e935aaa 100644
--- a/neuralnetworks/1.2/vts/functional/ValidateRequest.cpp
+++ b/neuralnetworks/1.2/vts/functional/ValidateRequest.cpp
@@ -274,6 +274,22 @@
}
}
+void ValidationTest::validateRequestFailure(const sp<IPreparedModel>& preparedModel,
+ const std::vector<Request>& requests) {
+ for (const Request& request : requests) {
+ SCOPED_TRACE("Expecting request to fail [executeSynchronously]");
+ Return<void> executeStatus = preparedModel->executeSynchronously(
+ request, MeasureTiming::NO,
+ [](ErrorStatus error, const hidl_vec<OutputShape>& outputShapes,
+ const Timing& timing) {
+ ASSERT_NE(ErrorStatus::NONE, error);
+ EXPECT_EQ(outputShapes.size(), 0);
+ EXPECT_TRUE(badTiming(timing));
+ });
+ ASSERT_TRUE(executeStatus.isOk());
+ }
+}
+
} // namespace functional
} // namespace vts
} // namespace V1_2
diff --git a/neuralnetworks/1.2/vts/functional/VtsHalNeuralnetworks.cpp b/neuralnetworks/1.2/vts/functional/VtsHalNeuralnetworks.cpp
index 4ddefe8..666f9b5 100644
--- a/neuralnetworks/1.2/vts/functional/VtsHalNeuralnetworks.cpp
+++ b/neuralnetworks/1.2/vts/functional/VtsHalNeuralnetworks.cpp
@@ -140,6 +140,20 @@
validateBurst(preparedModel, requests);
}
+void ValidationTest::validateFailure(const Model& model, const std::vector<Request>& requests) {
+ // TODO: Should this always succeed?
+ // What if the invalid input is part of the model (i.e., a parameter).
+ validateModel(model);
+
+ sp<IPreparedModel> preparedModel;
+ ASSERT_NO_FATAL_FAILURE(createPreparedModel(device, model, &preparedModel));
+ if (preparedModel == nullptr) {
+ return;
+ }
+
+ validateRequestFailure(preparedModel, requests);
+}
+
sp<IPreparedModel> getPreparedModel_1_2(
const sp<V1_2::implementation::PreparedModelCallback>& callback) {
sp<V1_0::IPreparedModel> preparedModelV1_0 = callback->getPreparedModel();
diff --git a/neuralnetworks/1.2/vts/functional/VtsHalNeuralnetworks.h b/neuralnetworks/1.2/vts/functional/VtsHalNeuralnetworks.h
index 8d1acbe..80e810a 100644
--- a/neuralnetworks/1.2/vts/functional/VtsHalNeuralnetworks.h
+++ b/neuralnetworks/1.2/vts/functional/VtsHalNeuralnetworks.h
@@ -73,11 +73,14 @@
class ValidationTest : public NeuralnetworksHidlTest {
protected:
void validateEverything(const Model& model, const std::vector<Request>& requests);
+ void validateFailure(const Model& model, const std::vector<Request>& requests);
private:
void validateModel(const Model& model);
void validateRequests(const sp<IPreparedModel>& preparedModel,
const std::vector<Request>& requests);
+ void validateRequestFailure(const sp<IPreparedModel>& preparedModel,
+ const std::vector<Request>& requests);
void validateBurst(const sp<IPreparedModel>& preparedModel,
const std::vector<Request>& requests);
};
diff --git a/neuralnetworks/1.2/vts/functional/generated/strided_slice_invalid_output_dims.example.cpp b/neuralnetworks/1.2/vts/functional/generated/strided_slice_invalid_output_dims.example.cpp
new file mode 100644
index 0000000..0640833
--- /dev/null
+++ b/neuralnetworks/1.2/vts/functional/generated/strided_slice_invalid_output_dims.example.cpp
@@ -0,0 +1,116 @@
+// clang-format off
+// Generated file (from: strided_slice_invalid_output_dims.mod.py). Do not edit
+std::vector<MixedTypedExample>& get_examples() {
+static std::vector<MixedTypedExample> examples = {
+// Begin of an example
+{
+.operands = {
+//Input(s)
+{ // See tools/test_generator/include/TestHarness.h:MixedTyped
+ // int -> Dimensions map
+ .operandDimensions = {{0, {2, 3}}},
+ // int -> FLOAT32 map
+ .float32Operands = {{0, {1.0f, 2.0f, 3.0f, 4.0f, 5.0f, 6.0f}}},
+ // int -> INT32 map
+ .int32Operands = {},
+ // int -> QUANT8_ASYMM map
+ .quant8AsymmOperands = {},
+ // int -> QUANT16_SYMM map
+ .quant16SymmOperands = {},
+ // int -> FLOAT16 map
+ .float16Operands = {},
+ // int -> BOOL8 map
+ .bool8Operands = {},
+ // int -> QUANT8_SYMM_PER_CHANNEL map
+ .quant8ChannelOperands = {},
+ // int -> QUANT16_ASYMM map
+ .quant16AsymmOperands = {},
+ // int -> QUANT8_SYMM map
+ .quant8SymmOperands = {},
+},
+//Output(s)
+{ // See tools/test_generator/include/TestHarness.h:MixedTyped
+ // int -> Dimensions map
+ .operandDimensions = {{0, {3}}},
+ // int -> FLOAT32 map
+ .float32Operands = {{0, {1.0f, 2.0f, 3.0f}}},
+ // int -> INT32 map
+ .int32Operands = {},
+ // int -> QUANT8_ASYMM map
+ .quant8AsymmOperands = {},
+ // int -> QUANT16_SYMM map
+ .quant16SymmOperands = {},
+ // int -> FLOAT16 map
+ .float16Operands = {},
+ // int -> BOOL8 map
+ .bool8Operands = {},
+ // int -> QUANT8_SYMM_PER_CHANNEL map
+ .quant8ChannelOperands = {},
+ // int -> QUANT16_ASYMM map
+ .quant16AsymmOperands = {},
+ // int -> QUANT8_SYMM map
+ .quant8SymmOperands = {},
+}
+},
+}, // End of an example
+};
+return examples;
+};
+
+std::vector<MixedTypedExample>& get_examples_dynamic_output_shape() {
+static std::vector<MixedTypedExample> examples_dynamic_output_shape = {
+// Begin of an example
+{
+.operands = {
+//Input(s)
+{ // See tools/test_generator/include/TestHarness.h:MixedTyped
+ // int -> Dimensions map
+ .operandDimensions = {{0, {2, 3}}},
+ // int -> FLOAT32 map
+ .float32Operands = {{0, {1.0f, 2.0f, 3.0f, 4.0f, 5.0f, 6.0f}}},
+ // int -> INT32 map
+ .int32Operands = {},
+ // int -> QUANT8_ASYMM map
+ .quant8AsymmOperands = {},
+ // int -> QUANT16_SYMM map
+ .quant16SymmOperands = {},
+ // int -> FLOAT16 map
+ .float16Operands = {},
+ // int -> BOOL8 map
+ .bool8Operands = {},
+ // int -> QUANT8_SYMM_PER_CHANNEL map
+ .quant8ChannelOperands = {},
+ // int -> QUANT16_ASYMM map
+ .quant16AsymmOperands = {},
+ // int -> QUANT8_SYMM map
+ .quant8SymmOperands = {},
+},
+//Output(s)
+{ // See tools/test_generator/include/TestHarness.h:MixedTyped
+ // int -> Dimensions map
+ .operandDimensions = {{0, {3}}},
+ // int -> FLOAT32 map
+ .float32Operands = {{0, {1.0f, 2.0f, 3.0f}}},
+ // int -> INT32 map
+ .int32Operands = {},
+ // int -> QUANT8_ASYMM map
+ .quant8AsymmOperands = {},
+ // int -> QUANT16_SYMM map
+ .quant16SymmOperands = {},
+ // int -> FLOAT16 map
+ .float16Operands = {},
+ // int -> BOOL8 map
+ .bool8Operands = {},
+ // int -> QUANT8_SYMM_PER_CHANNEL map
+ .quant8ChannelOperands = {},
+ // int -> QUANT16_ASYMM map
+ .quant16AsymmOperands = {},
+ // int -> QUANT8_SYMM map
+ .quant8SymmOperands = {},
+}
+},
+}, // End of an example
+};
+return examples_dynamic_output_shape;
+};
+
diff --git a/neuralnetworks/1.2/vts/functional/generated/strided_slice_invalid_output_dims.model.cpp b/neuralnetworks/1.2/vts/functional/generated/strided_slice_invalid_output_dims.model.cpp
new file mode 100644
index 0000000..106655a
--- /dev/null
+++ b/neuralnetworks/1.2/vts/functional/generated/strided_slice_invalid_output_dims.model.cpp
@@ -0,0 +1,216 @@
+// clang-format off
+// Generated file (from: strided_slice_invalid_output_dims.mod.py). Do not edit
+// Create the model
+Model createTestModel() {
+ const std::vector<Operand> operands = {
+ {
+ .type = OperandType::TENSOR_FLOAT32,
+ .dimensions = {2, 3},
+ .numberOfConsumers = 1,
+ .scale = 0.0f,
+ .zeroPoint = 0,
+ .lifetime = OperandLifeTime::MODEL_INPUT,
+ .location = {.poolIndex = 0, .offset = 0, .length = 0},
+ },
+ {
+ .type = OperandType::TENSOR_INT32,
+ .dimensions = {2},
+ .numberOfConsumers = 1,
+ .scale = 0.0f,
+ .zeroPoint = 0,
+ .lifetime = OperandLifeTime::CONSTANT_COPY,
+ .location = {.poolIndex = 0, .offset = 0, .length = 8},
+ },
+ {
+ .type = OperandType::TENSOR_INT32,
+ .dimensions = {2},
+ .numberOfConsumers = 1,
+ .scale = 0.0f,
+ .zeroPoint = 0,
+ .lifetime = OperandLifeTime::CONSTANT_COPY,
+ .location = {.poolIndex = 0, .offset = 8, .length = 8},
+ },
+ {
+ .type = OperandType::TENSOR_INT32,
+ .dimensions = {2},
+ .numberOfConsumers = 1,
+ .scale = 0.0f,
+ .zeroPoint = 0,
+ .lifetime = OperandLifeTime::CONSTANT_COPY,
+ .location = {.poolIndex = 0, .offset = 16, .length = 8},
+ },
+ {
+ .type = OperandType::INT32,
+ .dimensions = {},
+ .numberOfConsumers = 1,
+ .scale = 0.0f,
+ .zeroPoint = 0,
+ .lifetime = OperandLifeTime::CONSTANT_COPY,
+ .location = {.poolIndex = 0, .offset = 24, .length = 4},
+ },
+ {
+ .type = OperandType::INT32,
+ .dimensions = {},
+ .numberOfConsumers = 1,
+ .scale = 0.0f,
+ .zeroPoint = 0,
+ .lifetime = OperandLifeTime::CONSTANT_COPY,
+ .location = {.poolIndex = 0, .offset = 28, .length = 4},
+ },
+ {
+ .type = OperandType::INT32,
+ .dimensions = {},
+ .numberOfConsumers = 1,
+ .scale = 0.0f,
+ .zeroPoint = 0,
+ .lifetime = OperandLifeTime::CONSTANT_COPY,
+ .location = {.poolIndex = 0, .offset = 32, .length = 4},
+ },
+ {
+ .type = OperandType::TENSOR_FLOAT32,
+ .dimensions = {3},
+ .numberOfConsumers = 0,
+ .scale = 0.0f,
+ .zeroPoint = 0,
+ .lifetime = OperandLifeTime::MODEL_OUTPUT,
+ .location = {.poolIndex = 0, .offset = 0, .length = 0},
+ }
+ };
+
+ const std::vector<Operation> operations = {
+ {
+ .type = OperationType::STRIDED_SLICE,
+ .inputs = {0, 1, 2, 3, 4, 5, 6},
+ .outputs = {7},
+ }
+ };
+
+ const std::vector<uint32_t> inputIndexes = {0};
+ const std::vector<uint32_t> outputIndexes = {7};
+ std::vector<uint8_t> operandValues = {
+ 0, 0, 0, 0, 0, 0, 0, 0, 2, 0, 0, 0, 3, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0
+ };
+ const std::vector<hidl_memory> pools = {};
+
+ return {
+ .operands = operands,
+ .operations = operations,
+ .inputIndexes = inputIndexes,
+ .outputIndexes = outputIndexes,
+ .operandValues = operandValues,
+ .pools = pools,
+ };
+}
+
+inline bool is_ignored(int i) {
+ static std::set<int> ignore = {};
+ return ignore.find(i) != ignore.end();
+}
+
+// Create the model
+Model createTestModel_dynamic_output_shape() {
+ const std::vector<Operand> operands = {
+ {
+ .type = OperandType::TENSOR_FLOAT32,
+ .dimensions = {2, 3},
+ .numberOfConsumers = 1,
+ .scale = 0.0f,
+ .zeroPoint = 0,
+ .lifetime = OperandLifeTime::MODEL_INPUT,
+ .location = {.poolIndex = 0, .offset = 0, .length = 0},
+ },
+ {
+ .type = OperandType::TENSOR_INT32,
+ .dimensions = {2},
+ .numberOfConsumers = 1,
+ .scale = 0.0f,
+ .zeroPoint = 0,
+ .lifetime = OperandLifeTime::CONSTANT_COPY,
+ .location = {.poolIndex = 0, .offset = 0, .length = 8},
+ },
+ {
+ .type = OperandType::TENSOR_INT32,
+ .dimensions = {2},
+ .numberOfConsumers = 1,
+ .scale = 0.0f,
+ .zeroPoint = 0,
+ .lifetime = OperandLifeTime::CONSTANT_COPY,
+ .location = {.poolIndex = 0, .offset = 8, .length = 8},
+ },
+ {
+ .type = OperandType::TENSOR_INT32,
+ .dimensions = {2},
+ .numberOfConsumers = 1,
+ .scale = 0.0f,
+ .zeroPoint = 0,
+ .lifetime = OperandLifeTime::CONSTANT_COPY,
+ .location = {.poolIndex = 0, .offset = 16, .length = 8},
+ },
+ {
+ .type = OperandType::INT32,
+ .dimensions = {},
+ .numberOfConsumers = 1,
+ .scale = 0.0f,
+ .zeroPoint = 0,
+ .lifetime = OperandLifeTime::CONSTANT_COPY,
+ .location = {.poolIndex = 0, .offset = 24, .length = 4},
+ },
+ {
+ .type = OperandType::INT32,
+ .dimensions = {},
+ .numberOfConsumers = 1,
+ .scale = 0.0f,
+ .zeroPoint = 0,
+ .lifetime = OperandLifeTime::CONSTANT_COPY,
+ .location = {.poolIndex = 0, .offset = 28, .length = 4},
+ },
+ {
+ .type = OperandType::INT32,
+ .dimensions = {},
+ .numberOfConsumers = 1,
+ .scale = 0.0f,
+ .zeroPoint = 0,
+ .lifetime = OperandLifeTime::CONSTANT_COPY,
+ .location = {.poolIndex = 0, .offset = 32, .length = 4},
+ },
+ {
+ .type = OperandType::TENSOR_FLOAT32,
+ .dimensions = {0},
+ .numberOfConsumers = 0,
+ .scale = 0.0f,
+ .zeroPoint = 0,
+ .lifetime = OperandLifeTime::MODEL_OUTPUT,
+ .location = {.poolIndex = 0, .offset = 0, .length = 0},
+ }
+ };
+
+ const std::vector<Operation> operations = {
+ {
+ .type = OperationType::STRIDED_SLICE,
+ .inputs = {0, 1, 2, 3, 4, 5, 6},
+ .outputs = {7},
+ }
+ };
+
+ const std::vector<uint32_t> inputIndexes = {0};
+ const std::vector<uint32_t> outputIndexes = {7};
+ std::vector<uint8_t> operandValues = {
+ 0, 0, 0, 0, 0, 0, 0, 0, 2, 0, 0, 0, 3, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0
+ };
+ const std::vector<hidl_memory> pools = {};
+
+ return {
+ .operands = operands,
+ .operations = operations,
+ .inputIndexes = inputIndexes,
+ .outputIndexes = outputIndexes,
+ .operandValues = operandValues,
+ .pools = pools,
+ };
+}
+
+inline bool is_ignored_dynamic_output_shape(int i) {
+ static std::set<int> ignore = {};
+ return ignore.find(i) != ignore.end();
+}
+
diff --git a/neuralnetworks/1.2/vts/functional/spec/strided_slice_invalid_output_dims.mod.py b/neuralnetworks/1.2/vts/functional/spec/strided_slice_invalid_output_dims.mod.py
new file mode 100644
index 0000000..e8d30f3
--- /dev/null
+++ b/neuralnetworks/1.2/vts/functional/spec/strided_slice_invalid_output_dims.mod.py
@@ -0,0 +1,43 @@
+#
+# 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.
+#
+
+# This test makes sure that executing STRIDED_SLICE results in a failure when
+# the output dimensions do not match shrinkAxisMask.
+#
+# The test generator does not support generating tests resulting in execution
+# failure, so the gTest part of this test has been written by hand.
+# TODO(b/132155416): Move this under frameworks/ml/nn/runtime/test/specs/V1_2.
+#
+# Based on strided_slice_float_11.mod.py.
+
+model = Model()
+i1 = Input("input", "TENSOR_FLOAT32", "{2, 3}")
+begins = Parameter("begins", "TENSOR_INT32", "{2}", [0, 0])
+# The value "2" below makes the test invalid. See http://b/79856511#comment2.
+ends = Parameter("ends", "TENSOR_INT32", "{2}", [2, 3])
+strides = Parameter("strides", "TENSOR_INT32", "{2}", [1, 1])
+beginMask = Int32Scalar("beginMask", 0)
+endMask = Int32Scalar("endMask", 0)
+shrinkAxisMask = Int32Scalar("shrinkAxisMask", 1)
+
+output = Output("output", "TENSOR_FLOAT32", "{3}")
+
+model = model.Operation("STRIDED_SLICE", i1, begins, ends, strides, beginMask, endMask, shrinkAxisMask).To(output)
+
+Example({
+ i1: [1, 2, 3, 4, 5, 6],
+ output: [1, 2, 3],
+})
diff --git a/radio/1.4/vts/functional/radio_hidl_hal_api.cpp b/radio/1.4/vts/functional/radio_hidl_hal_api.cpp
index 2093c25..2fdf3ef 100644
--- a/radio/1.4/vts/functional/radio_hidl_hal_api.cpp
+++ b/radio/1.4/vts/functional/radio_hidl_hal_api.cpp
@@ -41,6 +41,12 @@
ALOGI("emergencyDial, rspInfo.error = %s\n", toString(radioRsp_v1_4->rspInfo.error).c_str());
EXPECT_EQ(RadioError::NONE, radioRsp_v1_4->rspInfo.error);
+
+ // Give some time for modem to establish the emergency call channel.
+ sleep(MODEM_EMERGENCY_CALL_ESTABLISH_TIME);
+
+ // Disconnect all the potential established calls to prevent them affecting other tests.
+ clearPotentialEstablishedCalls();
}
/*
@@ -67,6 +73,12 @@
ALOGI("emergencyDial_withServices, rspInfo.error = %s\n",
toString(radioRsp_v1_4->rspInfo.error).c_str());
EXPECT_EQ(RadioError::NONE, radioRsp_v1_4->rspInfo.error);
+
+ // Give some time for modem to establish the emergency call channel.
+ sleep(MODEM_EMERGENCY_CALL_ESTABLISH_TIME);
+
+ // Disconnect all the potential established calls to prevent them affecting other tests.
+ clearPotentialEstablishedCalls();
}
/*
@@ -93,6 +105,12 @@
ALOGI("emergencyDial_withEmergencyRouting, rspInfo.error = %s\n",
toString(radioRsp_v1_4->rspInfo.error).c_str());
EXPECT_EQ(RadioError::NONE, radioRsp_v1_4->rspInfo.error);
+
+ // Give some time for modem to establish the emergency call channel.
+ sleep(MODEM_EMERGENCY_CALL_ESTABLISH_TIME);
+
+ // Disconnect all the potential established calls to prevent them affecting other tests.
+ clearPotentialEstablishedCalls();
}
/*
diff --git a/radio/1.4/vts/functional/radio_hidl_hal_test.cpp b/radio/1.4/vts/functional/radio_hidl_hal_test.cpp
index 4d80696..147f8fe 100644
--- a/radio/1.4/vts/functional/radio_hidl_hal_test.cpp
+++ b/radio/1.4/vts/functional/radio_hidl_hal_test.cpp
@@ -80,6 +80,23 @@
return status;
}
+void RadioHidlTest_v1_4::clearPotentialEstablishedCalls() {
+ // Get the current call Id to hangup the established emergency call.
+ serial = GetRandomSerialNumber();
+ radio_v1_4->getCurrentCalls(serial);
+ EXPECT_EQ(std::cv_status::no_timeout, wait());
+
+ // Hang up to disconnect the established call channels.
+ for (const ::android::hardware::radio::V1_2::Call& call : radioRsp_v1_4->currentCalls) {
+ serial = GetRandomSerialNumber();
+ radio_v1_4->hangup(serial, call.base.index);
+ ALOGI("Hang up to disconnect the established call channel: %d", call.base.index);
+ EXPECT_EQ(std::cv_status::no_timeout, wait());
+ // Give some time for modem to disconnect the established call channel.
+ sleep(MODEM_EMERGENCY_CALL_DISCONNECT_TIME);
+ }
+}
+
void RadioHidlTest_v1_4::updateSimCardStatus() {
serial = GetRandomSerialNumber();
radio_v1_4->getIccCardStatus(serial);
diff --git a/radio/1.4/vts/functional/radio_hidl_hal_utils_v1_4.h b/radio/1.4/vts/functional/radio_hidl_hal_utils_v1_4.h
index de7742c..a32e5de 100644
--- a/radio/1.4/vts/functional/radio_hidl_hal_utils_v1_4.h
+++ b/radio/1.4/vts/functional/radio_hidl_hal_utils_v1_4.h
@@ -42,6 +42,9 @@
using ::android::hardware::Void;
#define TIMEOUT_PERIOD 75
+#define MODEM_EMERGENCY_CALL_ESTABLISH_TIME 3
+#define MODEM_EMERGENCY_CALL_DISCONNECT_TIME 3
+
#define RADIO_SERVICE_NAME "slot1"
class RadioHidlTest_v1_4;
@@ -57,6 +60,9 @@
RadioResponseInfo rspInfo;
+ // Call
+ hidl_vec<::android::hardware::radio::V1_2::Call> currentCalls;
+
// Modem
bool isModemEnabled;
bool enableModemResponseToggle;
@@ -723,6 +729,9 @@
/* Serial number for radio request */
int serial;
+ /* Clear Potential Established Calls */
+ void clearPotentialEstablishedCalls();
+
/* Update Sim Card Status */
void updateSimCardStatus();
diff --git a/radio/1.4/vts/functional/radio_response.cpp b/radio/1.4/vts/functional/radio_response.cpp
index eac0c68..a849926 100644
--- a/radio/1.4/vts/functional/radio_response.cpp
+++ b/radio/1.4/vts/functional/radio_response.cpp
@@ -77,7 +77,9 @@
return Void();
}
-Return<void> RadioResponse_v1_4::hangupConnectionResponse(const RadioResponseInfo& /*info*/) {
+Return<void> RadioResponse_v1_4::hangupConnectionResponse(const RadioResponseInfo& info) {
+ rspInfo = info;
+ parent_v1_4.notify(info.serial);
return Void();
}
@@ -729,9 +731,10 @@
Return<void> RadioResponse_v1_4::getCurrentCallsResponse_1_2(
const RadioResponseInfo& info,
- const ::android::hardware::hidl_vec<::android::hardware::radio::V1_2::Call>& /*calls*/) {
+ const ::android::hardware::hidl_vec<::android::hardware::radio::V1_2::Call>& calls) {
rspInfo = info;
parent_v1_4.notify(info.serial);
+ currentCalls = calls;
return Void();
}
diff --git a/tests/libhwbinder/aidl/Android.bp b/tests/libhwbinder/aidl/Android.bp
index 6d49704..c9e09f7 100644
--- a/tests/libhwbinder/aidl/Android.bp
+++ b/tests/libhwbinder/aidl/Android.bp
@@ -1,4 +1,4 @@
-cc_library_shared {
+cc_library {
name: "android.hardware.tests.libbinder",
defaults: ["hidl_defaults"],
diff --git a/wifi/1.2/default/wifi_chip.cpp b/wifi/1.2/default/wifi_chip.cpp
index 8c2d3cb..9a31d50 100644
--- a/wifi/1.2/default/wifi_chip.cpp
+++ b/wifi/1.2/default/wifi_chip.cpp
@@ -111,16 +111,16 @@
bool removeOldFilesInternal() {
time_t now = time(0);
const time_t delete_files_before = now - kMaxRingBufferFileAgeSeconds;
- DIR* dir_dump = opendir(kTombstoneFolderPath);
+ std::unique_ptr<DIR, decltype(&closedir)> dir_dump(
+ opendir(kTombstoneFolderPath), closedir);
if (!dir_dump) {
PLOG(ERROR) << "Failed to open directory";
return false;
}
- unique_fd dir_auto_closer(dirfd(dir_dump));
struct dirent* dp;
bool success = true;
std::list<std::pair<const time_t, std::string>> valid_files;
- while ((dp = readdir(dir_dump))) {
+ while ((dp = readdir(dir_dump.get()))) {
if (dp->d_type != DT_REG) {
continue;
}
@@ -242,13 +242,13 @@
size_t cpioArchiveFilesInDir(int out_fd, const char* input_dir) {
struct dirent* dp;
size_t n_error = 0;
- DIR* dir_dump = opendir(input_dir);
+ std::unique_ptr<DIR, decltype(&closedir)> dir_dump(opendir(input_dir),
+ closedir);
if (!dir_dump) {
PLOG(ERROR) << "Failed to open directory";
return ++n_error;
}
- unique_fd dir_auto_closer(dirfd(dir_dump));
- while ((dp = readdir(dir_dump))) {
+ while ((dp = readdir(dir_dump.get()))) {
if (dp->d_type != DT_REG) {
continue;
}