• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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