1 /*
2 * Copyright (C) 2023 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17 #include "tinysys_chre_connection.h"
18 #include "chre_host/file_stream.h"
19 #include "chre_host/generated/host_messages_generated.h"
20 #include "chre_host/host_protocol_host.h"
21
22 #include <hardware_legacy/power.h>
23 #include <sys/ioctl.h>
24 #include <cerrno>
25 #include <thread>
26
27 /* The definitions below must be the same as the ones defined in kernel. */
28 #define SCP_CHRE_MANAGER_STAT_UNINIT _IOW('a', 0, unsigned int)
29 #define SCP_CHRE_MANAGER_STAT_STOP _IOW('a', 1, unsigned int)
30 #define SCP_CHRE_MANAGER_STAT_START _IOW('a', 2, unsigned int)
31
32 namespace aidl::android::hardware::contexthub {
33
34 using namespace ::android::chre;
35 namespace fbs = ::chre::fbs;
36
37 namespace {
38
39 // The ChreStateMessage defines the message written by kernel indicating the
40 // current state of SCP. It must be consistent with the definition in the
41 // kernel.
42 struct ChreStateMessage {
43 long nextStateAddress;
44 };
45
46 // Possible states of SCP.
47 enum ChreState {
48 SCP_CHRE_UNINIT = 0,
49 SCP_CHRE_STOP = 1,
50 SCP_CHRE_START = 2,
51 };
52
53 ChreState chreCurrentState = SCP_CHRE_UNINIT;
54
getRequestCode(ChreState chreState)55 unsigned getRequestCode(ChreState chreState) {
56 switch (chreState) {
57 case SCP_CHRE_UNINIT:
58 return SCP_CHRE_MANAGER_STAT_UNINIT;
59 case SCP_CHRE_STOP:
60 return SCP_CHRE_MANAGER_STAT_STOP;
61 case SCP_CHRE_START:
62 return SCP_CHRE_MANAGER_STAT_START;
63 default:
64 LOGE("Unexpected CHRE state: %" PRIu32, chreState);
65 assert(false);
66 }
67 }
68 } // namespace
69
init()70 bool TinysysChreConnection::init() {
71 // Make sure the payload size is large enough for nanoapp binary fragment
72 static_assert(kMaxPayloadBytes > CHRE_HOST_DEFAULT_FRAGMENT_SIZE &&
73 kMaxPayloadBytes - CHRE_HOST_DEFAULT_FRAGMENT_SIZE >
74 kMaxPayloadOverheadBytes);
75 mChreFileDescriptor =
76 TEMP_FAILURE_RETRY(open(kChreFileDescriptorPath, O_RDWR));
77 if (mChreFileDescriptor < 0) {
78 LOGE("open chre device failed err=%d errno=%d\n", mChreFileDescriptor,
79 errno);
80 return false;
81 }
82 mLogger.init();
83 // launch the tasks
84 mMessageListener = std::thread(messageListenerTask, this);
85 mMessageSender = std::thread(messageSenderTask, this);
86 mStateListener = std::thread(chreStateMonitorTask, this);
87 mLpmaHandler.init();
88 return true;
89 }
90
messageListenerTask(TinysysChreConnection * chreConnection)91 [[noreturn]] void TinysysChreConnection::messageListenerTask(
92 TinysysChreConnection *chreConnection) {
93 auto chreFd = chreConnection->getChreFileDescriptor();
94 while (true) {
95 {
96 ssize_t payloadSize = TEMP_FAILURE_RETRY(
97 read(chreFd, chreConnection->mPayload.get(), kMaxPayloadBytes));
98 if (payloadSize == 0) {
99 // Payload size 0 is a fake signal from kernel which is normal if the
100 // device is in sleep.
101 LOGV("%s: Received a payload size 0. Ignored. errno=%d", __func__,
102 errno);
103 continue;
104 }
105 if (payloadSize < 0) {
106 LOGE("%s: read failed. payload size: %zu. errno=%d", __func__,
107 payloadSize, errno);
108 continue;
109 }
110 handleMessageFromChre(chreConnection, chreConnection->mPayload.get(),
111 payloadSize);
112 }
113 }
114 }
115
chreStateMonitorTask(TinysysChreConnection * chreConnection)116 [[noreturn]] void TinysysChreConnection::chreStateMonitorTask(
117 TinysysChreConnection *chreConnection) {
118 int chreFd = chreConnection->getChreFileDescriptor();
119 uint32_t nextState = 0;
120 ChreStateMessage chreMessage{.nextStateAddress =
121 reinterpret_cast<long>(&nextState)};
122 while (true) {
123 if (TEMP_FAILURE_RETRY(ioctl(chreFd, getRequestCode(chreCurrentState),
124 (unsigned long)&chreMessage)) < 0) {
125 LOGE("Unable to get an update for the CHRE state: errno=%d", errno);
126 continue;
127 }
128 auto chreNextState = static_cast<ChreState>(nextState);
129 if (chreCurrentState != chreNextState) {
130 LOGI("CHRE state changes from %" PRIu32 " to %" PRIu32, chreCurrentState,
131 chreNextState);
132 }
133 if (chreCurrentState == SCP_CHRE_STOP && chreNextState == SCP_CHRE_START) {
134 // TODO(b/277128368): We should have an explicit indication from CHRE for
135 // restart recovery.
136 LOGW("SCP restarted. Give it 5s for recovery before notifying clients");
137 std::this_thread::sleep_for(std::chrono::milliseconds(5000));
138 chreConnection->getCallback()->onChreRestarted();
139 }
140 chreCurrentState = chreNextState;
141 }
142 }
143
messageSenderTask(TinysysChreConnection * chreConnection)144 [[noreturn]] void TinysysChreConnection::messageSenderTask(
145 TinysysChreConnection *chreConnection) {
146 LOGI("Message sender task is launched.");
147 int chreFd = chreConnection->getChreFileDescriptor();
148 while (true) {
149 chreConnection->mQueue.waitForMessage();
150 ChreConnectionMessage &message = chreConnection->mQueue.front();
151 auto size =
152 TEMP_FAILURE_RETRY(write(chreFd, &message, message.getMessageSize()));
153 if (size < 0) {
154 LOGE("Failed to write to chre file descriptor. errno=%d\n", errno);
155 }
156 chreConnection->mQueue.pop();
157 }
158 }
159
sendMessage(void * data,size_t length)160 bool TinysysChreConnection::sendMessage(void *data, size_t length) {
161 if (length <= 0 || length > kMaxPayloadBytes) {
162 LOGE("length %zu is not within the accepted range.", length);
163 return false;
164 }
165 return mQueue.emplace(data, length);
166 }
167
handleMessageFromChre(TinysysChreConnection * chreConnection,const unsigned char * messageBuffer,size_t messageLen)168 void TinysysChreConnection::handleMessageFromChre(
169 TinysysChreConnection *chreConnection, const unsigned char *messageBuffer,
170 size_t messageLen) {
171 // TODO(b/267188769): Move the wake lock acquisition/release to RAII
172 // pattern.
173 bool isWakelockAcquired =
174 acquire_wake_lock(PARTIAL_WAKE_LOCK, kWakeLock) == 0;
175 if (!isWakelockAcquired) {
176 LOGE("Failed to acquire the wakelock before handling a message.");
177 } else {
178 LOGV("Wakelock is acquired before handling a message.");
179 }
180 HalClientId hostClientId;
181 fbs::ChreMessage messageType = fbs::ChreMessage::NONE;
182 if (!HostProtocolHost::extractHostClientIdAndType(
183 messageBuffer, messageLen, &hostClientId, &messageType)) {
184 LOGW("Failed to extract host client ID from message - sending broadcast");
185 hostClientId = ::chre::kHostClientIdUnspecified;
186 }
187 LOGV("Received a message (type: %hhu, len: %zu) from CHRE for client %d",
188 messageType, messageLen, hostClientId);
189
190 switch (messageType) {
191 case fbs::ChreMessage::LogMessageV2: {
192 std::unique_ptr<fbs::MessageContainerT> container =
193 fbs::UnPackMessageContainer(messageBuffer);
194 const auto *logMessage = container->message.AsLogMessageV2();
195 const std::vector<int8_t> &buffer = logMessage->buffer;
196 const auto *logData = reinterpret_cast<const uint8_t *>(buffer.data());
197 uint32_t numLogsDropped = logMessage->num_logs_dropped;
198 chreConnection->mLogger.logV2(logData, buffer.size(), numLogsDropped);
199 break;
200 }
201 case fbs::ChreMessage::LowPowerMicAccessRequest: {
202 chreConnection->getLpmaHandler()->enable(/* enabled= */ true);
203 break;
204 }
205 case fbs::ChreMessage::LowPowerMicAccessRelease: {
206 chreConnection->getLpmaHandler()->enable(/* enabled= */ false);
207 break;
208 }
209 case fbs::ChreMessage::MetricLog:
210 case fbs::ChreMessage::NanConfigurationRequest:
211 case fbs::ChreMessage::TimeSyncRequest:
212 case fbs::ChreMessage::LogMessage: {
213 LOGE("Unsupported message type %hhu received from CHRE.", messageType);
214 break;
215 }
216 default: {
217 chreConnection->getCallback()->handleMessageFromChre(messageBuffer,
218 messageLen);
219 break;
220 }
221 }
222 if (isWakelockAcquired) {
223 if (release_wake_lock(kWakeLock)) {
224 LOGE("Failed to release the wake lock");
225 } else {
226 LOGV("The wake lock is released after handling a message.");
227 }
228 }
229 }
230 } // namespace aidl::android::hardware::contexthub
231