blob: 402fb9c2b8cd2de1627257e397b43034d9aea8c8 [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 <cstdlib>
#include <fstream>
#include "chre_host/daemon_base.h"
#include "chre_host/log.h"
#include "chre_host/napp_header.h"
#include <json/json.h>
#ifdef CHRE_DAEMON_METRIC_ENABLED
#include <aidl/android/frameworks/stats/IStats.h>
#include <android/binder_manager.h>
#include <hardware/google/pixel/pixelstats/pixelatoms.pb.h>
using ::aidl::android::frameworks::stats::IStats;
using ::aidl::android::frameworks::stats::VendorAtom;
using ::aidl::android::frameworks::stats::VendorAtomValue;
namespace PixelAtoms = ::android::hardware::google::pixel::PixelAtoms;
#endif // CHRE_DAEMON_METRIC_ENABLED
// Aliased for consistency with the way these symbols are referenced in
// CHRE-side code
namespace fbs = ::chre::fbs;
namespace android {
namespace chre {
ChreDaemonBase::ChreDaemonBase() : mChreShutdownRequested(false) {
mLogger.init();
}
void ChreDaemonBase::loadPreloadedNanoapps() {
constexpr char kPreloadedNanoappsConfigPath[] =
"/vendor/etc/chre/preloaded_nanoapps.json";
std::ifstream configFileStream(kPreloadedNanoappsConfigPath);
Json::CharReaderBuilder builder;
Json::Value config;
if (!configFileStream) {
LOGE("Failed to open config file '%s': %d (%s)",
kPreloadedNanoappsConfigPath, errno, strerror(errno));
} else if (!Json::parseFromStream(builder, configFileStream, &config,
/* errorMessage = */ nullptr)) {
LOGE("Failed to parse nanoapp config file");
} else if (!config.isMember("nanoapps") || !config.isMember("source_dir")) {
LOGE("Malformed preloaded nanoapps config");
} else {
const Json::Value &directory = config["source_dir"];
for (Json::ArrayIndex i = 0; i < config["nanoapps"].size(); i++) {
const Json::Value &nanoapp = config["nanoapps"][i];
loadPreloadedNanoapp(directory.asString(), nanoapp.asString(),
static_cast<uint32_t>(i));
}
}
}
void ChreDaemonBase::loadPreloadedNanoapp(const std::string &directory,
const std::string &name,
uint32_t transactionId) {
std::vector<uint8_t> headerBuffer;
std::string headerFile = directory + "/" + name + ".napp_header";
// Only create the nanoapp filename as the CHRE framework will load from
// within the directory its own binary resides in.
std::string nanoappFilename = name + ".so";
if (readFileContents(headerFile.c_str(), &headerBuffer) &&
!loadNanoapp(headerBuffer, nanoappFilename, transactionId)) {
LOGE("Failed to load nanoapp: '%s'", name.c_str());
}
}
bool ChreDaemonBase::loadNanoapp(const std::vector<uint8_t> &header,
const std::string &nanoappName,
uint32_t transactionId) {
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 = sendNanoappLoad(appHeader->appId, appHeader->appVersion,
targetApiVersion, nanoappName, transactionId);
}
return success;
}
bool ChreDaemonBase::sendNanoappLoad(uint64_t appId, uint32_t appVersion,
uint32_t appTargetApiVersion,
const std::string &appBinaryName,
uint32_t transactionId) {
flatbuffers::FlatBufferBuilder builder;
HostProtocolHost::encodeLoadNanoappRequestForFile(
builder, transactionId, appId, appVersion, appTargetApiVersion,
appBinaryName.c_str());
bool success = sendMessageToChre(
kHostClientIdDaemon, builder.GetBufferPointer(), builder.GetSize());
if (!success) {
LOGE("Failed to send nanoapp filename.");
} else {
Transaction transaction = {
.transactionId = transactionId,
.nanoappId = appId,
};
mPreloadedNanoappPendingTransactions.push(transaction);
}
return success;
}
bool ChreDaemonBase::sendTimeSync(bool logOnError) {
bool success = false;
int64_t timeOffset = getTimeOffset(&success);
if (success) {
flatbuffers::FlatBufferBuilder builder(64);
HostProtocolHost::encodeTimeSyncMessage(builder, timeOffset);
success = sendMessageToChre(kHostClientIdDaemon, builder.GetBufferPointer(),
builder.GetSize());
if (!success && logOnError) {
LOGE("Failed to deliver time sync message from host to CHRE");
}
}
return success;
}
bool ChreDaemonBase::sendTimeSyncWithRetry(size_t numRetries,
useconds_t retryDelayUs,
bool logOnError) {
bool success = false;
while (!success && (numRetries-- != 0)) {
success = sendTimeSync(logOnError);
if (!success) {
usleep(retryDelayUs);
}
}
return success;
}
bool ChreDaemonBase::sendMessageToChre(uint16_t clientId, void *data,
size_t length) {
bool success = false;
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);
getLogger().dump(static_cast<const uint8_t *>(data), length);
success = doSendMessage(data, length);
}
return success;
}
void ChreDaemonBase::onMessageReceived(const unsigned char *messageBuffer,
size_t messageLen) {
getLogger().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;
getLogger().log(reinterpret_cast<const uint8_t *>(logData.data()),
logData.size());
} else if (messageType == fbs::ChreMessage::LogMessageV2) {
std::unique_ptr<fbs::MessageContainerT> container =
fbs::UnPackMessageContainer(messageBuffer);
const auto *logMessage = container->message.AsLogMessageV2();
const std::vector<int8_t> &logDataBuffer = logMessage->buffer;
const auto *logData =
reinterpret_cast<const uint8_t *>(logDataBuffer.data());
uint32_t numLogsDropped = logMessage->num_logs_dropped;
getLogger().logV2(logData, logDataBuffer.size(), numLogsDropped);
} else if (messageType == fbs::ChreMessage::TimeSyncRequest) {
sendTimeSync(true /* logOnError */);
} else if (messageType == fbs::ChreMessage::LowPowerMicAccessRequest) {
configureLpma(true /* enabled */);
} else if (messageType == fbs::ChreMessage::LowPowerMicAccessRelease) {
configureLpma(false /* enabled */);
} else if (messageType == fbs::ChreMessage::MetricLog) {
#ifdef CHRE_DAEMON_METRIC_ENABLED
std::unique_ptr<fbs::MessageContainerT> container =
fbs::UnPackMessageContainer(messageBuffer);
const auto *metricMsg = container->message.AsMetricLog();
handleMetricLog(metricMsg);
#endif // CHRE_DAEMON_METRIC_ENABLED
} else if (hostClientId == kHostClientIdDaemon) {
handleDaemonMessage(messageBuffer);
} else if (hostClientId == ::chre::kHostClientIdUnspecified) {
mServer.sendToAllClients(messageBuffer, static_cast<size_t>(messageLen));
} else {
mServer.sendToClientById(messageBuffer, static_cast<size_t>(messageLen),
hostClientId);
}
}
bool ChreDaemonBase::readFileContents(const char *filename,
std::vector<uint8_t> *buffer) {
bool success = false;
std::ifstream file(filename, std::ios::binary | std::ios::ate);
if (!file) {
LOGE("Couldn't open file '%s': %d (%s)", filename, 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, errno,
strerror(errno));
} else {
success = true;
}
}
return success;
}
void ChreDaemonBase::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();
if (mPreloadedNanoappPendingTransactions.empty()) {
LOGE("Received nanoapp load response with no pending load");
} else if (mPreloadedNanoappPendingTransactions.front().transactionId !=
response->transaction_id) {
LOGE("Received nanoapp load response with ID %" PRIu32
" expected transaction id %" PRIu32,
response->transaction_id,
mPreloadedNanoappPendingTransactions.front().transactionId);
} else {
if (!response->success) {
LOGE("Received unsuccessful nanoapp load response with ID %" PRIu32,
mPreloadedNanoappPendingTransactions.front().transactionId);
#ifdef CHRE_DAEMON_METRIC_ENABLED
std::vector<VendorAtomValue> values(3);
values[0].set<VendorAtomValue::longValue>(
mPreloadedNanoappPendingTransactions.front().nanoappId);
values[1].set<VendorAtomValue::intValue>(
PixelAtoms::ChreHalNanoappLoadFailed::TYPE_PRELOADED);
values[2].set<VendorAtomValue::intValue>(
PixelAtoms::ChreHalNanoappLoadFailed::REASON_ERROR_GENERIC);
const VendorAtom atom{
.reverseDomainName = PixelAtoms::ReverseDomainNames().pixel(),
.atomId = PixelAtoms::Atom::kChreHalNanoappLoadFailed,
.values{std::move(values)},
};
reportMetric(atom);
#endif // CHRE_DAEMON_METRIC_ENABLED
}
mPreloadedNanoappPendingTransactions.pop();
}
}
}
#ifdef CHRE_DAEMON_METRIC_ENABLED
void ChreDaemonBase::handleMetricLog(const ::chre::fbs::MetricLogT *metricMsg) {
const std::vector<int8_t> &encodedMetric = metricMsg->encoded_metric;
switch (metricMsg->id) {
case PixelAtoms::Atom::kChrePalOpenFailed: {
PixelAtoms::ChrePalOpenFailed metric;
if (!metric.ParseFromArray(encodedMetric.data(), encodedMetric.size())) {
LOGE("Failed to parse metric data");
} else {
std::vector<VendorAtomValue> values(2);
values[0].set<VendorAtomValue::intValue>(metric.pal());
values[1].set<VendorAtomValue::intValue>(metric.type());
const VendorAtom atom{
.reverseDomainName = PixelAtoms::ReverseDomainNames().pixel(),
.atomId = PixelAtoms::Atom::kChrePalOpenFailed,
.values{std::move(values)},
};
reportMetric(atom);
}
break;
}
default: {
LOGW("Unknown metric ID %" PRIu32, metricMsg->id);
}
}
}
#endif // CHRE_DAEMON_METRIC_ENABLED
#ifdef CHRE_DAEMON_METRIC_ENABLED
void ChreDaemonBase::reportMetric(const VendorAtom &atom) {
const std::string statsServiceName =
std::string(IStats::descriptor).append("/default");
if (!AServiceManager_isDeclared(statsServiceName.c_str())) {
LOGE("Stats service is not declared.");
return;
}
std::shared_ptr<IStats> stats_client = IStats::fromBinder(ndk::SpAIBinder(
AServiceManager_waitForService(statsServiceName.c_str())));
const ndk::ScopedAStatus ret = stats_client->reportVendorAtom(atom);
if (!ret.isOk()) {
LOGE("Failed to report vendor atom");
}
}
#endif // CHRE_DAEMON_METRIC_ENABLED
} // namespace chre
} // namespace android