Implement Context Hub HAL v4 for Cuttlefish
Bug: 361608756
Change-Id: I56466cb7e83bb6a3ee08a37b8a3461c28cb2e840
Flag: TEST_ONLY
Test: atest VtsAidlHalContextHubTargetTest
diff --git a/contexthub/aidl/default/Android.bp b/contexthub/aidl/default/Android.bp
index da173f9..c0b147c 100644
--- a/contexthub/aidl/default/Android.bp
+++ b/contexthub/aidl/default/Android.bp
@@ -30,6 +30,7 @@
shared_libs: [
"libbase",
"libbinder_ndk",
+ "liblog",
"android.hardware.contexthub-V4-ndk",
],
export_include_dirs: ["include"],
@@ -51,6 +52,7 @@
shared_libs: [
"libbase",
"libbinder_ndk",
+ "liblog",
"android.hardware.contexthub-V4-ndk",
],
static_libs: [
diff --git a/contexthub/aidl/default/ContextHub.cpp b/contexthub/aidl/default/ContextHub.cpp
index a915191..4ae9c09 100644
--- a/contexthub/aidl/default/ContextHub.cpp
+++ b/contexthub/aidl/default/ContextHub.cpp
@@ -16,10 +16,54 @@
#include "contexthub-impl/ContextHub.h"
-namespace aidl::android::hardware::contexthub {
+#ifndef LOG_TAG
+#define LOG_TAG "CHRE"
+#endif
+
+#include <inttypes.h>
+#include <log/log.h>
using ::ndk::ScopedAStatus;
+namespace aidl::android::hardware::contexthub {
+
+namespace {
+
+constexpr uint64_t kMockVendorHubId = 0x1234567812345678;
+constexpr uint64_t kMockVendorHub2Id = 0x0EADBEEFDEADBEEF;
+
+// Mock endpoints for the default implementation.
+// These endpoints just echo back any messages sent to them.
+constexpr size_t kMockEndpointCount = 4;
+const EndpointInfo kMockEndpointInfos[kMockEndpointCount] = {
+ {
+ .id = {.hubId = kMockVendorHubId, .id = UINT64_C(0x1)},
+ .type = EndpointInfo::EndpointType::GENERIC,
+ .name = "Mock Endpoint 1",
+ .version = 1,
+ },
+ {
+ .id = {.hubId = kMockVendorHubId, .id = UINT64_C(0x2)},
+ .type = EndpointInfo::EndpointType::GENERIC,
+ .name = "Mock Endpoint 2",
+ .version = 2,
+ },
+ {
+ .id = {.hubId = kMockVendorHub2Id, .id = UINT64_C(0x1)},
+ .type = EndpointInfo::EndpointType::GENERIC,
+ .name = "Mock Endpoint 3",
+ .version = 1,
+ },
+ {
+ .id = {.hubId = kMockVendorHub2Id, .id = UINT64_C(0x2)},
+ .type = EndpointInfo::EndpointType::GENERIC,
+ .name = "Mock Endpoint 4",
+ .version = 2,
+ },
+};
+
+} // anonymous namespace
+
ScopedAStatus ContextHub::getContextHubs(std::vector<ContextHubInfo>* out_contextHubInfos) {
ContextHubInfo hub = {};
hub.name = "Mock Context Hub";
@@ -112,7 +156,13 @@
}
}
-ScopedAStatus ContextHub::setTestMode(bool /* enable */) {
+ScopedAStatus ContextHub::setTestMode(bool enable) {
+ if (enable) {
+ std::unique_lock<std::mutex> lock(mEndpointMutex);
+ mEndpoints.clear();
+ mEndpointSessions.clear();
+ mEndpointCallback = nullptr;
+ }
return ScopedAStatus::ok();
}
@@ -137,6 +187,10 @@
}
ScopedAStatus ContextHub::getHubs(std::vector<HubInfo>* _aidl_return) {
+ if (_aidl_return == nullptr) {
+ return ScopedAStatus::fromExceptionCode(EX_ILLEGAL_ARGUMENT);
+ }
+
ContextHubInfo hub = {};
hub.name = "Mock Context Hub";
hub.vendor = "AOSP";
@@ -158,61 +212,217 @@
vendorHub.version = 42;
HubInfo hubInfo2 = {};
- hubInfo2.hubId = UINT64_C(0x1234567812345678);
+ hubInfo2.hubId = kMockVendorHubId;
hubInfo2.hubDetails =
HubInfo::HubDetails::make<HubInfo::HubDetails::Tag::vendorHubInfo>(vendorHub);
+ VendorHubInfo vendorHub2 = {};
+ vendorHub2.name = "Mock Vendor Hub 2";
+ vendorHub2.version = 24;
+
+ HubInfo hubInfo3 = {};
+ hubInfo3.hubId = kMockVendorHub2Id;
+ hubInfo3.hubDetails =
+ HubInfo::HubDetails::make<HubInfo::HubDetails::Tag::vendorHubInfo>(vendorHub2);
+
_aidl_return->push_back(hubInfo1);
_aidl_return->push_back(hubInfo2);
+ _aidl_return->push_back(hubInfo3);
return ScopedAStatus::ok();
};
-ScopedAStatus ContextHub::getEndpoints(std::vector<EndpointInfo>* /* _aidl_return */) {
- return ScopedAStatus::fromExceptionCode(EX_UNSUPPORTED_OPERATION);
+ScopedAStatus ContextHub::getEndpoints(std::vector<EndpointInfo>* _aidl_return) {
+ if (_aidl_return == nullptr) {
+ return ScopedAStatus::fromExceptionCode(EX_ILLEGAL_ARGUMENT);
+ }
+
+ Service echoService;
+ echoService.format = Service::RpcFormat::CUSTOM;
+ echoService.serviceDescriptor = "ECHO";
+ echoService.majorVersion = 1;
+ echoService.minorVersion = 0;
+
+ for (const EndpointInfo& endpoint : kMockEndpointInfos) {
+ EndpointInfo endpointWithService(endpoint);
+ endpointWithService.services.push_back(echoService);
+ _aidl_return->push_back(std::move(endpointWithService));
+ }
+
+ return ScopedAStatus::ok();
};
-ScopedAStatus ContextHub::registerEndpoint(const EndpointInfo& /* in_endpoint */) {
- return ScopedAStatus::fromExceptionCode(EX_UNSUPPORTED_OPERATION);
+ScopedAStatus ContextHub::registerEndpoint(const EndpointInfo& in_endpoint) {
+ std::unique_lock<std::mutex> lock(mEndpointMutex);
+
+ for (const EndpointInfo& endpoint : mEndpoints) {
+ if ((endpoint.id.id == in_endpoint.id.id && endpoint.id.hubId == in_endpoint.id.hubId) ||
+ endpoint.name == in_endpoint.name) {
+ return ScopedAStatus::fromExceptionCode(EX_ILLEGAL_ARGUMENT);
+ }
+ }
+ mEndpoints.push_back(in_endpoint);
+ return ScopedAStatus::ok();
};
-ScopedAStatus ContextHub::unregisterEndpoint(const EndpointInfo& /* in_endpoint */) {
- return ScopedAStatus::fromExceptionCode(EX_UNSUPPORTED_OPERATION);
+ScopedAStatus ContextHub::unregisterEndpoint(const EndpointInfo& in_endpoint) {
+ std::unique_lock<std::mutex> lock(mEndpointMutex);
+
+ for (auto it = mEndpoints.begin(); it != mEndpoints.end(); ++it) {
+ if (it->id.id == in_endpoint.id.id && it->id.hubId == in_endpoint.id.hubId) {
+ mEndpoints.erase(it);
+ return ScopedAStatus::ok();
+ }
+ }
+ return ScopedAStatus::fromExceptionCode(EX_ILLEGAL_ARGUMENT);
};
ScopedAStatus ContextHub::registerEndpointCallback(
- const std::shared_ptr<IEndpointCallback>& /* in_callback */) {
- return ScopedAStatus::fromExceptionCode(EX_UNSUPPORTED_OPERATION);
+ const std::shared_ptr<IEndpointCallback>& in_callback) {
+ std::unique_lock<std::mutex> lock(mEndpointMutex);
+
+ mEndpointCallback = in_callback;
+ return ScopedAStatus::ok();
};
-ScopedAStatus ContextHub::requestSessionIdRange(int32_t /* in_size */,
- std::vector<int32_t>* /* _aidl_return */) {
- return ScopedAStatus::fromExceptionCode(EX_UNSUPPORTED_OPERATION);
+ScopedAStatus ContextHub::requestSessionIdRange(int32_t in_size,
+ std::vector<int32_t>* _aidl_return) {
+ constexpr int32_t kMaxSize = 1024;
+ if (in_size > kMaxSize || _aidl_return == nullptr) {
+ return ScopedAStatus::fromExceptionCode(EX_ILLEGAL_ARGUMENT);
+ }
+
+ {
+ std::lock_guard<std::mutex> lock(mEndpointMutex);
+ mMaxValidSessionId = in_size;
+ }
+
+ _aidl_return->push_back(0);
+ _aidl_return->push_back(in_size);
+ return ScopedAStatus::ok();
};
ScopedAStatus ContextHub::openEndpointSession(
- int32_t /* in_sessionId */, const EndpointId& /* in_destination */,
- const EndpointId& /* in_initiator */,
- const std::optional<std::string>& /* in_serviceDescriptor */) {
- return ScopedAStatus::fromExceptionCode(EX_UNSUPPORTED_OPERATION);
+ int32_t in_sessionId, const EndpointId& in_destination, const EndpointId& in_initiator,
+ const std::optional<std::string>& in_serviceDescriptor) {
+ // We are not calling onCloseEndpointSession on failure because the remote endpoints (our
+ // mock endpoints) always accept the session.
+
+ std::shared_ptr<IEndpointCallback> callback = nullptr;
+ {
+ std::unique_lock<std::mutex> lock(mEndpointMutex);
+ if (in_sessionId > mMaxValidSessionId) {
+ ALOGE("openEndpointSession: session ID %" PRId32 " is invalid", in_sessionId);
+ return ScopedAStatus::fromExceptionCode(EX_ILLEGAL_ARGUMENT);
+ }
+
+ for (const EndpointSession& session : mEndpointSessions) {
+ bool sessionAlreadyExists =
+ (session.initiator == in_destination && session.peer == in_initiator) ||
+ (session.peer == in_destination && session.initiator == in_initiator);
+ if (sessionAlreadyExists) {
+ ALOGD("openEndpointSession: session ID %" PRId32 " already exists", in_sessionId);
+ return (session.sessionId == in_sessionId &&
+ session.serviceDescriptor == in_serviceDescriptor)
+ ? ScopedAStatus::ok()
+ : ScopedAStatus::fromExceptionCode(EX_ILLEGAL_ARGUMENT);
+ } else if (session.sessionId == in_sessionId) {
+ ALOGE("openEndpointSession: session ID %" PRId32 " is invalid: endpoint mismatch",
+ in_sessionId);
+ return ScopedAStatus::fromExceptionCode(EX_ILLEGAL_ARGUMENT);
+ }
+ }
+
+ // Verify the initiator and destination are valid endpoints
+ bool initiatorIsValid = findEndpoint(in_initiator, mEndpoints.begin(), mEndpoints.end());
+ if (!initiatorIsValid) {
+ ALOGE("openEndpointSession: initiator %" PRIu64 ":%" PRIu64 " is invalid",
+ in_initiator.id, in_initiator.hubId);
+ return ScopedAStatus::fromExceptionCode(EX_ILLEGAL_ARGUMENT);
+ }
+ bool destinationIsValid = findEndpoint(in_destination, &kMockEndpointInfos[0],
+ &kMockEndpointInfos[kMockEndpointCount]);
+ if (!destinationIsValid) {
+ ALOGE("openEndpointSession: destination %" PRIu64 ":%" PRIu64 " is invalid",
+ in_destination.id, in_destination.hubId);
+ return ScopedAStatus::fromExceptionCode(EX_ILLEGAL_ARGUMENT);
+ }
+
+ mEndpointSessions.push_back({
+ .sessionId = in_sessionId,
+ .initiator = in_initiator,
+ .peer = in_destination,
+ .serviceDescriptor = in_serviceDescriptor,
+ });
+
+ if (mEndpointCallback != nullptr) {
+ callback = mEndpointCallback;
+ }
+ }
+
+ if (callback != nullptr) {
+ callback->onEndpointSessionOpenComplete(in_sessionId);
+ }
+ return ScopedAStatus::ok();
};
-ScopedAStatus ContextHub::sendMessageToEndpoint(int32_t /* in_sessionId */,
- const Message& /* in_msg */) {
- return ScopedAStatus::fromExceptionCode(EX_UNSUPPORTED_OPERATION);
+ScopedAStatus ContextHub::sendMessageToEndpoint(int32_t in_sessionId, const Message& in_msg) {
+ bool foundSession = false;
+ std::shared_ptr<IEndpointCallback> callback = nullptr;
+ {
+ std::unique_lock<std::mutex> lock(mEndpointMutex);
+
+ for (const EndpointSession& session : mEndpointSessions) {
+ if (session.sessionId == in_sessionId) {
+ foundSession = true;
+ break;
+ }
+ }
+
+ if (mEndpointCallback != nullptr) {
+ callback = mEndpointCallback;
+ }
+ }
+
+ if (!foundSession) {
+ ALOGE("sendMessageToEndpoint: session ID %" PRId32 " is invalid", in_sessionId);
+ return ScopedAStatus::fromExceptionCode(EX_ILLEGAL_ARGUMENT);
+ }
+
+ if (callback != nullptr) {
+ if (in_msg.flags & Message::FLAG_REQUIRES_DELIVERY_STATUS) {
+ MessageDeliveryStatus msgStatus = {};
+ msgStatus.messageSequenceNumber = in_msg.sequenceNumber;
+ msgStatus.errorCode = ErrorCode::OK;
+ callback->onMessageDeliveryStatusReceived(in_sessionId, msgStatus);
+ }
+
+ // Echo the message back
+ callback->onMessageReceived(in_sessionId, in_msg);
+ }
+ return ScopedAStatus::ok();
};
ScopedAStatus ContextHub::sendMessageDeliveryStatusToEndpoint(
int32_t /* in_sessionId */, const MessageDeliveryStatus& /* in_msgStatus */) {
- return ScopedAStatus::fromExceptionCode(EX_UNSUPPORTED_OPERATION);
+ return ScopedAStatus::ok();
};
-ScopedAStatus ContextHub::closeEndpointSession(int32_t /* in_sessionId */, Reason /* in_reason */) {
- return ScopedAStatus::fromExceptionCode(EX_UNSUPPORTED_OPERATION);
+ScopedAStatus ContextHub::closeEndpointSession(int32_t in_sessionId, Reason /* in_reason */) {
+ std::unique_lock<std::mutex> lock(mEndpointMutex);
+
+ for (auto it = mEndpointSessions.begin(); it != mEndpointSessions.end(); ++it) {
+ if (it->sessionId == in_sessionId) {
+ mEndpointSessions.erase(it);
+ return ScopedAStatus::ok();
+ }
+ }
+ ALOGE("closeEndpointSession: session ID %" PRId32 " is invalid", in_sessionId);
+ return ScopedAStatus::fromExceptionCode(EX_ILLEGAL_ARGUMENT);
};
ScopedAStatus ContextHub::endpointSessionOpenComplete(int32_t /* in_sessionId */) {
- return ScopedAStatus::fromExceptionCode(EX_UNSUPPORTED_OPERATION);
+ return ScopedAStatus::ok();
};
} // namespace aidl::android::hardware::contexthub
diff --git a/contexthub/aidl/default/include/contexthub-impl/ContextHub.h b/contexthub/aidl/default/include/contexthub-impl/ContextHub.h
index 5680a77..4968878 100644
--- a/contexthub/aidl/default/include/contexthub-impl/ContextHub.h
+++ b/contexthub/aidl/default/include/contexthub-impl/ContextHub.h
@@ -18,6 +18,7 @@
#include <aidl/android/hardware/contexthub/BnContextHub.h>
+#include <mutex>
#include <unordered_set>
#include <vector>
@@ -72,10 +73,37 @@
::ndk::ScopedAStatus endpointSessionOpenComplete(int32_t in_sessionId) override;
private:
+ struct EndpointSession {
+ int32_t sessionId;
+ EndpointId initiator;
+ EndpointId peer;
+ std::optional<std::string> serviceDescriptor;
+ };
+
static constexpr uint32_t kMockHubId = 0;
+
+ //! Finds an endpoint in the range defined by the endpoints
+ //! @return whether the endpoint was found
+ template <typename Iter>
+ bool findEndpoint(const EndpointId& target, const Iter& begin, const Iter& end) {
+ for (auto iter = begin; iter != end; ++iter) {
+ if (iter->id.id == target.id && iter->id.hubId == target.hubId) {
+ return true;
+ }
+ }
+ return false;
+ }
+
std::shared_ptr<IContextHubCallback> mCallback;
std::unordered_set<char16_t> mConnectedHostEndpoints;
+
+ //! Endpoint storage and information
+ std::mutex mEndpointMutex;
+ std::vector<EndpointInfo> mEndpoints;
+ std::vector<EndpointSession> mEndpointSessions;
+ std::shared_ptr<IEndpointCallback> mEndpointCallback;
+ int32_t mMaxValidSessionId = 0;
};
} // namespace contexthub
diff --git a/contexthub/aidl/vts/Android.bp b/contexthub/aidl/vts/Android.bp
index 62a319e..a19b6fd 100644
--- a/contexthub/aidl/vts/Android.bp
+++ b/contexthub/aidl/vts/Android.bp
@@ -33,7 +33,7 @@
"libbinder",
],
static_libs: [
- "android.hardware.contexthub-V3-cpp",
+ "android.hardware.contexthub-V4-cpp",
"VtsHalContexthubUtils",
],
test_suites: [
diff --git a/contexthub/aidl/vts/VtsAidlHalContextHubTargetTest.cpp b/contexthub/aidl/vts/VtsAidlHalContextHubTargetTest.cpp
index fa427a5..95a96cd 100644
--- a/contexthub/aidl/vts/VtsAidlHalContextHubTargetTest.cpp
+++ b/contexthub/aidl/vts/VtsAidlHalContextHubTargetTest.cpp
@@ -20,8 +20,10 @@
#include <android/hardware/contexthub/BnContextHub.h>
#include <android/hardware/contexthub/BnContextHubCallback.h>
+#include <android/hardware/contexthub/BnEndpointCallback.h>
#include <android/hardware/contexthub/IContextHub.h>
#include <android/hardware/contexthub/IContextHubCallback.h>
+#include <android/hardware/contexthub/IEndpointCallback.h>
#include <binder/IServiceManager.h>
#include <binder/ProcessState.h>
#include <log/log.h>
@@ -34,18 +36,25 @@
using ::android::String16;
using ::android::binder::Status;
using ::android::hardware::contexthub::AsyncEventType;
+using ::android::hardware::contexthub::BnEndpointCallback;
using ::android::hardware::contexthub::ContextHubInfo;
using ::android::hardware::contexthub::ContextHubMessage;
+using ::android::hardware::contexthub::EndpointId;
+using ::android::hardware::contexthub::EndpointInfo;
using ::android::hardware::contexthub::ErrorCode;
using ::android::hardware::contexthub::HostEndpointInfo;
+using ::android::hardware::contexthub::HubInfo;
using ::android::hardware::contexthub::IContextHub;
using ::android::hardware::contexthub::IContextHubCallbackDefault;
+using ::android::hardware::contexthub::Message;
using ::android::hardware::contexthub::MessageDeliveryStatus;
using ::android::hardware::contexthub::NanoappBinary;
using ::android::hardware::contexthub::NanoappInfo;
using ::android::hardware::contexthub::NanoappRpcService;
using ::android::hardware::contexthub::NanSessionRequest;
using ::android::hardware::contexthub::NanSessionStateUpdate;
+using ::android::hardware::contexthub::Reason;
+using ::android::hardware::contexthub::Service;
using ::android::hardware::contexthub::Setting;
using ::android::hardware::contexthub::vts_utils::kNonExistentAppId;
using ::android::hardware::contexthub::vts_utils::waitForCallback;
@@ -61,8 +70,14 @@
contextHub = android::waitForDeclaredService<IContextHub>(
String16(std::get<0>(GetParam()).c_str()));
ASSERT_NE(contextHub, nullptr);
+
+ // Best effort enable test mode - this may not be supported on older HALS, so we
+ // ignore the return value.
+ contextHub->setTestMode(/* enable= */ true);
}
+ virtual void TearDown() override { contextHub->setTestMode(/* enable= */ false); }
+
uint32_t getHubId() { return std::get<1>(GetParam()); }
void testSettingChanged(Setting setting);
@@ -465,6 +480,290 @@
}
}
+class TestEndpointCallback : public BnEndpointCallback {
+ public:
+ Status onEndpointStarted(const std::vector<EndpointInfo>& /* endpointInfos */) override {
+ return Status::ok();
+ }
+
+ Status onEndpointStopped(const std::vector<EndpointId>& /* endpointIds */,
+ Reason /* reason */) override {
+ return Status::ok();
+ }
+
+ Status onMessageReceived(int32_t /* sessionId */, const Message& message) override {
+ mMessages.push_back(message);
+ return Status::ok();
+ }
+
+ Status onMessageDeliveryStatusReceived(int32_t /* sessionId */,
+ const MessageDeliveryStatus& /* msgStatus */) override {
+ return Status::ok();
+ }
+
+ Status onEndpointSessionOpenRequest(
+ int32_t /* sessionId */, const EndpointId& /* destination */,
+ const EndpointId& /* initiator */,
+ const std::optional<String16>& /* serviceDescriptor */) override {
+ return Status::ok();
+ }
+
+ Status onCloseEndpointSession(int32_t /* sessionId */, Reason /* reason */) override {
+ return Status::ok();
+ }
+
+ Status onEndpointSessionOpenComplete(int32_t /* sessionId */) override {
+ mWasOnEndpointSessionOpenCompleteCalled = true;
+ return Status::ok();
+ }
+
+ std::vector<Message> getMessages() { return mMessages; }
+
+ bool wasOnEndpointSessionOpenCompleteCalled() {
+ return mWasOnEndpointSessionOpenCompleteCalled;
+ }
+ void resetWasOnEndpointSessionOpenCompleteCalled() {
+ mWasOnEndpointSessionOpenCompleteCalled = false;
+ }
+
+ private:
+ std::vector<Message> mMessages;
+ bool mWasOnEndpointSessionOpenCompleteCalled = false;
+};
+
+TEST_P(ContextHubAidl, RegisterEndpoint) {
+ EndpointInfo endpointInfo;
+ endpointInfo.id.id = 1;
+ endpointInfo.id.hubId = 0xCAFECAFECAFECAFE;
+ endpointInfo.type = EndpointInfo::EndpointType::NATIVE;
+ endpointInfo.name = String16("Test host endpoint 1");
+ endpointInfo.version = 42;
+
+ Status status = contextHub->registerEndpoint(endpointInfo);
+ if (status.exceptionCode() == Status::EX_UNSUPPORTED_OPERATION ||
+ status.transactionError() == android::UNKNOWN_TRANSACTION) {
+ GTEST_SKIP() << "Not supported -> old API; or not implemented";
+ } else {
+ EXPECT_TRUE(status.isOk());
+ }
+}
+
+TEST_P(ContextHubAidl, RegisterEndpointSameNameFailure) {
+ EndpointInfo endpointInfo;
+ endpointInfo.id.id = 2;
+ endpointInfo.id.hubId = 0xCAFECAFECAFECAFE;
+ endpointInfo.type = EndpointInfo::EndpointType::NATIVE;
+ endpointInfo.name = String16("Test host endpoint 2");
+ endpointInfo.version = 42;
+
+ EndpointInfo endpointInfo2;
+ endpointInfo2.id.id = 3;
+ endpointInfo2.id.hubId = 0xCAFECAFECAFECAFE;
+ endpointInfo2.type = EndpointInfo::EndpointType::NATIVE;
+ endpointInfo2.name = String16("Test host endpoint 2");
+ endpointInfo2.version = 42;
+
+ Status status = contextHub->registerEndpoint(endpointInfo);
+ if (status.exceptionCode() == Status::EX_UNSUPPORTED_OPERATION ||
+ status.transactionError() == android::UNKNOWN_TRANSACTION) {
+ GTEST_SKIP() << "Not supported -> old API; or not implemented";
+ } else {
+ EXPECT_TRUE(status.isOk());
+ }
+
+ EXPECT_FALSE(contextHub->registerEndpoint(endpointInfo2).isOk());
+}
+
+TEST_P(ContextHubAidl, RegisterEndpointSameIdFailure) {
+ EndpointInfo endpointInfo;
+ endpointInfo.id.id = 4;
+ endpointInfo.id.hubId = 0xCAFECAFECAFECAFE;
+ endpointInfo.type = EndpointInfo::EndpointType::NATIVE;
+ endpointInfo.name = String16("Test host endpoint 4");
+ endpointInfo.version = 42;
+
+ EndpointInfo endpointInfo2;
+ endpointInfo2.id.id = 4;
+ endpointInfo2.id.hubId = 0xCAFECAFECAFECAFE;
+ endpointInfo2.type = EndpointInfo::EndpointType::NATIVE;
+ endpointInfo2.name = String16("Test host endpoint - same ID test");
+ endpointInfo2.version = 42;
+
+ Status status = contextHub->registerEndpoint(endpointInfo);
+ if (status.exceptionCode() == Status::EX_UNSUPPORTED_OPERATION ||
+ status.transactionError() == android::UNKNOWN_TRANSACTION) {
+ GTEST_SKIP() << "Not supported -> old API; or not implemented";
+ } else {
+ EXPECT_TRUE(status.isOk());
+ }
+
+ EXPECT_FALSE(contextHub->registerEndpoint(endpointInfo2).isOk());
+}
+
+TEST_P(ContextHubAidl, UnregisterEndpoint) {
+ EndpointInfo endpointInfo;
+ endpointInfo.id.id = 6;
+ endpointInfo.id.hubId = 0xCAFECAFECAFECAFE;
+ endpointInfo.type = EndpointInfo::EndpointType::NATIVE;
+ endpointInfo.name = String16("Test host endpoint 6");
+ endpointInfo.version = 42;
+
+ Status status = contextHub->registerEndpoint(endpointInfo);
+ if (status.exceptionCode() == Status::EX_UNSUPPORTED_OPERATION ||
+ status.transactionError() == android::UNKNOWN_TRANSACTION) {
+ GTEST_SKIP() << "Not supported -> old API; or not implemented";
+ } else {
+ EXPECT_TRUE(status.isOk());
+ }
+
+ EXPECT_TRUE(contextHub->unregisterEndpoint(endpointInfo).isOk());
+}
+
+TEST_P(ContextHubAidl, UnregisterEndpointNonexistent) {
+ EndpointInfo endpointInfo;
+ endpointInfo.id.id = 100;
+ endpointInfo.id.hubId = 0xCAFECAFECAFECAFE;
+ endpointInfo.type = EndpointInfo::EndpointType::NATIVE;
+ endpointInfo.name = String16("Test host endpoint 100");
+ endpointInfo.version = 42;
+
+ Status status = contextHub->unregisterEndpoint(endpointInfo);
+ if (status.exceptionCode() == Status::EX_UNSUPPORTED_OPERATION ||
+ status.transactionError() == android::UNKNOWN_TRANSACTION) {
+ GTEST_SKIP() << "Not supported -> old API; or not implemented";
+ } else {
+ EXPECT_FALSE(status.isOk());
+ }
+}
+
+TEST_P(ContextHubAidl, RegisterCallback) {
+ auto cb = sp<TestEndpointCallback>::make();
+ Status status = contextHub->registerEndpointCallback(cb);
+ if (status.exceptionCode() == Status::EX_UNSUPPORTED_OPERATION ||
+ status.transactionError() == android::UNKNOWN_TRANSACTION) {
+ GTEST_SKIP() << "Not supported -> old API; or not implemented";
+ } else {
+ EXPECT_TRUE(status.isOk());
+ }
+}
+
+TEST_P(ContextHubAidl, OpenEndpointSessionInvalidRange) {
+ auto cb = sp<TestEndpointCallback>::make();
+ Status status = contextHub->registerEndpointCallback(cb);
+ if (status.exceptionCode() == Status::EX_UNSUPPORTED_OPERATION ||
+ status.transactionError() == android::UNKNOWN_TRANSACTION) {
+ GTEST_SKIP() << "Not supported -> old API; or not implemented";
+ } else {
+ EXPECT_TRUE(status.isOk());
+ }
+
+ // Register the endpoint
+ EndpointInfo initiatorEndpoint;
+ initiatorEndpoint.id.id = 7;
+ initiatorEndpoint.id.hubId = 0xCAFECAFECAFECAFE;
+ initiatorEndpoint.type = EndpointInfo::EndpointType::NATIVE;
+ initiatorEndpoint.name = String16("Test host endpoint 7");
+ initiatorEndpoint.version = 42;
+ EXPECT_TRUE(contextHub->registerEndpoint(initiatorEndpoint).isOk());
+
+ // Find the destination, if it exists
+ std::vector<EndpointInfo> endpoints;
+ EXPECT_TRUE(contextHub->getEndpoints(&endpoints).isOk());
+ const EndpointInfo* destinationEndpoint = nullptr;
+ for (const EndpointInfo& endpoint : endpoints) {
+ for (const Service& service : endpoint.services) {
+ if (service.serviceDescriptor == String16("ECHO")) {
+ destinationEndpoint = &endpoint;
+ break;
+ }
+ }
+ }
+ if (destinationEndpoint == nullptr) {
+ return; // no echo service endpoint -> just return
+ }
+
+ // Request the range
+ constexpr int32_t requestedRange = 100;
+ std::vector<int32_t> range;
+ ASSERT_TRUE(contextHub->requestSessionIdRange(requestedRange, &range).isOk());
+ EXPECT_EQ(range.size(), 2);
+ EXPECT_GE(range[1] - range[0] + 1, requestedRange);
+
+ // Open the session
+ cb->resetWasOnEndpointSessionOpenCompleteCalled();
+ int32_t sessionId = range[1] + 10; // invalid
+ EXPECT_FALSE(contextHub
+ ->openEndpointSession(sessionId, destinationEndpoint->id,
+ initiatorEndpoint.id,
+ /* in_serviceDescriptor= */ String16("ECHO"))
+ .isOk());
+ EXPECT_FALSE(cb->wasOnEndpointSessionOpenCompleteCalled());
+}
+
+TEST_P(ContextHubAidl, OpenEndpointSessionAndSendMessageEchoesBack) {
+ auto cb = sp<TestEndpointCallback>::make();
+ Status status = contextHub->registerEndpointCallback(cb);
+ if (status.exceptionCode() == Status::EX_UNSUPPORTED_OPERATION ||
+ status.transactionError() == android::UNKNOWN_TRANSACTION) {
+ GTEST_SKIP() << "Not supported -> old API; or not implemented";
+ } else {
+ EXPECT_TRUE(status.isOk());
+ }
+
+ // Register the endpoint
+ EndpointInfo initiatorEndpoint;
+ initiatorEndpoint.id.id = 8;
+ initiatorEndpoint.id.hubId = 0xCAFECAFECAFECAFE;
+ initiatorEndpoint.type = EndpointInfo::EndpointType::NATIVE;
+ initiatorEndpoint.name = String16("Test host endpoint 7");
+ initiatorEndpoint.version = 42;
+ EXPECT_TRUE(contextHub->registerEndpoint(initiatorEndpoint).isOk());
+
+ // Find the destination, if it exists
+ std::vector<EndpointInfo> endpoints;
+ EXPECT_TRUE(contextHub->getEndpoints(&endpoints).isOk());
+ const EndpointInfo* destinationEndpoint = nullptr;
+ for (const EndpointInfo& endpoint : endpoints) {
+ for (const Service& service : endpoint.services) {
+ if (service.serviceDescriptor == String16("ECHO")) {
+ destinationEndpoint = &endpoint;
+ break;
+ }
+ }
+ }
+ if (destinationEndpoint == nullptr) {
+ return; // no echo service endpoint -> just return
+ }
+
+ // Request the range
+ constexpr int32_t requestedRange = 100;
+ std::vector<int32_t> range;
+ ASSERT_TRUE(contextHub->requestSessionIdRange(requestedRange, &range).isOk());
+ EXPECT_EQ(range.size(), 2);
+ EXPECT_GE(range[1] - range[0] + 1, requestedRange);
+
+ // Open the session
+ cb->resetWasOnEndpointSessionOpenCompleteCalled();
+ int32_t sessionId = range[0];
+ ASSERT_TRUE(contextHub
+ ->openEndpointSession(sessionId, destinationEndpoint->id,
+ initiatorEndpoint.id,
+ /* in_serviceDescriptor= */ String16("ECHO"))
+ .isOk());
+ EXPECT_TRUE(cb->wasOnEndpointSessionOpenCompleteCalled());
+
+ // Send the message
+ Message message;
+ message.flags = 0;
+ message.sequenceNumber = 0;
+ message.content.push_back(42);
+ ASSERT_TRUE(contextHub->sendMessageToEndpoint(sessionId, message).isOk());
+
+ // Check for echo
+ EXPECT_FALSE(cb->getMessages().empty());
+ EXPECT_EQ(cb->getMessages().back().content.back(), 42);
+}
+
std::string PrintGeneratedTest(const testing::TestParamInfo<ContextHubAidl::ParamType>& info) {
return std::string("CONTEXT_HUB_ID_") + std::to_string(std::get<1>(info.param));
}