transcoding: handle multiple uids in service
Bug: 171398942
Test: unit tests added; CTS; manual test with app
Change-Id: I07f1399c8f62953084e06fc87a6bb4da27c51e68
diff --git a/media/libmediatranscoding/TranscodingClientManager.cpp b/media/libmediatranscoding/TranscodingClientManager.cpp
index 06c5421..086c658 100644
--- a/media/libmediatranscoding/TranscodingClientManager.cpp
+++ b/media/libmediatranscoding/TranscodingClientManager.cpp
@@ -94,6 +94,12 @@
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 @@
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);
diff --git a/media/libmediatranscoding/TranscodingSessionController.cpp b/media/libmediatranscoding/TranscodingSessionController.cpp
index aeabe0f..2518e70 100644
--- a/media/libmediatranscoding/TranscodingSessionController.cpp
+++ b/media/libmediatranscoding/TranscodingSessionController.cpp
@@ -193,8 +193,8 @@
~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 @@
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) {
+bool TranscodingSessionController::Pacer::onSessionStarted(uid_t uid) {
+ // 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].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 (mUidHistoryMap.find(uid) == mUidHistoryMap.end()) {
+ mUidHistoryMap[uid].sessionActive = true;
+ ALOGV("Pacer::onSessionStarted: uid %d: new", uid);
return true;
}
@@ -236,25 +230,43 @@
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 @@
}
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::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 @@
// 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 @@
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,26 +549,40 @@
}
// Remove session from uid's queue.
- const uid_t uid = mSessionMap[sessionKey].clientUid;
- 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;
+ 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()) {
+ ALOGW("couldn't find session %s in queue for uid %d",
+ sessionToString(sessionKey).c_str(), uid);
+ continue;
+ }
+ sessionQueue.erase(it);
+
+ // If this is the last session in a real-time queue, remove this uid's queue.
+ if (uid != OFFLINE_UID && sessionQueue.empty()) {
+ mUidSortedList.remove(uid);
+ mSessionQueues.erase(uid);
+ mUidPolicy->unregisterMonitorUid(uid);
+
+ uidQueueRemoved = true;
+ }
}
- sessionQueue.erase(it);
- // If this is the last session in a real-time queue, remove this uid's queue.
- if (uid != OFFLINE_UID && sessionQueue.empty()) {
- mUidSortedList.remove(uid);
- mSessionQueues.erase(uid);
- mUidPolicy->unregisterMonitorUid(uid);
-
+ 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 @@
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 @@
// 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,14 +706,20 @@
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) {
- sessionsToRemove.push_back(it->first);
+ 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 {
@@ -688,6 +743,10 @@
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 @@
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 @@
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 @@
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
}
diff --git a/media/libmediatranscoding/aidl/android/media/ITranscodingClient.aidl b/media/libmediatranscoding/aidl/android/media/ITranscodingClient.aidl
index 151e3d0..c6fa57f 100644
--- a/media/libmediatranscoding/aidl/android/media/ITranscodingClient.aidl
+++ b/media/libmediatranscoding/aidl/android/media/ITranscodingClient.aidl
@@ -55,6 +55,31 @@
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.
*
* Client will not be able to perform any more transcoding after unregister.
diff --git a/media/libmediatranscoding/include/media/ControllerClientInterface.h b/media/libmediatranscoding/include/media/ControllerClientInterface.h
index 0d13607..9311e2e 100644
--- a/media/libmediatranscoding/include/media/ControllerClientInterface.h
+++ b/media/libmediatranscoding/include/media/ControllerClientInterface.h
@@ -60,6 +60,29 @@
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;
};
diff --git a/media/libmediatranscoding/include/media/TranscodingSessionController.h b/media/libmediatranscoding/include/media/TranscodingSessionController.h
index b2d6f0a..05234f4 100644
--- a/media/libmediatranscoding/include/media/TranscodingSessionController.h
+++ b/media/libmediatranscoding/include/media/TranscodingSessionController.h
@@ -54,6 +54,9 @@
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 @@
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 @@
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,
diff --git a/media/libmediatranscoding/tests/TranscodingClientManager_tests.cpp b/media/libmediatranscoding/tests/TranscodingClientManager_tests.cpp
index 57a2e27..b7b1279 100644
--- a/media/libmediatranscoding/tests/TranscodingClientManager_tests.cpp
+++ b/media/libmediatranscoding/tests/TranscodingClientManager_tests.cpp
@@ -50,6 +50,7 @@
constexpr const char* kClientName = "TestClientName";
constexpr const char* kClientPackage = "TestClientPackage";
+constexpr uid_t OFFLINE_UID = -1;
#define SESSION(n) (n)
@@ -135,8 +136,8 @@
virtual ~TestController() { ALOGI("TestController Destroyed"); }
- bool submit(ClientIdType clientId, SessionIdType sessionId, uid_t /*callingUid*/, uid_t /*uid*/,
- const TranscodingRequestParcel& request,
+ bool submit(ClientIdType clientId, SessionIdType sessionId, uid_t /*callingUid*/,
+ uid_t clientUid, const TranscodingRequestParcel& request,
const std::weak_ptr<ITranscodingClientCallback>& clientCallback) override {
SessionKeyType sessionKey = std::make_pair(clientId, sessionId);
if (mSessions.count(sessionKey) > 0) {
@@ -149,13 +150,47 @@
return false;
}
+ if (request.priority == TranscodingSessionPriority::kUnspecified) {
+ clientUid = OFFLINE_UID;
+ }
+
mSessions[sessionKey].request = request;
mSessions[sessionKey].callback = clientCallback;
+ mSessions[sessionKey].allClientUids.insert(clientUid);
mLastSession = sessionKey;
return true;
}
+ bool addClientUid(ClientIdType clientId, SessionIdType sessionId, uid_t clientUid) override {
+ SessionKeyType sessionKey = std::make_pair(clientId, sessionId);
+
+ if (mSessions.count(sessionKey) == 0) {
+ return false;
+ }
+ if (mSessions[sessionKey].allClientUids.count(clientUid) > 0) {
+ return false;
+ }
+ mSessions[sessionKey].allClientUids.insert(clientUid);
+ return true;
+ }
+
+ bool getClientUids(ClientIdType clientId, SessionIdType sessionId,
+ std::vector<int32_t>* out_clientUids) override {
+ SessionKeyType sessionKey = std::make_pair(clientId, sessionId);
+
+ if (mSessions.count(sessionKey) == 0) {
+ return false;
+ }
+ out_clientUids->clear();
+ for (uid_t uid : mSessions[sessionKey].allClientUids) {
+ if (uid != OFFLINE_UID) {
+ out_clientUids->push_back(uid);
+ }
+ }
+ return true;
+ }
+
bool cancel(ClientIdType clientId, SessionIdType sessionId) override {
SessionKeyType sessionKey = std::make_pair(clientId, sessionId);
@@ -211,6 +246,7 @@
struct Session {
TranscodingRequest request;
std::weak_ptr<ITranscodingClientCallback> callback;
+ std::unordered_set<uid_t> allClientUids;
};
typedef std::pair<ClientIdType, SessionIdType> SessionKeyType;
@@ -537,4 +573,93 @@
EXPECT_EQ(status.getServiceSpecificError(), IMediaTranscodingService::ERROR_DISCONNECTED);
}
+TEST_F(TranscodingClientManagerTest, TestAddGetClientUidsInvalidArgs) {
+ addMultipleClients();
+
+ bool result;
+ std::vector<int32_t> clientUids;
+ TranscodingRequestParcel request;
+ TranscodingSessionParcel session;
+ uid_t ownUid = ::getuid();
+
+ // Add/Get clients with invalid session id fails.
+ EXPECT_TRUE(mClient1->addClientUid(-1, ownUid, &result).isOk());
+ EXPECT_FALSE(result);
+ EXPECT_TRUE(mClient1->addClientUid(SESSION(0), ownUid, &result).isOk());
+ EXPECT_FALSE(result);
+ EXPECT_TRUE(mClient1->getClientUids(-1, &clientUids, &result).isOk());
+ EXPECT_FALSE(result);
+ EXPECT_TRUE(mClient1->getClientUids(SESSION(0), &clientUids, &result).isOk());
+ EXPECT_FALSE(result);
+
+ unregisterMultipleClients();
+}
+
+TEST_F(TranscodingClientManagerTest, TestAddGetClientUids) {
+ addMultipleClients();
+
+ bool result;
+ std::vector<int32_t> clientUids;
+ TranscodingRequestParcel request;
+ TranscodingSessionParcel session;
+ uid_t ownUid = ::getuid();
+
+ // Submit one real-time session.
+ request.sourceFilePath = "test_source_file_0";
+ request.destinationFilePath = "test_desintaion_file_0";
+ request.priority = TranscodingSessionPriority::kNormal;
+ EXPECT_TRUE(mClient1->submitRequest(request, &session, &result).isOk());
+ EXPECT_TRUE(result);
+
+ // Should have own uid in client uid list.
+ EXPECT_TRUE(mClient1->getClientUids(SESSION(0), &clientUids, &result).isOk());
+ EXPECT_TRUE(result);
+ EXPECT_EQ(clientUids.size(), 1);
+ EXPECT_EQ(clientUids[0], ownUid);
+
+ // Adding invalid client uid should fail.
+ EXPECT_TRUE(mClient1->addClientUid(SESSION(0), kInvalidClientUid, &result).isOk());
+ EXPECT_FALSE(result);
+
+ // Adding own uid again should fail.
+ EXPECT_TRUE(mClient1->addClientUid(SESSION(0), ownUid, &result).isOk());
+ EXPECT_FALSE(result);
+
+ // Submit one offline session.
+ request.sourceFilePath = "test_source_file_1";
+ request.destinationFilePath = "test_desintaion_file_1";
+ request.priority = TranscodingSessionPriority::kUnspecified;
+ EXPECT_TRUE(mClient1->submitRequest(request, &session, &result).isOk());
+ EXPECT_TRUE(result);
+
+ // Should not have own uid in client uid list.
+ EXPECT_TRUE(mClient1->getClientUids(SESSION(1), &clientUids, &result).isOk());
+ EXPECT_TRUE(result);
+ EXPECT_EQ(clientUids.size(), 0);
+
+ // Add own uid (with IMediaTranscodingService::USE_CALLING_UID) again, should succeed.
+ EXPECT_TRUE(
+ mClient1->addClientUid(SESSION(1), IMediaTranscodingService::USE_CALLING_UID, &result)
+ .isOk());
+ EXPECT_TRUE(result);
+ EXPECT_TRUE(mClient1->getClientUids(SESSION(1), &clientUids, &result).isOk());
+ EXPECT_TRUE(result);
+ EXPECT_EQ(clientUids.size(), 1);
+ EXPECT_EQ(clientUids[0], ownUid);
+
+ // Add more uids, should succeed.
+ int32_t kFakeUid = ::getuid() ^ 0x1;
+ EXPECT_TRUE(mClient1->addClientUid(SESSION(1), kFakeUid, &result).isOk());
+ EXPECT_TRUE(result);
+ EXPECT_TRUE(mClient1->getClientUids(SESSION(1), &clientUids, &result).isOk());
+ EXPECT_TRUE(result);
+ std::unordered_set<uid_t> uidSet;
+ uidSet.insert(clientUids.begin(), clientUids.end());
+ EXPECT_EQ(uidSet.size(), 2);
+ EXPECT_EQ(uidSet.count(ownUid), 1);
+ EXPECT_EQ(uidSet.count(kFakeUid), 1);
+
+ unregisterMultipleClients();
+}
+
} // namespace android
diff --git a/media/libmediatranscoding/tests/TranscodingSessionController_tests.cpp b/media/libmediatranscoding/tests/TranscodingSessionController_tests.cpp
index 560d1fe..2be9e7d 100644
--- a/media/libmediatranscoding/tests/TranscodingSessionController_tests.cpp
+++ b/media/libmediatranscoding/tests/TranscodingSessionController_tests.cpp
@@ -234,11 +234,14 @@
}
struct TestClientCallback : public BnTranscodingClientCallback {
- TestClientCallback(TestTranscoder* owner, int64_t clientId)
- : mOwner(owner), mClientId(clientId) {
+ TestClientCallback(TestTranscoder* owner, ClientIdType clientId, uid_t clientUid)
+ : mOwner(owner), mClientId(clientId), mClientUid(clientUid) {
ALOGD("TestClient Created");
}
+ ClientIdType clientId() const { return mClientId; }
+ uid_t clientUid() const { return mClientUid; }
+
Status openFileDescriptor(const std::string& /*in_fileUri*/, const std::string& /*in_mode*/,
::ndk::ScopedFileDescriptor* /*_aidl_return*/) override {
return Status::ok();
@@ -277,7 +280,8 @@
private:
TestTranscoder* mOwner;
- int64_t mClientId;
+ ClientIdType mClientId;
+ uid_t mClientUid;
TestClientCallback(const TestClientCallback&) = delete;
TestClientCallback& operator=(const TestClientCallback&) = delete;
};
@@ -313,14 +317,14 @@
// Set priority only, ignore other fields for now.
mOfflineRequest.priority = TranscodingSessionPriority::kUnspecified;
mRealtimeRequest.priority = TranscodingSessionPriority::kHigh;
- mClientCallback0 =
- ::ndk::SharedRefBase::make<TestClientCallback>(mTranscoder.get(), CLIENT(0));
- mClientCallback1 =
- ::ndk::SharedRefBase::make<TestClientCallback>(mTranscoder.get(), CLIENT(1));
- mClientCallback2 =
- ::ndk::SharedRefBase::make<TestClientCallback>(mTranscoder.get(), CLIENT(2));
- mClientCallback3 =
- ::ndk::SharedRefBase::make<TestClientCallback>(mTranscoder.get(), CLIENT(3));
+ mClientCallback0 = ::ndk::SharedRefBase::make<TestClientCallback>(mTranscoder.get(),
+ CLIENT(0), UID(0));
+ mClientCallback1 = ::ndk::SharedRefBase::make<TestClientCallback>(mTranscoder.get(),
+ CLIENT(1), UID(1));
+ mClientCallback2 = ::ndk::SharedRefBase::make<TestClientCallback>(mTranscoder.get(),
+ CLIENT(2), UID(2));
+ mClientCallback3 = ::ndk::SharedRefBase::make<TestClientCallback>(mTranscoder.get(),
+ CLIENT(3), UID(3));
}
void TearDown() override { ALOGI("TranscodingSessionControllerTest tear down"); }
@@ -337,34 +341,51 @@
void testPacerHelper(int numSubmits, int sessionDurationMs, int expectedSuccess,
bool pauseLastSuccessSession = false) {
+ testPacerHelper(numSubmits, sessionDurationMs, expectedSuccess, mClientCallback0, {},
+ pauseLastSuccessSession);
+ }
+
+ void testPacerHelper(int numSubmits, int sessionDurationMs, int expectedSuccess,
+ const std::shared_ptr<TestClientCallback>& client,
+ const std::vector<int>& additionalClientUids,
+ bool pauseLastSuccessSession) {
for (int i = 0; i < numSubmits; i++) {
- mController->submit(CLIENT(0), SESSION(i), UID(0), UID(0),
- mRealtimeRequest, mClientCallback0);
+ mController->submit(client->clientId(), SESSION(i), client->clientUid(),
+ client->clientUid(), mRealtimeRequest, client);
+ for (int additionalUid : additionalClientUids) {
+ mController->addClientUid(client->clientId(), SESSION(i), additionalUid);
+ }
}
for (int i = 0; i < expectedSuccess; i++) {
- EXPECT_EQ(mTranscoder->popEvent(), TestTranscoder::Start(CLIENT(0), SESSION(i)));
+ EXPECT_EQ(mTranscoder->popEvent(),
+ TestTranscoder::Start(client->clientId(), SESSION(i)));
if ((i == expectedSuccess - 1) && pauseLastSuccessSession) {
// Insert a pause of 3 sec to the last success running session
mController->onThrottlingStarted();
- EXPECT_EQ(mTranscoder->popEvent(), TestTranscoder::Pause(CLIENT(0), SESSION(i)));
+ EXPECT_EQ(mTranscoder->popEvent(),
+ TestTranscoder::Pause(client->clientId(), SESSION(i)));
sleep(3);
mController->onThrottlingStopped();
- EXPECT_EQ(mTranscoder->popEvent(), TestTranscoder::Resume(CLIENT(0), SESSION(i)));
+ EXPECT_EQ(mTranscoder->popEvent(),
+ TestTranscoder::Resume(client->clientId(), SESSION(i)));
}
usleep(sessionDurationMs * 1000);
// Test half of Finish and half of Error, both should be counted as burst runs.
if (i & 1) {
- mController->onFinish(CLIENT(0), SESSION(i));
- EXPECT_EQ(mTranscoder->popEvent(), TestTranscoder::Finished(CLIENT(0), SESSION(i)));
+ mController->onFinish(client->clientId(), SESSION(i));
+ EXPECT_EQ(mTranscoder->popEvent(),
+ TestTranscoder::Finished(client->clientId(), SESSION(i)));
} else {
- mController->onError(CLIENT(0), SESSION(i), TranscodingErrorCode::kUnknown);
+ mController->onError(client->clientId(), SESSION(i),
+ TranscodingErrorCode::kUnknown);
EXPECT_EQ(mTranscoder->popEvent(100000),
- TestTranscoder::Failed(CLIENT(0), SESSION(i)));
+ TestTranscoder::Failed(client->clientId(), SESSION(i)));
EXPECT_EQ(mTranscoder->getLastError(), TranscodingErrorCode::kUnknown);
}
}
for (int i = expectedSuccess; i < numSubmits; i++) {
- EXPECT_EQ(mTranscoder->popEvent(), TestTranscoder::Failed(CLIENT(0), SESSION(i)));
+ EXPECT_EQ(mTranscoder->popEvent(),
+ TestTranscoder::Failed(client->clientId(), SESSION(i)));
EXPECT_EQ(mTranscoder->getLastError(), TranscodingErrorCode::kDroppedByService);
}
}
@@ -470,6 +491,83 @@
EXPECT_EQ(mTranscoder->popEvent(), TestTranscoder::Stop(CLIENT(0), SESSION(3)));
}
+TEST_F(TranscodingSessionControllerTest, TestCancelSessionWithMultipleUids) {
+ ALOGD("TestCancelSessionWithMultipleUids");
+ std::vector<int32_t> clientUids;
+
+ // Submit real-time session SESSION(0), should start immediately.
+ mController->submit(CLIENT(0), SESSION(0), UID(0), UID(0), mRealtimeRequest, mClientCallback0);
+ EXPECT_EQ(mTranscoder->popEvent(), TestTranscoder::Start(CLIENT(0), SESSION(0)));
+
+ // Submit real-time session SESSION(1), should not start.
+ mController->submit(CLIENT(0), SESSION(1), UID(0), UID(0), mRealtimeRequest, mClientCallback0);
+ EXPECT_EQ(mTranscoder->popEvent(), TestTranscoder::NoEvent);
+
+ // Submit offline session SESSION(2), should not start.
+ mController->submit(CLIENT(0), SESSION(2), UID(0), UID(0), mOfflineRequest, mClientCallback0);
+ EXPECT_EQ(mTranscoder->popEvent(), TestTranscoder::NoEvent);
+
+ // UID(1) moves to top.
+ mUidPolicy->setTop(UID(1));
+
+ // Add UID(1) to the offline SESSION(2), SESSION(2) should start and SESSION(0) should pause.
+ EXPECT_TRUE(mController->addClientUid(CLIENT(0), SESSION(2), UID(1)));
+ EXPECT_EQ(mTranscoder->popEvent(), TestTranscoder::Pause(CLIENT(0), SESSION(0)));
+ EXPECT_EQ(mTranscoder->popEvent(), TestTranscoder::Start(CLIENT(0), SESSION(2)));
+
+ // Add UID(1) to SESSION(1) as well.
+ EXPECT_TRUE(mController->addClientUid(CLIENT(0), SESSION(1), UID(1)));
+
+ // Cancel SESSION(2), should be cancelled and SESSION(1) should start.
+ EXPECT_TRUE(mController->cancel(CLIENT(0), SESSION(2)));
+ EXPECT_FALSE(mController->getClientUids(CLIENT(0), SESSION(2), &clientUids));
+ EXPECT_EQ(mTranscoder->popEvent(), TestTranscoder::Stop(CLIENT(0), SESSION(2)));
+ EXPECT_EQ(mTranscoder->popEvent(), TestTranscoder::Start(CLIENT(0), SESSION(1)));
+
+ // Cancel SESSION(1), should be cancelled and SESSION(0) should resume.
+ EXPECT_TRUE(mController->cancel(CLIENT(0), SESSION(1)));
+ EXPECT_FALSE(mController->getClientUids(CLIENT(0), SESSION(1), &clientUids));
+ EXPECT_EQ(mTranscoder->popEvent(), TestTranscoder::Stop(CLIENT(0), SESSION(1)));
+ EXPECT_EQ(mTranscoder->popEvent(), TestTranscoder::Resume(CLIENT(0), SESSION(0)));
+}
+
+TEST_F(TranscodingSessionControllerTest, TestCancelAllSessionsForClient) {
+ // Submit real-time session SESSION(0), should start immediately.
+ mController->submit(CLIENT(0), SESSION(0), UID(0), UID(0), mRealtimeRequest, mClientCallback0);
+ EXPECT_EQ(mTranscoder->popEvent(), TestTranscoder::Start(CLIENT(0), SESSION(0)));
+
+ // Submit real-time session SESSION(1), should not start.
+ mController->submit(CLIENT(0), SESSION(1), UID(0), UID(0), mRealtimeRequest, mClientCallback0);
+ EXPECT_EQ(mTranscoder->popEvent(), TestTranscoder::NoEvent);
+
+ // Submit offline session SESSION(2), should not start.
+ mController->submit(CLIENT(0), SESSION(2), UID(0), UID(0), mOfflineRequest, mClientCallback0);
+ EXPECT_EQ(mTranscoder->popEvent(), TestTranscoder::NoEvent);
+
+ std::vector<int32_t> clientUids;
+ // Make some more uids blocked on the sessions.
+ EXPECT_TRUE(mController->addClientUid(CLIENT(0), SESSION(0), UID(1)));
+ EXPECT_TRUE(mController->addClientUid(CLIENT(0), SESSION(1), UID(1)));
+ EXPECT_TRUE(mController->addClientUid(CLIENT(0), SESSION(2), UID(1)));
+ EXPECT_TRUE(mController->getClientUids(CLIENT(0), SESSION(0), &clientUids));
+ EXPECT_EQ(clientUids.size(), 2);
+ EXPECT_TRUE(mController->getClientUids(CLIENT(0), SESSION(1), &clientUids));
+ EXPECT_EQ(clientUids.size(), 2);
+ EXPECT_TRUE(mController->getClientUids(CLIENT(0), SESSION(2), &clientUids));
+ EXPECT_EQ(clientUids.size(), 1);
+
+ // Cancel all sessions for CLIENT(0) with -1.
+ // Expect SESSION(0) and SESSION(1) to be gone.
+ // Expect SESSION(2) still there with empty client uid list (only kept for offline) and start.
+ EXPECT_TRUE(mController->cancel(CLIENT(0), -1));
+ EXPECT_EQ(mTranscoder->popEvent(), TestTranscoder::Stop(CLIENT(0), SESSION(0)));
+ EXPECT_FALSE(mController->getClientUids(CLIENT(0), SESSION(0), &clientUids));
+ EXPECT_FALSE(mController->getClientUids(CLIENT(0), SESSION(1), &clientUids));
+ EXPECT_TRUE(mController->getClientUids(CLIENT(0), SESSION(2), &clientUids));
+ EXPECT_EQ(clientUids.size(), 0);
+ EXPECT_EQ(mTranscoder->popEvent(), TestTranscoder::Start(CLIENT(0), SESSION(2)));
+}
+
TEST_F(TranscodingSessionControllerTest, TestFinishSession) {
ALOGD("TestFinishSession");
@@ -527,6 +625,45 @@
EXPECT_EQ(mTranscoder->popEvent(), TestTranscoder::NoEvent);
}
+TEST_F(TranscodingSessionControllerTest, TestFinishSessionWithMultipleUids) {
+ ALOGD("TestFinishSessionWithMultipleUids");
+ std::vector<int32_t> clientUids;
+
+ // Start with unspecified top uid.
+ // Submit real-time session SESSION(0), should start immediately.
+ mController->submit(CLIENT(0), SESSION(0), UID(0), UID(0), mRealtimeRequest, mClientCallback0);
+ EXPECT_EQ(mTranscoder->popEvent(), TestTranscoder::Start(CLIENT(0), SESSION(0)));
+
+ // Submit real-time session SESSION(1), should not start.
+ mController->submit(CLIENT(0), SESSION(1), UID(0), UID(0), mRealtimeRequest, mClientCallback0);
+ EXPECT_EQ(mTranscoder->popEvent(), TestTranscoder::NoEvent);
+ EXPECT_TRUE(mController->addClientUid(CLIENT(0), SESSION(1), UID(1)));
+
+ // Submit real-time session SESSION(2), should not start.
+ mController->submit(CLIENT(0), SESSION(2), UID(0), UID(0), mRealtimeRequest, mClientCallback0);
+ EXPECT_EQ(mTranscoder->popEvent(), TestTranscoder::NoEvent);
+ EXPECT_TRUE(mController->addClientUid(CLIENT(0), SESSION(2), UID(1)));
+ EXPECT_TRUE(mController->addClientUid(CLIENT(0), SESSION(2), UID(2)));
+
+ // UID(1) moves to top.
+ mUidPolicy->setTop(UID(1));
+ // SESSION(0) should pause, SESSION(1) should start.
+ EXPECT_EQ(mTranscoder->popEvent(), TestTranscoder::Pause(CLIENT(0), SESSION(0)));
+ EXPECT_EQ(mTranscoder->popEvent(), TestTranscoder::Start(CLIENT(0), SESSION(1)));
+
+ // Finish SESSION(1), SESSION(2) (next in line for UID(1)) should start.
+ mController->onFinish(CLIENT(0), SESSION(1));
+ EXPECT_EQ(mTranscoder->popEvent(), TestTranscoder::Finished(CLIENT(0), SESSION(1)));
+ EXPECT_EQ(mTranscoder->popEvent(), TestTranscoder::Start(CLIENT(0), SESSION(2)));
+ EXPECT_FALSE(mController->getClientUids(CLIENT(0), SESSION(1), &clientUids));
+
+ // Finish SESSION(2), SESSION(0) should resume.
+ mController->onFinish(CLIENT(0), SESSION(2));
+ EXPECT_EQ(mTranscoder->popEvent(), TestTranscoder::Finished(CLIENT(0), SESSION(2)));
+ EXPECT_EQ(mTranscoder->popEvent(), TestTranscoder::Resume(CLIENT(0), SESSION(0)));
+ EXPECT_FALSE(mController->getClientUids(CLIENT(0), SESSION(2), &clientUids));
+}
+
TEST_F(TranscodingSessionControllerTest, TestFailSession) {
ALOGD("TestFailSession");
@@ -588,6 +725,49 @@
EXPECT_EQ(mTranscoder->popEvent(), TestTranscoder::NoEvent);
}
+TEST_F(TranscodingSessionControllerTest, TestFailSessionWithMultipleUids) {
+ ALOGD("TestFailSessionWithMultipleUids");
+ std::vector<int32_t> clientUids;
+
+ // Start with unspecified top uid.
+ // Submit real-time session SESSION(0), should start immediately.
+ mController->submit(CLIENT(0), SESSION(0), UID(0), UID(0), mRealtimeRequest, mClientCallback0);
+ EXPECT_EQ(mTranscoder->popEvent(), TestTranscoder::Start(CLIENT(0), SESSION(0)));
+
+ // Submit real-time session SESSION(1), should not start.
+ mController->submit(CLIENT(0), SESSION(1), UID(0), UID(0), mRealtimeRequest, mClientCallback0);
+ EXPECT_EQ(mTranscoder->popEvent(), TestTranscoder::NoEvent);
+ EXPECT_TRUE(mController->addClientUid(CLIENT(0), SESSION(1), UID(1)));
+
+ // Submit real-time session SESSION(2), should not start.
+ mController->submit(CLIENT(0), SESSION(2), UID(0), UID(0), mRealtimeRequest, mClientCallback0);
+ EXPECT_EQ(mTranscoder->popEvent(), TestTranscoder::NoEvent);
+
+ // UID(1) moves to top.
+ mUidPolicy->setTop(UID(1));
+ // SESSION(0) should pause, SESSION(1) should start.
+ EXPECT_EQ(mTranscoder->popEvent(), TestTranscoder::Pause(CLIENT(0), SESSION(0)));
+ EXPECT_EQ(mTranscoder->popEvent(), TestTranscoder::Start(CLIENT(0), SESSION(1)));
+
+ // Add UID(1) and UID(2) to SESSION(2).
+ EXPECT_TRUE(mController->addClientUid(CLIENT(0), SESSION(2), UID(1)));
+ EXPECT_TRUE(mController->addClientUid(CLIENT(0), SESSION(2), UID(2)));
+
+ // Fail SESSION(1), SESSION(2) (next in line for UID(1)) should start.
+ mController->onError(CLIENT(0), SESSION(1), TranscodingErrorCode::kUnknown);
+ EXPECT_EQ(mTranscoder->popEvent(), TestTranscoder::Failed(CLIENT(0), SESSION(1)));
+ EXPECT_EQ(mTranscoder->getLastError(), TranscodingErrorCode::kUnknown);
+ EXPECT_EQ(mTranscoder->popEvent(), TestTranscoder::Start(CLIENT(0), SESSION(2)));
+ EXPECT_FALSE(mController->getClientUids(CLIENT(0), SESSION(1), &clientUids));
+
+ // Fail SESSION(2), SESSION(0) should resume.
+ mController->onError(CLIENT(0), SESSION(2), TranscodingErrorCode::kInvalidOperation);
+ EXPECT_EQ(mTranscoder->popEvent(), TestTranscoder::Failed(CLIENT(0), SESSION(2)));
+ EXPECT_EQ(mTranscoder->getLastError(), TranscodingErrorCode::kInvalidOperation);
+ EXPECT_EQ(mTranscoder->popEvent(), TestTranscoder::Resume(CLIENT(0), SESSION(0)));
+ EXPECT_FALSE(mController->getClientUids(CLIENT(0), SESSION(2), &clientUids));
+}
+
TEST_F(TranscodingSessionControllerTest, TestTopUidChanged) {
ALOGD("TestTopUidChanged");
@@ -630,8 +810,59 @@
EXPECT_EQ(mTranscoder->popEvent(), TestTranscoder::Start(CLIENT(1), SESSION(0)));
}
+TEST_F(TranscodingSessionControllerTest, TestTopUidChangedMultipleUids) {
+ ALOGD("TestTopUidChangedMultipleUids");
+
+ // Start with unspecified top UID.
+ // Submit real-time session to CLIENT(0), session should start immediately.
+ mController->submit(CLIENT(0), SESSION(0), UID(0), UID(0), mRealtimeRequest, mClientCallback0);
+ EXPECT_EQ(mTranscoder->popEvent(), TestTranscoder::Start(CLIENT(0), SESSION(0)));
+
+ // Submit offline session to CLIENT(0), should not start.
+ mController->submit(CLIENT(1), SESSION(0), UID(1), UID(0), mOfflineRequest, mClientCallback1);
+ EXPECT_EQ(mTranscoder->popEvent(), TestTranscoder::NoEvent);
+
+ // Bring UID(1) to top.
+ mUidPolicy->setTop(UID(1));
+ EXPECT_EQ(mTranscoder->popEvent(), TestTranscoder::NoEvent);
+
+ // Add UID(1) to SESSION(0), SESSION(0) should continue to run
+ // (no pause&resume of the same session).
+ EXPECT_TRUE(mController->addClientUid(CLIENT(0), SESSION(0), UID(1)));
+ EXPECT_EQ(mTranscoder->popEvent(), TestTranscoder::NoEvent);
+
+ // Bring UID(0) back to top, SESSION(0) should continue to run
+ // (no pause&resume of the same session).
+ mUidPolicy->setTop(UID(0));
+ EXPECT_EQ(mTranscoder->popEvent(), TestTranscoder::NoEvent);
+
+ // Bring UID(2) to top.
+ mUidPolicy->setTop(UID(2));
+ EXPECT_EQ(mTranscoder->popEvent(), TestTranscoder::NoEvent);
+ // Add UID(2) to the offline session, it should be started.
+ EXPECT_TRUE(mController->addClientUid(CLIENT(1), SESSION(0), UID(2)));
+ EXPECT_EQ(mTranscoder->popEvent(), TestTranscoder::Pause(CLIENT(0), SESSION(0)));
+ EXPECT_EQ(mTranscoder->popEvent(), TestTranscoder::Start(CLIENT(1), SESSION(0)));
+
+ // ADD UID(3) to SESSION(0).
+ EXPECT_TRUE(mController->addClientUid(CLIENT(0), SESSION(0), UID(3)));
+ EXPECT_EQ(mTranscoder->popEvent(), TestTranscoder::NoEvent);
+ // Bring UID(3) to top, SESSION(0) should resume.
+ mUidPolicy->setTop(UID(3));
+ EXPECT_EQ(mTranscoder->popEvent(), TestTranscoder::Pause(CLIENT(1), SESSION(0)));
+ EXPECT_EQ(mTranscoder->popEvent(), TestTranscoder::Resume(CLIENT(0), SESSION(0)));
+
+ // Now make UID(2) also blocked on CLIENT(0), SESSION(0).
+ EXPECT_TRUE(mController->addClientUid(CLIENT(0), SESSION(0), UID(2)));
+
+ // Bring UID(2) back to top, CLIENT(0), SESSION(0) should continue to run (even if it's
+ // added to UID(2)'s queue later than CLIENT(1)'s SESSION(0)).
+ mUidPolicy->setTop(UID(2));
+ EXPECT_EQ(mTranscoder->popEvent(), TestTranscoder::NoEvent);
+}
+
TEST_F(TranscodingSessionControllerTest, TestTopUidSetChanged) {
- ALOGD("TestTopUidChanged_MultipleUids");
+ ALOGD("TestTopUidSetChanged");
// Start with unspecified top UID.
// Submit real-time session to CLIENT(0), session should start immediately.
@@ -684,6 +915,54 @@
EXPECT_EQ(mTranscoder->popEvent(), TestTranscoder::Start(CLIENT(1), SESSION(0)));
}
+TEST_F(TranscodingSessionControllerTest, TestAddGetClientUids) {
+ ALOGD("TestAddGetClientUids");
+
+ // Add/get client uids with non-existent session, should fail.
+ std::vector<int32_t> clientUids;
+ uid_t ownUid = ::getuid();
+ EXPECT_FALSE(mController->addClientUid(CLIENT(0), SESSION(0), ownUid));
+ EXPECT_FALSE(mController->getClientUids(CLIENT(0), SESSION(0), &clientUids));
+
+ // Submit a real-time request.
+ EXPECT_TRUE(mController->submit(CLIENT(0), SESSION(0), UID(0), UID(0), mRealtimeRequest,
+ mClientCallback0));
+ EXPECT_EQ(mTranscoder->popEvent(), TestTranscoder::Start(CLIENT(0), SESSION(0)));
+
+ // Should have own uid in client uids.
+ EXPECT_TRUE(mController->getClientUids(CLIENT(0), SESSION(0), &clientUids));
+ EXPECT_EQ(clientUids.size(), 1);
+ EXPECT_EQ(clientUids[0], UID(0));
+
+ // Add UID(0) again should fail.
+ EXPECT_FALSE(mController->addClientUid(CLIENT(0), SESSION(0), UID(0)));
+
+ // Add own uid should succeed.
+ EXPECT_TRUE(mController->addClientUid(CLIENT(0), SESSION(0), ownUid));
+ EXPECT_TRUE(mController->getClientUids(CLIENT(0), SESSION(0), &clientUids));
+ std::unordered_set<uid_t> uidSet;
+ uidSet.insert(clientUids.begin(), clientUids.end());
+ EXPECT_EQ(uidSet.size(), 2);
+ EXPECT_EQ(uidSet.count(UID(0)), 1);
+ EXPECT_EQ(uidSet.count(ownUid), 1);
+
+ // Submit an offline request.
+ EXPECT_TRUE(mController->submit(CLIENT(0), SESSION(1), UID(0), UID(0), mOfflineRequest,
+ mClientCallback0));
+ EXPECT_EQ(mTranscoder->popEvent(), TestTranscoder::NoEvent);
+
+ // Should not have own uid in client uids.
+ EXPECT_TRUE(mController->getClientUids(CLIENT(0), SESSION(1), &clientUids));
+ EXPECT_EQ(clientUids.size(), 0);
+
+ // Move UID(1) to top.
+ mUidPolicy->setTop(UID(1));
+ // Add UID(1) to offline session, offline session should start and SESSION(0) should pause.
+ EXPECT_TRUE(mController->addClientUid(CLIENT(0), SESSION(1), UID(1)));
+ EXPECT_EQ(mTranscoder->popEvent(), TestTranscoder::Pause(CLIENT(0), SESSION(0)));
+ EXPECT_EQ(mTranscoder->popEvent(), TestTranscoder::Start(CLIENT(0), SESSION(1)));
+}
+
/* Test resource lost without thermal throttling */
TEST_F(TranscodingSessionControllerTest, TestResourceLost) {
ALOGD("TestResourceLost");
@@ -973,4 +1252,24 @@
true /*pauseLastSuccessSession*/);
}
+/*
+ * Test the case where multiple client uids request the same session. Session should only
+ * be dropped when all clients are over quota.
+ */
+TEST_F(TranscodingSessionControllerTest, TestTranscoderPacerMultipleUids) {
+ ALOGD("TestTranscoderPacerMultipleUids");
+ // First, run mClientCallback0 to the point of no quota.
+ testPacerHelper(12 /*numSubmits*/, 400 /*sessionDurationMs*/, 10 /*expectedSuccess*/,
+ mClientCallback0, {}, false /*pauseLastSuccessSession*/);
+ // Make UID(0) block on Client1's sessions too, Client1's quota should not be affected.
+ testPacerHelper(12 /*numSubmits*/, 400 /*sessionDurationMs*/, 10 /*expectedSuccess*/,
+ mClientCallback1, {UID(0)}, false /*pauseLastSuccessSession*/);
+ // Make UID(10) block on Client2's sessions. We expect to see 11 succeeds (instead of 10),
+ // because the addClientUid() is called after the submit, and first session is already
+ // started by the time UID(10) is added. UID(10) allowed us to run the 11th session,
+ // after that both UID(10) and UID(2) are out of quota.
+ testPacerHelper(12 /*numSubmits*/, 400 /*sessionDurationMs*/, 11 /*expectedSuccess*/,
+ mClientCallback2, {UID(10)}, false /*pauseLastSuccessSession*/);
+}
+
} // namespace android
diff --git a/services/mediatranscoding/tests/MediaTranscodingServiceTestHelper.h b/services/mediatranscoding/tests/MediaTranscodingServiceTestHelper.h
index 3f7d8d6..0d462d1 100644
--- a/services/mediatranscoding/tests/MediaTranscodingServiceTestHelper.h
+++ b/services/mediatranscoding/tests/MediaTranscodingServiceTestHelper.h
@@ -434,6 +434,30 @@
session.request.destinationFilePath == destinationFilePath));
}
+ template <bool expectation = success>
+ bool addClientUid(int32_t sessionId, uid_t clientUid) {
+ constexpr bool shouldSucceed = (expectation == success);
+ bool result;
+ Status status = mClient->addClientUid(sessionId, clientUid, &result);
+
+ EXPECT_TRUE(status.isOk());
+ EXPECT_EQ(result, shouldSucceed);
+
+ return status.isOk() && (result == shouldSucceed);
+ }
+
+ template <bool expectation = success>
+ bool getClientUids(int32_t sessionId, std::vector<int32_t>* clientUids) {
+ constexpr bool shouldSucceed = (expectation == success);
+ bool result;
+ Status status = mClient->getClientUids(sessionId, clientUids, &result);
+
+ EXPECT_TRUE(status.isOk());
+ EXPECT_EQ(result, shouldSucceed);
+
+ return status.isOk() && (result == shouldSucceed);
+ }
+
int32_t mClientId;
pid_t mClientPid;
uid_t mClientUid;
diff --git a/services/mediatranscoding/tests/mediatranscodingservice_simulated_tests.cpp b/services/mediatranscoding/tests/mediatranscodingservice_simulated_tests.cpp
index c8994ac..cb354f4 100644
--- a/services/mediatranscoding/tests/mediatranscodingservice_simulated_tests.cpp
+++ b/services/mediatranscoding/tests/mediatranscodingservice_simulated_tests.cpp
@@ -36,6 +36,7 @@
#include <iostream>
#include <list>
+#include <unordered_set>
#include "MediaTranscodingServiceTestHelper.h"
#include "SimulatedTranscoder.h"
@@ -255,6 +256,54 @@
unregisterMultipleClients();
}
+TEST_F(MediaTranscodingServiceSimulatedTest, TestAddGetClientUids) {
+ registerMultipleClients();
+
+ std::vector<int32_t> clientUids;
+ TranscodingRequestParcel request;
+ TranscodingSessionParcel session;
+ uid_t ownUid = ::getuid();
+
+ // Submit one real-time session.
+ EXPECT_TRUE(mClient1->submit(0, "test_source_file_0", "test_destination_file"));
+
+ // Should have mClientUid in client uid list.
+ EXPECT_TRUE(mClient1->getClientUids(0, &clientUids));
+ EXPECT_EQ(clientUids.size(), 1u);
+ EXPECT_EQ(clientUids[0], (int32_t)mClient1->mClientUid);
+
+ // Adding invalid client uid should fail.
+ EXPECT_TRUE(mClient1->addClientUid<fail>(0, kInvalidClientUid));
+
+ // Adding mClientUid again should fail.
+ EXPECT_TRUE(mClient1->addClientUid<fail>(0, mClient1->mClientUid));
+
+ // Submit one offline session.
+ EXPECT_TRUE(mClient1->submit(1, "test_source_file_1", "test_destination_file_1",
+ TranscodingSessionPriority::kUnspecified));
+
+ // Should not have any uids in client uid list.
+ EXPECT_TRUE(mClient1->getClientUids(1, &clientUids));
+ EXPECT_EQ(clientUids.size(), 0u);
+
+ // Add own uid (with IMediaTranscodingService::USE_CALLING_UID), should succeed.
+ EXPECT_TRUE(mClient1->addClientUid(1, IMediaTranscodingService::USE_CALLING_UID));
+ EXPECT_TRUE(mClient1->getClientUids(1, &clientUids));
+ EXPECT_EQ(clientUids.size(), 1u);
+ EXPECT_EQ(clientUids[0], (int32_t)ownUid);
+
+ // Adding mClientUid should succeed.
+ EXPECT_TRUE(mClient1->addClientUid(1, mClient1->mClientUid));
+ EXPECT_TRUE(mClient1->getClientUids(1, &clientUids));
+ std::unordered_set<uid_t> uidSet;
+ uidSet.insert(clientUids.begin(), clientUids.end());
+ EXPECT_EQ(uidSet.size(), 2u);
+ EXPECT_EQ(uidSet.count(ownUid), 1u);
+ EXPECT_EQ(uidSet.count(mClient1->mClientUid), 1u);
+
+ unregisterMultipleClients();
+}
+
TEST_F(MediaTranscodingServiceSimulatedTest, TestSubmitCancelWithOfflineSessions) {
registerMultipleClients();
@@ -378,6 +427,53 @@
ALOGD("TestTranscodingUidPolicy finished.");
}
+TEST_F(MediaTranscodingServiceSimulatedTest, TestTranscodingUidPolicyWithMultipleClientUids) {
+ ALOGD("TestTranscodingUidPolicyWithMultipleClientUids starting...");
+
+ EXPECT_TRUE(ShellHelper::RunCmd("input keyevent KEYCODE_WAKEUP"));
+ EXPECT_TRUE(ShellHelper::RunCmd("wm dismiss-keyguard"));
+ EXPECT_TRUE(ShellHelper::Stop(kClientPackageA));
+ EXPECT_TRUE(ShellHelper::Stop(kClientPackageB));
+ EXPECT_TRUE(ShellHelper::Stop(kClientPackageC));
+
+ registerMultipleClients();
+
+ ALOGD("Moving app A to top...");
+ EXPECT_TRUE(ShellHelper::Start(kClientPackageA, kTestActivityName));
+
+ // Submit 3 requests.
+ ALOGD("Submitting session to client1 (app A)...");
+ EXPECT_TRUE(mClient1->submit(0, "test_source_file_0", "test_destination_file_0"));
+ EXPECT_TRUE(mClient1->submit(1, "test_source_file_1", "test_destination_file_1"));
+ EXPECT_TRUE(mClient1->submit(2, "test_source_file_2", "test_destination_file_2"));
+
+ // mClient1's Session 0 should start immediately.
+ EXPECT_EQ(mClient1->pop(kPaddingUs), EventTracker::Start(CLIENT(1), 0));
+
+ // Add client2 (app B)'s uid to mClient1's session 1.
+ EXPECT_TRUE(mClient1->addClientUid(1, mClient2->mClientUid));
+
+ ALOGD("Moving app B to top...");
+ EXPECT_TRUE(ShellHelper::Start(kClientPackageB, kTestActivityName));
+
+ // mClient1's session 0 should pause, session 1 should start.
+ EXPECT_EQ(mClient1->pop(kPaddingUs), EventTracker::Pause(CLIENT(1), 0));
+ EXPECT_EQ(mClient1->pop(kPaddingUs), EventTracker::Start(CLIENT(1), 1));
+
+ ALOGD("Moving app A back to top...");
+ EXPECT_TRUE(ShellHelper::Start(kClientPackageA, kTestActivityName));
+ EXPECT_EQ(mClient1->pop(kSessionWithPaddingUs), EventTracker::Finished(CLIENT(1), 1));
+ EXPECT_EQ(mClient1->pop(kPaddingUs), EventTracker::Resume(CLIENT(1), 0));
+
+ unregisterMultipleClients();
+
+ EXPECT_TRUE(ShellHelper::Stop(kClientPackageA));
+ EXPECT_TRUE(ShellHelper::Stop(kClientPackageB));
+ EXPECT_TRUE(ShellHelper::Stop(kClientPackageC));
+
+ ALOGD("TestTranscodingUidPolicyWithMultipleClientUids finished.");
+}
+
TEST_F(MediaTranscodingServiceSimulatedTest, TestTranscodingThermalPolicy) {
ALOGD("TestTranscodingThermalPolicy starting...");