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

Commit 6134b84b authored by Chong Zhang's avatar Chong Zhang Committed by Android (Google) Code Review
Browse files

Merge "transcoding: handle multiple uids in service" into sc-dev

parents 78282f4a ebd86d38
Loading
Loading
Loading
Loading
+61 −0
Original line number Diff line number Diff line
@@ -94,6 +94,12 @@ struct TranscodingClientManager::ClientImpl : public BnTranscodingClient {
    Status getSessionWithId(int32_t /*in_sessionId*/, TranscodingSessionParcel* /*out_session*/,
                            bool* /*_aidl_return*/) override;

    Status addClientUid(int32_t /*in_sessionId*/, int32_t /*in_clientUid*/,
                        bool* /*_aidl_return*/) override;

    Status getClientUids(int32_t /*in_sessionId*/, std::vector<int32_t>* /*out_clientUids*/,
                         bool* /*_aidl_return*/) override;

    Status unregister() override;
};

@@ -217,6 +223,61 @@ Status TranscodingClientManager::ClientImpl::getSessionWithId(int32_t in_session
    return Status::ok();
}

Status TranscodingClientManager::ClientImpl::addClientUid(int32_t in_sessionId,
                                                          int32_t in_clientUid,
                                                          bool* _aidl_return) {
    *_aidl_return = false;

    std::shared_ptr<TranscodingClientManager> owner;
    if (mAbandoned || (owner = mOwner.lock()) == nullptr) {
        return Status::fromServiceSpecificError(IMediaTranscodingService::ERROR_DISCONNECTED);
    }

    if (in_sessionId < 0) {
        return Status::ok();
    }

    int32_t callingPid = AIBinder_getCallingPid();
    int32_t callingUid = AIBinder_getCallingUid();

    // Check if we can trust clientUid. Only privilege caller could add uid to existing sessions.
    if (in_clientUid == IMediaTranscodingService::USE_CALLING_UID) {
        in_clientUid = callingUid;
    } else if (in_clientUid < 0) {
        return Status::ok();
    } else if (in_clientUid != callingUid && !owner->isTrustedCaller(callingPid, callingUid)) {
        ALOGE("addClientUid rejected (clientUid %d) "
              "(don't trust callingUid %d)",
              in_clientUid, callingUid);
        return STATUS_ERROR_FMT(IMediaTranscodingService::ERROR_PERMISSION_DENIED,
                                "addClientUid rejected (clientUid %d) "
                                "(don't trust callingUid %d)",
                                in_clientUid, callingUid);
    }

    *_aidl_return = owner->mSessionController->addClientUid(mClientId, in_sessionId, in_clientUid);
    return Status::ok();
}

Status TranscodingClientManager::ClientImpl::getClientUids(int32_t in_sessionId,
                                                           std::vector<int32_t>* out_clientUids,
                                                           bool* _aidl_return) {
    *_aidl_return = false;

    std::shared_ptr<TranscodingClientManager> owner;
    if (mAbandoned || (owner = mOwner.lock()) == nullptr) {
        return Status::fromServiceSpecificError(IMediaTranscodingService::ERROR_DISCONNECTED);
    }

    if (in_sessionId < 0) {
        return Status::ok();
    }

    *_aidl_return =
            owner->mSessionController->getClientUids(mClientId, in_sessionId, out_clientUids);
    return Status::ok();
}

Status TranscodingClientManager::ClientImpl::unregister() {
    bool abandoned = mAbandoned.exchange(true);

+179 −68
Original line number Diff line number Diff line
@@ -193,8 +193,8 @@ struct TranscodingSessionController::Pacer {

    ~Pacer() = default;

    void onSessionCompleted(uid_t uid, std::chrono::microseconds runningTime);
    bool onSessionStarted(uid_t uid);
    void onSessionCompleted(uid_t uid, std::chrono::microseconds runningTime);

private:
    // Threshold of time between finish/start below which a back-to-back start is counted.
@@ -205,26 +205,20 @@ private:
    int32_t mBurstTimeQuotaSec;

    struct UidHistoryEntry {
        std::chrono::steady_clock::time_point lastCompletedTime;
        bool sessionActive = false;
        int32_t burstCount = 0;
        std::chrono::steady_clock::duration burstDuration{0};
        std::chrono::steady_clock::time_point lastCompletedTime;
    };
    std::map<uid_t, UidHistoryEntry> mUidHistoryMap;
};

void TranscodingSessionController::Pacer::onSessionCompleted(
        uid_t uid, std::chrono::microseconds runningTime) {
    if (mUidHistoryMap.find(uid) == mUidHistoryMap.end()) {
        mUidHistoryMap.emplace(uid, UidHistoryEntry{});
    }
    mUidHistoryMap[uid].lastCompletedTime = std::chrono::steady_clock::now();
    mUidHistoryMap[uid].burstCount++;
    mUidHistoryMap[uid].burstDuration += runningTime;
}

bool TranscodingSessionController::Pacer::onSessionStarted(uid_t uid) {
    // If uid doesn't exist, this uid has no completed sessions. Skip.
    // If uid doesn't exist, only insert the entry and mark session active. Skip quota checking.
    if (mUidHistoryMap.find(uid) == mUidHistoryMap.end()) {
        mUidHistoryMap.emplace(uid, UidHistoryEntry{});
        mUidHistoryMap[uid].sessionActive = true;
        ALOGV("Pacer::onSessionStarted: uid %d: new", uid);
        return true;
    }

@@ -236,25 +230,43 @@ bool TranscodingSessionController::Pacer::onSessionStarted(uid_t uid) {
            std::chrono::steady_clock::now() - mUidHistoryMap[uid].lastCompletedTime;
    if (mUidHistoryMap[uid].burstCount >= mBurstCountQuota &&
        mUidHistoryMap[uid].burstDuration >= std::chrono::seconds(mBurstTimeQuotaSec)) {
        ALOGW("Pacer: uid %d: over quota, burst count %d, time %lldms", uid,
              mUidHistoryMap[uid].burstCount, (long long)mUidHistoryMap[uid].burstDuration.count());
        ALOGW("Pacer::onSessionStarted: uid %d: over quota, burst count %d, time %lldms", uid,
              mUidHistoryMap[uid].burstCount,
              (long long)mUidHistoryMap[uid].burstDuration.count() / 1000000);
        return false;
    }

    // If not over quota, allow the session, and reset as long as this is not too close
    // to previous completion.
    if (timeSinceLastComplete > std::chrono::milliseconds(mBurstThresholdMs)) {
        ALOGV("Pacer: uid %d: reset quota", uid);
        ALOGV("Pacer::onSessionStarted: uid %d: reset quota", uid);
        mUidHistoryMap[uid].burstCount = 0;
        mUidHistoryMap[uid].burstDuration = std::chrono::milliseconds(0);
    } else {
        ALOGV("Pacer: uid %d: burst count %d, time %lldms", uid, mUidHistoryMap[uid].burstCount,
              (long long)mUidHistoryMap[uid].burstDuration.count());
        ALOGV("Pacer::onSessionStarted: uid %d: burst count %d, time %lldms", uid,
              mUidHistoryMap[uid].burstCount,
              (long long)mUidHistoryMap[uid].burstDuration.count() / 1000000);
    }

    mUidHistoryMap[uid].sessionActive = true;
    return true;
}

void TranscodingSessionController::Pacer::onSessionCompleted(
        uid_t uid, std::chrono::microseconds runningTime) {
    // Skip quota update if this uid missed the start. (Could happen if the uid is added via
    // addClientUid() after the session start.)
    if (mUidHistoryMap.find(uid) == mUidHistoryMap.end() || !mUidHistoryMap[uid].sessionActive) {
        ALOGV("Pacer::onSessionCompleted: uid %d: not started", uid);
        return;
    }
    ALOGV("Pacer::onSessionCompleted: uid %d: runningTime %lld", uid, runningTime.count() / 1000);
    mUidHistoryMap[uid].sessionActive = false;
    mUidHistoryMap[uid].burstCount++;
    mUidHistoryMap[uid].burstDuration += runningTime;
    mUidHistoryMap[uid].lastCompletedTime = std::chrono::steady_clock::now();
}

///////////////////////////////////////////////////////////////////////////////

TranscodingSessionController::TranscodingSessionController(
@@ -372,6 +384,14 @@ TranscodingSessionController::Session* TranscodingSessionController::getTopSessi
    }

    uid_t topUid = *mUidSortedList.begin();
    // If the current session is running, and it's in the topUid's queue, let it continue
    // to run even if it's not the earliest in that uid's queue.
    // For example, uid(B) is added to a session while it's pending in uid(A)'s queue, then
    // B is brought to front which caused the session to run, then user switches back to A.
    if (mCurrentSession != nullptr && mCurrentSession->getState() == Session::RUNNING &&
        mCurrentSession->allClientUids.count(topUid) > 0) {
        return mCurrentSession;
    }
    SessionKeyType topSessionKey = *mSessionQueues[topUid].begin();
    return &mSessionMap[topSessionKey];
}
@@ -427,7 +447,7 @@ void TranscodingSessionController::Session::setState(Session::State newState) {

void TranscodingSessionController::updateCurrentSession_l() {
    Session* curSession = mCurrentSession;
    Session* topSession = getTopSession_l();
    Session* topSession = nullptr;

    // Delayed init of transcoder and watchdog.
    if (mTranscoder == nullptr) {
@@ -458,9 +478,18 @@ void TranscodingSessionController::updateCurrentSession_l() {

        // Otherwise, ensure topSession is running.
        if (topSession->getState() == Session::NOT_STARTED) {
            if (!mPacer->onSessionStarted(topSession->clientUid)) {
                // Unfortunately this uid is out of quota for new sessions.
                // Drop this sesion and try another one.
            // Check if at least one client has quota to start the session.
            bool keepForClient = false;
            for (uid_t uid : topSession->allClientUids) {
                if (mPacer->onSessionStarted(uid)) {
                    keepForClient = true;
                    // DO NOT break here, because book-keeping still needs to happen
                    // for the other uids.
                }
            }
            if (!keepForClient) {
                // Unfortunately all uids requesting this session are out of quota.
                // Drop this session and try the next one.
                {
                    auto clientCallback = mSessionMap[topSession->key].callback.lock();
                    if (clientCallback != nullptr) {
@@ -484,8 +513,34 @@ void TranscodingSessionController::updateCurrentSession_l() {
    mCurrentSession = topSession;
}

void TranscodingSessionController::addUidToSession_l(uid_t clientUid,
                                                     const SessionKeyType& sessionKey) {
    // If it's an offline session, the queue was already added in constructor.
    // If it's a real-time sessions, check if a queue is already present for the uid,
    // and add a new queue if needed.
    if (clientUid != OFFLINE_UID) {
        if (mSessionQueues.count(clientUid) == 0) {
            mUidPolicy->registerMonitorUid(clientUid);
            if (mUidPolicy->isUidOnTop(clientUid)) {
                mUidSortedList.push_front(clientUid);
            } else {
                // Shouldn't be submitting real-time requests from non-top app,
                // put it in front of the offline queue.
                mUidSortedList.insert(mOfflineUidIterator, clientUid);
            }
        } else if (clientUid != *mUidSortedList.begin()) {
            if (mUidPolicy->isUidOnTop(clientUid)) {
                mUidSortedList.remove(clientUid);
                mUidSortedList.push_front(clientUid);
            }
        }
    }
    // Append this session to the uid's queue.
    mSessionQueues[clientUid].push_back(sessionKey);
}

void TranscodingSessionController::removeSession_l(const SessionKeyType& sessionKey,
                                                   Session::State finalState) {
                                                   Session::State finalState, bool keepForOffline) {
    ALOGV("%s: session %s", __FUNCTION__, sessionToString(sessionKey).c_str());

    if (mSessionMap.count(sessionKey) == 0) {
@@ -494,13 +549,17 @@ void TranscodingSessionController::removeSession_l(const SessionKeyType& session
    }

    // Remove session from uid's queue.
    const uid_t uid = mSessionMap[sessionKey].clientUid;
    bool uidQueueRemoved = false;
    for (uid_t uid : mSessionMap[sessionKey].allClientUids) {
        if (keepForOffline && uid == OFFLINE_UID) {
            continue;
        }
        SessionQueueType& sessionQueue = mSessionQueues[uid];
        auto it = std::find(sessionQueue.begin(), sessionQueue.end(), sessionKey);
        if (it == sessionQueue.end()) {
        ALOGE("couldn't find session %s in queue for uid %d", sessionToString(sessionKey).c_str(),
              uid);
        return;
            ALOGW("couldn't find session %s in queue for uid %d",
                  sessionToString(sessionKey).c_str(), uid);
            continue;
        }
        sessionQueue.erase(it);

@@ -510,10 +569,20 @@ void TranscodingSessionController::removeSession_l(const SessionKeyType& session
            mSessionQueues.erase(uid);
            mUidPolicy->unregisterMonitorUid(uid);

            uidQueueRemoved = true;
        }
    }

    if (uidQueueRemoved) {
        std::unordered_set<uid_t> topUids = mUidPolicy->getTopUids();
        moveUidsToTop_l(topUids, false /*preserveTopUid*/);
    }

    if (keepForOffline) {
        mSessionMap[sessionKey].allClientUids = {OFFLINE_UID};
        return;
    }

    // Clear current session.
    if (mCurrentSession == &mSessionMap[sessionKey]) {
        mCurrentSession = nullptr;
@@ -522,8 +591,9 @@ void TranscodingSessionController::removeSession_l(const SessionKeyType& session
    setSessionState_l(&mSessionMap[sessionKey], finalState);

    if (finalState == Session::FINISHED || finalState == Session::ERROR) {
        mPacer->onSessionCompleted(mSessionMap[sessionKey].clientUid,
                                   mSessionMap[sessionKey].runningTime);
        for (uid_t uid : mSessionMap[sessionKey].allClientUids) {
            mPacer->onSessionCompleted(uid, mSessionMap[sessionKey].runningTime);
        }
    }

    mSessionHistory.push_back(mSessionMap[sessionKey]);
@@ -617,34 +687,13 @@ bool TranscodingSessionController::submit(

    // Add session to session map.
    mSessionMap[sessionKey].key = sessionKey;
    mSessionMap[sessionKey].clientUid = clientUid;
    mSessionMap[sessionKey].callingUid = callingUid;
    mSessionMap[sessionKey].allClientUids.insert(clientUid);
    mSessionMap[sessionKey].request = request;
    mSessionMap[sessionKey].callback = callback;
    setSessionState_l(&mSessionMap[sessionKey], Session::NOT_STARTED);

    // If it's an offline session, the queue was already added in constructor.
    // If it's a real-time sessions, check if a queue is already present for the uid,
    // and add a new queue if needed.
    if (clientUid != OFFLINE_UID) {
        if (mSessionQueues.count(clientUid) == 0) {
            mUidPolicy->registerMonitorUid(clientUid);
            if (mUidPolicy->isUidOnTop(clientUid)) {
                mUidSortedList.push_front(clientUid);
            } else {
                // Shouldn't be submitting real-time requests from non-top app,
                // put it in front of the offline queue.
                mUidSortedList.insert(mOfflineUidIterator, clientUid);
            }
        } else if (clientUid != *mUidSortedList.begin()) {
            if (mUidPolicy->isUidOnTop(clientUid)) {
                mUidSortedList.remove(clientUid);
                mUidSortedList.push_front(clientUid);
            }
        }
    }
    // Append this session to the uid's queue.
    mSessionQueues[clientUid].push_back(sessionKey);
    addUidToSession_l(clientUid, sessionKey);

    updateCurrentSession_l();

@@ -657,16 +706,22 @@ bool TranscodingSessionController::cancel(ClientIdType clientId, SessionIdType s

    ALOGV("%s: session %s", __FUNCTION__, sessionToString(sessionKey).c_str());

    std::list<SessionKeyType> sessionsToRemove;
    std::list<SessionKeyType> sessionsToRemove, sessionsForOffline;

    std::scoped_lock lock{mLock};

    if (sessionId < 0) {
        for (auto it = mSessionMap.begin(); it != mSessionMap.end(); ++it) {
            if (it->first.first == clientId && it->second.clientUid != OFFLINE_UID) {
            if (it->first.first == clientId) {
                // If there is offline request, only keep the offline client;
                // otherwise remove the session.
                if (it->second.allClientUids.count(OFFLINE_UID) > 0) {
                    sessionsForOffline.push_back(it->first);
                } else {
                    sessionsToRemove.push_back(it->first);
                }
            }
        }
    } else {
        if (mSessionMap.count(sessionKey) == 0) {
            ALOGE("session %s doesn't exist", sessionToString(sessionKey).c_str());
@@ -688,6 +743,10 @@ bool TranscodingSessionController::cancel(ClientIdType clientId, SessionIdType s
        removeSession_l(*it, Session::CANCELED);
    }

    for (auto it = sessionsForOffline.begin(); it != sessionsForOffline.end(); ++it) {
        removeSession_l(*it, Session::CANCELED, true /*keepForOffline*/);
    }

    // Start next session.
    updateCurrentSession_l();

@@ -695,6 +754,51 @@ bool TranscodingSessionController::cancel(ClientIdType clientId, SessionIdType s
    return true;
}

bool TranscodingSessionController::addClientUid(ClientIdType clientId, SessionIdType sessionId,
                                                uid_t clientUid) {
    SessionKeyType sessionKey = std::make_pair(clientId, sessionId);

    std::scoped_lock lock{mLock};

    if (mSessionMap.count(sessionKey) == 0) {
        ALOGE("session %s doesn't exist", sessionToString(sessionKey).c_str());
        return false;
    }

    if (mSessionMap[sessionKey].allClientUids.count(clientUid) > 0) {
        ALOGE("session %s already has uid %d", sessionToString(sessionKey).c_str(), clientUid);
        return false;
    }

    mSessionMap[sessionKey].allClientUids.insert(clientUid);
    addUidToSession_l(clientUid, sessionKey);

    updateCurrentSession_l();

    validateState_l();
    return true;
}

bool TranscodingSessionController::getClientUids(ClientIdType clientId, SessionIdType sessionId,
                                                 std::vector<int32_t>* out_clientUids) {
    SessionKeyType sessionKey = std::make_pair(clientId, sessionId);

    std::scoped_lock lock{mLock};

    if (mSessionMap.count(sessionKey) == 0) {
        ALOGE("session %s doesn't exist", sessionToString(sessionKey).c_str());
        return false;
    }

    out_clientUids->clear();
    for (uid_t uid : mSessionMap[sessionKey].allClientUids) {
        if (uid != OFFLINE_UID) {
            out_clientUids->push_back(uid);
        }
    }
    return true;
}

bool TranscodingSessionController::getSession(ClientIdType clientId, SessionIdType sessionId,
                                              TranscodingRequestParcel* request) {
    SessionKeyType sessionKey = std::make_pair(clientId, sessionId);
@@ -938,7 +1042,8 @@ void TranscodingSessionController::validateState_l() {
    LOG_ALWAYS_FATAL_IF(*mOfflineUidIterator != OFFLINE_UID,
                        "mOfflineUidIterator not pointing to offline uid");
    LOG_ALWAYS_FATAL_IF(mUidSortedList.size() != mSessionQueues.size(),
                        "mUidList and mSessionQueues size mismatch");
                        "mUidSortedList and mSessionQueues size mismatch, %zu vs %zu",
                        mUidSortedList.size(), mSessionQueues.size());

    int32_t totalSessions = 0;
    for (auto uid : mUidSortedList) {
@@ -952,8 +1057,14 @@ void TranscodingSessionController::validateState_l() {

        totalSessions += mSessionQueues[uid].size();
    }
    LOG_ALWAYS_FATAL_IF(mSessionMap.size() != totalSessions,
                        "mSessions size doesn't match total sessions counted from uid queues");
    int32_t totalSessionsAlternative = 0;
    for (auto const& s : mSessionMap) {
        totalSessionsAlternative += s.second.allClientUids.size();
    }
    LOG_ALWAYS_FATAL_IF(totalSessions != totalSessionsAlternative,
                        "session count (including dup) from mSessionQueues doesn't match that from "
                        "mSessionMap, %d vs %d",
                        totalSessions, totalSessionsAlternative);
#endif  // VALIDATE_STATE
}

+25 −0
Original line number Diff line number Diff line
@@ -54,6 +54,31 @@ interface ITranscodingClient {
     */
    boolean getSessionWithId(in int sessionId, out TranscodingSessionParcel session);

    /**
     * Add an additional client uid requesting a session.
     *
     * @sessionId the session id to which to add the additional client uid.
     * @clientUid the additional client uid to be added.
     * @return false if the session doesn't exist or the client is already requesting the
     * session, true otherwise.
     */
    boolean addClientUid(in int sessionId, int clientUid);

    /**
     * Retrieves the (unsorted) list of all clients requesting a session.
     *
     * Note that if a session was submitted with offline priority (
     * TranscodingSessionPriority::kUnspecified), it initially will not be considered requested
     * by any particular client, because the client could go away any time after the submission.
     * However, additional uids could be added via addClientUid() after the submission, which
     * essentially make the request a real-time request instead of an offline request.
     *
     * @sessionId the session id for which to retrieve the client uid list.
     * @clientUids array to hold the retrieved client uid list.
     * @return false if the session doesn't exist, true otherwise.
     */
    boolean getClientUids(in int sessionId, out int[] clientUids);

    /**
    * Unregister the client with the MediaTranscodingService.
    *
+23 −0
Original line number Diff line number Diff line
@@ -60,6 +60,29 @@ public:
    virtual bool getSession(ClientIdType clientId, SessionIdType sessionId,
                            TranscodingRequestParcel* request) = 0;

    /**
     * Add an additional client uid requesting the session identified by <clientId, sessionId>.
     *
     * Returns false if the session doesn't exist, or the client is already requesting the
     * session. Returns true otherwise.
     */
    virtual bool addClientUid(ClientIdType clientId, SessionIdType sessionId, uid_t clientUid);

    /**
     * Retrieves the (unsorted) list of all clients requesting the session identified by
     * <clientId, sessionId>.
     *
     * Note that if a session was submitted with offline priority (
     * TranscodingSessionPriority::kUnspecified), it initially will not be considered requested
     * by any particular client, because the client could go away any time after the submission.
     * However, additional uids could be added via addClientUid() after the submission, which
     * essentially make the request a real-time request instead of an offline request.
     *
     * Returns false if the session doesn't exist. Returns true otherwise.
     */
    virtual bool getClientUids(ClientIdType clientId, SessionIdType sessionId,
                               std::vector<int32_t>* out_clientUids);

protected:
    virtual ~ControllerClientInterface() = default;
};
+7 −2
Original line number Diff line number Diff line
@@ -54,6 +54,9 @@ public:
    bool cancel(ClientIdType clientId, SessionIdType sessionId) override;
    bool getSession(ClientIdType clientId, SessionIdType sessionId,
                    TranscodingRequestParcel* request) override;
    bool addClientUid(ClientIdType clientId, SessionIdType sessionId, uid_t clientUid) override;
    bool getClientUids(ClientIdType clientId, SessionIdType sessionId,
                       std::vector<int32_t>* out_clientUids) override;
    // ~ControllerClientInterface

    // TranscoderCallbackInterface
@@ -120,8 +123,8 @@ private:
            DROPPED_BY_PACER,
        };
        SessionKeyType key;
        uid_t clientUid;
        uid_t callingUid;
        std::unordered_set<uid_t> allClientUids;
        int32_t lastProgress = 0;
        int32_t pauseCount = 0;
        std::chrono::time_point<std::chrono::steady_clock> stateEnterTime;
@@ -184,7 +187,9 @@ private:
    void dumpSession_l(const Session& session, String8& result, bool closedSession = false);
    Session* getTopSession_l();
    void updateCurrentSession_l();
    void removeSession_l(const SessionKeyType& sessionKey, Session::State finalState);
    void addUidToSession_l(uid_t uid, const SessionKeyType& sessionKey);
    void removeSession_l(const SessionKeyType& sessionKey, Session::State finalState,
                         bool keepForOffline = false);
    void moveUidsToTop_l(const std::unordered_set<uid_t>& uids, bool preserveTopUid);
    void setSessionState_l(Session* session, Session::State state);
    void notifyClient(ClientIdType clientId, SessionIdType sessionId, const char* reason,
Loading