1 /*
2 * Copyright (C) 2021 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 "contexthub-impl/ContextHub.h"
18
19 #ifndef LOG_TAG
20 #define LOG_TAG "CHRE"
21 #endif
22
23 #include <inttypes.h>
24 #include <log/log.h>
25
26 using ::ndk::ScopedAStatus;
27
28 namespace aidl::android::hardware::contexthub {
29
30 namespace {
31
32 constexpr uint64_t kMockVendorHubId = 0x1234567812345678;
33 constexpr uint64_t kMockVendorHub2Id = 0x0EADBEEFDEADBEEF;
34
35 // Mock endpoints for the default implementation.
36 // These endpoints just echo back any messages sent to them.
37 constexpr size_t kMockEndpointCount = 4;
38 const EndpointInfo kMockEndpointInfos[kMockEndpointCount] = {
39 {
40 .id = {.hubId = kMockVendorHubId, .id = UINT64_C(0x1)},
41 .type = EndpointInfo::EndpointType::GENERIC,
42 .name = "Mock Endpoint 1",
43 .version = 1,
44 },
45 {
46 .id = {.hubId = kMockVendorHubId, .id = UINT64_C(0x2)},
47 .type = EndpointInfo::EndpointType::GENERIC,
48 .name = "Mock Endpoint 2",
49 .version = 2,
50 },
51 {
52 .id = {.hubId = kMockVendorHub2Id, .id = UINT64_C(0x1)},
53 .type = EndpointInfo::EndpointType::GENERIC,
54 .name = "Mock Endpoint 3",
55 .version = 1,
56 },
57 {
58 .id = {.hubId = kMockVendorHub2Id, .id = UINT64_C(0x2)},
59 .type = EndpointInfo::EndpointType::GENERIC,
60 .name = "Mock Endpoint 4",
61 .version = 2,
62 },
63 };
64
65 } // anonymous namespace
66
getContextHubs(std::vector<ContextHubInfo> * out_contextHubInfos)67 ScopedAStatus ContextHub::getContextHubs(std::vector<ContextHubInfo>* out_contextHubInfos) {
68 ContextHubInfo hub = {};
69 hub.name = "Mock Context Hub";
70 hub.vendor = "AOSP";
71 hub.toolchain = "n/a";
72 hub.id = kMockHubId;
73 hub.peakMips = 1;
74 hub.maxSupportedMessageLengthBytes = 4096;
75 hub.chrePlatformId = UINT64_C(0x476f6f6754000000);
76 hub.chreApiMajorVersion = 1;
77 hub.chreApiMinorVersion = 6;
78 hub.supportsReliableMessages = false;
79
80 out_contextHubInfos->push_back(hub);
81
82 return ScopedAStatus::ok();
83 }
84
85 // We don't expose any nanoapps for the default impl, therefore all nanoapp-related APIs fail.
loadNanoapp(int32_t,const NanoappBinary &,int32_t)86 ScopedAStatus ContextHub::loadNanoapp(int32_t /* in_contextHubId */,
87 const NanoappBinary& /* in_appBinary */,
88 int32_t /* in_transactionId */) {
89 return ScopedAStatus::fromExceptionCode(EX_UNSUPPORTED_OPERATION);
90 }
91
unloadNanoapp(int32_t,int64_t,int32_t)92 ScopedAStatus ContextHub::unloadNanoapp(int32_t /* in_contextHubId */, int64_t /* in_appId */,
93 int32_t /* in_transactionId */) {
94 return ScopedAStatus::fromExceptionCode(EX_UNSUPPORTED_OPERATION);
95 }
96
disableNanoapp(int32_t,int64_t,int32_t)97 ScopedAStatus ContextHub::disableNanoapp(int32_t /* in_contextHubId */, int64_t /* in_appId */,
98 int32_t /* in_transactionId */) {
99 return ScopedAStatus::fromExceptionCode(EX_UNSUPPORTED_OPERATION);
100 }
101
enableNanoapp(int32_t,int64_t,int32_t)102 ScopedAStatus ContextHub::enableNanoapp(int32_t /* in_contextHubId */, int64_t /* in_appId */,
103 int32_t /* in_transactionId */) {
104 return ScopedAStatus::fromExceptionCode(EX_UNSUPPORTED_OPERATION);
105 }
106
onSettingChanged(Setting,bool)107 ScopedAStatus ContextHub::onSettingChanged(Setting /* in_setting */, bool /*in_enabled */) {
108 return ScopedAStatus::ok();
109 }
110
queryNanoapps(int32_t in_contextHubId)111 ScopedAStatus ContextHub::queryNanoapps(int32_t in_contextHubId) {
112 if (in_contextHubId == kMockHubId && mCallback != nullptr) {
113 std::vector<NanoappInfo> nanoapps;
114 mCallback->handleNanoappInfo(nanoapps);
115 return ScopedAStatus::ok();
116 } else {
117 return ScopedAStatus::fromExceptionCode(EX_ILLEGAL_ARGUMENT);
118 }
119 }
120
getPreloadedNanoappIds(int32_t,std::vector<int64_t> * out_preloadedNanoappIds)121 ScopedAStatus ContextHub::getPreloadedNanoappIds(int32_t /* in_contextHubId */,
122 std::vector<int64_t>* out_preloadedNanoappIds) {
123 if (out_preloadedNanoappIds == nullptr) {
124 return ScopedAStatus::fromExceptionCode(EX_ILLEGAL_ARGUMENT);
125 }
126
127 for (uint64_t i = 0; i < 10; ++i) {
128 out_preloadedNanoappIds->push_back(i);
129 }
130 return ScopedAStatus::ok();
131 }
132
onNanSessionStateChanged(const NanSessionStateUpdate &)133 ScopedAStatus ContextHub::onNanSessionStateChanged(const NanSessionStateUpdate& /*in_update*/) {
134 return ScopedAStatus::ok();
135 }
136
registerCallback(int32_t in_contextHubId,const std::shared_ptr<IContextHubCallback> & in_cb)137 ScopedAStatus ContextHub::registerCallback(int32_t in_contextHubId,
138 const std::shared_ptr<IContextHubCallback>& in_cb) {
139 if (in_contextHubId == kMockHubId) {
140 mCallback = in_cb;
141 return ScopedAStatus::ok();
142 } else {
143 return ScopedAStatus::fromExceptionCode(EX_ILLEGAL_ARGUMENT);
144 }
145 }
146
sendMessageToHub(int32_t in_contextHubId,const ContextHubMessage &)147 ScopedAStatus ContextHub::sendMessageToHub(int32_t in_contextHubId,
148 const ContextHubMessage& /* in_message */) {
149 if (in_contextHubId == kMockHubId) {
150 // Return true here to indicate that the HAL has accepted the message.
151 // Successful delivery of the message to a nanoapp should be handled at
152 // a higher level protocol.
153 return ScopedAStatus::ok();
154 } else {
155 return ScopedAStatus::fromExceptionCode(EX_ILLEGAL_ARGUMENT);
156 }
157 }
158
setTestMode(bool enable)159 ScopedAStatus ContextHub::setTestMode(bool enable) {
160 if (enable) {
161 std::unique_lock<std::mutex> lock(mEndpointMutex);
162 mEndpoints.clear();
163 mEndpointSessions.clear();
164 mEndpointCallback = nullptr;
165 }
166 return ScopedAStatus::ok();
167 }
168
onHostEndpointConnected(const HostEndpointInfo & in_info)169 ScopedAStatus ContextHub::onHostEndpointConnected(const HostEndpointInfo& in_info) {
170 mConnectedHostEndpoints.insert(in_info.hostEndpointId);
171
172 return ScopedAStatus::ok();
173 }
174
onHostEndpointDisconnected(char16_t in_hostEndpointId)175 ScopedAStatus ContextHub::onHostEndpointDisconnected(char16_t in_hostEndpointId) {
176 if (mConnectedHostEndpoints.count(in_hostEndpointId) > 0) {
177 mConnectedHostEndpoints.erase(in_hostEndpointId);
178 }
179
180 return ScopedAStatus::ok();
181 }
182
sendMessageDeliveryStatusToHub(int32_t,const MessageDeliveryStatus &)183 ScopedAStatus ContextHub::sendMessageDeliveryStatusToHub(
184 int32_t /* in_contextHubId */,
185 const MessageDeliveryStatus& /* in_messageDeliveryStatus */) {
186 return ScopedAStatus::fromExceptionCode(EX_UNSUPPORTED_OPERATION);
187 }
188
getHubs(std::vector<HubInfo> * _aidl_return)189 ScopedAStatus ContextHub::getHubs(std::vector<HubInfo>* _aidl_return) {
190 if (_aidl_return == nullptr) {
191 return ScopedAStatus::fromExceptionCode(EX_ILLEGAL_ARGUMENT);
192 }
193
194 ContextHubInfo hub = {};
195 hub.name = "Mock Context Hub";
196 hub.vendor = "AOSP";
197 hub.toolchain = "n/a";
198 hub.id = kMockHubId;
199 hub.peakMips = 1;
200 hub.maxSupportedMessageLengthBytes = 4096;
201 hub.chrePlatformId = UINT64_C(0x476f6f6754000000);
202 hub.chreApiMajorVersion = 1;
203 hub.chreApiMinorVersion = 6;
204 hub.supportsReliableMessages = false;
205
206 HubInfo hubInfo1 = {};
207 hubInfo1.hubId = hub.chrePlatformId;
208 hubInfo1.hubDetails = HubInfo::HubDetails::make<HubInfo::HubDetails::Tag::contextHubInfo>(hub);
209
210 VendorHubInfo vendorHub = {};
211 vendorHub.name = "Mock Vendor Hub";
212 vendorHub.version = 42;
213
214 HubInfo hubInfo2 = {};
215 hubInfo2.hubId = kMockVendorHubId;
216 hubInfo2.hubDetails =
217 HubInfo::HubDetails::make<HubInfo::HubDetails::Tag::vendorHubInfo>(vendorHub);
218
219 VendorHubInfo vendorHub2 = {};
220 vendorHub2.name = "Mock Vendor Hub 2";
221 vendorHub2.version = 24;
222
223 HubInfo hubInfo3 = {};
224 hubInfo3.hubId = kMockVendorHub2Id;
225 hubInfo3.hubDetails =
226 HubInfo::HubDetails::make<HubInfo::HubDetails::Tag::vendorHubInfo>(vendorHub2);
227
228 _aidl_return->push_back(hubInfo1);
229 _aidl_return->push_back(hubInfo2);
230 _aidl_return->push_back(hubInfo3);
231
232 return ScopedAStatus::ok();
233 };
234
getEndpoints(std::vector<EndpointInfo> * _aidl_return)235 ScopedAStatus ContextHub::getEndpoints(std::vector<EndpointInfo>* _aidl_return) {
236 if (_aidl_return == nullptr) {
237 return ScopedAStatus::fromExceptionCode(EX_ILLEGAL_ARGUMENT);
238 }
239
240 Service echoService;
241 echoService.format = Service::RpcFormat::CUSTOM;
242 echoService.serviceDescriptor = "ECHO";
243 echoService.majorVersion = 1;
244 echoService.minorVersion = 0;
245
246 for (const EndpointInfo& endpoint : kMockEndpointInfos) {
247 EndpointInfo endpointWithService(endpoint);
248 endpointWithService.services.push_back(echoService);
249 _aidl_return->push_back(std::move(endpointWithService));
250 }
251
252 return ScopedAStatus::ok();
253 };
254
registerEndpoint(const EndpointInfo & in_endpoint)255 ScopedAStatus ContextHub::registerEndpoint(const EndpointInfo& in_endpoint) {
256 std::unique_lock<std::mutex> lock(mEndpointMutex);
257
258 for (const EndpointInfo& endpoint : mEndpoints) {
259 if ((endpoint.id.id == in_endpoint.id.id && endpoint.id.hubId == in_endpoint.id.hubId) ||
260 endpoint.name == in_endpoint.name) {
261 return ScopedAStatus::fromExceptionCode(EX_ILLEGAL_ARGUMENT);
262 }
263 }
264 mEndpoints.push_back(in_endpoint);
265 return ScopedAStatus::ok();
266 };
267
unregisterEndpoint(const EndpointInfo & in_endpoint)268 ScopedAStatus ContextHub::unregisterEndpoint(const EndpointInfo& in_endpoint) {
269 std::unique_lock<std::mutex> lock(mEndpointMutex);
270
271 for (auto it = mEndpoints.begin(); it != mEndpoints.end(); ++it) {
272 if (it->id.id == in_endpoint.id.id && it->id.hubId == in_endpoint.id.hubId) {
273 mEndpoints.erase(it);
274 return ScopedAStatus::ok();
275 }
276 }
277 return ScopedAStatus::fromExceptionCode(EX_ILLEGAL_ARGUMENT);
278 };
279
registerEndpointCallback(const std::shared_ptr<IEndpointCallback> & in_callback)280 ScopedAStatus ContextHub::registerEndpointCallback(
281 const std::shared_ptr<IEndpointCallback>& in_callback) {
282 std::unique_lock<std::mutex> lock(mEndpointMutex);
283
284 mEndpointCallback = in_callback;
285 return ScopedAStatus::ok();
286 };
287
requestSessionIdRange(int32_t in_size,std::vector<int32_t> * _aidl_return)288 ScopedAStatus ContextHub::requestSessionIdRange(int32_t in_size,
289 std::vector<int32_t>* _aidl_return) {
290 constexpr int32_t kMaxSize = 1024;
291 if (in_size > kMaxSize || _aidl_return == nullptr) {
292 return ScopedAStatus::fromExceptionCode(EX_ILLEGAL_ARGUMENT);
293 }
294
295 {
296 std::lock_guard<std::mutex> lock(mEndpointMutex);
297 mMaxValidSessionId = in_size;
298 }
299
300 _aidl_return->push_back(0);
301 _aidl_return->push_back(in_size);
302 return ScopedAStatus::ok();
303 };
304
openEndpointSession(int32_t in_sessionId,const EndpointId & in_destination,const EndpointId & in_initiator,const std::optional<std::string> & in_serviceDescriptor)305 ScopedAStatus ContextHub::openEndpointSession(
306 int32_t in_sessionId, const EndpointId& in_destination, const EndpointId& in_initiator,
307 const std::optional<std::string>& in_serviceDescriptor) {
308 // We are not calling onCloseEndpointSession on failure because the remote endpoints (our
309 // mock endpoints) always accept the session.
310
311 std::shared_ptr<IEndpointCallback> callback = nullptr;
312 {
313 std::unique_lock<std::mutex> lock(mEndpointMutex);
314 if (in_sessionId > mMaxValidSessionId) {
315 ALOGE("openEndpointSession: session ID %" PRId32 " is invalid", in_sessionId);
316 return ScopedAStatus::fromExceptionCode(EX_ILLEGAL_ARGUMENT);
317 }
318
319 for (const EndpointSession& session : mEndpointSessions) {
320 bool sessionAlreadyExists =
321 (session.initiator == in_destination && session.peer == in_initiator) ||
322 (session.peer == in_destination && session.initiator == in_initiator);
323 if (sessionAlreadyExists) {
324 ALOGD("openEndpointSession: session ID %" PRId32 " already exists", in_sessionId);
325 return (session.sessionId == in_sessionId &&
326 session.serviceDescriptor == in_serviceDescriptor)
327 ? ScopedAStatus::ok()
328 : ScopedAStatus::fromExceptionCode(EX_ILLEGAL_ARGUMENT);
329 } else if (session.sessionId == in_sessionId) {
330 ALOGE("openEndpointSession: session ID %" PRId32 " is invalid: endpoint mismatch",
331 in_sessionId);
332 return ScopedAStatus::fromExceptionCode(EX_ILLEGAL_ARGUMENT);
333 }
334 }
335
336 // Verify the initiator and destination are valid endpoints
337 bool initiatorIsValid = findEndpoint(in_initiator, mEndpoints.begin(), mEndpoints.end());
338 if (!initiatorIsValid) {
339 ALOGE("openEndpointSession: initiator %" PRIu64 ":%" PRIu64 " is invalid",
340 in_initiator.id, in_initiator.hubId);
341 return ScopedAStatus::fromExceptionCode(EX_ILLEGAL_ARGUMENT);
342 }
343 bool destinationIsValid = findEndpoint(in_destination, &kMockEndpointInfos[0],
344 &kMockEndpointInfos[kMockEndpointCount]);
345 if (!destinationIsValid) {
346 ALOGE("openEndpointSession: destination %" PRIu64 ":%" PRIu64 " is invalid",
347 in_destination.id, in_destination.hubId);
348 return ScopedAStatus::fromExceptionCode(EX_ILLEGAL_ARGUMENT);
349 }
350
351 mEndpointSessions.push_back({
352 .sessionId = in_sessionId,
353 .initiator = in_initiator,
354 .peer = in_destination,
355 .serviceDescriptor = in_serviceDescriptor,
356 });
357
358 if (mEndpointCallback != nullptr) {
359 callback = mEndpointCallback;
360 }
361 }
362
363 if (callback != nullptr) {
364 callback->onEndpointSessionOpenComplete(in_sessionId);
365 }
366 return ScopedAStatus::ok();
367 };
368
sendMessageToEndpoint(int32_t in_sessionId,const Message & in_msg)369 ScopedAStatus ContextHub::sendMessageToEndpoint(int32_t in_sessionId, const Message& in_msg) {
370 bool foundSession = false;
371 std::shared_ptr<IEndpointCallback> callback = nullptr;
372 {
373 std::unique_lock<std::mutex> lock(mEndpointMutex);
374
375 for (const EndpointSession& session : mEndpointSessions) {
376 if (session.sessionId == in_sessionId) {
377 foundSession = true;
378 break;
379 }
380 }
381
382 if (mEndpointCallback != nullptr) {
383 callback = mEndpointCallback;
384 }
385 }
386
387 if (!foundSession) {
388 ALOGE("sendMessageToEndpoint: session ID %" PRId32 " is invalid", in_sessionId);
389 return ScopedAStatus::fromExceptionCode(EX_ILLEGAL_ARGUMENT);
390 }
391
392 if (callback != nullptr) {
393 if (in_msg.flags & Message::FLAG_REQUIRES_DELIVERY_STATUS) {
394 MessageDeliveryStatus msgStatus = {};
395 msgStatus.messageSequenceNumber = in_msg.sequenceNumber;
396 msgStatus.errorCode = ErrorCode::OK;
397 callback->onMessageDeliveryStatusReceived(in_sessionId, msgStatus);
398 }
399
400 // Echo the message back
401 callback->onMessageReceived(in_sessionId, in_msg);
402 }
403 return ScopedAStatus::ok();
404 };
405
sendMessageDeliveryStatusToEndpoint(int32_t,const MessageDeliveryStatus &)406 ScopedAStatus ContextHub::sendMessageDeliveryStatusToEndpoint(
407 int32_t /* in_sessionId */, const MessageDeliveryStatus& /* in_msgStatus */) {
408 return ScopedAStatus::ok();
409 };
410
closeEndpointSession(int32_t in_sessionId,Reason)411 ScopedAStatus ContextHub::closeEndpointSession(int32_t in_sessionId, Reason /* in_reason */) {
412 std::unique_lock<std::mutex> lock(mEndpointMutex);
413
414 for (auto it = mEndpointSessions.begin(); it != mEndpointSessions.end(); ++it) {
415 if (it->sessionId == in_sessionId) {
416 mEndpointSessions.erase(it);
417 return ScopedAStatus::ok();
418 }
419 }
420 ALOGE("closeEndpointSession: session ID %" PRId32 " is invalid", in_sessionId);
421 return ScopedAStatus::fromExceptionCode(EX_ILLEGAL_ARGUMENT);
422 };
423
endpointSessionOpenComplete(int32_t)424 ScopedAStatus ContextHub::endpointSessionOpenComplete(int32_t /* in_sessionId */) {
425 return ScopedAStatus::ok();
426 };
427
428 } // namespace aidl::android::hardware::contexthub
429