From 26587cbbaad5057e43f6648293fbe5a546fc809c Mon Sep 17 00:00:00 2001 From: hkuang Date: Thu, 16 Jan 2020 10:36:08 -0800 Subject: [PATCH] MediaTranscoding: Add TranscodingClientManager. TranscodingClientManager manages all the clients for MediaTranscodingService. Bug: 145233472 Test: Unit test. Change-Id: I29243eeb6dcc0271c9edc8cc28e1b9b2bf6b3912 --- media/libmediatranscoding/.clang-format | 29 ++ media/libmediatranscoding/Android.bp | 51 ++++ .../TranscodingClientManager.cpp | 146 +++++++++ .../include/media/TranscodingClientManager.h | 148 +++++++++ media/libmediatranscoding/tests/Android.bp | 38 +++ .../tests/TranscodingClientManager_tests.cpp | 285 ++++++++++++++++++ .../tests/build_and_run_all_unit_tests.sh | 23 ++ 7 files changed, 720 insertions(+) create mode 100644 media/libmediatranscoding/.clang-format create mode 100644 media/libmediatranscoding/TranscodingClientManager.cpp create mode 100644 media/libmediatranscoding/include/media/TranscodingClientManager.h create mode 100644 media/libmediatranscoding/tests/Android.bp create mode 100644 media/libmediatranscoding/tests/TranscodingClientManager_tests.cpp create mode 100644 media/libmediatranscoding/tests/build_and_run_all_unit_tests.sh diff --git a/media/libmediatranscoding/.clang-format b/media/libmediatranscoding/.clang-format new file mode 100644 index 0000000000..3198d009ba --- /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 9d0c5341b2..7468426f2c 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_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, + }, +} diff --git a/media/libmediatranscoding/TranscodingClientManager.cpp b/media/libmediatranscoding/TranscodingClientManager.cpp new file mode 100644 index 0000000000..5013a51526 --- /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 +#include + +namespace android { + +class DeathNotifier; +using Status = ::ndk::ScopedAStatus; + +// static +sp TranscodingClientManager::getInstance() { + static sp 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& 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 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 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 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(cookie); + ALOGD("Client %d is dead", *pClientId); + // Don't check for pid validity since we know it's already dead. + sp 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 0000000000..dbf837c2fe --- /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 +#include +#include +#include +#include +#include +#include + +#include +#include + +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 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 mDeathNotifier; + + ClientInfo(const std::shared_ptr& 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 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& 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 getInstance(); + + TranscodingClientManager(); + + mutable std::mutex mLock; + std::unordered_map> mClientIdToClientInfoMap + GUARDED_BY(mLock); + + std::vector> 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 0000000000..f3cc4c5b23 --- /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 0000000000..97c89190e9 --- /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 +#include +#include +#include +#include +#include +#include +#include +#include + +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& 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 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(mService); + } + + void TearDown() override { + ALOGI("TranscodingClientManagerTest tear down"); + mClientManager = nullptr; + mService = nullptr; + } + + ~TranscodingClientManagerTest() { ALOGD("TranscodingClientManagerTest destroyed"); } + + sp mClientManager = nullptr; + std::shared_ptr mTestClient = nullptr; + std::shared_ptr mService = nullptr; +}; + +TEST_F(TranscodingClientManagerTest, TestAddingWithInvalidClientId) { + std::shared_ptr client = + ::ndk::SharedRefBase::make(mService); + + // Create a client with invalid client id. + std::unique_ptr clientInfo = + std::make_unique( + 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 client = + ::ndk::SharedRefBase::make(mService); + + // Create a client with invalid Pid. + std::unique_ptr clientInfo = + std::make_unique( + 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 client = + ::ndk::SharedRefBase::make(mService); + + // Create a client with invalid Uid. + std::unique_ptr clientInfo = + std::make_unique( + 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 client = + ::ndk::SharedRefBase::make(mService); + + // Create a client with invalid packagename. + std::unique_ptr clientInfo = + std::make_unique( + 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 client1 = + ::ndk::SharedRefBase::make(mService); + + std::unique_ptr clientInfo = + std::make_unique( + 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 client1 = + ::ndk::SharedRefBase::make(mService); + + std::unique_ptr clientInfo = + std::make_unique( + 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 client1 = + ::ndk::SharedRefBase::make(mService); + + std::unique_ptr clientInfo1 = + std::make_unique( + client1, kClientId, kClientPid, kClientUid, kClientOpPackageName); + + status_t err = mClientManager->addClient(std::move(clientInfo1)); + EXPECT_TRUE(err == OK); + + std::shared_ptr client2 = + ::ndk::SharedRefBase::make(mService); + + std::unique_ptr clientInfo2 = + std::make_unique( + client2, kClientId + 1, kClientPid, kClientUid, kClientOpPackageName); + + err = mClientManager->addClient(std::move(clientInfo2)); + EXPECT_TRUE(err == OK); + + std::shared_ptr client3 = + ::ndk::SharedRefBase::make(mService); + + // Create a client with invalid packagename. + std::unique_ptr clientInfo3 = + std::make_unique( + 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 client = + ::ndk::SharedRefBase::make(mService); + + std::unique_ptr clientInfo = + std::make_unique( + 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 0000000000..98326961f8 --- /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