blob: 4ae9c09213b4d646272712287a34ba65d7215d44 [file] [log] [blame] [edit]
/*
* Copyright (C) 2021 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 "contexthub-impl/ContextHub.h"
#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";
hub.vendor = "AOSP";
hub.toolchain = "n/a";
hub.id = kMockHubId;
hub.peakMips = 1;
hub.maxSupportedMessageLengthBytes = 4096;
hub.chrePlatformId = UINT64_C(0x476f6f6754000000);
hub.chreApiMajorVersion = 1;
hub.chreApiMinorVersion = 6;
hub.supportsReliableMessages = false;
out_contextHubInfos->push_back(hub);
return ScopedAStatus::ok();
}
// We don't expose any nanoapps for the default impl, therefore all nanoapp-related APIs fail.
ScopedAStatus ContextHub::loadNanoapp(int32_t /* in_contextHubId */,
const NanoappBinary& /* in_appBinary */,
int32_t /* in_transactionId */) {
return ScopedAStatus::fromExceptionCode(EX_UNSUPPORTED_OPERATION);
}
ScopedAStatus ContextHub::unloadNanoapp(int32_t /* in_contextHubId */, int64_t /* in_appId */,
int32_t /* in_transactionId */) {
return ScopedAStatus::fromExceptionCode(EX_UNSUPPORTED_OPERATION);
}
ScopedAStatus ContextHub::disableNanoapp(int32_t /* in_contextHubId */, int64_t /* in_appId */,
int32_t /* in_transactionId */) {
return ScopedAStatus::fromExceptionCode(EX_UNSUPPORTED_OPERATION);
}
ScopedAStatus ContextHub::enableNanoapp(int32_t /* in_contextHubId */, int64_t /* in_appId */,
int32_t /* in_transactionId */) {
return ScopedAStatus::fromExceptionCode(EX_UNSUPPORTED_OPERATION);
}
ScopedAStatus ContextHub::onSettingChanged(Setting /* in_setting */, bool /*in_enabled */) {
return ScopedAStatus::ok();
}
ScopedAStatus ContextHub::queryNanoapps(int32_t in_contextHubId) {
if (in_contextHubId == kMockHubId && mCallback != nullptr) {
std::vector<NanoappInfo> nanoapps;
mCallback->handleNanoappInfo(nanoapps);
return ScopedAStatus::ok();
} else {
return ScopedAStatus::fromExceptionCode(EX_ILLEGAL_ARGUMENT);
}
}
ScopedAStatus ContextHub::getPreloadedNanoappIds(int32_t /* in_contextHubId */,
std::vector<int64_t>* out_preloadedNanoappIds) {
if (out_preloadedNanoappIds == nullptr) {
return ScopedAStatus::fromExceptionCode(EX_ILLEGAL_ARGUMENT);
}
for (uint64_t i = 0; i < 10; ++i) {
out_preloadedNanoappIds->push_back(i);
}
return ScopedAStatus::ok();
}
ScopedAStatus ContextHub::onNanSessionStateChanged(const NanSessionStateUpdate& /*in_update*/) {
return ScopedAStatus::ok();
}
ScopedAStatus ContextHub::registerCallback(int32_t in_contextHubId,
const std::shared_ptr<IContextHubCallback>& in_cb) {
if (in_contextHubId == kMockHubId) {
mCallback = in_cb;
return ScopedAStatus::ok();
} else {
return ScopedAStatus::fromExceptionCode(EX_ILLEGAL_ARGUMENT);
}
}
ScopedAStatus ContextHub::sendMessageToHub(int32_t in_contextHubId,
const ContextHubMessage& /* in_message */) {
if (in_contextHubId == kMockHubId) {
// Return true here to indicate that the HAL has accepted the message.
// Successful delivery of the message to a nanoapp should be handled at
// a higher level protocol.
return ScopedAStatus::ok();
} else {
return ScopedAStatus::fromExceptionCode(EX_ILLEGAL_ARGUMENT);
}
}
ScopedAStatus ContextHub::setTestMode(bool enable) {
if (enable) {
std::unique_lock<std::mutex> lock(mEndpointMutex);
mEndpoints.clear();
mEndpointSessions.clear();
mEndpointCallback = nullptr;
}
return ScopedAStatus::ok();
}
ScopedAStatus ContextHub::onHostEndpointConnected(const HostEndpointInfo& in_info) {
mConnectedHostEndpoints.insert(in_info.hostEndpointId);
return ScopedAStatus::ok();
}
ScopedAStatus ContextHub::onHostEndpointDisconnected(char16_t in_hostEndpointId) {
if (mConnectedHostEndpoints.count(in_hostEndpointId) > 0) {
mConnectedHostEndpoints.erase(in_hostEndpointId);
}
return ScopedAStatus::ok();
}
ScopedAStatus ContextHub::sendMessageDeliveryStatusToHub(
int32_t /* in_contextHubId */,
const MessageDeliveryStatus& /* in_messageDeliveryStatus */) {
return ScopedAStatus::fromExceptionCode(EX_UNSUPPORTED_OPERATION);
}
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";
hub.toolchain = "n/a";
hub.id = kMockHubId;
hub.peakMips = 1;
hub.maxSupportedMessageLengthBytes = 4096;
hub.chrePlatformId = UINT64_C(0x476f6f6754000000);
hub.chreApiMajorVersion = 1;
hub.chreApiMinorVersion = 6;
hub.supportsReliableMessages = false;
HubInfo hubInfo1 = {};
hubInfo1.hubId = hub.chrePlatformId;
hubInfo1.hubDetails = HubInfo::HubDetails::make<HubInfo::HubDetails::Tag::contextHubInfo>(hub);
VendorHubInfo vendorHub = {};
vendorHub.name = "Mock Vendor Hub";
vendorHub.version = 42;
HubInfo hubInfo2 = {};
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) {
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) {
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) {
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) {
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) {
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) {
// 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) {
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::ok();
};
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::ok();
};
} // namespace aidl::android::hardware::contexthub