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