blob: e5b21c6c22bd5061dbb693e10f791535b131db3a [file] [log] [blame]
/*
* Copyright (C) 2020 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 "usf_daemon.h"
#include <unistd.h>
#include <chrono>
#include <fstream>
#include <thread>
#include "usf/usf_android_time.h"
#include "usf/usf_client.h"
// Aliased for consistency with the way these symbols are referenced in
// CHRE-side code
namespace fbs = ::chre::fbs;
namespace android {
namespace chre {
namespace {
using ReffedTransportClientPtr = refcount::reffed_ptr<usf::UsfTransportClient>;
constexpr uint8_t kConnectRetries = 5;
constexpr int kRetryDelayMilliSeconds = 100;
bool connectToUsfWithRetry(ReffedTransportClientPtr &client) {
// client has already been nullptr-checked
uint8_t retryCount = 0;
bool success = false;
usf::UsfErr err;
while (retryCount < kConnectRetries) {
if ((err = client->Connect()) == usf::kErrNone) {
success = true;
break;
}
++retryCount;
std::this_thread::sleep_for(
std::chrono::milliseconds(kRetryDelayMilliSeconds));
}
if (success) {
LOGD("Connected to USF Transport server (retry count: %u)", retryCount + 1);
}
return success;
}
} // namespace
bool UsfChreDaemon::init() {
constexpr size_t kMaxTimeSyncRetries = 5;
constexpr useconds_t kTimeSyncRetryDelayUs = 50000; // 50 ms
bool success = false;
usf::UsfErr err;
if ((err = usf::UsfClientMgr::Init()) != usf::kErrNone) {
LOGE("Failed to initialize usf client mgr: (err %d)",
static_cast<int>(err));
} else if ((err = usf::UsfTransportClient::Create(
&mConnection.transportClient)) != usf::kErrNone) {
LOGE("Failed to create a USF transport client: (err %d)",
static_cast<int>(err));
} else if (mConnection.transportClient == nullptr) {
LOGE("Init Failed: Created a NULL transport client!");
} else if (!connectToUsfWithRetry(mConnection.transportClient)) {
LOGE("Failed to connect to USF transport server on %u retries",
kConnectRetries);
} else if ((err = usf::UsfTransportMgr::SetMsgHandler(
usf::UsfMsgType_CHRE, UsfChreDaemon::usfMessageHandler,
this)) != usf::kErrNone) {
LOGE("Failed to set Usf Message Handler");
} else if (!sendTimeSyncWithRetry(kMaxTimeSyncRetries, kTimeSyncRetryDelayUs,
true /* logOnError */)) {
LOGE("Failed to send initial time sync message");
} else {
mLogger = getLogMessageParser();
loadPreloadedNanoapps();
success = true;
LOGD("CHRE daemon initialized successfully");
}
return success;
}
void UsfChreDaemon::deinit() {
setShutdownRequested(true);
usf::UsfClientMgr::Deinit();
}
void UsfChreDaemon::run() {
constexpr char kChreSocketName[] = "chre";
auto serverCb = [&](uint16_t clientId, void *data, size_t len) {
sendMessageToChre(clientId, data, len);
};
mSocketServer.run(kChreSocketName, true /* allowSocketCreation */, serverCb);
}
usf::UsfErr UsfChreDaemon::sendUsfMessage(const uint8_t *data, size_t dataLen) {
usf::UsfTxMsg msg;
msg.SetMsgType(usf::UsfMsgType_CHRE);
msg.SetData(data, dataLen);
return mConnection.transportClient->SendMsg(&msg);
}
bool UsfChreDaemon::sendMessageToChre(uint16_t clientId, void *data,
size_t length) {
bool success = false;
usf::UsfErr err;
if (!HostProtocolHost::mutateHostClientId(data, length, clientId)) {
LOGE("Couldn't set host client ID in message container!");
} else {
LOGV("Delivering message from host (size %zu)", length);
if ((err = sendUsfMessage(static_cast<const uint8_t *>(data), length)) !=
usf::kErrNone) {
LOGE("Failed to deliver message from host to CHRE: %d",
static_cast<int>(err));
} else {
success = true;
}
}
return success;
}
// TODO(karthikmb): Consider moving platform independent parts of this
// function to the base class, revisit when implementing the daemon for
// another platform.
void UsfChreDaemon::onMessageReceived(const unsigned char *messageBuffer,
size_t messageLen) {
mLogger.dump(messageBuffer, messageLen);
uint16_t hostClientId;
fbs::ChreMessage messageType;
if (!HostProtocolHost::extractHostClientIdAndType(
messageBuffer, messageLen, &hostClientId, &messageType)) {
LOGW(
"Failed to extract host client ID from message - sending "
"broadcast");
hostClientId = ::chre::kHostClientIdUnspecified;
}
if (messageType == fbs::ChreMessage::LogMessage) {
std::unique_ptr<fbs::MessageContainerT> container =
fbs::UnPackMessageContainer(messageBuffer);
const auto *logMessage = container->message.AsLogMessage();
const std::vector<int8_t> &logData = logMessage->buffer;
mLogger.log(reinterpret_cast<const uint8_t *>(logData.data()),
logData.size());
} else if (messageType == fbs::ChreMessage::TimeSyncRequest) {
sendTimeSync(true /* logOnError */);
} else if (messageType == fbs::ChreMessage::LowPowerMicAccessRequest) {
LOGD("LpmaRequest unsupported");
} else if (messageType == fbs::ChreMessage::LowPowerMicAccessRelease) {
LOGD("LpmaRelease unsupported");
} else if (hostClientId == kHostClientIdDaemon) {
handleDaemonMessage(messageBuffer);
} else if (hostClientId == ::chre::kHostClientIdUnspecified) {
mSocketServer.sendToAllClients(messageBuffer,
static_cast<size_t>(messageLen));
} else {
mSocketServer.sendToClientById(
messageBuffer, static_cast<size_t>(messageLen), hostClientId);
}
}
void UsfChreDaemon::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 (mPreloadedNanoappPendingTransactionId !=
response->transaction_id) {
LOGE("Received nanoapp load response with invalid transaction id");
} else {
mPreloadedNanoappPending = false;
}
mPreloadedNanoappsCond.notify_all();
}
}
void UsfChreDaemon::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, headerBuffer) &&
readFileContents(nanoappFilename, nanoappBuffer) &&
!loadNanoapp(headerBuffer, nanoappBuffer, transactionId)) {
LOGE("Failed to load nanoapp: '%s'", name.c_str());
}
}
bool UsfChreDaemon::readFileContents(const std::string &filename,
std::vector<uint8_t> &buffer) {
bool success = false;
std::ifstream file(filename.c_str(), std::ios::binary | std::ios::ate);
if (!file) {
LOGE("Couldn't open file '%s': %d (%s)", filename.c_str(), errno,
strerror(errno));
} else {
ssize_t size = file.tellg();
file.seekg(0, std::ios::beg);
buffer.resize(size);
if (!file.read(reinterpret_cast<char *>(buffer.data()), size)) {
LOGE("Couldn't read from file '%s': %d (%s)", filename.c_str(), errno,
strerror(errno));
} else {
success = true;
}
}
return success;
}
bool UsfChreDaemon::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 UsfChreDaemon::sendFragmentAndWaitOnResponse(
uint32_t transactionId, flatbuffers::FlatBufferBuilder &builder) {
bool success = true;
std::unique_lock<std::mutex> lock(mPreloadedNanoappsMutex);
mPreloadedNanoappPendingTransactionId = transactionId;
mPreloadedNanoappPending = sendMessageToChre(
kHostClientIdDaemon, builder.GetBufferPointer(), builder.GetSize());
if (!mPreloadedNanoappPending) {
LOGE("Failed to send nanoapp fragment");
success = false;
} else {
// 60s timeout for fragments. Set high for busy first bootups where the
// PALs can block CHRE initialization while other subsystems come up.
std::chrono::seconds timeout(60);
bool signaled = mPreloadedNanoappsCond.wait_for(
lock, timeout, [this] { return !mPreloadedNanoappPending; });
if (!signaled) {
LOGE("Nanoapp fragment load timed out");
success = false;
}
}
return success;
}
bool UsfChreDaemon::sendFragmentedNanoappLoad(
uint64_t appId, uint32_t appVersion, uint32_t appFlags,
uint32_t appTargetApiVersion, const uint8_t *appBinary, size_t appSize,
uint32_t transactionId) {
// TODO: This is currently limited by the USF Message size, revisit
// and increase this when the USF message size increases.
constexpr size_t kFragmentSize = 512;
std::vector<uint8_t> binary(appSize);
std::copy(appBinary, appBinary + appSize, binary.begin());
FragmentedLoadTransaction transaction(transactionId, appId, appVersion,
appFlags, appTargetApiVersion, binary,
kFragmentSize);
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);
success = sendFragmentAndWaitOnResponse(transactionId, builder);
}
return success;
}
int64_t UsfChreDaemon::getTimeOffset(bool *success) {
int64_t offset = 0;
uint64_t androidTimeNs;
uint64_t sensorCoreTimeNs;
usf::UsfErr err =
usf::UsfGetAndroidAndSensorCoreTime(&androidTimeNs, &sensorCoreTimeNs);
if (err != usf::kErrNone) {
LOGE("Get Android and sensor core time failed.");
} else {
offset = static_cast<int64_t>(androidTimeNs) -
static_cast<int64_t>(sensorCoreTimeNs);
}
*success = (err == usf::kErrNone);
return offset;
}
ChreLogMessageParserBase UsfChreDaemon::getLogMessageParser() {
#ifdef CHRE_USE_TOKENIZED_LOGGING
return ChreTokenizedLogMessageParser();
#else
return ChreLogMessageParserBase();
#endif
}
usf::UsfErr UsfChreDaemon::usfMessageHandler(
usf::UsfTransport * /* transport */, const usf::UsfMsg *usfMsg, void *arg) {
usf::UsfErr err = usf::kErrInvalid;
auto *daemon = static_cast<UsfChreDaemon *>(arg);
if ((daemon != nullptr) && (usfMsg != nullptr)) {
const uint8_t *msgData = usfMsg->data()->data();
size_t msgLen = usfMsg->data()->size();
daemon->onMessageReceived(msgData, msgLen);
} else {
LOGE("Error Handling Usf Message");
}
return err;
}
} // namespace chre
} // namespace android