1 /*
2  * Copyright (C) 2020 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 <cassert>
18 #include <cinttypes>
19 
20 #include "chre_host/st_hal_lpma_handler.h"
21 
22 namespace android {
23 namespace chre {
24 
25 namespace {
26 
27 constexpr char kChreWakeLockName[] = "chre_lpma_handler";
28 
acquireWakeLock()29 void acquireWakeLock() {
30   int rc;
31   if ((rc = acquire_wake_lock(PARTIAL_WAKE_LOCK, kChreWakeLockName)) != 0) {
32     LOGE("Failed to acquire wakelock (err %d)", rc);
33   }
34 }
35 
releaseWakeLock()36 void releaseWakeLock() {
37   int rc;
38   static bool wakeLockInitialRelease = true;
39 
40   // It's expected to get an error when we first try to release the
41   // wakelock
42   // as it won't exist unless it was leaked previously - don't output a
43   // false warning for this case
44   if (((rc = release_wake_lock(kChreWakeLockName)) != 0) &&
45       !wakeLockInitialRelease) {
46     LOGE("Failed to release wakelock (err %d)", rc);
47   }
48 
49   wakeLockInitialRelease = false;
50 }
51 
52 }  // anonymous namespace
53 
StHalLpmaHandler(bool allowed)54 StHalLpmaHandler::StHalLpmaHandler(bool allowed) : mIsLpmaAllowed(allowed) {
55 #ifdef CHRE_ST_LPMA_HANDLER_AIDL
56   // TODO(b/278167963): Add death recipient
57 #else
58   auto cb = [&]() { onStHalServiceDeath(); };
59   mDeathRecipient = new StHalDeathRecipient(cb);
60 #endif  // CHRE_ST_LPMA_HANDLER_AIDL
61 }
62 
~StHalLpmaHandler()63 StHalLpmaHandler::~StHalLpmaHandler() {
64   if (mTargetLpmaEnabled) {
65     stopAndUnload();
66   }
67   if (mThread.has_value()) {
68     mStThreadShouldExit = true;
69     mCondVar.notify_all();
70     mThread->join();
71   }
72   releaseWakeLock();
73 }
74 
init()75 void StHalLpmaHandler::init() {
76   if (mIsLpmaAllowed) {
77     mThread = std::thread(&StHalLpmaHandler::stHalLpmaHandlerThreadEntry, this);
78   }
79 }
80 
enable(bool enabled)81 void StHalLpmaHandler::enable(bool enabled) {
82   if (mIsLpmaAllowed) {
83     std::lock_guard<std::mutex> lock(mMutex);
84     mTargetLpmaEnabled = enabled;
85     mCondVarPredicate = true;
86     mCondVar.notify_one();
87   } else {
88     LOGE("Trying to modify LPMA state when LPMA is disabled");
89   }
90 }
91 
loadAndStart()92 bool StHalLpmaHandler::loadAndStart() {
93   if (load()) {
94     if (start()) {
95       return true;
96     } else {
97       unload();
98     }
99   }
100   return false;
101 }
102 
stopAndUnload()103 void StHalLpmaHandler::stopAndUnload() {
104   stop();
105   unload();
106 }
107 
stHalRequestAndProcessLocked(std::unique_lock<std::mutex> const & locked)108 void StHalLpmaHandler::stHalRequestAndProcessLocked(
109     std::unique_lock<std::mutex> const &locked) {
110   // Cannot use assert(locked.owns_lock()) since locked are not use in other
111   // places and non-debug version will not use assert. Compiler will not compile
112   // if there is unused parameter.
113   if (!locked.owns_lock()) {
114     assert(false);
115   }
116   if (mCurrentLpmaEnabled == mTargetLpmaEnabled) {
117     return;
118   } else if (mTargetLpmaEnabled && loadAndStart()) {
119     mCurrentLpmaEnabled = mTargetLpmaEnabled;
120   } else if (!mTargetLpmaEnabled) {
121     // Regardless of whether the use case fails to unload, set the
122     // currentLpmaEnabled to the targetLpmaEnabled. This will allow the next
123     // enable request to proceed. After a failure to unload occurs, the
124     // supplied handle is invalid and should not be unloaded again.
125     stopAndUnload();
126     mCurrentLpmaEnabled = mTargetLpmaEnabled;
127   }
128 }
129 
stHalLpmaHandlerThreadEntry()130 void StHalLpmaHandler::stHalLpmaHandlerThreadEntry() {
131   LOGD("Starting LPMA thread");
132   constexpr useconds_t kInitialRetryDelayUs = 500000;
133   constexpr int kRetryGrowthFactor = 2;
134   constexpr int kRetryGrowthLimit = 5;     // Terminates at 8s retry interval.
135   constexpr int kRetryWakeLockLimit = 10;  // Retry with a wakelock 10 times.
136   std::unique_lock<std::mutex> lock(mMutex);
137   while (!mStThreadShouldExit) {
138     stHalRequestAndProcessLocked(lock);
139     bool retryNeeded = (mCurrentLpmaEnabled != mTargetLpmaEnabled);
140     releaseWakeLock();
141 
142     if (retryNeeded) {
143       if (mRetryCount < kRetryGrowthLimit) {
144         mRetryCount += 1;
145         mRetryDelay = mRetryDelay * kRetryGrowthFactor;
146       }
147       mCondVar.wait_for(lock, std::chrono::microseconds(mRetryDelay), [this] {
148         return mCondVarPredicate || mStThreadShouldExit;
149       });
150     } else {
151       mRetryCount = 0;
152       mRetryDelay = kInitialRetryDelayUs;
153       mCondVar.wait(
154           lock, [this] { return mCondVarPredicate || mStThreadShouldExit; });
155     }
156     mCondVarPredicate = false;
157     if (mRetryCount <= kRetryWakeLockLimit) {
158       acquireWakeLock();
159     }
160   }
161 }
162 
onStHalServiceDeath()163 void StHalLpmaHandler::onStHalServiceDeath() {
164   LOGE("ST HAL Service Died");
165   std::lock_guard<std::mutex> lock(mMutex);
166   mStHalService = nullptr;
167   if (mTargetLpmaEnabled) {
168     // ST HAL has died, so assume that the sound model is no longer active,
169     // and trigger a reload of the sound model.
170     mCurrentLpmaEnabled = false;
171     mCondVarPredicate = true;
172     mCondVar.notify_one();
173   }
174 }
175 
176 #ifdef CHRE_ST_LPMA_HANDLER_AIDL
checkConnectionToStHalServiceLocked()177 void StHalLpmaHandler::checkConnectionToStHalServiceLocked() {
178   if (mStHalService == nullptr) {
179     auto aidlServiceName =
180         std::string() + ISoundTriggerHw::descriptor + "/default";
181     ndk::SpAIBinder binder(
182         AServiceManager_waitForService(aidlServiceName.c_str()));
183     if (binder.get() != nullptr) {
184       LOGI("Connected to ST HAL service");
185       mStHalService = ISoundTriggerHw::fromBinder(binder);
186       // TODO(b/278167963): Add death recipient
187     }
188   }
189 }
190 
load()191 bool StHalLpmaHandler::load() {
192   LOGV("Loading LPMA");
193 
194   bool loaded = false;
195   checkConnectionToStHalServiceLocked();
196 
197   aidl::android::media::soundtrigger::SoundModel soundModel;
198   soundModel.type = aidl::android::media::soundtrigger::SoundModelType::GENERIC;
199   soundModel.vendorUuid = "57caddb1-acdb-4dce-8cb0-2e95a2313aee";
200   soundModel.dataSize = 0;
201   auto status =
202       mStHalService->loadSoundModel(soundModel, nullptr, &mLpmaHandle);
203   if (status.isOk()) {
204     LOGI("Loaded LPMA");
205     loaded = true;
206   } else {
207     LOGE("Failed to load LPMA with error code %" PRId32,
208          status.getExceptionCode());
209   }
210   return loaded;
211 }
212 
unload()213 void StHalLpmaHandler::unload() {
214   checkConnectionToStHalServiceLocked();
215   auto status = mStHalService->unloadSoundModel(mLpmaHandle);
216   if (!status.isOk()) {
217     LOGE("Failed to unload LPMA with error code %" PRId32,
218          status.getExceptionCode());
219   }
220 }
221 
start()222 bool StHalLpmaHandler::start() {
223   // TODO(b/278167963): Implement this
224   return true;
225 }
226 
stop()227 void StHalLpmaHandler::stop() {
228   // TODO(b/278167963): Implement this
229 }
230 
231 #else
232 
checkConnectionToStHalServiceLocked()233 void StHalLpmaHandler::checkConnectionToStHalServiceLocked() {
234   if (mStHalService == nullptr) {
235     mStHalService = ISoundTriggerHw::getService();
236     if (mStHalService != nullptr) {
237       LOGI("Connected to ST HAL service");
238       mStHalService->linkToDeath(mDeathRecipient, 0 /* flags */);
239     }
240   }
241 }
242 
load()243 bool StHalLpmaHandler::load() {
244   constexpr uint8_t kUuidNode[] = {0x2E, 0x95, 0xA2, 0x31, 0x3A, 0xEE};
245 
246   LOGV("Loading LPMA");
247 
248   ISoundTriggerHw::SoundModel soundModel;
249   soundModel.type = SoundModelType::GENERIC;
250   soundModel.vendorUuid.timeLow = 0x57CADDB1;
251   soundModel.vendorUuid.timeMid = 0xACDB;
252   soundModel.vendorUuid.versionAndTimeHigh = 0x4DCE;
253   soundModel.vendorUuid.variantAndClockSeqHigh = 0x8CB0;
254 
255   memcpy(&soundModel.vendorUuid.node[0], kUuidNode, sizeof(kUuidNode));
256   soundModel.data.resize(1);  // Insert an empty byte to bypass HAL NULL checks.
257 
258   bool loaded = false;
259   checkConnectionToStHalServiceLocked();
260   int32_t loadResult;
261   Return<void> hidlResult = mStHalService->loadSoundModel(
262       soundModel, nullptr /* callback */, 0 /* cookie */,
263       [&](int32_t retval, SoundModelHandle handle) {
264         loadResult = retval;
265         mLpmaHandle = handle;
266       });
267 
268   if (hidlResult.isOk()) {
269     if (loadResult == 0) {
270       LOGD("Loaded LPMA");
271       loaded = true;
272     } else {
273       LOGE("Failed to load LPMA with %" PRId32, loadResult);
274     }
275   } else {
276     LOGE("Failed to load LPMA due to hidl error %s",
277          hidlResult.description().c_str());
278   }
279 
280   return loaded;
281 }
282 
unload()283 void StHalLpmaHandler::unload() {
284   checkConnectionToStHalServiceLocked();
285   Return<int32_t> hidlResult = mStHalService->unloadSoundModel(mLpmaHandle);
286   mLpmaHandle = 0;
287 
288   if (hidlResult.isOk()) {
289     if (hidlResult != 0) {
290       LOGE("Failed to unload LPMA with %" PRId32, int32_t(hidlResult));
291     }
292   } else {
293     LOGE("Failed to unload LPMA due to hidl error %s",
294          hidlResult.description().c_str());
295   }
296 }
297 
start()298 bool StHalLpmaHandler::start() {
299 #ifdef CHRE_LPMA_REQUEST_START_RECOGNITION
300   // mLpmaHandle
301   ISoundTriggerHw::RecognitionConfig config = {};
302 
303   Return<int32_t> hidlResult = mStHalService->startRecognition(
304       mLpmaHandle, config, nullptr /* callback */, 0 /* cookie */);
305 
306   int32_t result = hidlResult.withDefault(-EPIPE);
307   if (result != 0) {
308     LOGE("Failed to start LPMA: %" PRId32, result);
309   }
310   return (result == 0);
311 #else
312   return true;
313 #endif  // CHRE_LPMA_REQUEST_START_RECOGNITION
314 }
315 
stop()316 void StHalLpmaHandler::stop() {
317 #ifdef CHRE_LPMA_REQUEST_START_RECOGNITION
318   Return<int32_t> hidlResult = mStHalService->stopRecognition(mLpmaHandle);
319 
320   int32_t result = hidlResult.withDefault(-EPIPE);
321   if (result != 0) {
322     LOGW("Failed to stop LPMA: %" PRId32, result);
323   }
324 #endif  // CHRE_LPMA_REQUEST_START_RECOGNITION
325 }
326 
327 #endif  // CHRE_ST_LPMA_HANDLER_AIDL
328 
329 }  // namespace chre
330 }  // namespace android
331