231 lines
8.1 KiB
C++
231 lines
8.1 KiB
C++
/*
|
|
* 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
|