Merge "MediaTranscoding: Add TranscodingClientManager."

gugelfrei
Hangyu Kuang 5 years ago committed by Android (Google) Code Review
commit 9d73033022

@ -0,0 +1,29 @@
#
# 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.
#
BasedOnStyle: Google
AllowShortFunctionsOnASingleLine: Inline
AllowShortIfStatementsOnASingleLine: true
AllowShortLoopsOnASingleLine: true
BinPackArguments: true
BinPackParameters: true
ColumnLimit: 100
CommentPragmas: NOLINT:.*
ContinuationIndentWidth: 8
DerivePointerAlignment: false
IndentWidth: 4
PointerAlignment: Left
TabWidth: 4

@ -1,3 +1,19 @@
/*
* 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.
*/
// AIDL interfaces of MediaTranscoding.
aidl_interface {
name: "mediatranscoding_aidl_interface",
@ -14,3 +30,38 @@ aidl_interface {
"aidl/android/media/TranscodingResultParcel.aidl",
],
}
cc_library_shared {
name: "libmediatranscoding",
srcs: [
"TranscodingClientManager.cpp"
],
shared_libs: [
"libbinder_ndk",
"libcutils",
"liblog",
"libutils",
],
export_include_dirs: ["include"],
static_libs: [
"mediatranscoding_aidl_interface-ndk_platform",
],
cflags: [
"-Werror",
"-Wno-error=deprecated-declarations",
"-Wall",
],
sanitize: {
misc_undefined: [
"unsigned-integer-overflow",
"signed-integer-overflow",
],
cfi: true,
},
}

@ -0,0 +1,146 @@
/*
* 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 <media/TranscodingClientManager.h>
#include <utils/Log.h>
namespace android {
class DeathNotifier;
using Status = ::ndk::ScopedAStatus;
// static
sp<TranscodingClientManager> TranscodingClientManager::getInstance() {
static sp<TranscodingClientManager> sInstance = new TranscodingClientManager();
return sInstance;
}
TranscodingClientManager::TranscodingClientManager()
: mDeathRecipient(AIBinder_DeathRecipient_new(
TranscodingClientManager::DeathNotifier::BinderDiedCallback)) {
ALOGD("TranscodingClientManager started");
}
TranscodingClientManager::~TranscodingClientManager() {
ALOGD("TranscodingClientManager exited");
}
bool TranscodingClientManager::isClientIdRegistered(int32_t clientId) const {
std::scoped_lock lock{mLock};
return mClientIdToClientInfoMap.find(clientId) != mClientIdToClientInfoMap.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", mClientIdToClientInfoMap.size());
result.append(buffer);
if (mClientIdToClientInfoMap.size() > 0) {
snprintf(buffer, SIZE, "========== Dumping all clients =========\n");
result.append(buffer);
}
for (const auto& iter : mClientIdToClientInfoMap) {
const std::shared_ptr<ITranscodingServiceClient> client = iter.second->mClient;
std::string clientName;
Status status = client->getName(&clientName);
if (!status.isOk()) {
ALOGE("Failed to get client: %d information", iter.first);
continue;
}
snprintf(buffer, SIZE, " -- Clients: %d name: %s\n", iter.first, clientName.c_str());
result.append(buffer);
}
write(fd, result.string(), result.size());
}
status_t TranscodingClientManager::addClient(std::unique_ptr<ClientInfo> client) {
// Validate the client.
if (client == nullptr || client->mClientId <= 0 || client->mClientPid <= 0 ||
client->mClientUid <= 0 || client->mClientOpPackageName.empty() ||
client->mClientOpPackageName == "") {
ALOGE("Invalid client");
return BAD_VALUE;
}
ALOGD("Adding client id %d %s", client->mClientId, client->mClientOpPackageName.c_str());
std::scoped_lock lock{mLock};
// Check if the client already exists.
if (mClientIdToClientInfoMap.count(client->mClientId) != 0) {
ALOGW("Client already exists.");
return ALREADY_EXISTS;
}
// Listen to the death of the client.
client->mDeathNotifier = new DeathNotifier();
AIBinder_linkToDeath(client->mClient->asBinder().get(), mDeathRecipient.get(),
client->mDeathNotifier.get());
// Adds the new client to the map.
mClientIdToClientInfoMap[client->mClientId] = std::move(client);
return OK;
}
status_t TranscodingClientManager::removeClient(int32_t clientId) {
ALOGD("Removing client id %d", clientId);
std::scoped_lock lock{mLock};
// Checks if the client is valid.
auto it = mClientIdToClientInfoMap.find(clientId);
if (it == mClientIdToClientInfoMap.end()) {
ALOGE("Client id %d does not exist", clientId);
return INVALID_OPERATION;
}
std::shared_ptr<ITranscodingServiceClient> client = it->second->mClient;
// Check if the client still live. If alive, unlink the death.
if (client) {
AIBinder_unlinkToDeath(client->asBinder().get(), mDeathRecipient.get(),
it->second->mDeathNotifier.get());
}
// Erase the entry.
mClientIdToClientInfoMap.erase(it);
return OK;
}
size_t TranscodingClientManager::getNumOfClients() const {
std::scoped_lock lock{mLock};
return mClientIdToClientInfoMap.size();
}
// static
void TranscodingClientManager::DeathNotifier::BinderDiedCallback(void* cookie) {
int32_t* pClientId = static_cast<int32_t*>(cookie);
ALOGD("Client %d is dead", *pClientId);
// Don't check for pid validity since we know it's already dead.
sp<TranscodingClientManager> manager = TranscodingClientManager::getInstance();
manager->removeClient(*pClientId);
}
} // namespace android

@ -0,0 +1,148 @@
/*
* 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.
*/
#ifndef ANDROID_MEDIA_TRANSCODING_CLIENT_MANAGER_H
#define ANDROID_MEDIA_TRANSCODING_CLIENT_MANAGER_H
#include <aidl/android/media/BnTranscodingServiceClient.h>
#include <android/binder_ibinder.h>
#include <sys/types.h>
#include <utils/Condition.h>
#include <utils/RefBase.h>
#include <utils/String8.h>
#include <utils/Vector.h>
#include <mutex>
#include <unordered_map>
namespace android {
using ::aidl::android::media::ITranscodingServiceClient;
class MediaTranscodingService;
/*
* TranscodingClientManager manages all the transcoding clients across different processes.
*
* TranscodingClientManager is a global singleton that could only acquired by
* MediaTranscodingService. It manages all the clients's registration/unregistration and clients'
* information. It also bookkeeps all the clients' information. It also monitors to the death of the
* clients. Upon client's death, it will remove the client from it.
*
* TODO(hkuang): Hook up with ResourceManager for resource management.
* TODO(hkuang): Hook up with MediaMetrics to log all the transactions.
*/
class TranscodingClientManager : public RefBase {
private:
// Forward declare it as it will be used in ClientInfo below.
class DeathNotifier;
public:
virtual ~TranscodingClientManager();
/**
* ClientInfo contains a single client's information.
*/
struct ClientInfo {
/* The remote client that this ClientInfo is associated with. */
std::shared_ptr<ITranscodingServiceClient> mClient;
/* A unique positive Id assigned to the client by the service. */
int32_t mClientId;
/* Process id of the client */
int32_t mClientPid;
/* User id of the client. */
int32_t mClientUid;
/* Package name of the client. */
std::string mClientOpPackageName;
/* Listener for the death of the client. */
sp<DeathNotifier> mDeathNotifier;
ClientInfo(const std::shared_ptr<ITranscodingServiceClient>& client, int64_t clientId,
int32_t pid, int32_t uid, const std::string& opPackageName)
: mClient(client),
mClientId(clientId),
mClientPid(pid),
mClientUid(uid),
mClientOpPackageName(opPackageName),
mDeathNotifier(nullptr) {}
};
/**
* Adds a new client to the manager.
*
* The client must have valid clientId, pid, uid and opPackageName, otherwise, this will return
* a non-zero errorcode. If the client has already been added, it will also return non-zero
* errorcode.
*
* @param client to be added to the manager.
* @return 0 if client is added successfully, non-zero errorcode otherwise.
*/
status_t addClient(std::unique_ptr<ClientInfo> client);
/**
* Removes an existing client from the manager.
*
* If the client does not exist, this will return non-zero errorcode.
*
* @param clientId id of the client to be removed..
* @return 0 if client is removed successfully, non-zero errorcode otherwise.
*/
status_t removeClient(int32_t clientId);
/**
* Gets the number of clients.
*/
size_t getNumOfClients() const;
/**
* Checks if a client with clientId is already registered.
*/
bool isClientIdRegistered(int32_t clientId) const;
/**
* Dump all the client information to the fd.
*/
void dumpAllClients(int fd, const Vector<String16>& args);
private:
friend class MediaTranscodingService;
friend class TranscodingClientManagerTest;
class DeathNotifier : public RefBase {
public:
DeathNotifier() = default;
~DeathNotifier() = default;
// Implement death recipient
static void BinderDiedCallback(void* cookie);
};
/** Get the singleton instance of the TranscodingClientManager. */
static sp<TranscodingClientManager> getInstance();
TranscodingClientManager();
mutable std::mutex mLock;
std::unordered_map<int32_t, std::unique_ptr<ClientInfo>> mClientIdToClientInfoMap
GUARDED_BY(mLock);
std::vector<sp<DeathNotifier>> mDeathNotifiers GUARDED_BY(mLock);
::ndk::ScopedAIBinder_DeathRecipient mDeathRecipient;
};
} // namespace android
#endif // ANDROID_MEDIA_TRANSCODING_SERVICE_H

@ -0,0 +1,38 @@
// Build the unit tests for libmediatranscoding.
cc_defaults {
name: "libmediatranscoding_test_defaults",
header_libs: [
"libbase_headers",
"libmedia_headers",
],
shared_libs: [
"libbinder_ndk",
"libcutils",
"liblog",
"libutils",
"libmediatranscoding"
],
static_libs: [
"mediatranscoding_aidl_interface-ndk_platform",
],
cflags: [
"-Werror",
"-Wall",
],
test_suites: ["device-tests"],
}
//
// TranscodingClientManager unit test
//
cc_test {
name: "TranscodingClientManager_tests",
defaults: ["libmediatranscoding_test_defaults"],
srcs: ["TranscodingClientManager_tests.cpp"],
}

@ -0,0 +1,285 @@
/*
* 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.
*/
// Unit Test for TranscodingClientManager
// #define LOG_NDEBUG 0
#define LOG_TAG "TranscodingClientManagerTest"
#include <aidl/android/media/BnTranscodingServiceClient.h>
#include <aidl/android/media/IMediaTranscodingService.h>
#include <aidl/android/media/ITranscodingServiceClient.h>
#include <android-base/logging.h>
#include <android/binder_manager.h>
#include <android/binder_process.h>
#include <gtest/gtest.h>
#include <media/TranscodingClientManager.h>
#include <utils/Log.h>
namespace android {
using Status = ::ndk::ScopedAStatus;
using aidl::android::media::BnTranscodingServiceClient;
using aidl::android::media::IMediaTranscodingService;
using aidl::android::media::ITranscodingServiceClient;
constexpr int32_t kInvalidClientId = -1;
constexpr int32_t kInvalidClientPid = -1;
constexpr int32_t kInvalidClientUid = -1;
constexpr const char* kInvalidClientOpPackageName = "";
constexpr int32_t kClientId = 1;
constexpr int32_t kClientPid = 2;
constexpr int32_t kClientUid = 3;
constexpr const char* kClientOpPackageName = "TestClient";
struct TestClient : public BnTranscodingServiceClient {
TestClient(const std::shared_ptr<IMediaTranscodingService>& service) : mService(service) {
ALOGD("TestClient Created");
}
Status getName(std::string* _aidl_return) override {
*_aidl_return = "test_client";
return Status::ok();
}
Status onTranscodingFinished(
int32_t /* in_jobId */,
const ::aidl::android::media::TranscodingResultParcel& /* in_result */) override {
return Status::ok();
}
Status onTranscodingFailed(
int32_t /* in_jobId */,
::aidl::android::media::TranscodingErrorCode /*in_errorCode */) override {
return Status::ok();
}
Status onAwaitNumberOfJobsChanged(int32_t /* in_jobId */, int32_t /* in_oldAwaitNumber */,
int32_t /* in_newAwaitNumber */) override {
return Status::ok();
}
Status onProgressUpdate(int32_t /* in_jobId */, int32_t /* in_progress */) override {
return Status::ok();
}
virtual ~TestClient() { ALOGI("TestClient destroyed"); };
private:
std::shared_ptr<IMediaTranscodingService> mService;
TestClient(const TestClient&) = delete;
TestClient& operator=(const TestClient&) = delete;
};
class TranscodingClientManagerTest : public ::testing::Test {
public:
TranscodingClientManagerTest() { ALOGD("TranscodingClientManagerTest created"); }
void SetUp() override {
mClientManager = TranscodingClientManager::getInstance();
if (mClientManager == nullptr) {
ALOGE("Failed to acquire TranscodingClientManager.");
return;
}
::ndk::SpAIBinder binder(AServiceManager_getService("media.transcoding"));
mService = IMediaTranscodingService::fromBinder(binder);
if (mService == nullptr) {
ALOGE("Failed to connect to the media.trascoding service.");
return;
}
mTestClient = ::ndk::SharedRefBase::make<TestClient>(mService);
}
void TearDown() override {
ALOGI("TranscodingClientManagerTest tear down");
mClientManager = nullptr;
mService = nullptr;
}
~TranscodingClientManagerTest() { ALOGD("TranscodingClientManagerTest destroyed"); }
sp<TranscodingClientManager> mClientManager = nullptr;
std::shared_ptr<ITranscodingServiceClient> mTestClient = nullptr;
std::shared_ptr<IMediaTranscodingService> mService = nullptr;
};
TEST_F(TranscodingClientManagerTest, TestAddingWithInvalidClientId) {
std::shared_ptr<ITranscodingServiceClient> client =
::ndk::SharedRefBase::make<TestClient>(mService);
// Create a client with invalid client id.
std::unique_ptr<TranscodingClientManager::ClientInfo> clientInfo =
std::make_unique<TranscodingClientManager::ClientInfo>(
client, kInvalidClientId, kClientPid, kClientUid, kClientOpPackageName);
// Add the client to the manager and expect failure.
status_t err = mClientManager->addClient(std::move(clientInfo));
EXPECT_TRUE(err != OK);
}
TEST_F(TranscodingClientManagerTest, TestAddingWithInvalidClientPid) {
std::shared_ptr<ITranscodingServiceClient> client =
::ndk::SharedRefBase::make<TestClient>(mService);
// Create a client with invalid Pid.
std::unique_ptr<TranscodingClientManager::ClientInfo> clientInfo =
std::make_unique<TranscodingClientManager::ClientInfo>(
client, kClientId, kInvalidClientPid, kClientUid, kClientOpPackageName);
// Add the client to the manager and expect failure.
status_t err = mClientManager->addClient(std::move(clientInfo));
EXPECT_TRUE(err != OK);
}
TEST_F(TranscodingClientManagerTest, TestAddingWithInvalidClientUid) {
std::shared_ptr<ITranscodingServiceClient> client =
::ndk::SharedRefBase::make<TestClient>(mService);
// Create a client with invalid Uid.
std::unique_ptr<TranscodingClientManager::ClientInfo> clientInfo =
std::make_unique<TranscodingClientManager::ClientInfo>(
client, kClientId, kClientPid, kInvalidClientUid, kClientOpPackageName);
// Add the client to the manager and expect failure.
status_t err = mClientManager->addClient(std::move(clientInfo));
EXPECT_TRUE(err != OK);
}
TEST_F(TranscodingClientManagerTest, TestAddingWithInvalidClientPackageName) {
std::shared_ptr<ITranscodingServiceClient> client =
::ndk::SharedRefBase::make<TestClient>(mService);
// Create a client with invalid packagename.
std::unique_ptr<TranscodingClientManager::ClientInfo> clientInfo =
std::make_unique<TranscodingClientManager::ClientInfo>(
client, kClientId, kClientPid, kClientUid, kInvalidClientOpPackageName);
// Add the client to the manager and expect failure.
status_t err = mClientManager->addClient(std::move(clientInfo));
EXPECT_TRUE(err != OK);
}
TEST_F(TranscodingClientManagerTest, TestAddingValidClient) {
std::shared_ptr<ITranscodingServiceClient> client1 =
::ndk::SharedRefBase::make<TestClient>(mService);
std::unique_ptr<TranscodingClientManager::ClientInfo> clientInfo =
std::make_unique<TranscodingClientManager::ClientInfo>(
client1, kClientId, kClientPid, kClientUid, kClientOpPackageName);
status_t err = mClientManager->addClient(std::move(clientInfo));
EXPECT_TRUE(err == OK);
size_t numOfClients = mClientManager->getNumOfClients();
EXPECT_EQ(numOfClients, 1);
err = mClientManager->removeClient(kClientId);
EXPECT_TRUE(err == OK);
}
TEST_F(TranscodingClientManagerTest, TestAddingDupliacteClient) {
std::shared_ptr<ITranscodingServiceClient> client1 =
::ndk::SharedRefBase::make<TestClient>(mService);
std::unique_ptr<TranscodingClientManager::ClientInfo> clientInfo =
std::make_unique<TranscodingClientManager::ClientInfo>(
client1, kClientId, kClientPid, kClientUid, kClientOpPackageName);
status_t err = mClientManager->addClient(std::move(clientInfo));
EXPECT_TRUE(err == OK);
err = mClientManager->addClient(std::move(clientInfo));
EXPECT_TRUE(err != OK);
err = mClientManager->removeClient(kClientId);
EXPECT_TRUE(err == OK);
}
TEST_F(TranscodingClientManagerTest, TestAddingMultipleClient) {
std::shared_ptr<ITranscodingServiceClient> client1 =
::ndk::SharedRefBase::make<TestClient>(mService);
std::unique_ptr<TranscodingClientManager::ClientInfo> clientInfo1 =
std::make_unique<TranscodingClientManager::ClientInfo>(
client1, kClientId, kClientPid, kClientUid, kClientOpPackageName);
status_t err = mClientManager->addClient(std::move(clientInfo1));
EXPECT_TRUE(err == OK);
std::shared_ptr<ITranscodingServiceClient> client2 =
::ndk::SharedRefBase::make<TestClient>(mService);
std::unique_ptr<TranscodingClientManager::ClientInfo> clientInfo2 =
std::make_unique<TranscodingClientManager::ClientInfo>(
client2, kClientId + 1, kClientPid, kClientUid, kClientOpPackageName);
err = mClientManager->addClient(std::move(clientInfo2));
EXPECT_TRUE(err == OK);
std::shared_ptr<ITranscodingServiceClient> client3 =
::ndk::SharedRefBase::make<TestClient>(mService);
// Create a client with invalid packagename.
std::unique_ptr<TranscodingClientManager::ClientInfo> clientInfo3 =
std::make_unique<TranscodingClientManager::ClientInfo>(
client3, kClientId + 2, kClientPid, kClientUid, kClientOpPackageName);
err = mClientManager->addClient(std::move(clientInfo3));
EXPECT_TRUE(err == OK);
size_t numOfClients = mClientManager->getNumOfClients();
EXPECT_EQ(numOfClients, 3);
err = mClientManager->removeClient(kClientId);
EXPECT_TRUE(err == OK);
err = mClientManager->removeClient(kClientId + 1);
EXPECT_TRUE(err == OK);
err = mClientManager->removeClient(kClientId + 2);
EXPECT_TRUE(err == OK);
}
TEST_F(TranscodingClientManagerTest, TestRemovingNonExistClient) {
status_t err = mClientManager->removeClient(kInvalidClientId);
EXPECT_TRUE(err != OK);
err = mClientManager->removeClient(1000 /* clientId */);
EXPECT_TRUE(err != OK);
}
TEST_F(TranscodingClientManagerTest, TestCheckClientWithClientId) {
std::shared_ptr<ITranscodingServiceClient> client =
::ndk::SharedRefBase::make<TestClient>(mService);
std::unique_ptr<TranscodingClientManager::ClientInfo> clientInfo =
std::make_unique<TranscodingClientManager::ClientInfo>(
client, kClientId, kClientPid, kClientUid, kClientOpPackageName);
status_t err = mClientManager->addClient(std::move(clientInfo));
EXPECT_TRUE(err == OK);
bool res = mClientManager->isClientIdRegistered(kClientId);
EXPECT_TRUE(res);
res = mClientManager->isClientIdRegistered(kInvalidClientId);
EXPECT_FALSE(res);
}
} // namespace android

@ -0,0 +1,23 @@
#!/bin/bash
#
# Run tests in this directory.
#
if [ -z "$ANDROID_BUILD_TOP" ]; then
echo "Android build environment not set"
exit -1
fi
# ensure we have mm
. $ANDROID_BUILD_TOP/build/envsetup.sh
mm
echo "waiting for device"
adb root && adb wait-for-device remount && adb sync
echo "========================================"
echo "testing TranscodingClientManager"
adb shell /data/nativetest64/TranscodingClientManager_tests/TranscodingClientManager_tests
Loading…
Cancel
Save