Implementation of Weaver HAL

Bug: 35628284
Test: esed_integration_tests
Change-Id: I2adc2c00272641922627607c823383486a4ed7cb
diff --git a/esed/Android.bp b/esed/Android.bp
index 445efd3..d15b985 100644
--- a/esed/Android.bp
+++ b/esed/Android.bp
@@ -14,6 +14,8 @@
 // limitations under the License.
 //
 
+subdirs = ["tests"]
+
 cc_defaults {
     name: "esed_defaults",
     proprietary: true,
@@ -35,12 +37,15 @@
     name: "esed",
     srcs: [
         "esed.cpp",
+	"Weaver.cpp"
     ],
     init_rc = ["esed.rc"],
     defaults: ["esed_defaults"],
     shared_libs: [
+        "android.hardware.weaver@1.0",
         "libbase",
         "libese_cpp_nxp_pn80t_nq_nci",
+	"libese-app-weaver",
         "libhidlbase",
         "libhidltransport",
         "libhwbinder",
diff --git a/esed/Weaver.cpp b/esed/Weaver.cpp
new file mode 100644
index 0000000..df0b91b
--- /dev/null
+++ b/esed/Weaver.cpp
@@ -0,0 +1,161 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "Weaver.h"
+
+#include <algorithm>
+#include <tuple>
+
+#include <android-base/logging.h>
+#include <utils/String8.h>
+
+#include "../apps/weaver/include/ese/app/weaver.h"
+
+// libutils
+using android::String8;
+
+namespace {
+
+const String8 WRONG_KEY_SIZE_MSG = String8{"Key must be 16 bytes"};
+const String8 WRONG_VALUE_SIZE_MSG = String8{"Value must be 16 bytes"};
+
+} // namespace
+
+namespace android {
+namespace esed {
+
+// libhidl
+using ::android::hardware::Status;
+using ::android::hardware::Void;
+
+// HAL
+using ::android::hardware::weaver::V1_0::WeaverConfig;
+using ::android::hardware::weaver::V1_0::WeaverReadResponse;
+using ::android::hardware::weaver::V1_0::WeaverReadStatus;
+
+// Methods from ::android::hardware::weaver::V1_0::IWeaver follow.
+Return<void> Weaver::getConfig(getConfig_cb _hidl_cb) {
+    LOG(VERBOSE) << "Running Weaver::getNumSlots";
+
+    // Open SE session for applet
+    EseWeaverSession ws;
+    ese_weaver_session_init(&ws);
+    if (ese_weaver_session_open(mEse.ese_interface(), &ws) != ESE_APP_RESULT_OK) {
+        _hidl_cb(WeaverStatus::FAILED, WeaverConfig{});
+        return Void();
+    }
+
+    // Call the applet
+    uint32_t numSlots;
+    if (ese_weaver_get_num_slots(&ws, &numSlots) != ESE_APP_RESULT_OK) {
+        _hidl_cb(WeaverStatus::FAILED, WeaverConfig{});
+        return Void();
+    }
+
+    // Try and close the session
+    if (ese_weaver_session_close(&ws) != ESE_APP_RESULT_OK) {
+        LOG(WARNING) << "Failed to close Weaver session";
+    }
+
+    _hidl_cb(WeaverStatus::OK, WeaverConfig{numSlots, kEseWeaverKeySize, kEseWeaverValueSize});
+    return Void();
+}
+
+Return<WeaverStatus> Weaver::write(uint32_t slotId, const hidl_vec<uint8_t>& key,
+                           const hidl_vec<uint8_t>& value) {
+    LOG(INFO) << "Running Weaver::write on slot " << slotId;
+
+    // Validate the key and value sizes
+    if (key.size() != kEseWeaverKeySize) {
+        return Status::fromExceptionCode(Status::EX_ILLEGAL_ARGUMENT, WRONG_KEY_SIZE_MSG);
+    }
+    if (value.size() != kEseWeaverValueSize) {
+        return Status::fromExceptionCode(Status::EX_ILLEGAL_ARGUMENT, WRONG_VALUE_SIZE_MSG);
+    }
+
+    // Open SE session for applet
+    EseWeaverSession ws;
+    ese_weaver_session_init(&ws);
+    if (ese_weaver_session_open(mEse.ese_interface(), &ws) != ESE_APP_RESULT_OK) {
+        return WeaverStatus::FAILED;
+    }
+
+    // Call the applet
+    if (ese_weaver_write(&ws, slotId, key.data(), value.data()) != ESE_APP_RESULT_OK) {
+        return WeaverStatus::FAILED;
+    }
+
+    // Try and close the session
+    if (ese_weaver_session_close(&ws) != ESE_APP_RESULT_OK) {
+        LOG(WARNING) << "Failed to close Weaver session";
+    }
+
+    return WeaverStatus::OK;
+}
+
+Return<void> Weaver::read(uint32_t slotId, const hidl_vec<uint8_t>& key, read_cb _hidl_cb) {
+    LOG(VERBOSE) << "Running Weaver::read on slot " << slotId;
+
+    // Validate the key size
+    if (key.size() != kEseWeaverKeySize) {
+        return Status::fromExceptionCode(Status::EX_ILLEGAL_ARGUMENT, WRONG_KEY_SIZE_MSG);
+    }
+
+    // Open SE session for applet
+    EseWeaverSession ws;
+    ese_weaver_session_init(&ws);
+    if (ese_weaver_session_open(mEse.ese_interface(), &ws) != ESE_APP_RESULT_OK) {
+        _hidl_cb(WeaverReadStatus::FAILED, WeaverReadResponse{});
+        return Void();
+    }
+
+    // Call the applet
+    hidl_vec<uint8_t> value;
+    value.resize(kEseWeaverValueSize);
+    uint32_t timeout;
+    const int res = ese_weaver_read(&ws, slotId, key.data(), value.data(), &timeout);
+    WeaverReadStatus status;
+    switch (res) {
+        case ESE_APP_RESULT_OK:
+            status = WeaverReadStatus::OK;
+            timeout = 0;
+            break;
+        case ESE_WEAVER_READ_WRONG_KEY:
+            status = WeaverReadStatus::INCORRECT_KEY;
+            value.resize(0);
+            break;
+        case ESE_WEAVER_READ_TIMEOUT:
+            status = WeaverReadStatus::THROTTLE;
+            value.resize(0);
+            break;
+        default:
+            status = WeaverReadStatus::FAILED;
+            timeout = 0;
+            value.resize(0);
+            break;
+    }
+
+    // Try and close the session
+    if (ese_weaver_session_close(&ws) != ESE_APP_RESULT_OK) {
+        LOG(WARNING) << "Failed to close Weaver session";
+    }
+
+    _hidl_cb(status, WeaverReadResponse{timeout, value});
+    return Void();
+}
+
+}  // namespace esed
+}  // namespace android
diff --git a/esed/Weaver.h b/esed/Weaver.h
new file mode 100644
index 0000000..4e007cc
--- /dev/null
+++ b/esed/Weaver.h
@@ -0,0 +1,51 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef ANDROID_HARDWARE_WEAVER_V1_0_WEAVER_H
+#define ANDROID_HARDWARE_WEAVER_V1_0_WEAVER_H
+
+#include <android/hardware/weaver/1.0/IWeaver.h>
+#include <hidl/MQDescriptor.h>
+#include <hidl/Status.h>
+
+#include <esecpp/EseInterface.h>
+
+namespace android {
+namespace esed {
+
+using ::android::EseInterface;
+using ::android::hardware::weaver::V1_0::IWeaver;
+using ::android::hardware::weaver::V1_0::WeaverStatus;
+using ::android::hardware::hidl_vec;
+using ::android::hardware::Return;
+
+struct Weaver : public IWeaver {
+    Weaver(EseInterface& ese) : mEse(ese) {};
+
+    // Methods from ::android::hardware::weaver::V1_0::IWeaver follow.
+    Return<void> getConfig(getConfig_cb _hidl_cb) override;
+    Return<WeaverStatus> write(uint32_t slotId, const hidl_vec<uint8_t>& key,
+                               const hidl_vec<uint8_t>& value) override;
+    Return<void> read(uint32_t slotId, const hidl_vec<uint8_t>& key, read_cb _hidl_cb) override;
+
+private:
+    EseInterface& mEse;
+};
+
+}  // namespace esed
+}  // namespace android
+
+#endif  // ANDROID_HARDWARE_WEAVER_V1_0_WEAVER_H
diff --git a/esed/esed.cpp b/esed/esed.cpp
index 2bfb890..a39d1c1 100644
--- a/esed/esed.cpp
+++ b/esed/esed.cpp
@@ -26,6 +26,8 @@
 #include <esecpp/NxpPn80tNqNci.h>
 using EseInterfaceImpl = android::NxpPn80tNqNci;
 
+#include "Weaver.h"
+
 using android::OK;
 using android::sp;
 using android::status_t;
@@ -34,13 +36,16 @@
 
 using namespace std::chrono_literals;
 
+// HALs
+using android::esed::Weaver;
+
 int main(int /* argc */, char** /* argv */) {
     LOG(INFO) << "Starting esed...";
 
     // Open connection to the eSE
+    EseInterfaceImpl ese;
     uint32_t failCount = 0;
     while (true) {
-        EseInterfaceImpl ese;
         ese.init();
         if (ese.open() < 0) {
             std::string errMsg = "Failed to open connection to eSE";
@@ -64,9 +69,17 @@
     // thread safe so we use binder to synchronize requests for us.
     constexpr bool thisThreadWillJoinPool = true;
     configureRpcThreadpool(1, thisThreadWillJoinPool);
+    status_t status;
 
     // -- Instantiate other applet HALs here --
 
+    // Create Weaver HAL instance
+    sp<Weaver> weaver = new Weaver{ese};
+    status = weaver->registerAsService();
+    if (status != OK) {
+        LOG(ERROR) << "Failed to register Weaver as a service (status: " << status << ")";
+    }
+
     joinRpcThreadpool();
     return -1; // Should never reach here
 }
diff --git a/esed/tests/Android.bp b/esed/tests/Android.bp
new file mode 100644
index 0000000..b5f504e
--- /dev/null
+++ b/esed/tests/Android.bp
@@ -0,0 +1,34 @@
+//
+// Copyright (C) 2017 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//
+
+// TODO: move to a VTS test
+cc_test {
+    name: "esed_integration_tests",
+    proprietary: true,
+    srcs: [
+        "weaver_integration_tests.cpp",
+    ],
+    host_supported: false,
+    shared_libs: [
+        "android.hardware.weaver@1.0",
+        "libbase",
+        "libhidlbase",
+        "libhidltransport",
+        "libhwbinder",
+        "liblog",
+        "libutils",
+    ],
+}
diff --git a/esed/tests/weaver_integration_tests.cpp b/esed/tests/weaver_integration_tests.cpp
new file mode 100644
index 0000000..747955c
--- /dev/null
+++ b/esed/tests/weaver_integration_tests.cpp
@@ -0,0 +1,115 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <string>
+
+#include <android-base/logging.h>
+#include <android/hardware/weaver/1.0/IWeaver.h>
+#include <hidl/Status.h>
+
+#include <gtest/gtest.h>
+
+using ::android::OK;
+using ::android::sp;
+using ::android::status_t;
+using ::android::hardware::weaver::V1_0::IWeaver;
+using ::android::hardware::weaver::V1_0::WeaverConfig;
+using ::android::hardware::weaver::V1_0::WeaverReadResponse;
+using ::android::hardware::weaver::V1_0::WeaverReadStatus;
+using ::android::hardware::weaver::V1_0::WeaverStatus;
+using ::android::hardware::hidl_string;
+using ::android::hardware::hidl_vec;
+using ::android::hardware::Return;
+using ::android::hardware::Void;
+
+using ::testing::Test;
+
+const std::vector<uint8_t> KEY{1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16};
+const std::vector<uint8_t> WRONG_KEY{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
+const std::vector<uint8_t> VALUE{16, 15, 14, 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1};
+
+struct WeaverClientTest : public Test {
+  sp<IWeaver> service;
+
+  WeaverClientTest() = default;
+  virtual ~WeaverClientTest() = default;
+  void SetUp() override {
+    service = IWeaver::getService();
+    ASSERT_NE(service, nullptr);
+  }
+  void TearDown() override {}
+};
+
+TEST_F(WeaverClientTest, getConfig) {
+  bool cbkCalled = false;
+  WeaverStatus status;
+  WeaverConfig config;
+  auto ret = service->getConfig([&](WeaverStatus s, WeaverConfig c) {
+    cbkCalled = true;
+    status = s;
+    config = c;
+  });
+
+  EXPECT_TRUE(cbkCalled);
+  EXPECT_TRUE(ret.isOk());
+  EXPECT_EQ(status, WeaverStatus::OK);
+  const WeaverConfig expectedConfig{64, 16, 16};
+  EXPECT_EQ(config, expectedConfig);
+}
+
+TEST_F(WeaverClientTest, writeAndReadBack) {
+  const uint32_t slotId = 3;
+  auto ret = service->write(slotId, KEY, VALUE);
+  EXPECT_TRUE(ret.isOk());
+  EXPECT_EQ(ret, WeaverStatus::OK);
+
+  bool cbkCalled = false;
+  WeaverReadStatus status;
+  std::vector<uint8_t> readValue;
+  uint32_t timeout;
+  auto readRet = service->read(slotId, KEY, [&](WeaverReadStatus s, WeaverReadResponse r) {
+    cbkCalled = true;
+    status = s;
+    readValue = r.value;
+    timeout = r.timeout;
+  });
+  EXPECT_TRUE(cbkCalled);
+  EXPECT_TRUE(readRet.isOk());
+  EXPECT_EQ(status, WeaverReadStatus::OK);
+  EXPECT_EQ(readValue, VALUE);
+}
+
+TEST_F(WeaverClientTest, writeAndReadWithWrongKey) {
+  const uint32_t slotId = 3;
+  auto ret = service->write(slotId, KEY, VALUE);
+  EXPECT_TRUE(ret.isOk());
+  EXPECT_EQ(ret, WeaverStatus::OK);
+
+  bool cbkCalled = false;
+  WeaverReadStatus status;
+  std::vector<uint8_t> readValue;
+  uint32_t timeout;
+  auto readRet = service->read(slotId, WRONG_KEY, [&](WeaverReadStatus s, WeaverReadResponse r) {
+    cbkCalled = true;
+    status = s;
+    readValue = r.value;
+    timeout = r.timeout;
+  });
+  EXPECT_TRUE(cbkCalled);
+  EXPECT_TRUE(readRet.isOk());
+  EXPECT_EQ(status, WeaverReadStatus::INCORRECT_KEY);
+  EXPECT_EQ(timeout, uint32_t{0}); // first timeout is 0
+}