MediaTranscoding: Add TranscodingClientManager.
TranscodingClientManager manages all the clients for
MediaTranscodingService.
Bug: 145233472
Test: Unit test.
Change-Id: I29243eeb6dcc0271c9edc8cc28e1b9b2bf6b3912
diff --git a/media/libmediatranscoding/.clang-format b/media/libmediatranscoding/.clang-format
new file mode 100644
index 0000000..3198d00
--- /dev/null
+++ b/media/libmediatranscoding/.clang-format
@@ -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
\ No newline at end of file
diff --git a/media/libmediatranscoding/Android.bp b/media/libmediatranscoding/Android.bp
index 9d0c534..7468426 100644
--- a/media/libmediatranscoding/Android.bp
+++ b/media/libmediatranscoding/Android.bp
@@ -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/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,
+ },
+}
diff --git a/media/libmediatranscoding/TranscodingClientManager.cpp b/media/libmediatranscoding/TranscodingClientManager.cpp
new file mode 100644
index 0000000..5013a51
--- /dev/null
+++ b/media/libmediatranscoding/TranscodingClientManager.cpp
@@ -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
diff --git a/media/libmediatranscoding/include/media/TranscodingClientManager.h b/media/libmediatranscoding/include/media/TranscodingClientManager.h
new file mode 100644
index 0000000..dbf837c
--- /dev/null
+++ b/media/libmediatranscoding/include/media/TranscodingClientManager.h
@@ -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
diff --git a/media/libmediatranscoding/tests/Android.bp b/media/libmediatranscoding/tests/Android.bp
new file mode 100644
index 0000000..f3cc4c5
--- /dev/null
+++ b/media/libmediatranscoding/tests/Android.bp
@@ -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"],
+}
\ No newline at end of file
diff --git a/media/libmediatranscoding/tests/TranscodingClientManager_tests.cpp b/media/libmediatranscoding/tests/TranscodingClientManager_tests.cpp
new file mode 100644
index 0000000..97c8919
--- /dev/null
+++ b/media/libmediatranscoding/tests/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
\ No newline at end of file
diff --git a/media/libmediatranscoding/tests/build_and_run_all_unit_tests.sh b/media/libmediatranscoding/tests/build_and_run_all_unit_tests.sh
new file mode 100644
index 0000000..9832696
--- /dev/null
+++ b/media/libmediatranscoding/tests/build_and_run_all_unit_tests.sh
@@ -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