xref: /aosp_15_r20/system/chre/host/hal_generic/common/message_hub_manager.h (revision 84e339476a462649f82315436d70fd732297a399)
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 #pragma once
18 
19 #include <unistd.h>
20 
21 #include <cstdint>
22 #include <functional>
23 #include <memory>
24 #include <mutex>
25 #include <optional>
26 #include <string>
27 #include <unordered_map>
28 #include <utility>
29 #include <vector>
30 
31 #include <aidl/android/hardware/contexthub/BnContextHub.h>
32 #include <android-base/thread_annotations.h>
33 
34 #include "pw_result/result.h"
35 #include "pw_status/status.h"
36 
37 namespace android::hardware::contexthub::common::implementation {
38 
39 using ::aidl::android::hardware::contexthub::EndpointId;
40 using ::aidl::android::hardware::contexthub::EndpointInfo;
41 using ::aidl::android::hardware::contexthub::HubInfo;
42 using ::aidl::android::hardware::contexthub::IEndpointCallback;
43 
44 /**
45  * Stores host and embedded MessageHub objects and maintains global mappings.
46  */
47 class MessageHubManager {
48  public:
49   /**
50    * Represents a host-side MessageHub. Clients of the IContextHub (V4+)
51    * interface each get a HostHub instance.
52    */
53   class HostHub {
54    public:
55     ~HostHub();
56 
57     /**
58      * Sets the callback for sending endpoint events back to the HAL client
59      *
60      * @param callback The callback provided by the client
61      * @return pw::OkStatus() on success.
62      */
63     pw::Status setCallback(std::shared_ptr<IEndpointCallback> callback)
64         EXCLUDES(mManager.mLock);
65 
66     /**
67      * Returns the callback registered in setCallback()
68      *
69      * @return The previously registered callback
70      */
71     std::shared_ptr<IEndpointCallback> getCallback() const
72         EXCLUDES(mManager.mLock);
73 
74     /**
75      * Adds an endpoint to this message hub
76      *
77      * @param self Self-reference for mapping from hub id
78      * @param info Description of the endpoint
79      * @return pw::OkStatus() on success
80      */
81     pw::Status addEndpoint(std::weak_ptr<HostHub> self,
82                            const EndpointInfo &info) EXCLUDES(mManager.mLock);
83 
84     /**
85      * Removes an endpoint from this message hub
86      *
87      * @param info Id of endpoint to remove
88      * @return pw::OkStatus() on success
89      */
90     pw::Status removeEndpoint(const EndpointId &info) EXCLUDES(mManager.mLock);
91 
92     /**
93      * Reserves a session id range to be used by this message hub
94      *
95      * @param size The size of this range, max 1024
96      * @return A pair of the smallest and largest id in the range on success
97      */
98     pw::Result<std::pair<uint16_t, uint16_t>> reserveSessionIdRange(
99         uint16_t size) EXCLUDES(mManager.mLock);
100 
101     /**
102      * Opens a session between the given endpoints with given session id
103      *
104      * The session is pending until updated by the destination endpoint.
105      *
106      * @param self Self-reference to be stored in session state
107      * @param localId The id of an endpoint hosted by this hub
108      * @param remoteId The id of the remote endpoint
109      * @param sessionId The id to be used for this session. Must be in the range
110      * allocated to this hub
111      * @return On success, returns a possibly null reference to the HostHub
112      * which hosted an endpoint on a pruned session with the same id. If not
113      * null, the HostHub should be notified that the session has been closed.
114      */
115     pw::Result<std::shared_ptr<HostHub>> openSession(
116         std::weak_ptr<HostHub> self, const EndpointId &localId,
117         const EndpointId &remoteId, uint16_t sessionId)
118         EXCLUDES(mManager.mLock);
119 
120     /**
121      * Acks a pending session.
122      *
123      * @param id Session id
124      * @return pw::OkStatus() on success, pw::Status::Unavailable() if the
125      * session is gone due to an endpoint going down.
126      */
127     pw::Status ackSession(uint16_t id) EXCLUDES(mManager.mLock);
128 
129     /**
130      * Checks that a session is open.
131      *
132      * @param id Session id
133      * @return pw::OkStatus() on success, pw::Status::Unavailable() if the
134      * session is gone due to an endpoint going down.
135      */
136     pw::Status checkSessionOpen(uint16_t id) EXCLUDES(mManager.mLock);
137 
138     /**
139      * Removes the given session and any local and global mappings
140      *
141      * @param id The session id
142      * @return pw::OkStatus() on success
143      */
144     pw::Status closeSession(uint16_t id) EXCLUDES(mManager.mLock);
145 
146     /**
147      * Returns the registered id of this message hub.
148      *
149      * @return kId
150      */
151     int64_t id() const;
152 
153    private:
154     friend class MessageHubManager;
155 
156     // Cookie associated with each registered client callback.
157     struct DeathRecipientCookie {
158       MessageHubManager *manager;
159       pid_t pid;
160     };
161 
162     static constexpr uint16_t kSessionIdMaxRange = 1024;
163 
164     static constexpr int64_t kHubIdInvalid = 0;
165 
HostHub(MessageHubManager & manager,pid_t pid)166     HostHub(MessageHubManager &manager, pid_t pid)
167         : mManager(manager), kPid(pid) {}
168 
169     // Unlinks this hub from the manager, destroying internal references.
170     // Returns the id so that it can be propagated to CHRE.
171     int64_t unlinkFromManager() EXCLUDES(mManager.mLock);
172 
173     // Unlink the current callback from the manager's death recipient.
174     void unlinkCallbackIfNecessaryLocked() REQUIRES(mManager.mLock);
175 
176     // Returns pw::OkStatus() if the hub is in a valid state.
177     pw::Status checkValidLocked() REQUIRES(mManager.mLock);
178 
179     // Returns a shared_ptr to the given endpoint.
180     pw::Result<std::shared_ptr<EndpointInfo>> getEndpointLocked(
181         const EndpointId &id) REQUIRES(mManager.mLock);
182 
183     // Returns pw::OkStatus() if the session id is in range for this hub.
184     bool sessionIdInRangeLocked(uint16_t id) REQUIRES(mManager.mLock);
185 
186     MessageHubManager &mManager;
187     const pid_t kPid;
188 
189     // Hub id, set when the first endpoint is registered.
190     int64_t kId GUARDED_BY(mManager.mLock) = kHubIdInvalid;
191 
192     // Callback to HAL client.
193     std::shared_ptr<IEndpointCallback> mCallback GUARDED_BY(mManager.mLock);
194 
195     // Cookie associated with mCallback.
196     DeathRecipientCookie *mCookie GUARDED_BY(mManager.mLock);
197 
198     // Used to lookup a host endpoint. Owns the associated EndpointInfo.
199     std::unordered_map<int64_t, std::shared_ptr<EndpointInfo>> mIdToEndpoint
200         GUARDED_BY(mManager.mLock);
201 
202     // Session id ranges allocated to this HostHub. The ranges are stored as a
203     // pair of the lowest and highest id in the range.
204     std::vector<std::pair<uint16_t, uint16_t>> mSessionIdRanges
205         GUARDED_BY(mManager.mLock);
206 
207     // Set in unlinkFromManager().
208     bool mUnlinked GUARDED_BY(mManager.mLock) = false;
209   };
210 
211   // Callback registered to pass up the id of a host hub which disconnected.
212   using HostHubDownCb = std::function<void(int64_t hubId)>;
213 
214   // The base session id for sessions initiated from host endpoints.
215   static constexpr uint16_t kHostSessionIdBase = 0x8000;
216 
217   explicit MessageHubManager(HostHubDownCb cb);
218   ~MessageHubManager() = default;
219 
220   /**
221    * Retrieves the HostHub instance for the calling process
222    *
223    * This API should be used for any HostHub lookup coming from the
224    * IContextHub interface. The first call to this API by any client process
225    * will trigger the creation of a HostHub for that client.
226    *
227    * @param pid The caller's system process id
228    * @return shared_ptr to the HostHub instance
229    */
230   std::shared_ptr<HostHub> getHostHubByPid(pid_t pid) EXCLUDES(mLock);
231 
232   /**
233    * Retrieves the HostHub instance for the given EndpointId
234    *
235    * @param id The endpoint id hosted by the returned hub
236    * @return shared_ptr to the HostHub instance
237    */
238   std::shared_ptr<HostHub> getHostHubByEndpointId(const EndpointId &id)
239       EXCLUDES(mLock);
240 
241   /**
242    * Checks that a given session is open and returns its HostHub.
243    *
244    * @param id Session id
245    * @return A strong reference to the HostHub. pw::Status::Unavailable()
246    * indicates that the session has been pruned.
247    */
248   pw::Result<std::shared_ptr<HostHub>> checkSessionOpenAndGetHostHub(
249       uint16_t id) EXCLUDES(mLock);
250 
251   /**
252    * Acks a session open request.
253    *
254    * This is called both when the destination endpoint approves and also when
255    * MessageRouter gives a final ack on a session initiated from an embedded
256    * endpoint. See the documentation on the Session class.
257    *
258    * @param id Session id
259    * @return A strong reference to the HostHub. pw::Status::Unavailable()
260    * indicates that the session has been pruned.
261    */
262   pw::Result<std::shared_ptr<HostHub>> ackSessionAndGetHostHub(uint16_t id)
263       EXCLUDES(mLock);
264 
265   /**
266    * Apply the given function to each host hub.
267    *
268    * @param fn The function to apply.
269    */
270   void forEachHostHub(std::function<void(HostHub &hub)> fn);
271 
272   /**
273    * Wipes and initializes the cache of embedded hubs and endpoints
274    *
275    * This should only be called once during startup as it invalidates session
276    * state (i.e. existing sessions will be pruned).
277    *
278    * @param hubs The list of message hubs
279    * @param endpoints The list of endpoints
280    */
281   void initEmbeddedHubsAndEndpoints(const std::vector<HubInfo> &hubs,
282                                     const std::vector<EndpointInfo> &endpoints)
283       EXCLUDES(mLock);
284 
285   /**
286    * Adds the given hub to the cache
287    *
288    * Ignored if the hub already exists
289    *
290    * @param hub The hub to add
291    */
292   void addEmbeddedHub(const HubInfo &hub) EXCLUDES(mLock);
293 
294   /**
295    * Removes the hub with given id from the cache
296    *
297    * @param id The id of the hub to remove
298    * @return The ids of all endpoints on the embedded hub
299    */
300   std::vector<EndpointId> removeEmbeddedHub(int64_t id) EXCLUDES(mLock);
301 
302   /**
303    * Returns the cached list of embedded message hubs
304    *
305    * @return HubInfo for every embedded message hub
306    */
307   std::vector<HubInfo> getEmbeddedHubs() const EXCLUDES(mLock);
308 
309   /**
310    * Adds an embedded endpoint to the cache
311    *
312    * Ignored if the endpoint already exists
313    *
314    * @param endpoint The endpoint to add
315    */
316   void addEmbeddedEndpoint(const EndpointInfo &endpoint);
317 
318   /**
319    * Removes an embedded endpoint from the cache
320    *
321    * @param id The id of the endpoint to remove
322    */
323   void removeEmbeddedEndpoint(const EndpointId &endpoint);
324 
325   /**
326    * Returns a list of embedded endpoints
327    *
328    * @return EndpointInfo for every embedded endpoint
329    */
330   std::vector<EndpointInfo> getEmbeddedEndpoints() const EXCLUDES(mLock);
331 
332  private:
333   // Callback invoked when a client goes down.
334   using UnlinkToDeathFn = std::function<bool(
335       const std::shared_ptr<IEndpointCallback> &callback, void *cookie)>;
336 
337   // Represents an embedded MessageHub. Stores the hub details as well as a map
338   // of all endpoints hosted by the hub.
339   struct EmbeddedHub {
340     std::unordered_map<int64_t, std::shared_ptr<EndpointInfo>> idToEndpoint;
341     HubInfo info;
342   };
343 
344   // Represents a session between a host and embedded endpoint. Only stores weak
345   // references to the endpoints and HostHub owning the host endpoint. Must be
346   // converted to a SessionStrongRef to temporarily access state. The weak
347   // references expire when the associated entity is unregistered. A
348   // SessionStrongRef cannot be created if any reference has expired.
349   //
350   // A Session is created on an openSession() request (triggered either by a
351   // local or remote endpoint) with mPendingDestination unset via a call to
352   // ackSession*() from the destination endpoint. For Sessions started by
353   // embedded endpoints, an additional ackSession*() must be received from the
354   // CHRE MessageRouter after passing it the ack from the destination host
355   // endpoint. This unsets mPendingMessageRouter. A session is only open for
356   // messages once both mPendingDestination and mPendingMessageRouter are unset.
357   struct SessionStrongRef;
358   class Session {
359    public:
Session(std::weak_ptr<HostHub> hub,std::weak_ptr<EndpointInfo> local,std::weak_ptr<EndpointInfo> remote,bool hostInitiated)360     Session(std::weak_ptr<HostHub> hub, std::weak_ptr<EndpointInfo> local,
361             std::weak_ptr<EndpointInfo> remote, bool hostInitiated)
362         : mHub(hub),
363           mLocal(local),
364           mRemote(remote),
365           mPendingMessageRouter(!hostInitiated) {}
366 
367    private:
368     friend struct SessionStrongRef;
369 
370     std::weak_ptr<HostHub> mHub;
371     std::weak_ptr<EndpointInfo> mLocal;
372     std::weak_ptr<EndpointInfo> mRemote;
373     bool mPendingDestination = true;
374     bool mPendingMessageRouter;
375   };
376 
377   // A strong reference to a Session's underlying endpoints and HostHub as well
378   // as Session metadata. A SessionStrongRef should be created and destroyed
379   // within a single critical section.
380   struct SessionStrongRef {
381     std::shared_ptr<HostHub> hub;
382     std::shared_ptr<EndpointInfo> local;
383     std::shared_ptr<EndpointInfo> remote;
384     bool &pendingDestination;
385     bool &pendingMessageRouter;
386 
SessionStrongRefSessionStrongRef387     SessionStrongRef(Session &session)
388         : hub(session.mHub.lock()),
389           local(session.mLocal.lock()),
390           remote(session.mRemote.lock()),
391           pendingDestination(session.mPendingDestination),
392           pendingMessageRouter(session.mPendingMessageRouter) {}
393     operator bool() const {
394       return hub && local && remote;
395     }
396   };
397 
398   // The hub id reserved for the ContextHub service.
399   static constexpr int64_t kContextHubServiceHubId = 0x416e64726f696400;
400 
401   // The Linux uid of the system_server.
402   static constexpr uid_t kSystemServerUid = 1000;
403 
404   // Invoked on client death. Cleans up references to the client.
405   static void onClientDeath(void *cookie);
406 
407   // Retrieves a strong reference to the session with given id.
408   pw::Result<SessionStrongRef> checkSessionLocked(uint16_t id) REQUIRES(mLock);
409 
410   // Adds an embedded endpoint to the cache.
411   void addEmbeddedEndpointLocked(const EndpointInfo &endpoint) REQUIRES(mLock);
412 
413   // Returns true if the embedded endpoint with given id is in the cache.
414   pw::Result<std::shared_ptr<EndpointInfo>> getEmbeddedEndpointLocked(
415       const EndpointId &id) REQUIRES(mLock);
416 
417   // Callback to pass up the id of a host hub for a client that disconnected.
418   HostHubDownCb mHostHubDownCb;
419 
420   // Death recipient handling clients' disconnections.
421   ndk::ScopedAIBinder_DeathRecipient mDeathRecipient;
422 
423   // Guards hub, endpoint, and session state.
424   mutable std::mutex mLock;
425 
426   // Map of EmbeddedHubs.
427   std::unordered_map<int64_t, EmbeddedHub> mIdToEmbeddedHub GUARDED_BY(mLock);
428 
429   // Used to look up the HostHub associated with the client on IContextHub
430   // calls.
431   std::unordered_map<pid_t, std::shared_ptr<HostHub>> mPidToHostHub
432       GUARDED_BY(mLock);
433 
434   // Used when an embedded endpoint wants to start a session with an endpoint
435   // hosted by a specific HostHub.
436   std::unordered_map<int64_t, std::weak_ptr<HostHub>> mIdToHostHub
437       GUARDED_BY(mLock);
438 
439   // Used to lookup the host endpoint to receive a message on an endpoint
440   // session.
441   std::unordered_map<uint16_t, Session> mIdToSession GUARDED_BY(mLock);
442 
443   // Next session id from which to allocate ranges.
444   uint16_t mNextSessionId GUARDED_BY(mLock) = kHostSessionIdBase;
445 };
446 
447 }  // namespace android::hardware::contexthub::common::implementation
448