blob: 3be581ce5028c604e641616a5a853ca86ed400e7 [file] [log] [blame]
/*
* Copyright (C) 2023 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 "tinysys_chre_connection.h"
#include "chre_host/file_stream.h"
#include "chre_host/generated/host_messages_generated.h"
#include "chre_host/host_protocol_host.h"
#include <hardware_legacy/power.h>
#include <sys/ioctl.h>
#include <cerrno>
#include <thread>
/* The definitions below must be the same as the ones defined in kernel. */
#define SCP_CHRE_MANAGER_STAT_UNINIT _IOW('a', 0, unsigned int)
#define SCP_CHRE_MANAGER_STAT_STOP _IOW('a', 1, unsigned int)
#define SCP_CHRE_MANAGER_STAT_START _IOW('a', 2, unsigned int)
namespace aidl::android::hardware::contexthub {
using namespace ::android::chre;
namespace fbs = ::chre::fbs;
namespace {
// The ChreStateMessage defines the message written by kernel indicating the
// current state of SCP. It must be consistent with the definition in the
// kernel.
struct ChreStateMessage {
long nextStateAddress;
};
// Possible states of SCP.
enum ChreState {
SCP_CHRE_UNINIT = 0,
SCP_CHRE_STOP = 1,
SCP_CHRE_START = 2,
};
ChreState chreCurrentState = SCP_CHRE_UNINIT;
unsigned getRequestCode(ChreState chreState) {
switch (chreState) {
case SCP_CHRE_UNINIT:
return SCP_CHRE_MANAGER_STAT_UNINIT;
case SCP_CHRE_STOP:
return SCP_CHRE_MANAGER_STAT_STOP;
case SCP_CHRE_START:
return SCP_CHRE_MANAGER_STAT_START;
default:
LOGE("Unexpected CHRE state: %" PRIu32, chreState);
assert(false);
}
}
} // namespace
bool TinysysChreConnection::init() {
// Make sure the payload size is large enough for nanoapp binary fragment
static_assert(kMaxPayloadBytes > CHRE_HOST_DEFAULT_FRAGMENT_SIZE &&
kMaxPayloadBytes - CHRE_HOST_DEFAULT_FRAGMENT_SIZE >
kMaxPayloadOverheadBytes);
mChreFileDescriptor =
TEMP_FAILURE_RETRY(open(kChreFileDescriptorPath, O_RDWR));
if (mChreFileDescriptor < 0) {
LOGE("open chre device failed err=%d errno=%d\n", mChreFileDescriptor,
errno);
return false;
}
mLogger.init();
// launch the tasks
mMessageListener = std::thread(messageListenerTask, this);
mMessageSender = std::thread(messageSenderTask, this);
mStateListener = std::thread(chreStateMonitorTask, this);
mLpmaHandler.init();
return true;
}
[[noreturn]] void TinysysChreConnection::messageListenerTask(
TinysysChreConnection *chreConnection) {
auto chreFd = chreConnection->getChreFileDescriptor();
while (true) {
{
ssize_t payloadSize = TEMP_FAILURE_RETRY(
read(chreFd, chreConnection->mPayload.get(), kMaxPayloadBytes));
if (payloadSize == 0) {
// Payload size 0 is a fake signal from kernel which is normal if the
// device is in sleep.
LOGV("%s: Received a payload size 0. Ignored. errno=%d", __func__,
errno);
continue;
}
if (payloadSize < 0) {
LOGE("%s: read failed. payload size: %zu. errno=%d", __func__,
payloadSize, errno);
continue;
}
handleMessageFromChre(chreConnection, chreConnection->mPayload.get(),
payloadSize);
}
}
}
[[noreturn]] void TinysysChreConnection::chreStateMonitorTask(
TinysysChreConnection *chreConnection) {
int chreFd = chreConnection->getChreFileDescriptor();
uint32_t nextState = 0;
ChreStateMessage chreMessage{.nextStateAddress =
reinterpret_cast<long>(&nextState)};
while (true) {
if (TEMP_FAILURE_RETRY(ioctl(chreFd, getRequestCode(chreCurrentState),
(unsigned long)&chreMessage)) < 0) {
LOGE("Unable to get an update for the CHRE state: errno=%d", errno);
continue;
}
auto chreNextState = static_cast<ChreState>(nextState);
if (chreCurrentState != chreNextState) {
LOGI("CHRE state changes from %" PRIu32 " to %" PRIu32, chreCurrentState,
chreNextState);
}
if (chreCurrentState == SCP_CHRE_STOP && chreNextState == SCP_CHRE_START) {
// TODO(b/277128368): We should have an explicit indication from CHRE for
// restart recovery.
LOGW("SCP restarted. Give it 5s for recovery before notifying clients");
std::this_thread::sleep_for(std::chrono::milliseconds(5000));
chreConnection->getCallback()->onChreRestarted();
}
chreCurrentState = chreNextState;
}
}
[[noreturn]] void TinysysChreConnection::messageSenderTask(
TinysysChreConnection *chreConnection) {
LOGI("Message sender task is launched.");
int chreFd = chreConnection->getChreFileDescriptor();
while (true) {
chreConnection->mQueue.waitForMessage();
ChreConnectionMessage &message = chreConnection->mQueue.front();
auto size =
TEMP_FAILURE_RETRY(write(chreFd, &message, message.getMessageSize()));
if (size < 0) {
LOGE("Failed to write to chre file descriptor. errno=%d\n", errno);
}
chreConnection->mQueue.pop();
}
}
bool TinysysChreConnection::sendMessage(void *data, size_t length) {
if (length <= 0 || length > kMaxPayloadBytes) {
LOGE("length %zu is not within the accepted range.", length);
return false;
}
return mQueue.emplace(data, length);
}
void TinysysChreConnection::handleMessageFromChre(
TinysysChreConnection *chreConnection, const unsigned char *messageBuffer,
size_t messageLen) {
// TODO(b/267188769): Move the wake lock acquisition/release to RAII
// pattern.
bool isWakelockAcquired =
acquire_wake_lock(PARTIAL_WAKE_LOCK, kWakeLock) == 0;
if (!isWakelockAcquired) {
LOGE("Failed to acquire the wakelock before handling a message.");
} else {
LOGV("Wakelock is acquired before handling a message.");
}
HalClientId hostClientId;
fbs::ChreMessage messageType = fbs::ChreMessage::NONE;
if (!HostProtocolHost::extractHostClientIdAndType(
messageBuffer, messageLen, &hostClientId, &messageType)) {
LOGW("Failed to extract host client ID from message - sending broadcast");
hostClientId = ::chre::kHostClientIdUnspecified;
}
LOGV("Received a message (type: %hhu, len: %zu) from CHRE for client %d",
messageType, messageLen, hostClientId);
switch (messageType) {
case fbs::ChreMessage::LogMessageV2: {
std::unique_ptr<fbs::MessageContainerT> container =
fbs::UnPackMessageContainer(messageBuffer);
const auto *logMessage = container->message.AsLogMessageV2();
const std::vector<int8_t> &buffer = logMessage->buffer;
const auto *logData = reinterpret_cast<const uint8_t *>(buffer.data());
uint32_t numLogsDropped = logMessage->num_logs_dropped;
chreConnection->mLogger.logV2(logData, buffer.size(), numLogsDropped);
break;
}
case fbs::ChreMessage::LowPowerMicAccessRequest: {
chreConnection->getLpmaHandler()->enable(/* enabled= */ true);
break;
}
case fbs::ChreMessage::LowPowerMicAccessRelease: {
chreConnection->getLpmaHandler()->enable(/* enabled= */ false);
break;
}
case fbs::ChreMessage::MetricLog:
case fbs::ChreMessage::NanConfigurationRequest:
case fbs::ChreMessage::TimeSyncRequest:
case fbs::ChreMessage::LogMessage: {
LOGE("Unsupported message type %hhu received from CHRE.", messageType);
break;
}
default: {
chreConnection->getCallback()->handleMessageFromChre(messageBuffer,
messageLen);
break;
}
}
if (isWakelockAcquired) {
if (release_wake_lock(kWakeLock)) {
LOGE("Failed to release the wake lock");
} else {
LOGV("The wake lock is released after handling a message.");
}
}
}
} // namespace aidl::android::hardware::contexthub