• 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 #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/IContextHub.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 using ::aidl::android::hardware::contexthub::Message;
44 using ::aidl::android::hardware::contexthub::MessageDeliveryStatus;
45 using ::aidl::android::hardware::contexthub::Reason;
46 using ::aidl::android::hardware::contexthub::Service;
47 
48 /**
49  * Stores host and embedded MessageHub objects and maintains global mappings.
50  */
51 class MessageHubManager {
52  public:
53   /**
54    * Represents a host-side MessageHub. Clients of the IContextHub (V4+)
55    * interface each get a HostHub instance.
56    */
57   class HostHub {
58    public:
59     ~HostHub() = default;
60 
61     /**
62      * Adds an endpoint to this message hub
63      *
64      * @param info Description of the endpoint
65      * @return pw::OkStatus() on success
66      */
67     pw::Status addEndpoint(const EndpointInfo &info) EXCLUDES(mManager.mLock);
68 
69     /**
70      * Removes an endpoint from this message hub
71      *
72      * @param info Id of endpoint to remove
73      * @return List of sessions to prune on success
74      */
75     pw::Result<std::vector<uint16_t>> removeEndpoint(const EndpointId &info)
76         EXCLUDES(mManager.mLock);
77 
78     /**
79      * Reserves a session id range to be used by this message hub
80      *
81      * @param size The size of this range, max 1024
82      * @return A pair of the smallest and largest id in the range on success
83      */
84     pw::Result<std::pair<uint16_t, uint16_t>> reserveSessionIdRange(
85         uint16_t size) EXCLUDES(mManager.mLock);
86 
87     /**
88      * Opens a session between the given endpoints with given session id
89      *
90      * The session is pending until updated by the destination endpoint.
91      *
92      * @param hostEndpoint The id of an endpoint hosted by this hub
93      * @param embeddedEndpoint The id of the embedded endpoint
94      * @param sessionId The id to be used for this session. Must be in the range
95      * allocated to this hub
96      * @param serviceDescriptor Optional service for the session
97      * @param hostInitiated true if the request came from a host endpoint
98      * @return pw::OkStatus() on success.
99      */
100     pw::Status openSession(const EndpointId &hostEndpoint,
101                            const EndpointId &embeddedEndpoint,
102                            uint16_t sessionId,
103                            std::optional<std::string> serviceDescriptor,
104                            bool hostInitiated) EXCLUDES(mManager.mLock);
105 
106     /**
107      * Acks a pending session.
108      *
109      * @param id Session id
110      * @param hostAcked true if a host endpoint is acking the session
111      * @return pw::OkStatus() on success
112      */
113     pw::Status ackSession(uint16_t id, bool hostAcked) EXCLUDES(mManager.mLock);
114 
115     /**
116      * Checks that a session is open.
117      *
118      * @param id Session id
119      * @return pw::OkStatus() on success
120      */
121     pw::Status checkSessionOpen(uint16_t id) EXCLUDES(mManager.mLock);
122 
123     /**
124      * Removes the given session and any local and global mappings
125      *
126      * @param id The session id
127      * @param reason If present, reason for closing to be passed to the host
128      * endpoint
129      * @return pw::OkStatus() on success
130      */
131     pw::Status closeSession(uint16_t id, std::optional<Reason> reason = {})
132         EXCLUDES(mManager.mLock);
133 
134     /**
135      * Forwards a message to an endpoint on this hub.
136      *
137      * @param id The session in which the message was sent
138      * @param message The message
139      * @return pw::OkStatus() on success
140      */
141     pw::Status handleMessage(uint16_t sessionId, const Message &message)
142         EXCLUDES(mManager.mLock);
143 
144     /**
145      * Forwards a message delivery status to an endpoint on this hub.
146      *
147      * @param id The session in which the message was sent
148      * @param status The message delivery status
149      * @return pw::OkStatus() on success
150      */
151     pw::Status handleMessageDeliveryStatus(uint16_t sessionId,
152                                            const MessageDeliveryStatus &status)
153         EXCLUDES(mManager.mLock);
154 
155     /**
156      * Unregisters this HostHub.
157      *
158      * @return pw::OkStatus() if the host hub was successfully initialized and
159      * not yet unregistered.
160      */
161     pw::Status unregister() EXCLUDES(mManager.mLock);
162 
163     /**
164      * Returns the list of endpoints registered on this hub.
165      */
166     std::vector<EndpointInfo> getEndpoints() const;
167 
168     /**
169      * Returns the Message Hub info
170      */
info()171     const HubInfo &info() const {
172       return kInfo;
173     }
174 
175     /**
176      * Returns the registered id of this message hub.
177      */
id()178     int64_t id() const {
179       return kInfo.hubId;
180     }
181 
182    private:
183     friend class MessageHubManager;
184     friend class MessageHubManagerTest;
185 
186     // Reresents a session between a host and embedded endpoint.
187     //
188     // A Session is created on an openSession() request (triggered either by a
189     // local or remote endpoint) with mPendingDestination unset via a call to
190     // ackSession*() from the destination endpoint. For Sessions started by
191     // embedded endpoints, an additional ackSession*() must be received from the
192     // CHRE MessageRouter after passing it the ack from the destination host
193     // endpoint. This unsets mPendingMessageRouter. A session is only open for
194     // messages once both mPendingDestination and mPendingMessageRouter are
195     // unset.
196     struct Session {
197       EndpointId mHostEndpoint;
198       EndpointId mEmbeddedEndpoint;
199       bool mPendingDestination = true;
200       bool mPendingMessageRouter;
201 
SessionSession202       Session(const EndpointId &hostEndpoint,
203               const EndpointId &embeddedEndpoint, bool hostInitiated)
204           : mHostEndpoint(hostEndpoint),
205             mEmbeddedEndpoint(embeddedEndpoint),
206             mPendingMessageRouter(!hostInitiated) {}
207     };
208 
209     // Cookie associated with each registered client callback.
210     struct DeathRecipientCookie {
211       MessageHubManager *manager;
212       int64_t hubId;
213     };
214 
215     static constexpr uint16_t kSessionIdMaxRange = 1024;
216 
217     static constexpr int64_t kHubIdInvalid = 0;
218 
219     HostHub(MessageHubManager &manager,
220             std::shared_ptr<IEndpointCallback> callback, const HubInfo &info);
221 
222     // Unlinks this hub from the manager, destroying internal references.
223     // Propagates the unlinking to CHRE. If already unlinked, returns early with
224     // error.
225     pw::Status unlinkFromManager() EXCLUDES(mManager.mLock);
226 
227     // Returns pw::OkStatus() if the hub is in a valid state.
228     pw::Status checkValidLocked() REQUIRES(mManager.mLock);
229 
230     // Returns pw::OkStatus() if the given endpoint (with service if given)
231     // exists on this hub.
232     pw::Status endpointExistsLocked(
233         const EndpointId &id, std::optional<std::string> serviceDescriptor)
234         REQUIRES(mManager.mLock);
235 
236     // Returns pw::OkStatus() if the session id is in range for this hub.
237     bool sessionIdInRangeLocked(uint16_t id) REQUIRES(mManager.mLock);
238 
239     // Returns pw::OkStatus() if the session is open.
240     pw::Status checkSessionOpenLocked(uint16_t id) REQUIRES(mManager.mLock);
241 
242     // Returns a pointer to the session with given id.
243     pw::Result<Session *> getSessionLocked(uint16_t id)
244         REQUIRES(mManager.mLock);
245 
246     MessageHubManager &mManager;
247     std::shared_ptr<IEndpointCallback> mCallback;  // Callback to client.
248     DeathRecipientCookie *mCookie;  // Death cookie associated with mCallback.
249     const HubInfo kInfo;            // Details of this hub.
250 
251     // Used to lookup a host endpoint.
252     std::unordered_map<int64_t, EndpointInfo> mIdToEndpoint
253         GUARDED_BY(mManager.mLock);
254 
255     // Used to lookup state for sessions including an endpoint on this hub.
256     std::unordered_map<uint16_t, Session> mIdToSession
257         GUARDED_BY(mManager.mLock);
258 
259     // Session id ranges allocated to this HostHub. The ranges are stored as a
260     // pair of the lowest and highest id in the range.
261     std::vector<std::pair<uint16_t, uint16_t>> mSessionIdRanges
262         GUARDED_BY(mManager.mLock);
263 
264     // Set in unlinkFromManager().
265     bool mUnlinked GUARDED_BY(mManager.mLock) = false;
266   };
267 
268   // Callback registered to pass up the id of a host hub which disconnected.
269   using HostHubDownCb =
270       std::function<void(std::function<pw::Result<int64_t>()> unlinkFn)>;
271 
272   // The base session id for sessions initiated from host endpoints.
273   static constexpr uint16_t kHostSessionIdBase = 0x8000;
274 
MessageHubManager(HostHubDownCb cb)275   explicit MessageHubManager(HostHubDownCb cb)
276       : mHostHubDownCb(std::move(cb)),
277         mDeathRecipient(std::make_unique<RealDeathRecipient>()) {}
278   ~MessageHubManager() = default;
279 
280   /**
281    * Registers a new client, creating a HostHub instance for it
282    *
283    * @param callback Interface for communicating with the client
284    * @param info Details of the hub being registered
285    * @param uid The UID of the client
286    * @param pid The PID of the client
287    * @return On success, shared_ptr to the HostHub instance
288    */
289   pw::Result<std::shared_ptr<HostHub>> createHostHub(
290       std::shared_ptr<IEndpointCallback> callback, const HubInfo &info,
291       uid_t uid, pid_t pid) EXCLUDES(mLock);
292 
293   /**
294    * Retrieves a HostHub instance given its id
295    *
296    * @param id The HostHub id
297    * @return shared_ptr to the HostHub instance, nullptr if not found
298    */
299   std::shared_ptr<HostHub> getHostHub(int64_t id) EXCLUDES(mLock);
300 
301   /**
302    * Apply the given function to each host hub.
303    *
304    * @param fn The function to apply.
305    */
306   void forEachHostHub(std::function<void(HostHub &hub)> fn) EXCLUDES(mLock);
307 
308   /**
309    * Wipes and marks the embedded state cache ready.
310    *
311    * This should only be called once during startup as it invalidates session
312    * state (i.e. existing sessions will be pruned).
313    */
314   void initEmbeddedState() EXCLUDES(mLock);
315 
316   /**
317    * Clears cache of embedded state and closes all sessions.
318    *
319    * Called on CHRE disconnection. Invalidates the cache. initEmbeddedState()
320    * must be called again before sessions can be established.
321    */
322   void clearEmbeddedState() EXCLUDES(mLock);
323 
324   /**
325    * Adds the given hub to the cache
326    *
327    * Ignored if the hub already exists
328    *
329    * @param hub The hub to add
330    */
331   void addEmbeddedHub(const HubInfo &hub) EXCLUDES(mLock);
332 
333   /**
334    * Removes the hub with given id from the cache
335    *
336    * @param id The id of the hub to remove
337    */
338   void removeEmbeddedHub(int64_t id) EXCLUDES(mLock);
339 
340   /**
341    * Returns the cached list of embedded message hubs
342    *
343    * @return HubInfo for every embedded message hub
344    */
345   std::vector<HubInfo> getEmbeddedHubs() const EXCLUDES(mLock);
346 
347   /**
348    * Adds an embedded endpoint to the cache
349    *
350    * Ignored if the endpoint already exists
351    *
352    * @param endpoint The endpoint to add
353    */
354   void addEmbeddedEndpoint(const EndpointInfo &endpoint);
355 
356   /**
357    * Adds a service to an embedded endpoint in the cache
358    *
359    * Ignored if the endpoint is already marked ready
360    *
361    * @param endpoint the new endpoint being updated
362    * @param service the service being added
363    */
364   void addEmbeddedEndpointService(const EndpointId &endpoint,
365                                   const Service &service);
366 
367   /**
368    * Sets the ready flag on an embedded endpoint
369    *
370    * @param id The id of the endpoint to remove
371    */
372   void setEmbeddedEndpointReady(const EndpointId &endpoint);
373 
374   /**
375    * Removes an embedded endpoint from the cache
376    *
377    * @param id The id of the endpoint to remove
378    */
379   void removeEmbeddedEndpoint(const EndpointId &endpoint);
380 
381   /**
382    * Returns a list of embedded endpoints
383    *
384    * @return EndpointInfo for every embedded endpoint
385    */
386   std::vector<EndpointInfo> getEmbeddedEndpoints() const EXCLUDES(mLock);
387 
388  private:
389   friend class MessageHubManagerTest;
390 
391   // Callback invoked when a client goes down.
392   using UnlinkToDeathFn = std::function<bool(
393       const std::shared_ptr<IEndpointCallback> &callback, void *cookie)>;
394 
395   // Base class for a Binder DeathRecipient wrapper so that this functionality
396   // can be mocked in unit tests.
397   class DeathRecipient {
398    public:
399     virtual ~DeathRecipient() = default;
400 
401     virtual pw::Status linkCallback(
402         const std::shared_ptr<IEndpointCallback> &callback,
403         HostHub::DeathRecipientCookie *cookie) = 0;
404 
405     virtual pw::Status unlinkCallback(
406         const std::shared_ptr<IEndpointCallback> &callback,
407         HostHub::DeathRecipientCookie *cookie) = 0;
408 
409    protected:
410     DeathRecipient() = default;
411   };
412 
413   // Real implementation of DeathRecipient.
414   class RealDeathRecipient : public DeathRecipient {
415    public:
416     RealDeathRecipient();
417     RealDeathRecipient(RealDeathRecipient &&) = default;
418     RealDeathRecipient &operator=(RealDeathRecipient &&) = default;
419     virtual ~RealDeathRecipient() = default;
420 
421     pw::Status linkCallback(const std::shared_ptr<IEndpointCallback> &callback,
422                             HostHub::DeathRecipientCookie *cookie) override;
423 
424     pw::Status unlinkCallback(
425         const std::shared_ptr<IEndpointCallback> &callback,
426         HostHub::DeathRecipientCookie *cookie) override;
427 
428    private:
429     ndk::ScopedAIBinder_DeathRecipient mDeathRecipient;
430   };
431 
432   // Represents an embedded MessageHub. Stores the hub details as well as a map
433   // of all endpoints hosted by the hub.
434   struct EmbeddedHub {
435     std::unordered_map<int64_t, std::pair<EndpointInfo, bool>> idToEndpoint;
436     HubInfo info;
437   };
438 
439   // The hub id reserved for the ContextHub service.
440   static constexpr int64_t kContextHubServiceHubId = 0x416e64726f696400;
441 
442   // The Linux uid of the system_server.
443   static constexpr uid_t kSystemServerUid = 1000;
444 
445   // Invoked on client death. Cleans up references to the client.
446   static void onClientDeath(void *cookie);
447 
448   // Constructor used by tests to inject a mock DeathRecipient.
MessageHubManager(std::unique_ptr<DeathRecipient> deathRecipient,HostHubDownCb cb)449   MessageHubManager(std::unique_ptr<DeathRecipient> deathRecipient,
450                     HostHubDownCb cb)
451       : mHostHubDownCb(std::move(cb)),
452         mDeathRecipient(std::move(deathRecipient)) {}
453 
454   // Adds an embedded endpoint to the cache.
455   void addEmbeddedEndpointLocked(const EndpointInfo &endpoint) REQUIRES(mLock);
456 
457   // Returns pw::OkStatus() if the given embedded endpoint (with service, if
458   // given), is in the cache.
459   pw::Status embeddedEndpointExistsLocked(
460       const EndpointId &id, std::optional<std::string> serviceDescriptor)
461       REQUIRES(mLock);
462 
463   // Returns a pointer to an embedded endpoint entry if it exists.
464   pw::Result<std::pair<EndpointInfo, bool> *> lookupEmbeddedEndpointLocked(
465       const EndpointId &id) REQUIRES(mLock);
466 
467   // Callback to pass up the id of a host hub for a client that disconnected.
468   HostHubDownCb mHostHubDownCb;
469 
470   // Death recipient handling clients' disconnections.
471   std::unique_ptr<DeathRecipient> mDeathRecipient;
472 
473   // Guards hub, endpoint, and session state.
474   mutable std::mutex mLock;
475 
476   // Map of EmbeddedHubs.
477   std::unordered_map<int64_t, EmbeddedHub> mIdToEmbeddedHub GUARDED_BY(mLock);
478 
479   // Map of HostHubs for registered IContextHub V4+ clients.
480   std::unordered_map<int64_t, std::shared_ptr<HostHub>> mIdToHostHub
481       GUARDED_BY(mLock);
482 
483   // Next session id from which to allocate ranges.
484   uint16_t mNextSessionId GUARDED_BY(mLock) = kHostSessionIdBase;
485 
486   // True if the embedded hub cache is initialized.
487   bool mIdToEmbeddedHubReady GUARDED_BY(mLock) = false;
488 };
489 
490 }  // namespace android::hardware::contexthub::common::implementation
491