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