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