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