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 <utils/SystemClock.h>
25 #include <cerrno>
26 #include <thread>
27
28 /* The definitions below must be the same as the ones defined in kernel. */
29 #define SCP_CHRE_MANAGER_STAT_UNINIT _IOW('a', 0, unsigned int)
30 #define SCP_CHRE_MANAGER_STAT_STOP _IOW('a', 1, unsigned int)
31 #define SCP_CHRE_MANAGER_STAT_START _IOW('a', 2, unsigned int)
32
33 namespace aidl::android::hardware::contexthub {
34
35 using namespace ::android::chre;
36 namespace fbs = ::chre::fbs;
37
38 namespace {
39
40 // The ChreStateMessage defines the message written by kernel indicating the
41 // current state of SCP. It must be consistent with the definition in the
42 // kernel.
43 struct ChreStateMessage {
44 long nextStateAddress;
45 };
46
47 // Possible states of SCP.
48 enum ChreState {
49 SCP_CHRE_UNINIT = 0,
50 SCP_CHRE_STOP = 1,
51 SCP_CHRE_START = 2,
52 };
53
54 ChreState chreCurrentState = SCP_CHRE_UNINIT;
55
getRequestCode(ChreState chreState)56 unsigned getRequestCode(ChreState chreState) {
57 switch (chreState) {
58 case SCP_CHRE_UNINIT:
59 return SCP_CHRE_MANAGER_STAT_UNINIT;
60 case SCP_CHRE_STOP:
61 return SCP_CHRE_MANAGER_STAT_STOP;
62 case SCP_CHRE_START:
63 return SCP_CHRE_MANAGER_STAT_START;
64 default:
65 LOGE("Unexpected CHRE state: %" PRIu32, chreState);
66 assert(false);
67 }
68 }
69 } // namespace
70
init()71 bool TinysysChreConnection::init() {
72 // Make sure the payload size is large enough for nanoapp binary fragment
73 static_assert(kMaxSendingPayloadBytes > CHRE_HOST_DEFAULT_FRAGMENT_SIZE &&
74 kMaxSendingPayloadBytes - CHRE_HOST_DEFAULT_FRAGMENT_SIZE >
75 kMaxPayloadOverheadBytes);
76 mChreFileDescriptor =
77 TEMP_FAILURE_RETRY(open(kChreFileDescriptorPath, O_RDWR));
78 if (mChreFileDescriptor < 0) {
79 LOGE("open chre device failed err=%d errno=%d\n", mChreFileDescriptor,
80 errno);
81 return false;
82 }
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(read(
97 chreFd, chreConnection->mPayload.get(), kMaxReceivingPayloadBytes));
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 int64_t startTime = ::android::elapsedRealtime();
135 // Though usually CHRE is recovered within 1s after SCP is up, in a corner
136 // case it can go beyond 5s. Wait for 10s to cover more extreme cases.
137 chreConnection->waitChreBackOnline(
138 /* timeoutMs= */ std::chrono::milliseconds(10000));
139 LOGW("SCP restarted! CHRE recover time: %" PRIu64 "ms.",
140 ::android::elapsedRealtime() - startTime);
141 chreConnection->getCallback()->onChreRestarted();
142 }
143 chreCurrentState = chreNextState;
144 }
145 }
146
messageSenderTask(TinysysChreConnection * chreConnection)147 [[noreturn]] void TinysysChreConnection::messageSenderTask(
148 TinysysChreConnection *chreConnection) {
149 LOGI("Message sender task is launched.");
150 int chreFd = chreConnection->getChreFileDescriptor();
151 while (true) {
152 chreConnection->mQueue.waitForMessage();
153 ChreConnectionMessage &message = chreConnection->mQueue.front();
154 auto size =
155 TEMP_FAILURE_RETRY(write(chreFd, &message, message.getMessageSize()));
156 if (size < 0) {
157 LOGE("Failed to write to chre file descriptor. errno=%d\n", errno);
158 }
159 chreConnection->mQueue.pop();
160 }
161 }
162
sendMessage(void * data,size_t length)163 bool TinysysChreConnection::sendMessage(void *data, size_t length) {
164 if (length <= 0 || length > kMaxSendingPayloadBytes) {
165 LOGE("length %zu is not within the accepted range.", length);
166 return false;
167 }
168 return mQueue.emplace(data, length);
169 }
170
handleMessageFromChre(TinysysChreConnection * chreConnection,const unsigned char * messageBuffer,size_t messageLen)171 void TinysysChreConnection::handleMessageFromChre(
172 TinysysChreConnection *chreConnection, const unsigned char *messageBuffer,
173 size_t messageLen) {
174 // TODO(b/267188769): Move the wake lock acquisition/release to RAII
175 // pattern.
176 bool isWakelockAcquired =
177 acquire_wake_lock(PARTIAL_WAKE_LOCK, kWakeLock) == 0;
178 if (!isWakelockAcquired) {
179 LOGE("Failed to acquire the wakelock before handling a message.");
180 } else {
181 LOGV("Wakelock is acquired before handling a message.");
182 }
183 HalClientId hostClientId;
184 fbs::ChreMessage messageType = fbs::ChreMessage::NONE;
185 if (!HostProtocolHost::extractHostClientIdAndType(
186 messageBuffer, messageLen, &hostClientId, &messageType)) {
187 LOGW("Failed to extract host client ID from message - sending broadcast");
188 hostClientId = ::chre::kHostClientIdUnspecified;
189 }
190 LOGV("Received a message (type: %hhu, len: %zu) from CHRE for client %d",
191 messageType, messageLen, hostClientId);
192
193 switch (messageType) {
194 case fbs::ChreMessage::LowPowerMicAccessRequest: {
195 chreConnection->getLpmaHandler()->enable(/* enabled= */ true);
196 break;
197 }
198 case fbs::ChreMessage::LowPowerMicAccessRelease: {
199 chreConnection->getLpmaHandler()->enable(/* enabled= */ false);
200 break;
201 }
202 case fbs::ChreMessage::PulseResponse: {
203 chreConnection->notifyChreBackOnline();
204 break;
205 }
206 case fbs::ChreMessage::MetricLog:
207 case fbs::ChreMessage::NanConfigurationRequest:
208 case fbs::ChreMessage::TimeSyncRequest:
209 case fbs::ChreMessage::LogMessage: {
210 LOGE("Unsupported message type %hhu received from CHRE.", messageType);
211 break;
212 }
213 default: {
214 chreConnection->getCallback()->handleMessageFromChre(messageBuffer,
215 messageLen);
216 break;
217 }
218 }
219 if (isWakelockAcquired) {
220 if (release_wake_lock(kWakeLock)) {
221 LOGE("Failed to release the wake lock");
222 } else {
223 LOGV("The wake lock is released after handling a message.");
224 }
225 }
226 }
227 } // namespace aidl::android::hardware::contexthub
228