1 /*
2 * Copyright (C) 2017 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 #define LOG_TAG "ContextHubHal"
18 #define LOG_NDEBUG 0
19
20 #include "generic_context_hub.h"
21
22 #include <chrono>
23 #include <cinttypes>
24 #include <vector>
25
26 #include <utils/Log.h>
27
28 namespace android {
29 namespace hardware {
30 namespace contexthub {
31 namespace V1_0 {
32 namespace implementation {
33
34 using ::android::hardware::Return;
35 using ::android::hardware::contexthub::V1_0::AsyncEventType;
36 using ::android::hardware::contexthub::V1_0::Result;
37 using ::android::hardware::contexthub::V1_0::TransactionResult;
38 using ::android::chre::HostProtocolHost;
39 using ::flatbuffers::FlatBufferBuilder;
40
41 // Aliased for consistency with the way these symbols are referenced in
42 // CHRE-side code
43 namespace fbs = ::chre::fbs;
44
45 namespace {
46
47 constexpr uint32_t kDefaultHubId = 0;
48
49 // TODO: remove this macro once all methods are implemented
50 #define UNUSED(param) (void) (param)
51
extractChreApiMajorVersion(uint32_t chreVersion)52 constexpr uint8_t extractChreApiMajorVersion(uint32_t chreVersion) {
53 return static_cast<uint8_t>(chreVersion >> 24);
54 }
55
extractChreApiMinorVersion(uint32_t chreVersion)56 constexpr uint8_t extractChreApiMinorVersion(uint32_t chreVersion) {
57 return static_cast<uint8_t>(chreVersion >> 16);
58 }
59
extractChrePatchVersion(uint32_t chreVersion)60 constexpr uint16_t extractChrePatchVersion(uint32_t chreVersion) {
61 return static_cast<uint16_t>(chreVersion);
62 }
63
64 } // anonymous namespace
65
GenericContextHub()66 GenericContextHub::GenericContextHub() {
67 constexpr char kChreSocketName[] = "chre";
68
69 mSocketCallbacks = new SocketCallbacks(*this);
70 if (!mClient.connectInBackground(kChreSocketName, mSocketCallbacks)) {
71 ALOGE("Couldn't start socket client");
72 }
73 }
74
getHubs(getHubs_cb _hidl_cb)75 Return<void> GenericContextHub::getHubs(getHubs_cb _hidl_cb) {
76 constexpr auto kHubInfoQueryTimeout = std::chrono::seconds(5);
77 std::vector<ContextHub> hubs;
78 ALOGV("%s", __func__);
79
80 // If we're not connected yet, give it some time
81 int maxSleepIterations = 50;
82 while (!mHubInfoValid && !mClient.isConnected() && --maxSleepIterations > 0) {
83 std::this_thread::sleep_for(std::chrono::milliseconds(100));
84 }
85
86 if (!mClient.isConnected()) {
87 ALOGE("Couldn't connect to hub daemon");
88 } else if (!mHubInfoValid) {
89 // We haven't cached the hub details yet, so send a request and block
90 // waiting on a response
91 std::unique_lock<std::mutex> lock(mHubInfoMutex);
92 FlatBufferBuilder builder;
93 HostProtocolHost::encodeHubInfoRequest(builder);
94
95 ALOGD("Sending hub info request");
96 if (!mClient.sendMessage(builder.GetBufferPointer(), builder.GetSize())) {
97 ALOGE("Couldn't send hub info request");
98 } else {
99 mHubInfoCond.wait_for(lock, kHubInfoQueryTimeout,
100 [this]() { return mHubInfoValid; });
101 }
102 }
103
104 if (mHubInfoValid) {
105 hubs.push_back(mHubInfo);
106 } else {
107 ALOGE("Unable to get hub info from CHRE");
108 }
109
110 _hidl_cb(hubs);
111 return Void();
112 }
113
registerCallback(uint32_t hubId,const sp<IContexthubCallback> & cb)114 Return<Result> GenericContextHub::registerCallback(
115 uint32_t hubId, const sp<IContexthubCallback>& cb) {
116 Result result;
117 ALOGV("%s", __func__);
118
119 // TODO: currently we only support 1 hub behind this HAL implementation
120 if (hubId == kDefaultHubId) {
121 mCallbacks = cb; // TODO: special handling for null?
122 result = Result::OK;
123 } else {
124 result = Result::BAD_PARAMS;
125 }
126
127 return result;
128 }
129
sendMessageToHub(uint32_t hubId,const ContextHubMsg & msg)130 Return<Result> GenericContextHub::sendMessageToHub(uint32_t hubId,
131 const ContextHubMsg& msg) {
132 Result result;
133 ALOGV("%s", __func__);
134
135 if (hubId != kDefaultHubId) {
136 result = Result::BAD_PARAMS;
137 } else {
138 FlatBufferBuilder builder(1024);
139 HostProtocolHost::encodeNanoappMessage(
140 builder, msg.appName, msg.msgType, msg.hostEndPoint, msg.msg.data(),
141 msg.msg.size());
142
143 if (!mClient.sendMessage(builder.GetBufferPointer(), builder.GetSize())) {
144 result = Result::UNKNOWN_FAILURE;
145 } else {
146 result = Result::OK;
147 }
148 }
149
150 return result;
151 }
152
loadNanoApp(uint32_t hubId,const NanoAppBinary & appBinary,uint32_t transactionId)153 Return<Result> GenericContextHub::loadNanoApp(
154 uint32_t hubId, const NanoAppBinary& appBinary, uint32_t transactionId) {
155 Result result;
156 ALOGV("%s", __func__);
157
158 if (hubId != kDefaultHubId) {
159 result = Result::BAD_PARAMS;
160 } else {
161 FlatBufferBuilder builder(128 + appBinary.customBinary.size());
162 uint32_t targetApiVersion = (appBinary.targetChreApiMajorVersion << 24) |
163 (appBinary.targetChreApiMinorVersion << 16);
164 HostProtocolHost::encodeLoadNanoappRequest(
165 builder, transactionId, appBinary.appId, appBinary.appVersion,
166 targetApiVersion, appBinary.customBinary);
167 if (!mClient.sendMessage(builder.GetBufferPointer(), builder.GetSize())) {
168 result = Result::UNKNOWN_FAILURE;
169 } else {
170 result = Result::OK;
171 }
172 }
173
174 ALOGD("Attempted to send load nanoapp request for app of size %zu with ID "
175 "0x%016" PRIx64 " as transaction ID %" PRIu32 ": result %" PRIu32,
176 appBinary.customBinary.size(), appBinary.appId, transactionId, result);
177
178 return result;
179 }
180
unloadNanoApp(uint32_t hubId,uint64_t appId,uint32_t transactionId)181 Return<Result> GenericContextHub::unloadNanoApp(
182 uint32_t hubId, uint64_t appId, uint32_t transactionId) {
183 // TODO
184 UNUSED(hubId);
185 UNUSED(appId);
186 UNUSED(transactionId);
187 ALOGV("%s", __func__);
188 return Result::UNKNOWN_FAILURE;
189 }
190
enableNanoApp(uint32_t hubId,uint64_t appId,uint32_t transactionId)191 Return<Result> GenericContextHub::enableNanoApp(
192 uint32_t hubId, uint64_t appId, uint32_t transactionId) {
193 // TODO
194 UNUSED(hubId);
195 UNUSED(appId);
196 UNUSED(transactionId);
197 ALOGV("%s", __func__);
198 return Result::UNKNOWN_FAILURE;
199 }
200
disableNanoApp(uint32_t hubId,uint64_t appId,uint32_t transactionId)201 Return<Result> GenericContextHub::disableNanoApp(
202 uint32_t hubId, uint64_t appId, uint32_t transactionId) {
203 // TODO
204 UNUSED(hubId);
205 UNUSED(appId);
206 UNUSED(transactionId);
207 ALOGV("%s", __func__);
208 return Result::UNKNOWN_FAILURE;
209 }
210
queryApps(uint32_t hubId)211 Return<Result> GenericContextHub::queryApps(uint32_t hubId) {
212 Result result;
213 ALOGV("%s", __func__);
214
215 if (hubId != kDefaultHubId) {
216 result = Result::BAD_PARAMS;
217 } else {
218 FlatBufferBuilder builder(64);
219 HostProtocolHost::encodeNanoappListRequest(builder);
220 if (!mClient.sendMessage(builder.GetBufferPointer(), builder.GetSize())) {
221 result = Result::UNKNOWN_FAILURE;
222 } else {
223 result = Result::OK;
224 }
225 }
226
227 return Result::UNKNOWN_FAILURE;
228 }
229
SocketCallbacks(GenericContextHub & parent)230 GenericContextHub::SocketCallbacks::SocketCallbacks(GenericContextHub& parent)
231 : mParent(parent) {}
232
onMessageReceived(const void * data,size_t length)233 void GenericContextHub::SocketCallbacks::onMessageReceived(const void *data,
234 size_t length) {
235 if (!HostProtocolHost::decodeMessageFromChre(data, length, *this)) {
236 ALOGE("Failed to decode message");
237 }
238 }
239
onConnected()240 void GenericContextHub::SocketCallbacks::onConnected() {
241 if (mHaveConnected) {
242 ALOGI("Reconnected to CHRE daemon");
243 if (mParent.mCallbacks != nullptr) {
244 mParent.mCallbacks->handleHubEvent(AsyncEventType::RESTARTED);
245 }
246 }
247 mHaveConnected = true;
248 }
249
onDisconnected()250 void GenericContextHub::SocketCallbacks::onDisconnected() {
251 ALOGW("Lost connection to CHRE daemon");
252 }
253
handleNanoappMessage(uint64_t appId,uint32_t messageType,uint16_t hostEndpoint,const void * messageData,size_t messageDataLen)254 void GenericContextHub::SocketCallbacks::handleNanoappMessage(
255 uint64_t appId, uint32_t messageType, uint16_t hostEndpoint,
256 const void *messageData, size_t messageDataLen) {
257 // TODO: this is not thread-safe w/registerCallback... we need something else
258 // to confirm that it's safe for us to invoke the callback, and likely a lock
259 // on stuff
260 if (mParent.mCallbacks != nullptr) {
261 ContextHubMsg msg;
262 msg.appName = appId;
263 msg.hostEndPoint = hostEndpoint;
264 msg.msgType = messageType;
265
266 // Dropping const from messageData when we wrap it in hidl_vec here. This is
267 // safe because we won't modify it here, and the ContextHubMsg we pass to
268 // the callback is const.
269 msg.msg.setToExternal(
270 const_cast<uint8_t *>(static_cast<const uint8_t *>(messageData)),
271 messageDataLen, false /* shouldOwn */);
272
273 mParent.mCallbacks->handleClientMsg(msg);
274 }
275 }
276
handleHubInfoResponse(const char * name,const char * vendor,const char * toolchain,uint32_t legacyPlatformVersion,uint32_t legacyToolchainVersion,float peakMips,float stoppedPower,float sleepPower,float peakPower,uint32_t maxMessageLen,uint64_t platformId,uint32_t version)277 void GenericContextHub::SocketCallbacks::handleHubInfoResponse(
278 const char *name, const char *vendor,
279 const char *toolchain, uint32_t legacyPlatformVersion,
280 uint32_t legacyToolchainVersion, float peakMips, float stoppedPower,
281 float sleepPower, float peakPower, uint32_t maxMessageLen,
282 uint64_t platformId, uint32_t version) {
283 ALOGD("Got hub info response");
284
285 std::lock_guard<std::mutex> lock(mParent.mHubInfoMutex);
286 if (mParent.mHubInfoValid) {
287 ALOGI("Ignoring duplicate/unsolicited hub info response");
288 } else {
289 mParent.mHubInfo.name = name;
290 mParent.mHubInfo.vendor = vendor;
291 mParent.mHubInfo.toolchain = toolchain;
292 mParent.mHubInfo.platformVersion = legacyPlatformVersion;
293 mParent.mHubInfo.toolchainVersion = legacyToolchainVersion;
294 mParent.mHubInfo.hubId = kDefaultHubId;
295
296 mParent.mHubInfo.peakMips = peakMips;
297 mParent.mHubInfo.stoppedPowerDrawMw = stoppedPower;
298 mParent.mHubInfo.sleepPowerDrawMw = sleepPower;
299 mParent.mHubInfo.peakPowerDrawMw = peakPower;
300
301 mParent.mHubInfo.maxSupportedMsgLen = maxMessageLen;
302 mParent.mHubInfo.chrePlatformId = platformId;
303
304 mParent.mHubInfo.chreApiMajorVersion = extractChreApiMajorVersion(version);
305 mParent.mHubInfo.chreApiMinorVersion = extractChreApiMinorVersion(version);
306 mParent.mHubInfo.chrePatchVersion = extractChrePatchVersion(version);
307
308 mParent.mHubInfoValid = true;
309 mParent.mHubInfoCond.notify_all();
310 }
311 }
312
handleNanoappListResponse(const fbs::NanoappListResponseT & response)313 void GenericContextHub::SocketCallbacks::handleNanoappListResponse(
314 const fbs::NanoappListResponseT& response) {
315 std::vector<HubAppInfo> appInfoList;
316
317 ALOGV("Got nanoapp list response with %zu apps", response.nanoapps.size());
318 for (const std::unique_ptr<fbs::NanoappListEntryT>& nanoapp
319 : response.nanoapps) {
320 // TODO: determine if this is really required, and if so, have
321 // HostProtocolHost strip out null entries as part of decode
322 if (nanoapp == nullptr) {
323 continue;
324 }
325
326 ALOGV("App 0x%016" PRIx64 " ver 0x%" PRIx32 " enabled %d system %d",
327 nanoapp->app_id, nanoapp->version, nanoapp->enabled,
328 nanoapp->is_system);
329 if (!nanoapp->is_system) {
330 HubAppInfo appInfo;
331
332 appInfo.appId = nanoapp->app_id;
333 appInfo.version = nanoapp->version;
334 appInfo.enabled = nanoapp->enabled;
335
336 appInfoList.push_back(appInfo);
337 }
338 }
339
340 // TODO: make this thread-safe w/setCallback
341 mParent.mCallbacks->handleAppsInfo(appInfoList);
342 }
343
handleLoadNanoappResponse(const::chre::fbs::LoadNanoappResponseT & response)344 void GenericContextHub::SocketCallbacks::handleLoadNanoappResponse(
345 const ::chre::fbs::LoadNanoappResponseT& response) {
346 ALOGV("Got load nanoapp response for transaction %" PRIu32 " with result %d",
347 response.transaction_id, response.success);
348
349 TransactionResult result = (response.success) ?
350 TransactionResult::SUCCESS : TransactionResult::FAILURE;
351 mParent.mCallbacks->handleTxnResult(response.transaction_id, result);
352 }
353
HIDL_FETCH_IContexthub(const char *)354 IContexthub* HIDL_FETCH_IContexthub(const char* /* name */) {
355 return new GenericContextHub();
356 }
357
358 } // namespace implementation
359 } // namespace V1_0
360 } // namespace contexthub
361 } // namespace hardware
362 } // namespace android
363