Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 26587cbb authored by hkuang's avatar hkuang
Browse files

MediaTranscoding: Add TranscodingClientManager.

TranscodingClientManager manages all the clients for
MediaTranscodingService.

Bug: 145233472
Test: Unit test.
Change-Id: I29243eeb6dcc0271c9edc8cc28e1b9b2bf6b3912
parent 48c365e0
Loading
Loading
Loading
Loading
+29 −0
Original line number Diff line number Diff line
#
# 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
+51 −0
Original line number Diff line number Diff line
/*
 * 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,
    },
}
+146 −0
Original line number Diff line number Diff line
/*
 * 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
+148 −0
Original line number Diff line number Diff line
/*
 * 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
+38 −0
Original line number Diff line number Diff line
// 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
Loading