• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2024 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 "message_hub_manager.h"
18 
19 #include <inttypes.h>
20 #include <unistd.h>
21 
22 #include <cstdint>
23 #include <functional>
24 #include <list>
25 #include <memory>
26 #include <optional>
27 #include <string>
28 #include <unordered_map>
29 #include <utility>
30 #include <vector>
31 
32 #include <aidl/android/hardware/contexthub/BnContextHub.h>
33 #include <android-base/thread_annotations.h>
34 
35 #include "chre_host/log.h"
36 #include "pw_result/result.h"
37 #include "pw_status/status.h"
38 #include "pw_status/try.h"
39 
40 namespace android::hardware::contexthub::common::implementation {
41 
42 using ::aidl::android::hardware::contexthub::Reason;
43 
44 using HostHub = MessageHubManager::HostHub;
45 
HostHub(MessageHubManager & manager,std::shared_ptr<IEndpointCallback> callback,const HubInfo & info)46 HostHub::HostHub(MessageHubManager &manager,
47                  std::shared_ptr<IEndpointCallback> callback,
48                  const HubInfo &info)
49     : mManager(manager), kInfo(info) {
50   auto *cookie = new DeathRecipientCookie{&mManager, kInfo.hubId};
51   if (!manager.mDeathRecipient->linkCallback(callback, cookie).ok()) {
52     LOGE("Failed to link callback for hub %" PRId64 " to death recipient",
53          kInfo.hubId);
54     delete cookie;
55     return;
56   }
57   mCookie = cookie;
58   mCallback = std::move(callback);
59 }
60 
addEndpoint(const EndpointInfo & info)61 pw::Status HostHub::addEndpoint(const EndpointInfo &info) {
62   std::lock_guard lock(mManager.mLock);
63   PW_TRY(checkValidLocked());
64   if (info.id.hubId != kInfo.hubId) {
65     LOGE("Hub %" PRId64 " registering endpoint for different hub %" PRId64,
66          kInfo.hubId, info.id.hubId);
67     return pw::Status::PermissionDenied();
68   }
69   int64_t id = info.id.id;
70   if (auto it = mIdToEndpoint.find(id); it != mIdToEndpoint.end()) {
71     LOGE("Endpoint %" PRId64 " already exists in hub %" PRId64, id,
72          kInfo.hubId);
73     return pw::Status::AlreadyExists();
74   }
75   mIdToEndpoint.insert({id, info});
76   return pw::OkStatus();
77 }
78 
removeEndpoint(const EndpointId & id)79 pw::Result<std::vector<uint16_t>> HostHub::removeEndpoint(
80     const EndpointId &id) {
81   std::lock_guard lock(mManager.mLock);
82   PW_TRY(checkValidLocked());
83   if (auto it = mIdToEndpoint.find(id.id); it != mIdToEndpoint.end()) {
84     std::vector<uint16_t> sessions;
85     for (const auto &[sessionId, session] : mIdToSession) {
86       if (session.mHostEndpoint == id) sessions.push_back(sessionId);
87     }
88     for (auto sessionId : sessions) mIdToSession.erase(sessionId);
89     mIdToEndpoint.erase(it);
90     return sessions;
91   }
92   LOGE("Hub %" PRId64 " tried to remove unknown endpoint %" PRId64, kInfo.hubId,
93        id.id);
94   return pw::Status::NotFound();
95 }
96 
reserveSessionIdRange(uint16_t size)97 pw::Result<std::pair<uint16_t, uint16_t>> HostHub::reserveSessionIdRange(
98     uint16_t size) {
99   std::lock_guard lock(mManager.mLock);
100   PW_TRY(checkValidLocked());
101   if (!size || size > kSessionIdMaxRange) {
102     LOGE("Hub %" PRId64 " tried to allocate %" PRIu16 " session ids",
103          kInfo.hubId, size);
104     return pw::Status::InvalidArgument();
105   }
106   if (mManager.mNextSessionId < kHostSessionIdBase ||
107       USHRT_MAX - mManager.mNextSessionId + 1 < size) {
108     LOGW("Could not allocate %" PRIu16 " session ids, ids exhausted", size);
109     return pw::Status::ResourceExhausted();
110   }
111   mSessionIdRanges.push_back(
112       {mManager.mNextSessionId, mManager.mNextSessionId + size - 1});
113   mManager.mNextSessionId += size;
114   return mSessionIdRanges.back();
115 }
116 
openSession(const EndpointId & hostEndpoint,const EndpointId & embeddedEndpoint,uint16_t sessionId,std::optional<std::string> serviceDescriptor,bool hostInitiated)117 pw::Status HostHub::openSession(const EndpointId &hostEndpoint,
118                                 const EndpointId &embeddedEndpoint,
119                                 uint16_t sessionId,
120                                 std::optional<std::string> serviceDescriptor,
121                                 bool hostInitiated) {
122   std::lock_guard lock(mManager.mLock);
123   PW_TRY(checkValidLocked());
124 
125   // Lookup the endpoints.
126   PW_TRY(endpointExistsLocked(
127       hostEndpoint, hostInitiated ? std::nullopt : serviceDescriptor));
128   PW_TRY(mManager.embeddedEndpointExistsLocked(
129       embeddedEndpoint, hostInitiated ? serviceDescriptor : std::nullopt));
130 
131   // Validate the session id.
132   if (hostInitiated) {
133     if (!sessionIdInRangeLocked(sessionId)) {
134       LOGE("Session id %" PRIu16 " out of range for hub %" PRId64, sessionId,
135            kInfo.hubId);
136       return pw::Status::OutOfRange();
137     }
138   } else if (sessionId >= kHostSessionIdBase) {
139     LOGE("Remote endpoint (%" PRId64 ", %" PRId64
140          ") attempting to start "
141          "session with invalid id %" PRIu16,
142          embeddedEndpoint.hubId, embeddedEndpoint.id, sessionId);
143     return pw::Status::InvalidArgument();
144   }
145 
146   // Prune a stale session with this id if present.
147   if (auto it = mIdToSession.find(sessionId); it != mIdToSession.end()) {
148     Session &session = it->second;
149     // If the session is in a valid state, prune it if it was not host
150     // initiated and is pending a final ack from message router.
151     if (!hostInitiated && !session.mPendingDestination &&
152         session.mPendingMessageRouter) {
153       mCallback->onCloseEndpointSession(sessionId, Reason::UNSPECIFIED);
154       LOGD("Pruned session %" PRIu16, sessionId);
155     } else if (hostInitiated && session.mHostEndpoint == hostEndpoint) {
156       LOGE("Hub %" PRId64 " trying to override its own session %" PRIu16,
157            kInfo.hubId, sessionId);
158       return pw::Status::InvalidArgument();
159     } else {
160       LOGE("(host? %" PRId32 ") trying to override session id %" PRIu16
161            ", hub %" PRId64,
162            hostInitiated, sessionId, kInfo.hubId);
163       return pw::Status::AlreadyExists();
164     }
165     mIdToSession.erase(it);
166   }
167 
168   // Create and map the new session.
169   mIdToSession.emplace(
170       std::piecewise_construct, std::forward_as_tuple(sessionId),
171       std::forward_as_tuple(hostEndpoint, embeddedEndpoint, hostInitiated));
172 
173   // Pass a request from a embedded endpoint to the host endpoint.
174   if (!hostInitiated) {
175     mCallback->onEndpointSessionOpenRequest(sessionId, hostEndpoint,
176                                             embeddedEndpoint,
177                                             std::move(serviceDescriptor));
178   }
179   return pw::OkStatus();
180 }
181 
closeSession(uint16_t id,std::optional<Reason> reason)182 pw::Status HostHub::closeSession(uint16_t id, std::optional<Reason> reason) {
183   std::lock_guard lock(mManager.mLock);
184   PW_TRY(checkValidLocked());
185   auto it = mIdToSession.find(id);
186   if (it == mIdToSession.end()) {
187     LOGE("Closing unopened session %" PRIu16, id);
188     return pw::Status::NotFound();
189   }
190   mIdToSession.erase(it);
191   if (reason) mCallback->onCloseEndpointSession(id, *reason);
192   return pw::OkStatus();
193 }
194 
checkSessionOpen(uint16_t id)195 pw::Status HostHub::checkSessionOpen(uint16_t id) {
196   std::lock_guard lock(mManager.mLock);
197   PW_TRY(checkValidLocked());
198   return checkSessionOpenLocked(id);
199 }
200 
ackSession(uint16_t id,bool hostAcked)201 pw::Status HostHub::ackSession(uint16_t id, bool hostAcked) {
202   std::lock_guard lock(mManager.mLock);
203   PW_TRY(checkValidLocked());
204   PW_TRY_ASSIGN(Session * session, getSessionLocked(id));
205   bool isHostSession = id >= kHostSessionIdBase;
206   if (session->mPendingDestination) {
207     if (isHostSession == hostAcked) {
208       LOGE("Session %" PRIu16 " must be acked by other side (host? %" PRId32
209            ")",
210            id, !hostAcked);
211       return pw::Status::PermissionDenied();
212     }
213     session->mPendingDestination = false;
214     // Notify the initiator that the session has been opened.
215     if (isHostSession) mCallback->onEndpointSessionOpenComplete(id);
216   } else if (session->mPendingMessageRouter) {
217     if (hostAcked) {
218       LOGE("Message router must ack session %" PRIu16, id);
219       return pw::Status::PermissionDenied();
220     }
221     session->mPendingMessageRouter = false;
222   } else {
223     LOGE("Received unexpected ack on session %" PRIu16 ", host: %" PRId32, id,
224          hostAcked);
225   }
226   return pw::OkStatus();
227 }
228 
handleMessage(uint16_t sessionId,const Message & message)229 pw::Status HostHub::handleMessage(uint16_t sessionId, const Message &message) {
230   std::lock_guard lock(mManager.mLock);
231   PW_TRY(checkValidLocked());
232   PW_TRY(checkSessionOpenLocked(sessionId));
233   mCallback->onMessageReceived(sessionId, message);
234   return pw::OkStatus();
235 }
236 
handleMessageDeliveryStatus(uint16_t sessionId,const MessageDeliveryStatus & status)237 pw::Status HostHub::handleMessageDeliveryStatus(
238     uint16_t sessionId, const MessageDeliveryStatus &status) {
239   std::lock_guard lock(mManager.mLock);
240   PW_TRY(checkValidLocked());
241   PW_TRY(checkSessionOpenLocked(sessionId));
242   mCallback->onMessageDeliveryStatusReceived(sessionId, status);
243   return pw::OkStatus();
244 }
245 
unregister()246 pw::Status HostHub::unregister() {
247   // If unlinkFromManager() fails, onClientDeath() was already called for this
248   // and we do not need to unlink the death recipient.
249   PW_TRY(unlinkFromManager());
250   if (!mManager.mDeathRecipient->unlinkCallback(mCallback, mCookie).ok()) {
251     LOGW("Process hosting hub %" PRId64 " died simultaneously with unregister",
252          kInfo.hubId);
253   }
254   return pw::OkStatus();
255 }
256 
getEndpoints() const257 std::vector<EndpointInfo> HostHub::getEndpoints() const {
258   std::vector<EndpointInfo> endpoints;
259   std::lock_guard lock(mManager.mLock);
260   for (const auto &[id, endpoint] : mIdToEndpoint)
261     endpoints.push_back(endpoint);
262   return endpoints;
263 }
264 
unlinkFromManager()265 pw::Status HostHub::unlinkFromManager() {
266   std::lock_guard lock(mManager.mLock);
267   PW_TRY(checkValidLocked());  // returns early if already unlinked
268   // TODO(b/378545373): Release the session id range.
269   mManager.mIdToHostHub.erase(kInfo.hubId);
270   mUnlinked = true;
271   return pw::OkStatus();
272 }
273 
checkValidLocked()274 pw::Status HostHub::checkValidLocked() {
275   if (!mCallback) {
276     ALOGE("APIs invoked on hub %" PRId64
277           " which was not successfully registered.",
278           kInfo.hubId);
279     return pw::Status::FailedPrecondition();
280   } else if (mUnlinked) {
281     ALOGW("Hub %" PRId64 " went down mid-operation", kInfo.hubId);
282     return pw::Status::Aborted();
283   }
284   return pw::OkStatus();
285 }
286 
endpointExistsLocked(const EndpointId & id,std::optional<std::string> serviceDescriptor)287 pw::Status HostHub::endpointExistsLocked(
288     const EndpointId &id, std::optional<std::string> serviceDescriptor) {
289   if (id.hubId != kInfo.hubId) {
290     LOGE("Rejecting lookup on unowned endpoint (%" PRId64 ", %" PRId64
291          ") from hub %" PRId64,
292          id.hubId, id.id, kInfo.hubId);
293     return pw::Status::InvalidArgument();
294   }
295   if (auto it = mIdToEndpoint.find(id.id); it != mIdToEndpoint.end()) {
296     if (!serviceDescriptor) return pw::OkStatus();
297     for (const auto &service : it->second.services) {
298       if (service.serviceDescriptor == *serviceDescriptor)
299         return pw::OkStatus();
300     }
301     LOGW("Endpoint (%" PRId64 ", %" PRId64 ") doesn't have service %s",
302          id.hubId, id.id, serviceDescriptor->c_str());
303   }
304   return pw::Status::NotFound();
305 }
306 
sessionIdInRangeLocked(uint16_t id)307 bool HostHub::sessionIdInRangeLocked(uint16_t id) {
308   for (auto range : mSessionIdRanges) {
309     if (id >= range.first && id <= range.second) return true;
310   }
311   return false;
312 }
313 
checkSessionOpenLocked(uint16_t id)314 pw::Status HostHub::checkSessionOpenLocked(uint16_t id) {
315   PW_TRY_ASSIGN(Session * session, getSessionLocked(id));
316   if (!session->mPendingDestination && !session->mPendingMessageRouter)
317     return pw::OkStatus();
318   LOGE("Session %" PRIu16 " is pending", id);
319   return pw::Status::FailedPrecondition();
320 }
321 
getSessionLocked(uint16_t id)322 pw::Result<HostHub::Session *> HostHub::getSessionLocked(uint16_t id) {
323   auto sessionIt = mIdToSession.find(id);
324   if (sessionIt == mIdToSession.end()) {
325     LOGE("Did not find expected session %" PRIu16 " in hub %" PRId64, id,
326          kInfo.hubId);
327     return pw::Status::NotFound();
328   }
329   return &sessionIt->second;
330 }
331 
createHostHub(std::shared_ptr<IEndpointCallback> callback,const HubInfo & info,uid_t uid,pid_t pid)332 pw::Result<std::shared_ptr<HostHub>> MessageHubManager::createHostHub(
333     std::shared_ptr<IEndpointCallback> callback, const HubInfo &info, uid_t uid,
334     pid_t pid) {
335   if (info.hubId == kContextHubServiceHubId && uid != kSystemServerUid) {
336     LOGE("(pid %" PRId32 ", uid %" PRId32
337          ") attempting to impersonate ContextHubService",
338          pid, uid);
339     return pw::Status::PermissionDenied();
340   }
341   std::lock_guard lock(mLock);
342   if (mIdToHostHub.count(info.hubId)) return pw::Status::AlreadyExists();
343   std::shared_ptr<HostHub> hub(new HostHub(*this, std::move(callback), info));
344   if (!hub->mCallback) return pw::Status::Internal();
345   mIdToHostHub.insert({info.hubId, hub});
346   LOGI("Registered host hub %" PRId64, info.hubId);
347   return hub;
348 }
349 
getHostHub(int64_t id)350 std::shared_ptr<HostHub> MessageHubManager::getHostHub(int64_t id) {
351   std::lock_guard lock(mLock);
352   if (auto it = mIdToHostHub.find(id); it != mIdToHostHub.end())
353     return it->second;
354   return {};
355 }
356 
forEachHostHub(std::function<void (HostHub & hub)> fn)357 void MessageHubManager::forEachHostHub(std::function<void(HostHub &hub)> fn) {
358   std::list<std::shared_ptr<HostHub>> hubs;
359   {
360     std::lock_guard lock(mLock);
361     for (auto &[id, hub] : mIdToHostHub) hubs.push_back(hub);
362   }
363   for (auto &hub : hubs) fn(*hub);
364 }
365 
initEmbeddedState()366 void MessageHubManager::initEmbeddedState() {
367   std::lock_guard lock(mLock);
368   mIdToEmbeddedHub.clear();
369   mIdToEmbeddedHubReady = true;
370 }
371 
clearEmbeddedState()372 void MessageHubManager::clearEmbeddedState() {
373   std::lock_guard lock(mLock);
374   mIdToEmbeddedHubReady = false;
375 
376   // Clear embedded hub state, caching the list of now removed endpoints.
377   std::vector<EndpointId> endpoints;
378   for (const auto &[hubId, hub] : mIdToEmbeddedHub) {
379     for (const auto &[endpointId, endpoint] : hub.idToEndpoint)
380       if (endpoint.second) endpoints.push_back(endpoint.first.id);
381   }
382   mIdToEmbeddedHub.clear();
383 
384   // For each host hub, close all sessions and send all removed endpoints.
385   for (const auto &[hubId, hub] : mIdToHostHub) {
386     ::android::base::ScopedLockAssertion lockAssertion(hub->mManager.mLock);
387     for (const auto &[sessionId, session] : hub->mIdToSession)
388       hub->mCallback->onCloseEndpointSession(sessionId, Reason::HUB_RESET);
389     hub->mCallback->onEndpointStopped(endpoints, Reason::HUB_RESET);
390   }
391 }
392 
addEmbeddedHub(const HubInfo & hub)393 void MessageHubManager::addEmbeddedHub(const HubInfo &hub) {
394   std::lock_guard lock(mLock);
395   if (!mIdToEmbeddedHubReady) {
396     LOGW("Skipping embedded hub registration before initEmbeddedState()");
397     return;
398   }
399   if (mIdToEmbeddedHub.count(hub.hubId)) return;
400   mIdToEmbeddedHub[hub.hubId].info = hub;
401 }
402 
removeEmbeddedHub(int64_t id)403 void MessageHubManager::removeEmbeddedHub(int64_t id) {
404   std::lock_guard lock(mLock);
405 
406   // Get the list of endpoints being removed and remove the hub.
407   std::vector<EndpointId> endpoints;
408   auto it = mIdToEmbeddedHub.find(id);
409   if (it == mIdToEmbeddedHub.end()) return;
410   for (const auto &[endpointId, info] : it->second.idToEndpoint)
411     if (info.second) endpoints.push_back(info.first.id);
412   mIdToEmbeddedHub.erase(it);
413 
414   // For each host hub, determine which sessions if any are now closed and send
415   // notifications as appropriate. Also send the list of removed endpoints.
416   for (auto &[hostHubId, hub] : mIdToHostHub) {
417     ::android::base::ScopedLockAssertion lockAssertion(hub->mManager.mLock);
418     std::vector<uint16_t> closedSessions;
419     for (const auto &[sessionId, session] : hub->mIdToSession) {
420       if (session.mEmbeddedEndpoint.hubId == id) {
421         hub->mCallback->onCloseEndpointSession(sessionId, Reason::HUB_RESET);
422         closedSessions.push_back(sessionId);
423       }
424     }
425     for (auto session : closedSessions) hub->mIdToSession.erase(session);
426     hub->mCallback->onEndpointStopped(endpoints, Reason::HUB_RESET);
427   }
428 }
429 
getEmbeddedHubs() const430 std::vector<HubInfo> MessageHubManager::getEmbeddedHubs() const {
431   std::lock_guard lock(mLock);
432   std::vector<HubInfo> hubs;
433   for (const auto &[id, hub] : mIdToEmbeddedHub) hubs.push_back(hub.info);
434   return hubs;
435 }
436 
addEmbeddedEndpoint(const EndpointInfo & endpoint)437 void MessageHubManager::addEmbeddedEndpoint(const EndpointInfo &endpoint) {
438   std::lock_guard lock(mLock);
439   if (!mIdToEmbeddedHubReady) {
440     LOGW("Skipping embedded endpoint registration before initEmbeddedState()");
441     return;
442   }
443   addEmbeddedEndpointLocked(endpoint);
444 }
445 
addEmbeddedEndpointService(const EndpointId & endpoint,const Service & service)446 void MessageHubManager::addEmbeddedEndpointService(const EndpointId &endpoint,
447                                                    const Service &service) {
448   std::lock_guard lock(mLock);
449   if (!mIdToEmbeddedHubReady) {
450     LOGW("Skipping embedded endpoint registration before initEmbeddedState()");
451     return;
452   }
453   auto statusOrEndpoint = lookupEmbeddedEndpointLocked(endpoint);
454   if (!statusOrEndpoint.ok()) return;
455   if ((*statusOrEndpoint)->second) {
456     LOGE("Adding service to embedded endpoint after ready");
457     return;
458   }
459   (*statusOrEndpoint)->first.services.push_back(service);
460 }
461 
setEmbeddedEndpointReady(const EndpointId & id)462 void MessageHubManager::setEmbeddedEndpointReady(const EndpointId &id) {
463   std::lock_guard lock(mLock);
464   if (!mIdToEmbeddedHubReady) {
465     LOGW("Skipping embedded endpoint registration before initEmbeddedState()");
466     return;
467   }
468   auto statusOrEndpoint = lookupEmbeddedEndpointLocked(id);
469   if (!statusOrEndpoint.ok() || (*statusOrEndpoint)->second) return;
470   (*statusOrEndpoint)->second = true;
471   for (auto &[hostHubId, hub] : mIdToHostHub) {
472     ::android::base::ScopedLockAssertion lockAssertion(hub->mManager.mLock);
473     hub->mCallback->onEndpointStarted({(*statusOrEndpoint)->first});
474   }
475 }
476 
getEmbeddedEndpoints() const477 std::vector<EndpointInfo> MessageHubManager::getEmbeddedEndpoints() const {
478   std::lock_guard lock(mLock);
479   std::vector<EndpointInfo> endpoints;
480   for (const auto &[id, hub] : mIdToEmbeddedHub) {
481     for (const auto &[endptId, endptInfo] : hub.idToEndpoint)
482       if (endptInfo.second) endpoints.push_back(endptInfo.first);
483   }
484   return endpoints;
485 }
486 
removeEmbeddedEndpoint(const EndpointId & id)487 void MessageHubManager::removeEmbeddedEndpoint(const EndpointId &id) {
488   std::lock_guard lock(mLock);
489   auto hubIt = mIdToEmbeddedHub.find(id.hubId);
490   if (hubIt == mIdToEmbeddedHub.end()) return;
491   if (!hubIt->second.idToEndpoint.erase(id.id)) return;
492 
493   // For each host hub, determine which sessions if any are now closed and send
494   // notifications as appropriate. Also send the removed endpoint notification.
495   for (auto &[hostHubId, hub] : mIdToHostHub) {
496     ::android::base::ScopedLockAssertion lockAssertion(hub->mManager.mLock);
497     std::vector<uint16_t> closedSessions;
498     for (const auto &[sessionId, session] : hub->mIdToSession) {
499       if (session.mEmbeddedEndpoint == id) {
500         hub->mCallback->onCloseEndpointSession(sessionId,
501                                                Reason::ENDPOINT_GONE);
502         closedSessions.push_back(sessionId);
503       }
504     }
505     for (auto session : closedSessions) hub->mIdToSession.erase(session);
506     hub->mCallback->onEndpointStopped({id}, Reason::ENDPOINT_GONE);
507   }
508 }
509 
RealDeathRecipient()510 MessageHubManager::RealDeathRecipient::RealDeathRecipient() {
511   mDeathRecipient = ndk::ScopedAIBinder_DeathRecipient(
512       AIBinder_DeathRecipient_new(&MessageHubManager::onClientDeath));
513   AIBinder_DeathRecipient_setOnUnlinked(
514       mDeathRecipient.get(), /*onUnlinked= */ [](void *cookie) {
515         LOGD("Callback is unlinked. Releasing the death recipient cookie.");
516         delete static_cast<HostHub::DeathRecipientCookie *>(cookie);
517       });
518 }
519 
linkCallback(const std::shared_ptr<IEndpointCallback> & callback,HostHub::DeathRecipientCookie * cookie)520 pw::Status MessageHubManager::RealDeathRecipient::linkCallback(
521     const std::shared_ptr<IEndpointCallback> &callback,
522     HostHub::DeathRecipientCookie *cookie) {
523   return AIBinder_linkToDeath(callback->asBinder().get(), mDeathRecipient.get(),
524                               cookie) == STATUS_OK
525              ? pw::OkStatus()
526              : pw::Status::Internal();
527 }
528 
unlinkCallback(const std::shared_ptr<IEndpointCallback> & callback,HostHub::DeathRecipientCookie * cookie)529 pw::Status MessageHubManager::RealDeathRecipient::unlinkCallback(
530     const std::shared_ptr<IEndpointCallback> &callback,
531     HostHub::DeathRecipientCookie *cookie) {
532   return AIBinder_unlinkToDeath(callback->asBinder().get(),
533                                 mDeathRecipient.get(), cookie) == STATUS_OK
534              ? pw::OkStatus()
535              : pw::Status::NotFound();
536 }
537 
onClientDeath(void * cookie)538 void MessageHubManager::onClientDeath(void *cookie) {
539   auto *cookieData = reinterpret_cast<HostHub::DeathRecipientCookie *>(cookie);
540   LOGW("Process hosting hub %" PRId64 " died", cookieData->hubId);
541   MessageHubManager *manager = cookieData->manager;
542   std::shared_ptr<HostHub> hub = manager->getHostHub(cookieData->hubId);
543   // NOTE: if IEndpointCommunication.unregister() was called simultaneously, hub
544   // may be null or unlinkFromManager() may fail.
545   if (hub) {
546     manager->mHostHubDownCb([hubPtr = hub.get()]() -> pw::Result<int64_t> {
547       PW_TRY(hubPtr->unlinkFromManager());
548       return hubPtr->id();
549     });
550   }
551 }
552 
addEmbeddedEndpointLocked(const EndpointInfo & endpoint)553 void MessageHubManager::addEmbeddedEndpointLocked(
554     const EndpointInfo &endpoint) {
555   auto it = mIdToEmbeddedHub.find(endpoint.id.hubId);
556   if (it == mIdToEmbeddedHub.end()) {
557     LOGW("Could not find hub %" PRId64 " for endpoint %" PRId64,
558          endpoint.id.hubId, endpoint.id.id);
559     return;
560   }
561   it->second.idToEndpoint.insert({endpoint.id.id, {endpoint, false}});
562 }
563 
embeddedEndpointExistsLocked(const EndpointId & id,std::optional<std::string> serviceDescriptor)564 pw::Status MessageHubManager::embeddedEndpointExistsLocked(
565     const EndpointId &id, std::optional<std::string> serviceDescriptor) {
566   PW_TRY_ASSIGN(const auto *endpoint, lookupEmbeddedEndpointLocked(id));
567   if (!endpoint->second) {
568     LOGW("Accessing remote endpoint (%" PRId64 ", %" PRId64 ") before ready",
569          id.hubId, id.id);
570     return pw::Status::NotFound();
571   }
572   if (!serviceDescriptor) return pw::OkStatus();
573   for (const auto &service : endpoint->first.services) {
574     if (service.serviceDescriptor == *serviceDescriptor) return pw::OkStatus();
575   }
576   LOGW("Endpoint (%" PRId64 ", %" PRId64 ") doesn't have service %s", id.hubId,
577        id.id, serviceDescriptor->c_str());
578   return pw::Status::NotFound();
579 }
580 
581 pw::Result<std::pair<EndpointInfo, bool> *>
lookupEmbeddedEndpointLocked(const EndpointId & id)582 MessageHubManager::lookupEmbeddedEndpointLocked(const EndpointId &id) {
583   auto hubIt = mIdToEmbeddedHub.find(id.hubId);
584   if (hubIt != mIdToEmbeddedHub.end()) {
585     auto it = hubIt->second.idToEndpoint.find(id.id);
586     if (it != hubIt->second.idToEndpoint.end()) return &(it->second);
587   }
588   LOGW("Could not find remote endpoint (%" PRId64 ", %" PRId64 ")", id.hubId,
589        id.id);
590   return pw::Status::NotFound();
591 }
592 
593 }  // namespace android::hardware::contexthub::common::implementation
594