You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
466 lines
17 KiB
466 lines
17 KiB
/*
|
|
* 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 <aidl/android/media/BnTranscodingClient.h>
|
|
#include <aidl/android/media/IMediaTranscodingService.h>
|
|
#include <android/binder_ibinder.h>
|
|
#include <android/permission_manager.h>
|
|
#include <inttypes.h>
|
|
#include <media/TranscodingClientManager.h>
|
|
#include <media/TranscodingRequest.h>
|
|
#include <media/TranscodingUidPolicy.h>
|
|
#include <private/android_filesystem_config.h>
|
|
#include <utils/Log.h>
|
|
#include <utils/String16.h>
|
|
|
|
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<ClientIdType> TranscodingClientManager::sCookieCounter = 0;
|
|
//static
|
|
std::mutex TranscodingClientManager::sCookie2ClientLock;
|
|
//static
|
|
std::map<ClientIdType, std::shared_ptr<TranscodingClientManager::ClientImpl>>
|
|
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<ITranscodingClientCallback> 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<int32_t> mNextSessionId;
|
|
// Whether this client has been unregistered already.
|
|
std::atomic<bool> mAbandoned;
|
|
// Weak pointer to the client manager for this client.
|
|
std::weak_ptr<TranscodingClientManager> mOwner;
|
|
|
|
ClientImpl(const std::shared_ptr<ITranscodingClientCallback>& callback,
|
|
const std::string& clientName, const std::string& opPackageName,
|
|
const std::weak_ptr<TranscodingClientManager>& 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<std::vector<int32_t>>* /*_aidl_return*/) override;
|
|
|
|
Status unregister() override;
|
|
};
|
|
|
|
TranscodingClientManager::ClientImpl::ClientImpl(
|
|
const std::shared_ptr<ITranscodingClientCallback>& callback, const std::string& clientName,
|
|
const std::string& opPackageName, const std::weak_ptr<TranscodingClientManager>& 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<TranscodingClientManager> 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<TranscodingClientManager> 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<TranscodingClientManager> 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<TranscodingClientManager> 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<std::vector<int32_t>>* _aidl_return) {
|
|
*_aidl_return = std::nullopt;
|
|
|
|
std::shared_ptr<TranscodingClientManager> owner;
|
|
if (mAbandoned || (owner = mOwner.lock()) == nullptr) {
|
|
return Status::fromServiceSpecificError(IMediaTranscodingService::ERROR_DISCONNECTED);
|
|
}
|
|
|
|
if (in_sessionId < 0) {
|
|
return Status::ok();
|
|
}
|
|
|
|
std::vector<int32_t> 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<TranscodingClientManager> 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<ClientIdType>(cookie);
|
|
|
|
ALOGD("Client %lld is dead", (long long)clientId);
|
|
|
|
std::shared_ptr<ClientImpl> 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<ControllerClientInterface>& 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<String16>& 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<ITranscodingClientCallback>& callback, const std::string& clientName,
|
|
const std::string& opPackageName, std::shared_ptr<ITranscodingClient>* 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<ClientImpl> client = ::ndk::SharedRefBase::make<ClientImpl>(
|
|
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<void*>(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<void*>(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
|