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