• 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 #ifndef CHRE_UTIL_SYSTEM_MESSAGE_ROUTER_H_
18 #define CHRE_UTIL_SYSTEM_MESSAGE_ROUTER_H_
19 
20 #include "chre/platform/mutex.h"
21 #include "chre/util/dynamic_vector.h"
22 #include "chre/util/memory.h"
23 #include "chre/util/singleton.h"
24 #include "chre/util/system/intrusive_ref_base.h"
25 #include "chre/util/system/message_common.h"
26 
27 #include "pw_allocator/unique_ptr.h"
28 #include "pw_containers/vector.h"
29 #include "pw_function/function.h"
30 #include "pw_intrusive_ptr/intrusive_ptr.h"
31 #include "pw_intrusive_ptr/recyclable.h"
32 
33 #include <cstddef>
34 #include <cstdint>
35 #include <optional>
36 
37 namespace chre::message {
38 
39 //! MessageRouter routes messages between endpoints connected to MessageHubs. It
40 //! provides an API for registering MessageHubs, opening and closing sessions,
41 //! and sending messages between endpoints. Each MessageHub is expected to
42 //! register a callback to handle messages sent to its endpoints and other
43 //! functions to provide information about the endpoints connected to it.
44 //!
45 //! MessageRouter is thread-safe.
46 //!
47 //! Usage:
48 //! 1. Create a MessageRouter instance.
49 //! 2. Register MessageHubs with the MessageRouter. Each MessageHub must have
50 //!    a unique ID and a callback to handle messages sent to its endpoints.
51 //! 3. Open sessions from endpoints connected to MessageHubs to endpoints
52 //!    connected to other MessageHubs.
53 //! 4. Send messages to endpoints using the MessageRouter API.
54 //! 5. Close sessions when they are no longer needed.
55 class MessageRouter {
56  public:
57   //! The callback used to register a MessageHub with the MessageRouter
58   class MessageHubCallback : public IntrusiveRefBase,
59                              public pw::Recyclable<MessageHubCallback> {
60    public:
61     virtual ~MessageHubCallback() = default;
62 
63     //! Message processing callback. If this function returns true,
64     //! the MessageHub received the message and will deliver it to the
65     //! receiving endpoint, or close the session if an error occurs.
66     //! @see sendMessage
67     //! @param session The session that the message was sent on (this reference
68     //!                is only valid for the duration of the callback)
69     //! @param sentBySessionInitiator Whether the message was sent by the
70     //! initiator of the session
71     //! @return true if the message was accepted for processing
72     virtual bool onMessageReceived(pw::UniquePtr<std::byte[]> &&data,
73                                    uint32_t messageType,
74                                    uint32_t messagePermissions,
75                                    const Session &session,
76                                    bool sentBySessionInitiator) = 0;
77 
78     //! Callback called when a session has been requested to be opened. The
79     //! message hub should call onSessionOpenComplete or closeSession to
80     //! accept or reject the session, respectively.
81     //! This function is called before returning from openSession in the
82     //! requestor hub.
83     virtual void onSessionOpenRequest(const Session &session) = 0;
84 
85     //! Callback called when the peer message hub has accepted the session
86     //! and the session is now open for messages
87     virtual void onSessionOpened(const Session &session) = 0;
88 
89     //! Callback called when the session is closed
90     virtual void onSessionClosed(const Session &session, Reason reason) = 0;
91 
92     //! Callback called to iterate over all endpoints connected to the
93     //! MessageHub. Underlying endpoint storage must not change during this
94     //! callback. If function returns true, the MessageHub can stop iterating
95     //! over future endpoints.
96     virtual void forEachEndpoint(
97         const pw::Function<bool(const EndpointInfo &)> &function) = 0;
98 
99     //! @return The EndpointInfo for the given endpoint ID.
100     virtual std::optional<EndpointInfo> getEndpointInfo(
101         EndpointId endpointId) = 0;
102 
103     //! @return The first endpoint that has the given service descriptor, a
104     //! null-terminated ASCII string. If no endpoint has the service descriptor,
105     //! std::nullopt is returned.
106     virtual std::optional<EndpointId> getEndpointForService(
107         const char *serviceDescriptor) = 0;
108 
109     //! @return true if the endpoint has the given service descriptor, a
110     //! null-terminated ASCII string, false otherwise.
111     virtual bool doesEndpointHaveService(EndpointId endpointId,
112                                          const char *serviceDescriptor) = 0;
113 
114     //! Callback called to iterate over all services provided by endpoints
115     //! connected to the MessageHub. Underlying endpoint and service storage
116     //! must not change during this callback. If function returns true, the
117     //! MessageHub can stop iterating over future endpoints. The service
118     //! descriptor must be valid for the duration of the callback.
119     virtual void forEachService(
120         const pw::Function<bool(const EndpointInfo &, const ServiceInfo &)>
121             &function) = 0;
122 
123     //! Callback called when a message hub except this one is registered.
124     virtual void onHubRegistered(const MessageHubInfo &info) = 0;
125 
126     //! Callback called when a message hub except this one is unregistered.
127     virtual void onHubUnregistered(MessageHubId id) = 0;
128 
129     //! Callback called when an endpoint is registered to any MessageHub,
130     //! except for this MessageHub.
131     virtual void onEndpointRegistered(MessageHubId messageHubId,
132                                       EndpointId endpointId) = 0;
133 
134     //! Callback called when an endpoint is unregistered from any MessageHub,
135     //! except for this MessageHub.
136     virtual void onEndpointUnregistered(MessageHubId messageHubId,
137                                         EndpointId endpointId) = 0;
138 
139     //! Recycle function called by pw::IntrusivePtr when the MessageHubCallback
140     //! is no longer in use. The default behavior in Pigweed is to `delete
141     //! this`. The callbacks derived from this class should also inherit from
142     //! pw::Recyclable and override this function.
143     virtual void pw_recycle() = 0;
144   };
145 
146   //! The API returned when registering a MessageHub with the MessageRouter.
147   class MessageHub {
148    public:
149     //! Creates an empty MessageHub that is not usable, similar to a moved-from
150     //! MessageHub. Attempting to call any method on this object results in
151     //! undefined behavior.
152     MessageHub();
153 
154     // There can only be one live MessageHub instance for a given hub ID, so
155     // only move operations are supported.
156     MessageHub(const MessageHub &) = delete;
157     MessageHub &operator=(const MessageHub &) = delete;
158     MessageHub(MessageHub &&other);
159     MessageHub &operator=(MessageHub &&other);
160 
161     //! Destructor. Unregisters the MessageHub from the MessageRouter.
162     ~MessageHub();
163 
164     //! Accepts the session open request from the peer message hub.
165     //! onSessionOpened will be called on both hubs.
166     void onSessionOpenComplete(SessionId sessionId);
167 
168     //! Opens a session from an endpoint connected to the current MessageHub
169     //! to the listed MessageHub ID and endpoint ID, with the given service
170     //! descriptor, a null-terminated ASCII string.
171     //! onSessionOpenRequest will be called to request the session to be
172     //! opened. Once the peer message hub calls onSessionOpenComplete or
173     //! closeSession, onSessionOpened or onSessionClosed will be called,
174     //! depending on the result. If the session ID is provided (not
175     //! SESSION_ID_INVALID), it must be unique and from the reserved session ID
176     //! range. MessageRouter does not guarantee anything about the session ID if
177     //! it is provided in this API. If the session ID is not provided,
178     //! MessageRouter will assign a session ID normally.
179     //! @return The session ID or SESSION_ID_INVALID if the session could
180     //! not be opened
181     SessionId openSession(EndpointId fromEndpointId,
182                           MessageHubId toMessageHubId, EndpointId toEndpointId,
183                           const char *serviceDescriptor = nullptr,
184                           SessionId sessionId = SESSION_ID_INVALID);
185 
186     //! Closes the session with sessionId and reason
187     //! @return true if the session was closed, false if the session was not
188     //! found
189     bool closeSession(SessionId sessionId,
190                       Reason reason = Reason::CLOSE_ENDPOINT_SESSION_REQUESTED);
191 
192     //! Returns a session if it exists
193     //! @return The session or std::nullopt if the session was not found
194     std::optional<Session> getSessionWithId(SessionId sessionId);
195 
196     //! Sends a message to the session specified by sessionId.
197     //! @see chreSendReliableMessageAsync. Sends the message in a reliable
198     //! manner if possible. If the message cannot be delivered, the session
199     //! is closed and subsequent calls to this function with the same sessionId
200     //! will return false.
201     //! @param data The data to send
202     //! @param messageType The type of the message, a bit flagged value
203     //! @param messagePermissions The permissions of the message, a bit flagged
204     //! value
205     //! @param sessionId The session to send the message on
206     //! @param fromEndpointId The endpoint ID of the sender or ENDPOINT_ID_ANY
207     //! to allow MessageRouter to infer the sender endpoint ID. If the
208     //! sender endpoint ID cannot be inferred, (i.e. the session is between
209     //! endpoints on the same message hub), this function will return false.
210     //! @return true if the message was sent, false if the message could not be
211     //! sent
212     bool sendMessage(pw::UniquePtr<std::byte[]> &&data, uint32_t messageType,
213                      uint32_t messagePermissions, SessionId sessionId,
214                      EndpointId fromEndpointId = ENDPOINT_ID_ANY);
215 
216     //! Registers an endpoint with the MessageHub.
217     //! @return true if the endpoint was registered, otherwise false.
218     bool registerEndpoint(EndpointId endpointId);
219 
220     //! Unregisters an endpoint from the MessageHub.
221     //! @return true if the endpoint was unregistered, otherwise false.
222     bool unregisterEndpoint(EndpointId endpointId);
223 
224     //! @return The MessageHub ID of the currently connected MessageHub
225     MessageHubId getId();
226 
227     //! @return If the MessageHub is active and registered with the
228     //! MessageRouter.
229     bool isRegistered();
230 
231     //! Unregisters this MessageHub from the MessageRouter.
232     void unregister();
233 
234    private:
235     friend class MessageRouter;
236 
237     MessageHub(MessageRouter &router, MessageHubId id);
238 
239     //! The MessageRouter that this MessageHub is connected to
240     MessageRouter *mRouter;
241 
242     //! The id of this message hub
243     MessageHubId mHubId;
244   };
245 
246   //! Represents a MessageHub and its connected endpoints
247   struct MessageHubRecord {
248     MessageHubInfo info;
249     pw::IntrusivePtr<MessageHubCallback> callback;
250   };
251 
252   //! The default reserved session ID value
253   static constexpr SessionId kDefaultReservedSessionId = 0x8000;
254 
255   MessageRouter() = delete;
256 
257   //! Constructor for the MessageRouter.
258   //! @param messageHubs The list of MessageHubs connected to the MessageRouter
259   //! @param sessions The list of sessions connected to the MessageRouter
260   //! @param reservedSessionId The first reserved session ID - MessageRouter
261   //! will not assign session IDs greater than or equal to this value
262   MessageRouter(pw::Vector<MessageHubRecord> &messageHubs,
263                 pw::Vector<Session> &sessions,
264                 SessionId reservedSessionId = kDefaultReservedSessionId)
kReservedSessionId(reservedSessionId)265       : kReservedSessionId(reservedSessionId),
266         mMessageHubs(messageHubs),
267         mSessions(sessions) {}
268 
269   //! Registers a MessageHub with the MessageRouter.
270   //! The provided name must be unique and not registered before and be a valid
271   //! C string. The data underlying name must outlive the MessageHub. The
272   //! callback must outlive the MessageHub. The ID must be unique and not
273   //! registered before. When the returned MessageHub is destroyed, it will
274   //! unregister itself from the MessageRouter.
275   //! @param name The name of the MessageHub
276   //! @param id The ID of the MessageHub
277   //! @param callback The callback to handle messages sent to the MessageHub
278   //! @return The MessageHub API or std::nullopt if the MessageHub could not be
279   //! registered
280   std::optional<MessageHub> registerMessageHub(
281       const char *name, MessageHubId id,
282       pw::IntrusivePtr<MessageHubCallback> callback);
283 
284   //! Executes the function for each endpoint connected to this MessageHub.
285   //! If function returns true, the iteration will stop.
286   //! @return true if the MessageHub is found, false otherwise
287   bool forEachEndpointOfHub(
288       MessageHubId messageHubId,
289       const pw::Function<bool(const EndpointInfo &)> &function);
290 
291   //! Executes the function for each endpoint connected to all Message Hubs.
292   //! @return true if successful, false otherwise
293   bool forEachEndpoint(
294       const pw::Function<void(const MessageHubInfo &, const EndpointInfo &)>
295           &function);
296 
297   //! @return The EndpointInfo for the given hub and endpoint IDs
298   std::optional<EndpointInfo> getEndpointInfo(MessageHubId messageHubId,
299                                               EndpointId endpointId);
300 
301   //! @return The Endpoint for the given service descriptor. If multiple
302   //! endpoints have the same service descriptor, the first one is returned.
303   //! If the message hub ID is MESSAGE_HUB_ID_ANY, all message hubs are
304   //! searched.
305   std::optional<Endpoint> getEndpointForService(MessageHubId messageHubId,
306                                                 const char *serviceDescriptor);
307 
308   //! @return true if the endpoint has the given service descriptor, a
309   //! null-terminated ASCII string, false otherwise.
310   bool doesEndpointHaveService(MessageHubId messageHubId, EndpointId endpointId,
311                                const char *serviceDescriptor);
312 
313   //! Executes the function for each service provided by an endpoint connected
314   //! to this MessageHub. If function returns true, the iteration will stop.
315   //! @return true if successful, false otherwise
316   bool forEachService(
317       const pw::Function<bool(const MessageHubInfo &, const EndpointInfo &,
318                               const ServiceInfo &)> &function);
319 
320   //! Executes the function for each MessageHub connected to the
321   //! MessageRouter. If function returns true, the iteration will stop.
322   //! @return true if successful, false if failed
323   bool forEachMessageHub(
324       const pw::Function<bool(const MessageHubInfo &)> &function);
325 
326  private:
327   //! Unregisters a MessageHub from the MessageRouter. This function will
328   //! close all sessions that were initiated by or connected to the MessageHub
329   //! and destroy the MessageHubRecord. This function will call the callback
330   //! for each session that was closed only for the other message hub in the
331   //! session.
332   //! @return true if the MessageHub was unregistered, false if the MessageHub
333   //! was not found.
334   bool unregisterMessageHub(MessageHubId fromMessageHubId);
335 
336   //! Accepts the session open request from the peer message hub.
337   //! onSessionOpened will be called on both hubs.
338   void onSessionOpenComplete(MessageHubId fromMessageHubId,
339                              SessionId sessionId);
340 
341   //! Opens a session from an endpoint connected to the current MessageHub
342   //! to the listed MessageHub ID and endpoint ID, with the given service
343   //! descriptor, a null-terminated ASCII string.
344   //! onSessionOpenRequest will be called to request the session to be
345   //! opened. Once the peer message hub calls onSessionOpenComplete or
346   //! closeSession, onSessionOpened or onSessionClosed will be called,
347   //! depending on the result. If the session ID is provided (not
348   //! SESSION_ID_INVALID), it must be unique and from the reserved session ID
349   //! range. MessageRouter does not guarantee anything about the session ID if
350   //! it is provided in this API. If the session ID is not provided,
351   //! MessageRouter will assign a session ID normally.
352   //! @return The session ID or SESSION_ID_INVALID if the session could not be
353   //! opened
354   SessionId openSession(MessageHubId fromMessageHubId,
355                         EndpointId fromEndpointId, MessageHubId toMessageHubId,
356                         EndpointId toEndpointId,
357                         const char *serviceDescriptor = nullptr,
358                         SessionId sessionId = SESSION_ID_INVALID);
359 
360   //! Closes the session with sessionId and reason
361   //! @return true if the session was closed, false if the session was not
362   //! found
363   bool closeSession(MessageHubId fromMessageHubId, SessionId sessionId,
364                     Reason reason = Reason::CLOSE_ENDPOINT_SESSION_REQUESTED);
365 
366   //! Finalizes the session with sessionId and reason. If reason is provided,
367   //! the session will be closed, else the session will be fully opened.
368   //! @return true if the session was finalized, false if the session was not
369   //! found or one of the message hubs were not found or not linked to the
370   //! session.
371   bool finalizeSession(MessageHubId fromMessageHubId, SessionId sessionId,
372                        std::optional<Reason> reason);
373 
374   //! Returns a session if it exists
375   //! @return The session or std::nullopt if the session was not found
376   std::optional<Session> getSessionWithId(MessageHubId fromMessageHubId,
377                                           SessionId sessionId);
378 
379   //! Sends a message to the session specified by sessionId.
380   //! @see chreSendReliableMessageAsync. Sends the message in a reliable
381   //! manner if possible. If the message cannot be delivered, the session
382   //! is closed and subsequent calls to this function with the same sessionId
383   //! will return false.
384   //! @see MessageHub::sendMessage
385   //! @return true if the message was sent, false if the message could not be
386   //! sent
387   bool sendMessage(pw::UniquePtr<std::byte[]> &&data, uint32_t messageType,
388                    uint32_t messagePermissions, SessionId sessionId,
389                    EndpointId fromEndpointId, MessageHubId fromMessageHubId);
390 
391   //! Registers an endpoint with the MessageHub.
392   //! @return true if the endpoint was registered, otherwise false.
393   bool registerEndpoint(MessageHubId messageHubId, EndpointId endpointId);
394 
395   //! Unregisters an endpoint from the MessageHub.
396   //! @return true if the endpoint was unregistered, otherwise false.
397   bool unregisterEndpoint(MessageHubId messageHubId, EndpointId endpointId);
398 
399   //! Helper function for registering or unregistering an endpoint with a
400   //! MessageHub.
401   //! @return true if the endpoint was registered or unregistered, otherwise
402   //! false.
403   bool onEndpointRegistrationStateChanged(MessageHubId messageHubId,
404                                           EndpointId endpointId,
405                                           bool isRegistered);
406 
407   //! @return The a copy of the list of MessageHubRecords
408   std::optional<DynamicVector<MessageHubRecord>> getMessageHubRecords();
409 
410   //! @return A copy of the list of MessageHubRecords while holding mMutex.
411   std::optional<DynamicVector<MessageHubRecord>> getMessageHubRecordsLocked();
412 
413   //! @return The MessageHubRecord for the given MessageHub ID
414   const MessageHubRecord *getMessageHubRecordLocked(MessageHubId messageHubId);
415 
416   //! @return The index of the session if it exists
417   //! Requires the caller to hold the mutex
418   std::optional<size_t> findSessionIndexLocked(MessageHubId fromMessageHubId,
419                                                SessionId sessionId);
420 
421   //! @return The callback for the given MessageHub ID or nullptr if not found
422   //! Requires the caller to hold the mutex
423   pw::IntrusivePtr<MessageHubCallback> getCallbackFromMessageHubId(
424       MessageHubId messageHubId);
425 
426   //! @return The callback for the given MessageHub ID or nullptr if not found
427   pw::IntrusivePtr<MessageHubCallback> getCallbackFromMessageHubIdLocked(
428       MessageHubId messageHubId);
429 
430   //! @return true if the endpoint exists in the MessageHub with the given
431   //! callback
432   bool checkIfEndpointExists(
433       const pw::IntrusivePtr<MessageHubCallback> &callback,
434       EndpointId endpointId);
435 
436   //! @return The next available Session ID. Will wrap around if needed and
437   //! ensures the returned ID is not in the reserved range nor is it already in
438   //! use. Requires the caller to hold the mutex.
439   SessionId getNextSessionIdLocked();
440 
441   //! The mutex to protect MessageRouter state
442   Mutex mMutex;
443 
444   //! The next available Session ID
445   SessionId mNextSessionId = 0;
446 
447   //! The start of the reserved session ID range
448   const SessionId kReservedSessionId;
449 
450   //! The list of MessageHubs connected to the MessageRouter
451   pw::Vector<MessageHubRecord> &mMessageHubs;
452 
453   //! The list of sessions connected to the MessageRouter
454   pw::Vector<Session> &mSessions;
455 };
456 
457 //! Define the singleton instance of the MessageRouter
458 typedef Singleton<MessageRouter> MessageRouterSingleton;
459 
460 //! Routes messages between MessageHubs
461 template <size_t kMaxMessageHubs, size_t kMaxSessions>
462 class MessageRouterWithStorage : public MessageRouter {
463  public:
464   MessageRouterWithStorage(
465       SessionId reservedSessionId = MessageRouter::kDefaultReservedSessionId)
MessageRouter(mMessageHubs,mSessions,reservedSessionId)466       : MessageRouter(mMessageHubs, mSessions, reservedSessionId) {}
467 
468  private:
469   //! The list of MessageHubs connected to the MessageRouter
470   pw::Vector<MessageHubRecord, kMaxMessageHubs> mMessageHubs;
471 
472   //! The list of sessions connected to the MessageRouter
473   pw::Vector<Session, kMaxSessions> mSessions;
474 };
475 
476 }  // namespace chre::message
477 
478 #endif  // CHRE_UTIL_SYSTEM_MESSAGE_ROUTER_H_
479