blob: 71ffcba9b25b3e5a0445760566379a73681c7272 [file] [log] [blame]
/*
* 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 <android/binder_ibinder.h>
#include <inttypes.h>
#include <media/TranscodingClientManager.h>
#include <media/TranscodingRequest.h>
#include <utils/Log.h>
namespace android {
using ::aidl::android::media::BnTranscodingClient;
using ::aidl::android::media::TranscodingJobParcel;
using ::aidl::android::media::TranscodingRequestParcel;
using Status = ::ndk::ScopedAStatus;
using ::ndk::SpAIBinder;
///////////////////////////////////////////////////////////////////////////////
/**
* 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 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;
pid_t mClientPid;
uid_t mClientUid;
std::string mClientName;
std::string mClientOpPackageName;
// Next jobId to assign
std::atomic<std::int32_t> mNextJobId;
// Pointer to the client manager for this client
TranscodingClientManager* mOwner;
ClientImpl(const std::shared_ptr<ITranscodingClientCallback>& callback, pid_t pid, uid_t uid,
const std::string& clientName, const std::string& opPackageName,
TranscodingClientManager* owner);
Status submitRequest(const TranscodingRequestParcel& /*in_request*/,
TranscodingJobParcel* /*out_job*/, bool* /*_aidl_return*/) override;
Status cancelJob(int32_t /*in_jobId*/, bool* /*_aidl_return*/) override;
Status getJobWithId(int32_t /*in_jobId*/, TranscodingJobParcel* /*out_job*/,
bool* /*_aidl_return*/) override;
Status unregister() override;
};
TranscodingClientManager::ClientImpl::ClientImpl(
const std::shared_ptr<ITranscodingClientCallback>& callback, pid_t pid, uid_t uid,
const std::string& clientName, const std::string& opPackageName,
TranscodingClientManager* owner)
: mClientCallback((callback != nullptr) ? callback->asBinder() : nullptr),
mClientId((int64_t)mClientCallback.get()),
mClientPid(pid),
mClientUid(uid),
mClientName(clientName),
mClientOpPackageName(opPackageName),
mNextJobId(0),
mOwner(owner) {}
Status TranscodingClientManager::ClientImpl::submitRequest(
const TranscodingRequestParcel& in_request, TranscodingJobParcel* out_job,
bool* _aidl_return) {
if (in_request.fileName.empty()) {
// This is the only error we check for now.
*_aidl_return = false;
return Status::ok();
}
int32_t jobId = mNextJobId.fetch_add(1);
*_aidl_return =
mOwner->mJobScheduler->submit(mClientId, jobId, mClientUid, in_request,
ITranscodingClientCallback::fromBinder(mClientCallback));
if (*_aidl_return) {
out_job->jobId = jobId;
// TODO(chz): is some of this coming from JobScheduler?
*(TranscodingRequest*)&out_job->request = in_request;
out_job->awaitNumberOfJobs = 0;
}
return Status::ok();
}
Status TranscodingClientManager::ClientImpl::cancelJob(int32_t in_jobId, bool* _aidl_return) {
*_aidl_return = mOwner->mJobScheduler->cancel(mClientId, in_jobId);
return Status::ok();
}
Status TranscodingClientManager::ClientImpl::getJobWithId(int32_t in_jobId,
TranscodingJobParcel* out_job,
bool* _aidl_return) {
*_aidl_return = mOwner->mJobScheduler->getJob(mClientId, in_jobId, &out_job->request);
if (*_aidl_return) {
out_job->jobId = in_jobId;
out_job->awaitNumberOfJobs = 0;
}
return Status::ok();
}
Status TranscodingClientManager::ClientImpl::unregister() {
// TODO(chz): Decide what to do about this client's jobs.
// If app crashed, it could be relaunched later. Do we want to keep the
// jobs around for that?
mOwner->removeClient(mClientId);
return Status::ok();
}
///////////////////////////////////////////////////////////////////////////////
// static
void TranscodingClientManager::BinderDiedCallback(void* cookie) {
ClientImpl* client = static_cast<ClientImpl*>(cookie);
ALOGD("Client %lld is dead", (long long)client->mClientId);
client->unregister();
}
TranscodingClientManager::TranscodingClientManager(
const std::shared_ptr<SchedulerClientInterface>& scheduler)
: mDeathRecipient(AIBinder_DeathRecipient_new(BinderDiedCallback)), mJobScheduler(scheduler) {
ALOGD("TranscodingClientManager started");
}
TranscodingClientManager::~TranscodingClientManager() {
ALOGD("TranscodingClientManager exited");
}
bool TranscodingClientManager::isClientIdRegistered(ClientIdType clientId) const {
return mClientIdToClientMap.find(clientId) != mClientIdToClientMap.end();
}
void TranscodingClientManager::dumpAllClients(int fd, const Vector<String16>& args __unused) {
String8 result;
const size_t SIZE = 256;
char buffer[SIZE];
snprintf(buffer, SIZE, " Total num of Clients: %zu\n", mClientIdToClientMap.size());
result.append(buffer);
if (mClientIdToClientMap.size() > 0) {
snprintf(buffer, SIZE, "========== Dumping all clients =========\n");
result.append(buffer);
}
for (const auto& iter : mClientIdToClientMap) {
snprintf(buffer, SIZE, " -- Client id: %lld name: %s\n", (long long)iter.first,
iter.second->mClientName.c_str());
result.append(buffer);
}
write(fd, result.string(), result.size());
}
status_t TranscodingClientManager::addClient(
const std::shared_ptr<ITranscodingClientCallback>& callback, pid_t pid, uid_t uid,
const std::string& clientName, const std::string& opPackageName,
std::shared_ptr<ITranscodingClient>* outClient) {
// Validate the client.
if (callback == nullptr || pid < 0 || clientName.empty() || opPackageName.empty()) {
ALOGE("Invalid client");
return BAD_VALUE;
}
// Creates the client and uses its process id as client id.
std::shared_ptr<ClientImpl> client = ::ndk::SharedRefBase::make<ClientImpl>(
callback, pid, uid, clientName, opPackageName, this);
std::scoped_lock lock{mLock};
// Checks if the client already registers.
if (isClientIdRegistered(client->mClientId)) {
return ALREADY_EXISTS;
}
ALOGD("Adding client id %lld, pid %d, uid %d, name %s, package %s",
(long long)client->mClientId, client->mClientPid, client->mClientUid,
client->mClientName.c_str(), client->mClientOpPackageName.c_str());
AIBinder_linkToDeath(client->mClientCallback.get(), mDeathRecipient.get(),
reinterpret_cast<void*>(client.get()));
// Adds the new client to the map.
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 INVALID_OPERATION;
}
SpAIBinder callback = it->second->mClientCallback;
// Check if the client still live. If alive, unlink the death.
if (callback.get() != nullptr) {
AIBinder_unlinkToDeath(callback.get(), mDeathRecipient.get(),
reinterpret_cast<void*>(it->second.get()));
}
// Erase the entry.
mClientIdToClientMap.erase(it);
return OK;
}
size_t TranscodingClientManager::getNumOfClients() const {
std::scoped_lock lock{mLock};
return mClientIdToClientMap.size();
}
} // namespace android