/* * Copyright (C) 2020 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ // #define LOG_NDEBUG 0 #define LOG_TAG "TranscodingClientManager" #include #include #include #include #include #include #include #include #include #include #include namespace android { static_assert(sizeof(ClientIdType) == sizeof(void*), "ClientIdType should be pointer-sized"); using ::aidl::android::media::BnTranscodingClient; using ::aidl::android::media::IMediaTranscodingService; // For service error codes using ::aidl::android::media::TranscodingRequestParcel; using ::aidl::android::media::TranscodingSessionParcel; using Status = ::ndk::ScopedAStatus; using ::ndk::SpAIBinder; //static std::atomic TranscodingClientManager::sCookieCounter = 0; //static std::mutex TranscodingClientManager::sCookie2ClientLock; //static std::map> TranscodingClientManager::sCookie2Client; /////////////////////////////////////////////////////////////////////////////// // Convenience methods for constructing binder::Status objects for error returns #define STATUS_ERROR_FMT(errorCode, errorString, ...) \ Status::fromServiceSpecificErrorWithMessage( \ errorCode, \ String8::format("%s:%d: " errorString, __FUNCTION__, __LINE__, ##__VA_ARGS__)) /** * ClientImpl implements a single client and contains all its information. */ struct TranscodingClientManager::ClientImpl : public BnTranscodingClient { /* The remote client callback that this ClientInfo is associated with. * Once the ClientInfo is created, we hold an SpAIBinder so that the binder * object doesn't get created again, otherwise the binder object pointer * may not be unique. */ SpAIBinder mClientBinder; std::shared_ptr mClientCallback; /* A unique id assigned to the client by the service. This number is used * by the service for indexing. Here we use the binder object's pointer * (casted to int64t_t) as the client id. */ ClientIdType mClientId; std::string mClientName; std::string mClientOpPackageName; // Next sessionId to assign. std::atomic mNextSessionId; // Whether this client has been unregistered already. std::atomic mAbandoned; // Weak pointer to the client manager for this client. std::weak_ptr mOwner; ClientImpl(const std::shared_ptr& callback, const std::string& clientName, const std::string& opPackageName, const std::weak_ptr& owner); Status submitRequest(const TranscodingRequestParcel& /*in_request*/, TranscodingSessionParcel* /*out_session*/, bool* /*_aidl_return*/) override; Status cancelSession(int32_t /*in_sessionId*/, bool* /*_aidl_return*/) override; Status getSessionWithId(int32_t /*in_sessionId*/, TranscodingSessionParcel* /*out_session*/, bool* /*_aidl_return*/) override; Status addClientUid(int32_t /*in_sessionId*/, int32_t /*in_clientUid*/, bool* /*_aidl_return*/) override; Status getClientUids(int32_t /*in_sessionId*/, std::optional>* /*_aidl_return*/) override; Status unregister() override; }; TranscodingClientManager::ClientImpl::ClientImpl( const std::shared_ptr& callback, const std::string& clientName, const std::string& opPackageName, const std::weak_ptr& owner) : mClientBinder((callback != nullptr) ? callback->asBinder() : nullptr), mClientCallback(callback), mClientId(sCookieCounter.fetch_add(1, std::memory_order_relaxed)), mClientName(clientName), mClientOpPackageName(opPackageName), mNextSessionId(0), mAbandoned(false), mOwner(owner) {} Status TranscodingClientManager::ClientImpl::submitRequest( const TranscodingRequestParcel& in_request, TranscodingSessionParcel* out_session, bool* _aidl_return) { *_aidl_return = false; std::shared_ptr owner; if (mAbandoned || (owner = mOwner.lock()) == nullptr) { return Status::fromServiceSpecificError(IMediaTranscodingService::ERROR_DISCONNECTED); } if (in_request.sourceFilePath.empty() || in_request.destinationFilePath.empty()) { return Status::ok(); } int32_t callingPid = AIBinder_getCallingPid(); int32_t callingUid = AIBinder_getCallingUid(); int32_t in_clientUid = in_request.clientUid; int32_t in_clientPid = in_request.clientPid; // Check if we can trust clientUid. Only privilege caller could forward the // uid on app client's behalf. if (in_clientUid == IMediaTranscodingService::USE_CALLING_UID) { in_clientUid = callingUid; } else if (in_clientUid < 0) { return Status::ok(); } else if (in_clientUid != callingUid && !owner->isTrustedCaller(callingPid, callingUid)) { ALOGE("submitRequest rejected (clientPid %d, clientUid %d) " "(don't trust callingUid %d)", in_clientPid, in_clientUid, callingUid); return STATUS_ERROR_FMT(IMediaTranscodingService::ERROR_PERMISSION_DENIED, "submitRequest rejected (clientPid %d, clientUid %d) " "(don't trust callingUid %d)", in_clientPid, in_clientUid, callingUid); } // Check if we can trust clientPid. Only privilege caller could forward the // pid on app client's behalf. if (in_clientPid == IMediaTranscodingService::USE_CALLING_PID) { in_clientPid = callingPid; } else if (in_clientPid < 0) { return Status::ok(); } else if (in_clientPid != callingPid && !owner->isTrustedCaller(callingPid, callingUid)) { ALOGE("submitRequest rejected (clientPid %d, clientUid %d) " "(don't trust callingUid %d)", in_clientPid, in_clientUid, callingUid); return STATUS_ERROR_FMT(IMediaTranscodingService::ERROR_PERMISSION_DENIED, "submitRequest rejected (clientPid %d, clientUid %d) " "(don't trust callingUid %d)", in_clientPid, in_clientUid, callingUid); } int32_t sessionId = mNextSessionId.fetch_add(1); *_aidl_return = owner->mSessionController->submit(mClientId, sessionId, callingUid, in_clientUid, in_request, mClientCallback); if (*_aidl_return) { out_session->sessionId = sessionId; // TODO(chz): is some of this coming from SessionController? *(TranscodingRequest*)&out_session->request = in_request; out_session->awaitNumberOfSessions = 0; } return Status::ok(); } Status TranscodingClientManager::ClientImpl::cancelSession(int32_t in_sessionId, bool* _aidl_return) { *_aidl_return = false; std::shared_ptr owner; if (mAbandoned || (owner = mOwner.lock()) == nullptr) { return Status::fromServiceSpecificError(IMediaTranscodingService::ERROR_DISCONNECTED); } if (in_sessionId < 0) { return Status::ok(); } *_aidl_return = owner->mSessionController->cancel(mClientId, in_sessionId); return Status::ok(); } Status TranscodingClientManager::ClientImpl::getSessionWithId(int32_t in_sessionId, TranscodingSessionParcel* out_session, bool* _aidl_return) { *_aidl_return = false; std::shared_ptr owner; if (mAbandoned || (owner = mOwner.lock()) == nullptr) { return Status::fromServiceSpecificError(IMediaTranscodingService::ERROR_DISCONNECTED); } if (in_sessionId < 0) { return Status::ok(); } *_aidl_return = owner->mSessionController->getSession(mClientId, in_sessionId, &out_session->request); if (*_aidl_return) { out_session->sessionId = in_sessionId; out_session->awaitNumberOfSessions = 0; } return Status::ok(); } Status TranscodingClientManager::ClientImpl::addClientUid(int32_t in_sessionId, int32_t in_clientUid, bool* _aidl_return) { *_aidl_return = false; std::shared_ptr owner; if (mAbandoned || (owner = mOwner.lock()) == nullptr) { return Status::fromServiceSpecificError(IMediaTranscodingService::ERROR_DISCONNECTED); } if (in_sessionId < 0) { return Status::ok(); } int32_t callingPid = AIBinder_getCallingPid(); int32_t callingUid = AIBinder_getCallingUid(); // Check if we can trust clientUid. Only privilege caller could add uid to existing sessions. if (in_clientUid == IMediaTranscodingService::USE_CALLING_UID) { in_clientUid = callingUid; } else if (in_clientUid < 0) { return Status::ok(); } else if (in_clientUid != callingUid && !owner->isTrustedCaller(callingPid, callingUid)) { ALOGE("addClientUid rejected (clientUid %d) " "(don't trust callingUid %d)", in_clientUid, callingUid); return STATUS_ERROR_FMT(IMediaTranscodingService::ERROR_PERMISSION_DENIED, "addClientUid rejected (clientUid %d) " "(don't trust callingUid %d)", in_clientUid, callingUid); } *_aidl_return = owner->mSessionController->addClientUid(mClientId, in_sessionId, in_clientUid); return Status::ok(); } Status TranscodingClientManager::ClientImpl::getClientUids( int32_t in_sessionId, std::optional>* _aidl_return) { *_aidl_return = std::nullopt; std::shared_ptr owner; if (mAbandoned || (owner = mOwner.lock()) == nullptr) { return Status::fromServiceSpecificError(IMediaTranscodingService::ERROR_DISCONNECTED); } if (in_sessionId < 0) { return Status::ok(); } std::vector result; if (owner->mSessionController->getClientUids(mClientId, in_sessionId, &result)) { *_aidl_return = result; } return Status::ok(); } Status TranscodingClientManager::ClientImpl::unregister() { bool abandoned = mAbandoned.exchange(true); std::shared_ptr owner; if (abandoned || (owner = mOwner.lock()) == nullptr) { return Status::fromServiceSpecificError(IMediaTranscodingService::ERROR_DISCONNECTED); } // Use sessionId == -1 to cancel all realtime sessions for this client with the controller. owner->mSessionController->cancel(mClientId, -1); owner->removeClient(mClientId); return Status::ok(); } /////////////////////////////////////////////////////////////////////////////// // static void TranscodingClientManager::BinderDiedCallback(void* cookie) { ClientIdType clientId = reinterpret_cast(cookie); ALOGD("Client %lld is dead", (long long)clientId); std::shared_ptr client; { std::scoped_lock lock{sCookie2ClientLock}; auto it = sCookie2Client.find(clientId); if (it != sCookie2Client.end()) { client = it->second; } } if (client != nullptr) { client->unregister(); } } TranscodingClientManager::TranscodingClientManager( const std::shared_ptr& controller) : mDeathRecipient(AIBinder_DeathRecipient_new(BinderDiedCallback)), mSessionController(controller) { ALOGD("TranscodingClientManager started"); for (uid_t uid : {AID_ROOT, AID_SYSTEM, AID_SHELL, AID_MEDIA}) { mTrustedUids.insert(uid); } } TranscodingClientManager::~TranscodingClientManager() { ALOGD("TranscodingClientManager exited"); } void TranscodingClientManager::dumpAllClients(int fd, const Vector& args __unused) { String8 result; const size_t SIZE = 256; char buffer[SIZE]; std::scoped_lock lock{mLock}; if (mClientIdToClientMap.size() > 0) { snprintf(buffer, SIZE, "\n========== Dumping all clients =========\n"); result.append(buffer); } snprintf(buffer, SIZE, " Total num of Clients: %zu\n", mClientIdToClientMap.size()); result.append(buffer); for (const auto& iter : mClientIdToClientMap) { snprintf(buffer, SIZE, " Client %lld: pkg: %s\n", (long long)iter.first, iter.second->mClientName.c_str()); result.append(buffer); } write(fd, result.string(), result.size()); } bool TranscodingClientManager::isTrustedCaller(pid_t pid, uid_t uid) { if (uid > 0 && mTrustedUids.count(uid) > 0) { return true; } int32_t result; if (__builtin_available(android __TRANSCODING_MIN_API__, *)) { if (APermissionManager_checkPermission("android.permission.WRITE_MEDIA_STORAGE", pid, uid, &result) == PERMISSION_MANAGER_STATUS_OK && result == PERMISSION_MANAGER_PERMISSION_GRANTED) { mTrustedUids.insert(uid); return true; } } return false; } status_t TranscodingClientManager::addClient( const std::shared_ptr& callback, const std::string& clientName, const std::string& opPackageName, std::shared_ptr* outClient) { int32_t callingPid = AIBinder_getCallingPid(); int32_t callingUid = AIBinder_getCallingUid(); // Check if client has the permission if (!isTrustedCaller(callingPid, callingUid)) { ALOGE("addClient rejected (clientPid %d, clientUid %d)", callingPid, callingUid); return IMediaTranscodingService::ERROR_PERMISSION_DENIED; } // Validate the client. if (callback == nullptr || clientName.empty() || opPackageName.empty()) { ALOGE("Invalid client"); return IMediaTranscodingService::ERROR_ILLEGAL_ARGUMENT; } SpAIBinder binder = callback->asBinder(); std::scoped_lock lock{mLock}; // Checks if the client already registers. if (mRegisteredCallbacks.count((uintptr_t)binder.get()) > 0) { return IMediaTranscodingService::ERROR_ALREADY_EXISTS; } // Creates the client (with the id assigned by ClientImpl). std::shared_ptr client = ::ndk::SharedRefBase::make( callback, clientName, opPackageName, shared_from_this()); ALOGD("Adding client id %lld, name %s, package %s", (long long)client->mClientId, client->mClientName.c_str(), client->mClientOpPackageName.c_str()); { std::scoped_lock lock{sCookie2ClientLock}; sCookie2Client.emplace(std::make_pair(client->mClientId, client)); } AIBinder_linkToDeath(binder.get(), mDeathRecipient.get(), reinterpret_cast(client->mClientId)); // Adds the new client to the map. mRegisteredCallbacks.insert((uintptr_t)binder.get()); mClientIdToClientMap[client->mClientId] = client; *outClient = client; return OK; } status_t TranscodingClientManager::removeClient(ClientIdType clientId) { ALOGD("Removing client id %lld", (long long)clientId); std::scoped_lock lock{mLock}; // Checks if the client is valid. auto it = mClientIdToClientMap.find(clientId); if (it == mClientIdToClientMap.end()) { ALOGE("Client id %lld does not exist", (long long)clientId); return IMediaTranscodingService::ERROR_INVALID_OPERATION; } SpAIBinder binder = it->second->mClientBinder; // Check if the client still live. If alive, unlink the death. if (binder.get() != nullptr) { AIBinder_unlinkToDeath(binder.get(), mDeathRecipient.get(), reinterpret_cast(it->second->mClientId)); } { std::scoped_lock lock{sCookie2ClientLock}; sCookie2Client.erase(it->second->mClientId); } // Erase the entry. mClientIdToClientMap.erase(it); mRegisteredCallbacks.erase((uintptr_t)binder.get()); return OK; } size_t TranscodingClientManager::getNumOfClients() const { std::scoped_lock lock{mLock}; return mClientIdToClientMap.size(); } } // namespace android