xref: /aosp_15_r20/hardware/interfaces/contexthub/aidl/default/ContextHub.cpp (revision 4d7e907c777eeecc4c5bd7cf640a754fac206ff7)
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