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