315 lines
10 KiB
C++
315 lines
10 KiB
C++
/*
|
|
* Copyright (C) 2022 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 "exynos_daemon.h"
|
|
#include <sys/epoll.h>
|
|
#include <utils/SystemClock.h>
|
|
#include <array>
|
|
#include <csignal>
|
|
#include "chre_host/file_stream.h"
|
|
|
|
// Aliased for consistency with the way these symbols are referenced in
|
|
// CHRE-side code
|
|
namespace fbs = ::chre::fbs;
|
|
|
|
namespace android {
|
|
namespace chre {
|
|
|
|
namespace {
|
|
|
|
int createEpollFd(int fdToEpoll) {
|
|
struct epoll_event event;
|
|
event.data.fd = fdToEpoll;
|
|
event.events = EPOLLIN | EPOLLWAKEUP;
|
|
int epollFd = epoll_create1(EPOLL_CLOEXEC);
|
|
if (epoll_ctl(epollFd, EPOLL_CTL_ADD, event.data.fd, &event) != 0) {
|
|
LOGE("Failed to add control interface to msg read fd errno: %s",
|
|
strerror(errno));
|
|
epollFd = -1;
|
|
}
|
|
return epollFd;
|
|
}
|
|
|
|
} // anonymous namespace
|
|
|
|
ExynosDaemon::ExynosDaemon() : mLpmaHandler(true /* LPMA enabled */) {
|
|
// TODO(b/235631242): Implement this.
|
|
}
|
|
|
|
bool ExynosDaemon::init() {
|
|
constexpr size_t kMaxTimeSyncRetries = 5;
|
|
constexpr useconds_t kTimeSyncRetryDelayUs = 50000; // 50 ms
|
|
bool success = false;
|
|
mNativeThreadHandle = 0;
|
|
siginterrupt(SIGINT, true);
|
|
std::signal(SIGINT, signalHandler);
|
|
if ((mCommsReadFd = open(kCommsDeviceFilename, O_RDONLY | O_CLOEXEC)) < 0) {
|
|
LOGE("Read FD open failed: %s", strerror(errno));
|
|
} else if ((mCommsWriteFd =
|
|
open(kCommsDeviceFilename, O_WRONLY | O_CLOEXEC)) < 0) {
|
|
LOGE("Write FD open failed: %s", strerror(errno));
|
|
} else {
|
|
mProcessThreadRunning = true;
|
|
mIncomingMsgProcessThread =
|
|
std::thread([&] { this->processIncomingMsgs(); });
|
|
mNativeThreadHandle = mIncomingMsgProcessThread.native_handle();
|
|
|
|
if (!sendTimeSyncWithRetry(kMaxTimeSyncRetries, kTimeSyncRetryDelayUs,
|
|
true /* logOnError */)) {
|
|
LOGE("Failed to send initial time sync message");
|
|
} else {
|
|
loadPreloadedNanoapps();
|
|
success = true;
|
|
LOGD("CHRE daemon initialized successfully");
|
|
}
|
|
}
|
|
return success;
|
|
}
|
|
|
|
void ExynosDaemon::deinit() {
|
|
stopMsgProcessingThread();
|
|
|
|
close(mCommsWriteFd);
|
|
mCommsWriteFd = kInvalidFd;
|
|
|
|
close(mCommsReadFd);
|
|
mCommsReadFd = kInvalidFd;
|
|
}
|
|
|
|
void ExynosDaemon::run() {
|
|
constexpr char kChreSocketName[] = "chre";
|
|
auto serverCb = [&](uint16_t clientId, void *data, size_t len) {
|
|
sendMessageToChre(clientId, data, len);
|
|
};
|
|
|
|
mServer.run(kChreSocketName, true /* allowSocketCreation */, serverCb);
|
|
}
|
|
|
|
void ExynosDaemon::stopMsgProcessingThread() {
|
|
if (mProcessThreadRunning) {
|
|
mProcessThreadRunning = false;
|
|
pthread_kill(mNativeThreadHandle, SIGINT);
|
|
if (mIncomingMsgProcessThread.joinable()) {
|
|
mIncomingMsgProcessThread.join();
|
|
}
|
|
}
|
|
}
|
|
|
|
void ExynosDaemon::processIncomingMsgs() {
|
|
std::array<uint8_t, kIpcMsgSizeMax> message;
|
|
int epollFd = createEpollFd(mCommsReadFd);
|
|
|
|
while (mProcessThreadRunning) {
|
|
struct epoll_event retEvent;
|
|
int nEvents = epoll_wait(epollFd, &retEvent, 1 /* maxEvents */,
|
|
-1 /* infinite timeout */);
|
|
if (nEvents < 0) {
|
|
// epoll_wait will get interrupted if the CHRE daemon is shutting down,
|
|
// check this condition before logging an error.
|
|
if (mProcessThreadRunning) {
|
|
LOGE("Epolling failed: %s", strerror(errno));
|
|
}
|
|
} else if (nEvents == 0) {
|
|
LOGW("Epoll returned with 0 FDs ready despite no timeout (errno: %s)",
|
|
strerror(errno));
|
|
} else {
|
|
int bytesRead = read(mCommsReadFd, message.data(), message.size());
|
|
if (bytesRead < 0) {
|
|
LOGE("Failed to read from fd: %s", strerror(errno));
|
|
} else if (bytesRead == 0) {
|
|
LOGE("Read 0 bytes from fd");
|
|
} else {
|
|
onMessageReceived(message.data(), bytesRead);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
bool ExynosDaemon::doSendMessage(void *data, size_t length) {
|
|
bool success = false;
|
|
if (length > kIpcMsgSizeMax) {
|
|
LOGE("Msg size %zu larger than max msg size %zu", length, kIpcMsgSizeMax);
|
|
} else {
|
|
ssize_t rv = write(mCommsWriteFd, data, length);
|
|
|
|
if (rv < 0) {
|
|
LOGE("Failed to send message: %s", strerror(errno));
|
|
} else if (rv != length) {
|
|
LOGW("Msg send data loss: %zd of %zu bytes were written", rv, length);
|
|
} else {
|
|
success = true;
|
|
}
|
|
}
|
|
return success;
|
|
}
|
|
|
|
int64_t ExynosDaemon::getTimeOffset(bool *success) {
|
|
// TODO(b/235631242): Implement this.
|
|
*success = false;
|
|
return 0;
|
|
}
|
|
|
|
void ExynosDaemon::loadPreloadedNanoapp(const std::string &directory,
|
|
const std::string &name,
|
|
uint32_t transactionId) {
|
|
std::vector<uint8_t> headerBuffer;
|
|
std::vector<uint8_t> nanoappBuffer;
|
|
|
|
std::string headerFilename = directory + "/" + name + ".napp_header";
|
|
std::string nanoappFilename = directory + "/" + name + ".so";
|
|
|
|
if (readFileContents(headerFilename.c_str(), headerBuffer) &&
|
|
readFileContents(nanoappFilename.c_str(), nanoappBuffer) &&
|
|
!loadNanoapp(headerBuffer, nanoappBuffer, transactionId)) {
|
|
LOGE("Failed to load nanoapp: '%s'", name.c_str());
|
|
}
|
|
}
|
|
|
|
bool ExynosDaemon::loadNanoapp(const std::vector<uint8_t> &header,
|
|
const std::vector<uint8_t> &nanoapp,
|
|
uint32_t transactionId) {
|
|
// This struct comes from build/build_template.mk and must not be modified.
|
|
// Refer to that file for more details.
|
|
struct NanoAppBinaryHeader {
|
|
uint32_t headerVersion;
|
|
uint32_t magic;
|
|
uint64_t appId;
|
|
uint32_t appVersion;
|
|
uint32_t flags;
|
|
uint64_t hwHubType;
|
|
uint8_t targetChreApiMajorVersion;
|
|
uint8_t targetChreApiMinorVersion;
|
|
uint8_t reserved[6];
|
|
} __attribute__((packed));
|
|
|
|
bool success = false;
|
|
if (header.size() != sizeof(NanoAppBinaryHeader)) {
|
|
LOGE("Header size mismatch");
|
|
} else {
|
|
// The header blob contains the struct above.
|
|
const auto *appHeader =
|
|
reinterpret_cast<const NanoAppBinaryHeader *>(header.data());
|
|
|
|
// Build the target API version from major and minor.
|
|
uint32_t targetApiVersion = (appHeader->targetChreApiMajorVersion << 24) |
|
|
(appHeader->targetChreApiMinorVersion << 16);
|
|
|
|
success = sendFragmentedNanoappLoad(
|
|
appHeader->appId, appHeader->appVersion, appHeader->flags,
|
|
targetApiVersion, nanoapp.data(), nanoapp.size(), transactionId);
|
|
}
|
|
|
|
return success;
|
|
}
|
|
|
|
bool ExynosDaemon::sendFragmentedNanoappLoad(
|
|
uint64_t appId, uint32_t appVersion, uint32_t appFlags,
|
|
uint32_t appTargetApiVersion, const uint8_t *appBinary, size_t appSize,
|
|
uint32_t transactionId) {
|
|
std::vector<uint8_t> binary(appSize);
|
|
std::copy(appBinary, appBinary + appSize, binary.begin());
|
|
|
|
FragmentedLoadTransaction transaction(transactionId, appId, appVersion,
|
|
appFlags, appTargetApiVersion, binary);
|
|
|
|
bool success = true;
|
|
|
|
while (success && !transaction.isComplete()) {
|
|
// Pad the builder to avoid allocation churn.
|
|
const auto &fragment = transaction.getNextRequest();
|
|
flatbuffers::FlatBufferBuilder builder(fragment.binary.size() + 128);
|
|
HostProtocolHost::encodeFragmentedLoadNanoappRequest(
|
|
builder, fragment, true /* respondBeforeStart */);
|
|
success = sendFragmentAndWaitOnResponse(transactionId, builder,
|
|
fragment.fragmentId, appId);
|
|
}
|
|
|
|
return success;
|
|
}
|
|
|
|
bool ExynosDaemon::sendFragmentAndWaitOnResponse(
|
|
uint32_t transactionId, flatbuffers::FlatBufferBuilder &builder,
|
|
uint32_t fragmentId, uint64_t appId) {
|
|
bool success = true;
|
|
std::unique_lock<std::mutex> lock(mPreloadedNanoappsMutex);
|
|
|
|
mPreloadedNanoappPendingTransaction = {
|
|
.transactionId = transactionId,
|
|
.fragmentId = fragmentId,
|
|
.nanoappId = appId,
|
|
};
|
|
mPreloadedNanoappPending = sendMessageToChre(
|
|
kHostClientIdDaemon, builder.GetBufferPointer(), builder.GetSize());
|
|
if (!mPreloadedNanoappPending) {
|
|
LOGE("Failed to send nanoapp fragment");
|
|
success = false;
|
|
} else {
|
|
std::chrono::seconds timeout(2);
|
|
bool signaled = mPreloadedNanoappsCond.wait_for(
|
|
lock, timeout, [this] { return !mPreloadedNanoappPending; });
|
|
|
|
if (!signaled) {
|
|
LOGE("Nanoapp fragment load timed out");
|
|
success = false;
|
|
}
|
|
}
|
|
return success;
|
|
}
|
|
|
|
void ExynosDaemon::handleDaemonMessage(const uint8_t *message) {
|
|
std::unique_ptr<fbs::MessageContainerT> container =
|
|
fbs::UnPackMessageContainer(message);
|
|
if (container->message.type != fbs::ChreMessage::LoadNanoappResponse) {
|
|
LOGE("Invalid message from CHRE directed to daemon");
|
|
} else {
|
|
const auto *response = container->message.AsLoadNanoappResponse();
|
|
std::unique_lock<std::mutex> lock(mPreloadedNanoappsMutex);
|
|
|
|
if (!mPreloadedNanoappPending) {
|
|
LOGE("Received nanoapp load response with no pending load");
|
|
} else if (mPreloadedNanoappPendingTransaction.transactionId !=
|
|
response->transaction_id) {
|
|
LOGE("Received nanoapp load response with invalid transaction id");
|
|
} else if (mPreloadedNanoappPendingTransaction.fragmentId !=
|
|
response->fragment_id) {
|
|
LOGE("Received nanoapp load response with invalid fragment id");
|
|
} else if (!response->success) {
|
|
#ifdef CHRE_DAEMON_METRIC_ENABLED
|
|
std::vector<VendorAtomValue> values(3);
|
|
values[0].set<VendorAtomValue::longValue>(
|
|
mPreloadedNanoappPendingTransaction.nanoappId);
|
|
values[1].set<VendorAtomValue::intValue>(
|
|
Atoms::ChreHalNanoappLoadFailed::TYPE_PRELOADED);
|
|
values[2].set<VendorAtomValue::intValue>(
|
|
Atoms::ChreHalNanoappLoadFailed::REASON_ERROR_GENERIC);
|
|
const VendorAtom atom{
|
|
.atomId = Atoms::CHRE_HAL_NANOAPP_LOAD_FAILED,
|
|
.values{std::move(values)},
|
|
};
|
|
ChreDaemonBase::reportMetric(atom);
|
|
#endif // CHRE_DAEMON_METRIC_ENABLED
|
|
|
|
} else {
|
|
mPreloadedNanoappPending = false;
|
|
}
|
|
|
|
mPreloadedNanoappsCond.notify_all();
|
|
}
|
|
}
|
|
|
|
} // namespace chre
|
|
} // namespace android
|