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 <cinttypes>
18
19 #include "chre_host/st_hal_lpma_handler.h"
20
21 namespace android {
22 namespace chre {
23
24 namespace {
25
26 constexpr char kChreWakeLockName[] = "chre_daemon";
27
acquireWakeLock()28 void acquireWakeLock() {
29 int rc;
30 if ((rc = acquire_wake_lock(PARTIAL_WAKE_LOCK, kChreWakeLockName)) != 0) {
31 LOGE("Failed to acquire wakelock (err %d)", rc);
32 }
33 }
34
releaseWakeLock()35 void releaseWakeLock() {
36 int rc;
37 static bool wakeLockInitialRelease = true;
38
39 // It's expected to get an error when we first try to release the
40 // wakelock
41 // as it won't exist unless it was leaked previously - don't output a
42 // false warning for this case
43 if (((rc = release_wake_lock(kChreWakeLockName)) != 0) &&
44 !wakeLockInitialRelease) {
45 LOGE("Failed to release wakelock (err %d)", rc);
46 }
47
48 wakeLockInitialRelease = false;
49 }
50
51 } // anonymous namespace
52
StHalLpmaHandler(bool allowed)53 StHalLpmaHandler::StHalLpmaHandler(bool allowed) : mIsLpmaAllowed(allowed) {
54 auto cb = [&]() { onStHalServiceDeath(); };
55 mDeathRecipient = new StHalDeathRecipient(cb);
56 }
57
init()58 void StHalLpmaHandler::init() {
59 if (mIsLpmaAllowed) {
60 mThread = std::thread(&StHalLpmaHandler::StHalLpmaHandlerThreadEntry, this);
61 }
62 }
63
enable(bool enabled)64 void StHalLpmaHandler::enable(bool enabled) {
65 if (mIsLpmaAllowed) {
66 std::lock_guard<std::mutex> lock(mMutex);
67 mTargetLpmaEnabled = enabled;
68 mCondVarPredicate = true;
69 mCondVar.notify_one();
70 } else {
71 LOGE("Trying to modify LPMA state when LPMA is disabled");
72 }
73 }
74
load()75 bool StHalLpmaHandler::load() {
76 constexpr uint8_t kUuidNode[] = {0x2E, 0x95, 0xA2, 0x31, 0x3A, 0xEE};
77
78 LOGV("Loading LPMA");
79
80 ISoundTriggerHw::SoundModel soundModel;
81 soundModel.type = SoundModelType::GENERIC;
82 soundModel.vendorUuid.timeLow = 0x57CADDB1;
83 soundModel.vendorUuid.timeMid = 0xACDB;
84 soundModel.vendorUuid.versionAndTimeHigh = 0x4DCE;
85 soundModel.vendorUuid.variantAndClockSeqHigh = 0x8CB0;
86
87 memcpy(&soundModel.vendorUuid.node[0], kUuidNode, sizeof(kUuidNode));
88 soundModel.data.resize(1); // Insert an empty byte to bypass HAL NULL checks.
89
90 bool loaded = false;
91 checkConnectionToStHalServiceLocked();
92 int32_t loadResult;
93 Return<void> hidlResult = mStHalService->loadSoundModel(
94 soundModel, nullptr /* callback */, 0 /* cookie */,
95 [&](int32_t retval, SoundModelHandle handle) {
96 loadResult = retval;
97 mLpmaHandle = handle;
98 });
99
100 if (hidlResult.isOk()) {
101 if (loadResult == 0) {
102 LOGD("Loaded LPMA");
103 loaded = true;
104 } else {
105 LOGE("Failed to load LPMA with %" PRId32, loadResult);
106 }
107 } else {
108 LOGE("Failed to load LPMA due to hidl error %s",
109 hidlResult.description().c_str());
110 }
111
112 return loaded;
113 }
114
unload()115 void StHalLpmaHandler::unload() {
116 checkConnectionToStHalServiceLocked();
117 Return<int32_t> hidlResult = mStHalService->unloadSoundModel(mLpmaHandle);
118 mLpmaHandle = 0;
119
120 if (hidlResult.isOk()) {
121 if (hidlResult != 0) {
122 LOGE("Failed to unload LPMA with %" PRId32, int32_t(hidlResult));
123 }
124 } else {
125 LOGE("Failed to unload LPMA due to hidl error %s",
126 hidlResult.description().c_str());
127 }
128 }
129
checkConnectionToStHalServiceLocked()130 void StHalLpmaHandler::checkConnectionToStHalServiceLocked() {
131 if (mStHalService == nullptr) {
132 mStHalService = ISoundTriggerHw::getService();
133 if (mStHalService != nullptr) {
134 LOGI("Connected to ST HAL service");
135 mStHalService->linkToDeath(mDeathRecipient, 0 /* flags */);
136 }
137 }
138 }
139
waitOnStHalRequestAndProcess()140 bool StHalLpmaHandler::waitOnStHalRequestAndProcess() {
141 bool noDelayNeeded = true;
142 std::unique_lock<std::mutex> lock(mMutex);
143
144 if (mCurrentLpmaEnabled == mTargetLpmaEnabled) {
145 mRetryDelay = 0;
146 mRetryCount = 0;
147 releaseWakeLock(); // Allow the system to suspend while waiting.
148 mCondVar.wait(lock, [this] { return mCondVarPredicate; });
149 mCondVarPredicate = false;
150 acquireWakeLock(); // Ensure the system stays up while retrying.
151 } else if (mTargetLpmaEnabled && load()) {
152 mCurrentLpmaEnabled = mTargetLpmaEnabled;
153 } else if (!mTargetLpmaEnabled) {
154 // Regardless of whether the use case fails to unload, set the
155 // currentLpmaEnabled to the targetLpmaEnabled. This will allow the next
156 // enable request to proceed. After a failure to unload occurs, the
157 // supplied handle is invalid and should not be unloaded again.
158 unload();
159 mCurrentLpmaEnabled = mTargetLpmaEnabled;
160 } else {
161 noDelayNeeded = false;
162 }
163
164 return noDelayNeeded;
165 }
166
delay()167 void StHalLpmaHandler::delay() {
168 constexpr useconds_t kInitialRetryDelayUs = 500000;
169 constexpr int kRetryGrowthFactor = 2;
170 constexpr int kRetryGrowthLimit = 5; // Terminates at 8s retry interval.
171 constexpr int kRetryWakeLockLimit = 10; // Retry with a wakelock 10 times.
172
173 if (mRetryDelay == 0) {
174 mRetryDelay = kInitialRetryDelayUs;
175 } else if (mRetryCount < kRetryGrowthLimit) {
176 mRetryDelay *= kRetryGrowthFactor;
177 }
178 usleep(mRetryDelay);
179 mRetryCount++;
180 if (mRetryCount > kRetryWakeLockLimit) {
181 releaseWakeLock();
182 }
183 }
184
StHalLpmaHandlerThreadEntry()185 void StHalLpmaHandler::StHalLpmaHandlerThreadEntry() {
186 LOGD("Starting LPMA thread");
187
188 while (true) {
189 if (!waitOnStHalRequestAndProcess()) {
190 // processing an LPMA state update failed, retry after a little while
191 delay();
192 }
193 }
194 }
195
onStHalServiceDeath()196 void StHalLpmaHandler::onStHalServiceDeath() {
197 LOGE("ST HAL Service Died");
198 std::lock_guard<std::mutex> lock(mMutex);
199 mStHalService = nullptr;
200 if (mTargetLpmaEnabled) {
201 // ST HAL has died, so assume that the sound model is no longer active,
202 // and trigger a reload of the sound model.
203 mCurrentLpmaEnabled = false;
204 mCondVarPredicate = true;
205 mCondVar.notify_one();
206 }
207 }
208
209 } // namespace chre
210 } // namespace android
211