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 52 constexpr uint8_t extractChreApiMajorVersion(uint32_t chreVersion) { 53 return static_cast<uint8_t>(chreVersion >> 24); 54 } 55 56 constexpr uint8_t extractChreApiMinorVersion(uint32_t chreVersion) { 57 return static_cast<uint8_t>(chreVersion >> 16); 58 } 59 60 constexpr uint16_t extractChrePatchVersion(uint32_t chreVersion) { 61 return static_cast<uint16_t>(chreVersion); 62 } 63 64 } // anonymous namespace 65 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 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 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 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 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 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 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 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 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 230 GenericContextHub::SocketCallbacks::SocketCallbacks(GenericContextHub& parent) 231 : mParent(parent) {} 232 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 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 250 void GenericContextHub::SocketCallbacks::onDisconnected() { 251 ALOGW("Lost connection to CHRE daemon"); 252 } 253 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 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 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 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 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