xref: /aosp_15_r20/system/chre/host/tinysys/hal/tinysys_chre_connection.cc (revision 84e339476a462649f82315436d70fd732297a399)
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