/* * Copyright (C) 2020 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ // Unit Test for TranscodingSessionController // #define LOG_NDEBUG 0 #define LOG_TAG "TranscodingSessionControllerTest" #include #include #include #include #include #include #include #include #include #include #include #include namespace android { using Status = ::ndk::ScopedAStatus; using aidl::android::media::BnTranscodingClientCallback; using aidl::android::media::IMediaTranscodingService; using aidl::android::media::ITranscodingClient; using aidl::android::media::TranscodingRequestParcel; constexpr ClientIdType kClientId = 1000; constexpr SessionIdType kClientSessionId = 0; constexpr uid_t kClientUid = 5000; constexpr pid_t kClientPid = 10000; constexpr uid_t kInvalidUid = (uid_t)-1; constexpr pid_t kInvalidPid = (pid_t)-1; #define CLIENT(n) (kClientId + (n)) #define SESSION(n) (kClientSessionId + (n)) #define UID(n) (kClientUid + (n)) #define PID(n) (kClientPid + (n)) class TestUidPolicy : public UidPolicyInterface { public: TestUidPolicy() = default; virtual ~TestUidPolicy() = default; // UidPolicyInterface void registerMonitorUid(uid_t /*uid*/) override {} void unregisterMonitorUid(uid_t /*uid*/) override {} bool isUidOnTop(uid_t uid) override { return mTopUids.count(uid) > 0; } std::unordered_set getTopUids() const override { return mTopUids; } void setCallback(const std::shared_ptr& cb) override { mUidPolicyCallback = cb; } void setTop(uid_t uid) { std::unordered_set uids = {uid}; setTop(uids); } void setTop(const std::unordered_set& uids) { mTopUids = uids; auto uidPolicyCb = mUidPolicyCallback.lock(); if (uidPolicyCb != nullptr) { uidPolicyCb->onTopUidsChanged(mTopUids); } } std::unordered_set mTopUids; std::weak_ptr mUidPolicyCallback; }; class TestResourcePolicy : public ResourcePolicyInterface { public: TestResourcePolicy() { reset(); } virtual ~TestResourcePolicy() = default; // ResourcePolicyInterface void setCallback(const std::shared_ptr& /*cb*/) override {} void setPidResourceLost(pid_t pid) override { mResourceLostPid = pid; } // ~ResourcePolicyInterface pid_t getPid() { pid_t result = mResourceLostPid; reset(); return result; } private: void reset() { mResourceLostPid = kInvalidPid; } pid_t mResourceLostPid; }; class TestThermalPolicy : public ThermalPolicyInterface { public: TestThermalPolicy() = default; virtual ~TestThermalPolicy() = default; // ThermalPolicyInterface void setCallback(const std::shared_ptr& /*cb*/) override {} bool getThrottlingStatus() { return false; } // ~ThermalPolicyInterface private: }; class TestTranscoder : public TranscoderInterface { public: TestTranscoder() : mGeneration(0) {} virtual ~TestTranscoder() {} // TranscoderInterface void start(ClientIdType clientId, SessionIdType sessionId, const TranscodingRequestParcel& /*request*/, uid_t /*callingUid*/, const std::shared_ptr& /*clientCallback*/) override { append(Start(clientId, sessionId)); } void pause(ClientIdType clientId, SessionIdType sessionId) override { append(Pause(clientId, sessionId)); } void resume(ClientIdType clientId, SessionIdType sessionId, const TranscodingRequestParcel& /*request*/, uid_t /*callingUid*/, const std::shared_ptr& /*clientCallback*/) override { append(Resume(clientId, sessionId)); } void stop(ClientIdType clientId, SessionIdType sessionId, bool abandon) override { append(abandon ? Abandon(clientId, sessionId) : Stop(clientId, sessionId)); } void onFinished(ClientIdType clientId, SessionIdType sessionId) { append(Finished(clientId, sessionId)); } void onFailed(ClientIdType clientId, SessionIdType sessionId, TranscodingErrorCode err) { append(Failed(clientId, sessionId), err); } void onCreated() { std::scoped_lock lock{mLock}; mGeneration++; } struct Event { enum { NoEvent, Start, Pause, Resume, Stop, Finished, Failed, Abandon } type; ClientIdType clientId; SessionIdType sessionId; }; static constexpr Event NoEvent = {Event::NoEvent, 0, 0}; #define DECLARE_EVENT(action) \ static Event action(ClientIdType clientId, SessionIdType sessionId) { \ return {Event::action, clientId, sessionId}; \ } DECLARE_EVENT(Start); DECLARE_EVENT(Pause); DECLARE_EVENT(Resume); DECLARE_EVENT(Stop); DECLARE_EVENT(Finished); DECLARE_EVENT(Failed); DECLARE_EVENT(Abandon); // Push 1 event to back. void append(const Event& event, const TranscodingErrorCode err = TranscodingErrorCode::kNoError) { std::unique_lock lock(mLock); mEventQueue.push_back(event); // Error is sticky, non-error event will not erase it, only getLastError() // clears last error. if (err != TranscodingErrorCode::kNoError) { mLastErrorQueue.push_back(err); } mCondition.notify_one(); } // Pop 1 event from front, wait for up to timeoutUs if empty. const Event& popEvent(int64_t timeoutUs = 0) { std::unique_lock lock(mLock); if (mEventQueue.empty() && timeoutUs > 0) { mCondition.wait_for(lock, std::chrono::microseconds(timeoutUs)); } if (mEventQueue.empty()) { mPoppedEvent = NoEvent; } else { mPoppedEvent = *mEventQueue.begin(); mEventQueue.pop_front(); } return mPoppedEvent; } TranscodingErrorCode getLastError() { std::scoped_lock lock{mLock}; if (mLastErrorQueue.empty()) { return TranscodingErrorCode::kNoError; } TranscodingErrorCode err = mLastErrorQueue.front(); mLastErrorQueue.pop_front(); return err; } int32_t getGeneration() { std::scoped_lock lock{mLock}; return mGeneration; } private: std::mutex mLock; std::condition_variable mCondition; Event mPoppedEvent; std::list mEventQueue; std::list mLastErrorQueue; int32_t mGeneration; }; bool operator==(const TestTranscoder::Event& lhs, const TestTranscoder::Event& rhs) { return lhs.type == rhs.type && lhs.clientId == rhs.clientId && lhs.sessionId == rhs.sessionId; } struct TestClientCallback : public BnTranscodingClientCallback { 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(); } Status onTranscodingStarted(int32_t /*in_sessionId*/) override { return Status::ok(); } Status onTranscodingPaused(int32_t /*in_sessionId*/) override { return Status::ok(); } Status onTranscodingResumed(int32_t /*in_sessionId*/) override { return Status::ok(); } Status onTranscodingFinished(int32_t in_sessionId, const TranscodingResultParcel& in_result) override { EXPECT_EQ(in_sessionId, in_result.sessionId); ALOGD("TestClientCallback: received onTranscodingFinished"); mOwner->onFinished(mClientId, in_sessionId); return Status::ok(); } Status onTranscodingFailed(int32_t in_sessionId, TranscodingErrorCode in_errorCode) override { mOwner->onFailed(mClientId, in_sessionId, in_errorCode); return Status::ok(); } Status onAwaitNumberOfSessionsChanged(int32_t /* in_sessionId */, int32_t /* in_oldAwaitNumber */, int32_t /* in_newAwaitNumber */) override { return Status::ok(); } Status onProgressUpdate(int32_t /* in_sessionId */, int32_t /* in_progress */) override { return Status::ok(); } virtual ~TestClientCallback() { ALOGI("TestClient destroyed"); }; private: TestTranscoder* mOwner; ClientIdType mClientId; uid_t mClientUid; TestClientCallback(const TestClientCallback&) = delete; TestClientCallback& operator=(const TestClientCallback&) = delete; }; class TranscodingSessionControllerTest : public ::testing::Test { public: TranscodingSessionControllerTest() { ALOGI("TranscodingSessionControllerTest created"); } ~TranscodingSessionControllerTest() { ALOGD("TranscodingSessionControllerTest destroyed"); } void SetUp() override { ALOGI("TranscodingSessionControllerTest set up"); mTranscoder.reset(new TestTranscoder()); mUidPolicy.reset(new TestUidPolicy()); mResourcePolicy.reset(new TestResourcePolicy()); mThermalPolicy.reset(new TestThermalPolicy()); // Overrid default burst params with shorter values for testing. TranscodingSessionController::ControllerConfig config = { .pacerBurstThresholdMs = 500, .pacerBurstCountQuota = 10, .pacerBurstTimeQuotaSeconds = 3, }; mController.reset(new TranscodingSessionController( [this](const std::shared_ptr& /*cb*/) { // Here we require that the SessionController clears out all its refcounts of // the transcoder object when it calls create. EXPECT_EQ(mTranscoder.use_count(), 1); mTranscoder->onCreated(); return mTranscoder; }, mUidPolicy, mResourcePolicy, mThermalPolicy, &config)); mUidPolicy->setCallback(mController); // Set priority only, ignore other fields for now. mOfflineRequest.priority = TranscodingSessionPriority::kUnspecified; mRealtimeRequest.priority = TranscodingSessionPriority::kHigh; mClientCallback0 = ::ndk::SharedRefBase::make(mTranscoder.get(), CLIENT(0), UID(0)); mClientCallback1 = ::ndk::SharedRefBase::make(mTranscoder.get(), CLIENT(1), UID(1)); mClientCallback2 = ::ndk::SharedRefBase::make(mTranscoder.get(), CLIENT(2), UID(2)); mClientCallback3 = ::ndk::SharedRefBase::make(mTranscoder.get(), CLIENT(3), UID(3)); } void TearDown() override { ALOGI("TranscodingSessionControllerTest tear down"); } void expectTimeout(int64_t clientId, int32_t sessionId, int32_t generation) { EXPECT_EQ(mTranscoder->popEvent(2900000), TestTranscoder::NoEvent); EXPECT_EQ(mTranscoder->popEvent(200000), TestTranscoder::Abandon(clientId, sessionId)); EXPECT_EQ(mTranscoder->popEvent(100000), TestTranscoder::Failed(clientId, sessionId)); EXPECT_EQ(mTranscoder->getLastError(), TranscodingErrorCode::kWatchdogTimeout); // Should have created new transcoder. EXPECT_EQ(mTranscoder->getGeneration(), generation); EXPECT_EQ(mTranscoder.use_count(), 2); } void testPacerHelper(int numSubmits, int sessionDurationMs, int expectedSuccess) { testPacerHelper(numSubmits, sessionDurationMs, expectedSuccess, mClientCallback0, {}, false /*pauseLastSuccessSession*/, true /*useRealCallingUid*/); } void testPacerHelperWithPause(int numSubmits, int sessionDurationMs, int expectedSuccess) { testPacerHelper(numSubmits, sessionDurationMs, expectedSuccess, mClientCallback0, {}, true /*pauseLastSuccessSession*/, true /*useRealCallingUid*/); } void testPacerHelperWithMultipleUids(int numSubmits, int sessionDurationMs, int expectedSuccess, const std::shared_ptr& client, const std::vector& additionalClientUids) { testPacerHelper(numSubmits, sessionDurationMs, expectedSuccess, client, additionalClientUids, false /*pauseLastSuccessSession*/, true /*useRealCallingUid*/); } void testPacerHelperWithSelfUid(int numSubmits, int sessionDurationMs, int expectedSuccess) { testPacerHelper(numSubmits, sessionDurationMs, expectedSuccess, mClientCallback0, {}, false /*pauseLastSuccessSession*/, false /*useRealCallingUid*/); } void testPacerHelper(int numSubmits, int sessionDurationMs, int expectedSuccess, const std::shared_ptr& client, const std::vector& additionalClientUids, bool pauseLastSuccessSession, bool useRealCallingUid) { uid_t callingUid = useRealCallingUid ? ::getuid() : client->clientUid(); for (int i = 0; i < numSubmits; i++) { mController->submit(client->clientId(), SESSION(i), callingUid, 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->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->clientId(), SESSION(i))); sleep(3); mController->onThrottlingStopped(); 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->clientId(), SESSION(i)); EXPECT_EQ(mTranscoder->popEvent(), TestTranscoder::Finished(client->clientId(), SESSION(i))); } else { mController->onError(client->clientId(), SESSION(i), TranscodingErrorCode::kUnknown); EXPECT_EQ(mTranscoder->popEvent(100000), 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->clientId(), SESSION(i))); EXPECT_EQ(mTranscoder->getLastError(), TranscodingErrorCode::kDroppedByService); } } std::shared_ptr mTranscoder; std::shared_ptr mUidPolicy; std::shared_ptr mResourcePolicy; std::shared_ptr mThermalPolicy; std::shared_ptr mController; TranscodingRequestParcel mOfflineRequest; TranscodingRequestParcel mRealtimeRequest; std::shared_ptr mClientCallback0; std::shared_ptr mClientCallback1; std::shared_ptr mClientCallback2; std::shared_ptr mClientCallback3; }; TEST_F(TranscodingSessionControllerTest, TestSubmitSession) { ALOGD("TestSubmitSession"); // Start with UID(1) on top. mUidPolicy->setTop(UID(1)); // Submit offline session to CLIENT(0) in UID(0). // Should start immediately (because this is the only session). mController->submit(CLIENT(0), SESSION(0), UID(0), UID(0), mOfflineRequest, mClientCallback0); EXPECT_EQ(mTranscoder->popEvent(), TestTranscoder::Start(CLIENT(0), 0)); // Submit real-time session to CLIENT(0). // Should pause offline session and start new session, even if UID(0) is not on top. mController->submit(CLIENT(0), SESSION(1), UID(0), UID(0), mRealtimeRequest, mClientCallback0); EXPECT_EQ(mTranscoder->popEvent(), TestTranscoder::Pause(CLIENT(0), SESSION(0))); EXPECT_EQ(mTranscoder->popEvent(), TestTranscoder::Start(CLIENT(0), SESSION(1))); // Submit real-time session to CLIENT(0), should be queued after the previous session. mController->submit(CLIENT(0), SESSION(2), UID(0), UID(0), mRealtimeRequest, mClientCallback0); EXPECT_EQ(mTranscoder->popEvent(), TestTranscoder::NoEvent); // Submit real-time session to CLIENT(1) in same uid, should be queued after the previous // session. mController->submit(CLIENT(1), SESSION(0), UID(1), UID(0), mRealtimeRequest, mClientCallback1); EXPECT_EQ(mTranscoder->popEvent(), TestTranscoder::NoEvent); // Submit real-time session to CLIENT(2) in UID(1). // Should pause previous session and start new session, because UID(1) is (has been) top. mController->submit(CLIENT(2), SESSION(0), UID(2), UID(1), mRealtimeRequest, mClientCallback2); EXPECT_EQ(mTranscoder->popEvent(), TestTranscoder::Pause(CLIENT(0), SESSION(1))); EXPECT_EQ(mTranscoder->popEvent(), TestTranscoder::Start(CLIENT(2), SESSION(0))); // Submit offline session, shouldn't generate any event. mController->submit(CLIENT(2), SESSION(1), UID(2), UID(1), mOfflineRequest, mClientCallback2); EXPECT_EQ(mTranscoder->popEvent(), TestTranscoder::NoEvent); // Bring UID(0) to top. mUidPolicy->setTop(UID(0)); // Should pause current session, and resume last session in UID(0). EXPECT_EQ(mTranscoder->popEvent(), TestTranscoder::Pause(CLIENT(2), SESSION(0))); EXPECT_EQ(mTranscoder->popEvent(), TestTranscoder::Resume(CLIENT(0), SESSION(1))); } TEST_F(TranscodingSessionControllerTest, TestCancelSession) { ALOGD("TestCancelSession"); // 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); // Cancel queued real-time session. // Cancel real-time session SESSION(1), should be cancelled. EXPECT_TRUE(mController->cancel(CLIENT(0), SESSION(1))); // Cancel queued offline session. // Cancel offline session SESSION(2), should be cancelled. EXPECT_TRUE(mController->cancel(CLIENT(0), SESSION(2))); // Submit offline session SESSION(3), shouldn't cause any event. mController->submit(CLIENT(0), SESSION(3), UID(0), UID(0), mOfflineRequest, mClientCallback0); EXPECT_EQ(mTranscoder->popEvent(), TestTranscoder::NoEvent); // Cancel running real-time session SESSION(0). // - Should be stopped first then cancelled. // - Should also start offline session SESSION(2) because real-time queue is empty. EXPECT_TRUE(mController->cancel(CLIENT(0), SESSION(0))); EXPECT_EQ(mTranscoder->popEvent(), TestTranscoder::Stop(CLIENT(0), SESSION(0))); EXPECT_EQ(mTranscoder->popEvent(), TestTranscoder::Start(CLIENT(0), SESSION(3))); // Submit real-time session SESSION(4), offline SESSION(3) should pause and SESSION(4) // should start. mController->submit(CLIENT(0), SESSION(4), UID(0), UID(0), mRealtimeRequest, mClientCallback0); EXPECT_EQ(mTranscoder->popEvent(), TestTranscoder::Pause(CLIENT(0), SESSION(3))); EXPECT_EQ(mTranscoder->popEvent(), TestTranscoder::Start(CLIENT(0), SESSION(4))); // Cancel paused SESSION(3). SESSION(3) should be stopped. EXPECT_TRUE(mController->cancel(CLIENT(0), SESSION(3))); EXPECT_EQ(mTranscoder->popEvent(), TestTranscoder::Stop(CLIENT(0), SESSION(3))); } TEST_F(TranscodingSessionControllerTest, TestCancelSessionWithMultipleUids) { ALOGD("TestCancelSessionWithMultipleUids"); std::vector 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 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"); // Start with unspecified top UID. // Finish without any sessions submitted, should be ignored. mController->onFinish(CLIENT(0), SESSION(0)); EXPECT_EQ(mTranscoder->popEvent(), TestTranscoder::NoEvent); // Submit offline session SESSION(0), should start immediately. mController->submit(CLIENT(0), SESSION(0), UID(0), UID(0), mOfflineRequest, mClientCallback0); EXPECT_EQ(mTranscoder->popEvent(), TestTranscoder::Start(CLIENT(0), SESSION(0))); // Submit real-time session SESSION(1), should pause offline session and start immediately. mController->submit(CLIENT(0), SESSION(1), UID(0), UID(0), mRealtimeRequest, mClientCallback0); EXPECT_EQ(mTranscoder->popEvent(), TestTranscoder::Pause(CLIENT(0), SESSION(0))); EXPECT_EQ(mTranscoder->popEvent(), TestTranscoder::Start(CLIENT(0), SESSION(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); // Finish when the session never started, should be ignored. mController->onFinish(CLIENT(0), SESSION(2)); EXPECT_EQ(mTranscoder->popEvent(), TestTranscoder::NoEvent); // UID(1) moves to top. mUidPolicy->setTop(UID(1)); // Submit real-time session to CLIENT(1) in UID(1), should pause previous session and start // new session. mController->submit(CLIENT(1), SESSION(0), UID(1), UID(1), mRealtimeRequest, mClientCallback1); EXPECT_EQ(mTranscoder->popEvent(), TestTranscoder::Pause(CLIENT(0), SESSION(1))); EXPECT_EQ(mTranscoder->popEvent(), TestTranscoder::Start(CLIENT(1), SESSION(0))); // Simulate Finish that arrived late, after pause issued by controller. // Should still be propagated to client, but shouldn't trigger any new start. mController->onFinish(CLIENT(0), SESSION(1)); EXPECT_EQ(mTranscoder->popEvent(), TestTranscoder::Finished(CLIENT(0), SESSION(1))); // Finish running real-time session, should start next real-time session in queue. mController->onFinish(CLIENT(1), SESSION(0)); EXPECT_EQ(mTranscoder->popEvent(), TestTranscoder::Finished(CLIENT(1), SESSION(0))); EXPECT_EQ(mTranscoder->popEvent(), TestTranscoder::Start(CLIENT(0), SESSION(2))); // Finish running real-time session, should resume next session (offline session) in queue. 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))); // Finish running offline session. mController->onFinish(CLIENT(0), SESSION(0)); EXPECT_EQ(mTranscoder->popEvent(), TestTranscoder::Finished(CLIENT(0), SESSION(0))); // Duplicate finish for last session, should be ignored. mController->onFinish(CLIENT(0), SESSION(0)); EXPECT_EQ(mTranscoder->popEvent(), TestTranscoder::NoEvent); } TEST_F(TranscodingSessionControllerTest, TestFinishSessionWithMultipleUids) { ALOGD("TestFinishSessionWithMultipleUids"); std::vector 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"); // Start with unspecified top UID. // Fail without any sessions submitted, should be ignored. mController->onError(CLIENT(0), SESSION(0), TranscodingErrorCode::kUnknown); EXPECT_EQ(mTranscoder->popEvent(), TestTranscoder::NoEvent); // Submit offline session SESSION(0), should start immediately. mController->submit(CLIENT(0), SESSION(0), UID(0), UID(0), mOfflineRequest, mClientCallback0); EXPECT_EQ(mTranscoder->popEvent(), TestTranscoder::Start(CLIENT(0), SESSION(0))); // Submit real-time session SESSION(1), should pause offline session and start immediately. mController->submit(CLIENT(0), SESSION(1), UID(0), UID(0), mRealtimeRequest, mClientCallback0); EXPECT_EQ(mTranscoder->popEvent(), TestTranscoder::Pause(CLIENT(0), SESSION(0))); EXPECT_EQ(mTranscoder->popEvent(), TestTranscoder::Start(CLIENT(0), SESSION(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); // Fail when the session never started, should be ignored. mController->onError(CLIENT(0), SESSION(2), TranscodingErrorCode::kUnknown); EXPECT_EQ(mTranscoder->popEvent(), TestTranscoder::NoEvent); // UID(1) moves to top. mUidPolicy->setTop(UID(1)); // Submit real-time session to CLIENT(1) in UID(1), should pause previous session and start // new session. mController->submit(CLIENT(1), SESSION(0), UID(1), UID(1), mRealtimeRequest, mClientCallback1); EXPECT_EQ(mTranscoder->popEvent(), TestTranscoder::Pause(CLIENT(0), SESSION(1))); EXPECT_EQ(mTranscoder->popEvent(), TestTranscoder::Start(CLIENT(1), SESSION(0))); // Simulate Fail that arrived late, after pause issued by controller. // Should still be propagated to client, but shouldn't trigger any new 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); // Fail running real-time session, should start next real-time session in queue. mController->onError(CLIENT(1), SESSION(0), TranscodingErrorCode::kUnknown); EXPECT_EQ(mTranscoder->popEvent(), TestTranscoder::Failed(CLIENT(1), SESSION(0))); EXPECT_EQ(mTranscoder->getLastError(), TranscodingErrorCode::kUnknown); EXPECT_EQ(mTranscoder->popEvent(), TestTranscoder::Start(CLIENT(0), SESSION(2))); // Fail running real-time session, should resume next session (offline session) in queue. mController->onError(CLIENT(0), SESSION(2), TranscodingErrorCode::kUnknown); EXPECT_EQ(mTranscoder->popEvent(), TestTranscoder::Failed(CLIENT(0), SESSION(2))); EXPECT_EQ(mTranscoder->getLastError(), TranscodingErrorCode::kUnknown); EXPECT_EQ(mTranscoder->popEvent(), TestTranscoder::Resume(CLIENT(0), SESSION(0))); // Fail running offline session, and test error code propagation. mController->onError(CLIENT(0), SESSION(0), TranscodingErrorCode::kInvalidOperation); EXPECT_EQ(mTranscoder->popEvent(), TestTranscoder::Failed(CLIENT(0), SESSION(0))); EXPECT_EQ(mTranscoder->getLastError(), TranscodingErrorCode::kInvalidOperation); // Duplicate fail for last session, should be ignored. mController->onError(CLIENT(0), SESSION(0), TranscodingErrorCode::kUnknown); EXPECT_EQ(mTranscoder->popEvent(), TestTranscoder::NoEvent); } TEST_F(TranscodingSessionControllerTest, TestFailSessionWithMultipleUids) { ALOGD("TestFailSessionWithMultipleUids"); std::vector 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"); // 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); // Move UID(1) to top. mUidPolicy->setTop(UID(1)); EXPECT_EQ(mTranscoder->popEvent(), TestTranscoder::NoEvent); // Submit real-time session to CLIENT(2) in different uid UID(1). // Should pause previous session and start new session. mController->submit(CLIENT(2), SESSION(0), UID(2), UID(1), mRealtimeRequest, mClientCallback2); EXPECT_EQ(mTranscoder->popEvent(), TestTranscoder::Pause(CLIENT(0), SESSION(0))); EXPECT_EQ(mTranscoder->popEvent(), TestTranscoder::Start(CLIENT(2), SESSION(0))); // Bring UID(0) back to top. mUidPolicy->setTop(UID(0)); EXPECT_EQ(mTranscoder->popEvent(), TestTranscoder::Pause(CLIENT(2), SESSION(0))); EXPECT_EQ(mTranscoder->popEvent(), TestTranscoder::Resume(CLIENT(0), SESSION(0))); // Bring invalid uid to top. mUidPolicy->setTop(kInvalidUid); EXPECT_EQ(mTranscoder->popEvent(), TestTranscoder::NoEvent); // Finish session, next real-time session should resume. mController->onFinish(CLIENT(0), SESSION(0)); EXPECT_EQ(mTranscoder->popEvent(), TestTranscoder::Finished(CLIENT(0), SESSION(0))); EXPECT_EQ(mTranscoder->popEvent(), TestTranscoder::Resume(CLIENT(2), SESSION(0))); // Finish session, offline session should start. mController->onFinish(CLIENT(2), SESSION(0)); EXPECT_EQ(mTranscoder->popEvent(), TestTranscoder::Finished(CLIENT(2), SESSION(0))); 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("TestTopUidSetChanged"); // 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); // Set UID(0), UID(1) to top set. // UID(0) should continue to run. mUidPolicy->setTop({UID(0), UID(1)}); EXPECT_EQ(mTranscoder->popEvent(), TestTranscoder::NoEvent); // Submit real-time session to CLIENT(2) in different uid UID(1). // UID(0) should pause and UID(1) should start. mController->submit(CLIENT(2), SESSION(0), UID(2), UID(1), mRealtimeRequest, mClientCallback2); EXPECT_EQ(mTranscoder->popEvent(), TestTranscoder::Pause(CLIENT(0), SESSION(0))); EXPECT_EQ(mTranscoder->popEvent(), TestTranscoder::Start(CLIENT(2), SESSION(0))); // Remove UID(0) from top set, and only leave UID(1) in the set. // UID(1) should continue to run. mUidPolicy->setTop(UID(1)); EXPECT_EQ(mTranscoder->popEvent(), TestTranscoder::NoEvent); // Set UID(0), UID(2) to top set. // UID(1) should continue to run. mUidPolicy->setTop({UID(1), UID(2)}); EXPECT_EQ(mTranscoder->popEvent(), TestTranscoder::NoEvent); // Bring UID(0) back to top. mUidPolicy->setTop(UID(0)); EXPECT_EQ(mTranscoder->popEvent(), TestTranscoder::Pause(CLIENT(2), SESSION(0))); EXPECT_EQ(mTranscoder->popEvent(), TestTranscoder::Resume(CLIENT(0), SESSION(0))); // Bring invalid uid to top. mUidPolicy->setTop(kInvalidUid); EXPECT_EQ(mTranscoder->popEvent(), TestTranscoder::NoEvent); // Finish session, next real-time session from UID(1) should resume, even if UID(1) // no longer top. mController->onFinish(CLIENT(0), SESSION(0)); EXPECT_EQ(mTranscoder->popEvent(), TestTranscoder::Finished(CLIENT(0), SESSION(0))); EXPECT_EQ(mTranscoder->popEvent(), TestTranscoder::Resume(CLIENT(2), SESSION(0))); // Finish session, offline session should start. mController->onFinish(CLIENT(2), SESSION(0)); EXPECT_EQ(mTranscoder->popEvent(), TestTranscoder::Finished(CLIENT(2), SESSION(0))); EXPECT_EQ(mTranscoder->popEvent(), TestTranscoder::Start(CLIENT(1), SESSION(0))); } TEST_F(TranscodingSessionControllerTest, TestUidGone) { ALOGD("TestUidGone"); mUidPolicy->setTop(UID(0)); // Start with unspecified top UID. // Submit real-time sessions 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))); 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 to CLIENT(1), should not start. mController->submit(CLIENT(1), SESSION(0), UID(1), UID(1), mOfflineRequest, mClientCallback1); EXPECT_EQ(mTranscoder->popEvent(), TestTranscoder::NoEvent); EXPECT_TRUE(mController->addClientUid(CLIENT(1), SESSION(0), UID(1))); // Tell the controller that UID(0) is gone. mUidPolicy->setTop(UID(1)); // CLIENT(0)'s SESSION(1) should start, SESSION(0) should be cancelled. EXPECT_EQ(mTranscoder->popEvent(), TestTranscoder::Pause(CLIENT(0), SESSION(0))); EXPECT_EQ(mTranscoder->popEvent(), TestTranscoder::Start(CLIENT(0), SESSION(1))); mController->onUidGone(UID(0)); EXPECT_EQ(mTranscoder->popEvent(), TestTranscoder::Stop(CLIENT(0), SESSION(0))); EXPECT_EQ(mTranscoder->popEvent(), TestTranscoder::Failed(CLIENT(0), SESSION(0))); EXPECT_EQ(mTranscoder->getLastError(), TranscodingErrorCode::kUidGoneCancelled); std::vector clientUids; EXPECT_FALSE(mController->getClientUids(CLIENT(0), SESSION(0), &clientUids)); EXPECT_TRUE(mController->getClientUids(CLIENT(0), SESSION(1), &clientUids)); EXPECT_EQ(clientUids.size(), 1); EXPECT_EQ(clientUids[0], UID(1)); // Tell the controller that UID(1) is gone too. mController->onUidGone(UID(1)); // CLIENT(1)'s SESSION(0) should start, CLIENT(0)'s SESSION(1) should be cancelled. EXPECT_EQ(mTranscoder->popEvent(), TestTranscoder::Stop(CLIENT(0), SESSION(1))); EXPECT_EQ(mTranscoder->popEvent(), TestTranscoder::Failed(CLIENT(0), SESSION(1))); EXPECT_EQ(mTranscoder->getLastError(), TranscodingErrorCode::kUidGoneCancelled); EXPECT_EQ(mTranscoder->popEvent(), TestTranscoder::Start(CLIENT(1), SESSION(0))); // CLIENT(1) SESSION(0) should not have any client uids as it's only kept for offline. EXPECT_TRUE(mController->getClientUids(CLIENT(1), SESSION(0), &clientUids)); EXPECT_EQ(clientUids.size(), 0); } TEST_F(TranscodingSessionControllerTest, TestAddGetClientUids) { ALOGD("TestAddGetClientUids"); // Add/get client uids with non-existent session, should fail. std::vector 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 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"); // Start with unspecified top UID. // Submit real-time session to CLIENT(0), session should start immediately. mRealtimeRequest.clientPid = PID(0); 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. mOfflineRequest.clientPid = PID(0); mController->submit(CLIENT(1), SESSION(0), UID(1), UID(0), mOfflineRequest, mClientCallback1); EXPECT_EQ(mTranscoder->popEvent(), TestTranscoder::NoEvent); // Move UID(1) to top. mUidPolicy->setTop(UID(1)); EXPECT_EQ(mTranscoder->popEvent(), TestTranscoder::NoEvent); // Submit real-time session to CLIENT(2) in different uid UID(1). // Should pause previous session and start new session. mRealtimeRequest.clientPid = PID(1); mController->submit(CLIENT(2), SESSION(0), UID(2), UID(1), mRealtimeRequest, mClientCallback2); EXPECT_EQ(mTranscoder->popEvent(), TestTranscoder::Pause(CLIENT(0), SESSION(0))); EXPECT_EQ(mTranscoder->popEvent(), TestTranscoder::Start(CLIENT(2), SESSION(0))); // Test 0: No call into ResourcePolicy if resource lost is from a non-running // or non-existent session. mController->onResourceLost(CLIENT(0), SESSION(0)); EXPECT_EQ(mResourcePolicy->getPid(), kInvalidPid); mController->onResourceLost(CLIENT(3), SESSION(0)); EXPECT_EQ(mResourcePolicy->getPid(), kInvalidPid); // Test 1: No queue change during resource loss. // Signal resource lost. mController->onResourceLost(CLIENT(2), SESSION(0)); EXPECT_EQ(mResourcePolicy->getPid(), PID(1)); EXPECT_EQ(mTranscoder->popEvent(), TestTranscoder::NoEvent); // Signal resource available, CLIENT(2) should resume. mController->onResourceAvailable(); EXPECT_EQ(mTranscoder->popEvent(), TestTranscoder::Resume(CLIENT(2), SESSION(0))); // Test 2: Change of queue order during resource loss. // Signal resource lost. mController->onResourceLost(CLIENT(2), SESSION(0)); EXPECT_EQ(mResourcePolicy->getPid(), PID(1)); EXPECT_EQ(mTranscoder->popEvent(), TestTranscoder::NoEvent); // Move UID(0) back to top, should have no resume due to no resource. mUidPolicy->setTop(UID(0)); EXPECT_EQ(mTranscoder->popEvent(), TestTranscoder::NoEvent); // Signal resource available, CLIENT(0) should resume. mController->onResourceAvailable(); EXPECT_EQ(mTranscoder->popEvent(), TestTranscoder::Resume(CLIENT(0), SESSION(0))); // Test 3: mController->onResourceLost(CLIENT(0), SESSION(0)); EXPECT_EQ(mResourcePolicy->getPid(), PID(0)); EXPECT_EQ(mTranscoder->popEvent(), TestTranscoder::NoEvent); // Cancel the paused top session during resource lost. EXPECT_TRUE(mController->cancel(CLIENT(0), SESSION(0))); EXPECT_EQ(mTranscoder->popEvent(), TestTranscoder::Stop(CLIENT(0), SESSION(0))); // Signal resource available, CLIENT(2)'s session should start. mController->onResourceAvailable(); EXPECT_EQ(mTranscoder->popEvent(), TestTranscoder::Resume(CLIENT(2), SESSION(0))); // Test 4: Adding new queue during resource loss. // Signal resource lost. mController->onResourceLost(CLIENT(2), SESSION(0)); EXPECT_EQ(mResourcePolicy->getPid(), PID(1)); EXPECT_EQ(mTranscoder->popEvent(), TestTranscoder::NoEvent); // Move UID(2) to top. mUidPolicy->setTop(UID(2)); // Submit real-time session to CLIENT(3) in UID(2), session shouldn't start due to no resource. mRealtimeRequest.clientPid = PID(2); mController->submit(CLIENT(3), SESSION(0), UID(3), UID(2), mRealtimeRequest, mClientCallback3); EXPECT_EQ(mTranscoder->popEvent(), TestTranscoder::NoEvent); // Signal resource available, CLIENT(3)'s session should start. mController->onResourceAvailable(); EXPECT_EQ(mTranscoder->popEvent(), TestTranscoder::Start(CLIENT(3), SESSION(0))); } /* Test thermal throttling without resource lost */ TEST_F(TranscodingSessionControllerTest, TestThermalCallback) { ALOGD("TestThermalCallback"); // Start with unspecified top UID. // Submit real-time session to CLIENT(0), session should start immediately. mRealtimeRequest.clientPid = PID(0); 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. mOfflineRequest.clientPid = PID(0); mController->submit(CLIENT(1), SESSION(0), UID(1), UID(0), mOfflineRequest, mClientCallback1); EXPECT_EQ(mTranscoder->popEvent(), TestTranscoder::NoEvent); // Move UID(1) to top. mUidPolicy->setTop(UID(1)); EXPECT_EQ(mTranscoder->popEvent(), TestTranscoder::NoEvent); // Submit real-time session to CLIENT(2) in different uid UID(1). // Should pause previous session and start new session. mRealtimeRequest.clientPid = PID(1); mController->submit(CLIENT(2), SESSION(0), UID(2), UID(1), mRealtimeRequest, mClientCallback2); EXPECT_EQ(mTranscoder->popEvent(), TestTranscoder::Pause(CLIENT(0), SESSION(0))); EXPECT_EQ(mTranscoder->popEvent(), TestTranscoder::Start(CLIENT(2), SESSION(0))); // Test 0: Basic case, no queue change during throttling, top session should pause/resume // with throttling. mController->onThrottlingStarted(); EXPECT_EQ(mTranscoder->popEvent(), TestTranscoder::Pause(CLIENT(2), SESSION(0))); mController->onThrottlingStopped(); EXPECT_EQ(mTranscoder->popEvent(), TestTranscoder::Resume(CLIENT(2), SESSION(0))); // Test 1: Change of queue order during thermal throttling, when throttling stops, // new top session should resume. mController->onThrottlingStarted(); EXPECT_EQ(mTranscoder->popEvent(), TestTranscoder::Pause(CLIENT(2), SESSION(0))); mUidPolicy->setTop(UID(0)); EXPECT_EQ(mTranscoder->popEvent(), TestTranscoder::NoEvent); mController->onThrottlingStopped(); EXPECT_EQ(mTranscoder->popEvent(), TestTranscoder::Resume(CLIENT(0), SESSION(0))); // Test 2: Cancel session during throttling, when throttling stops, new top // session should resume. mController->onThrottlingStarted(); EXPECT_EQ(mTranscoder->popEvent(), TestTranscoder::Pause(CLIENT(0), SESSION(0))); // Cancel the paused top session during throttling. EXPECT_TRUE(mController->cancel(CLIENT(0), SESSION(0))); EXPECT_EQ(mTranscoder->popEvent(), TestTranscoder::Stop(CLIENT(0), SESSION(0))); // Throttling stops, CLIENT(2)'s session should start. mController->onThrottlingStopped(); EXPECT_EQ(mTranscoder->popEvent(), TestTranscoder::Resume(CLIENT(2), SESSION(0))); // Test 3: Add new queue during throttling, when throttling stops, new top // session should resume. mController->onThrottlingStarted(); EXPECT_EQ(mTranscoder->popEvent(), TestTranscoder::Pause(CLIENT(2), SESSION(0))); // Move UID(2) to top. mUidPolicy->setTop(UID(2)); // Submit real-time session to CLIENT(3) in UID(2), session shouldn't start during throttling. mRealtimeRequest.clientPid = PID(2); mController->submit(CLIENT(3), SESSION(0), UID(3), UID(2), mRealtimeRequest, mClientCallback3); EXPECT_EQ(mTranscoder->popEvent(), TestTranscoder::NoEvent); // Throttling stops, CLIENT(3)'s session should start. mController->onThrottlingStopped(); EXPECT_EQ(mTranscoder->popEvent(), TestTranscoder::Start(CLIENT(3), SESSION(0))); } /* Test resource lost and thermal throttling happening simultaneously */ TEST_F(TranscodingSessionControllerTest, TestResourceLostAndThermalCallback) { ALOGD("TestResourceLostAndThermalCallback"); // Start with unspecified top UID. // Submit real-time session to CLIENT(0), session should start immediately. mRealtimeRequest.clientPid = PID(0); 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. mOfflineRequest.clientPid = PID(0); mController->submit(CLIENT(1), SESSION(0), UID(1), UID(0), mOfflineRequest, mClientCallback1); EXPECT_EQ(mTranscoder->popEvent(), TestTranscoder::NoEvent); // Move UID(1) to top. mUidPolicy->setTop(UID(1)); EXPECT_EQ(mTranscoder->popEvent(), TestTranscoder::NoEvent); // Submit real-time session to CLIENT(2) in different uid UID(1). // Should pause previous session and start new session. mRealtimeRequest.clientPid = PID(1); mController->submit(CLIENT(2), SESSION(0), UID(2), UID(1), mRealtimeRequest, mClientCallback2); EXPECT_EQ(mTranscoder->popEvent(), TestTranscoder::Pause(CLIENT(0), SESSION(0))); EXPECT_EQ(mTranscoder->popEvent(), TestTranscoder::Start(CLIENT(2), SESSION(0))); // Test 0: Resource lost during throttling. // Throttling starts, top session should pause. mController->onThrottlingStarted(); EXPECT_EQ(mTranscoder->popEvent(), TestTranscoder::Pause(CLIENT(2), SESSION(0))); // Signal resource lost, this should get ignored because the session is now paused. mController->onResourceLost(CLIENT(2), SESSION(0)); EXPECT_EQ(mResourcePolicy->getPid(), kInvalidPid); // Signal resource available, CLIENT(2) shouldn't resume. mController->onResourceAvailable(); EXPECT_EQ(mTranscoder->popEvent(), TestTranscoder::NoEvent); // Throttling ends, top session should resume. mController->onThrottlingStopped(); EXPECT_EQ(mTranscoder->popEvent(), TestTranscoder::Resume(CLIENT(2), SESSION(0))); // Test 1: Throttling during resource lost. mController->onResourceLost(CLIENT(2), SESSION(0)); EXPECT_EQ(mResourcePolicy->getPid(), PID(1)); mController->onThrottlingStarted(); mController->onThrottlingStopped(); EXPECT_EQ(mTranscoder->popEvent(), TestTranscoder::NoEvent); mController->onResourceAvailable(); EXPECT_EQ(mTranscoder->popEvent(), TestTranscoder::Resume(CLIENT(2), SESSION(0))); // Test 2: Interleaving resource lost and throttling. mController->onResourceLost(CLIENT(2), SESSION(0)); EXPECT_EQ(mResourcePolicy->getPid(), PID(1)); mController->onThrottlingStarted(); mController->onResourceAvailable(); EXPECT_EQ(mTranscoder->popEvent(), TestTranscoder::NoEvent); mController->onThrottlingStopped(); EXPECT_EQ(mTranscoder->popEvent(), TestTranscoder::Resume(CLIENT(2), SESSION(0))); } TEST_F(TranscodingSessionControllerTest, TestTranscoderWatchdogNoHeartbeat) { ALOGD("TestTranscoderWatchdogTimeout"); // Submit session to CLIENT(0) in UID(0). // Should start immediately (because this is the only session). mController->submit(CLIENT(0), SESSION(0), UID(0), UID(0), mRealtimeRequest, mClientCallback0); EXPECT_EQ(mTranscoder->popEvent(), TestTranscoder::Start(CLIENT(0), SESSION(0))); // Test 1: If not sending keep-alive at all, timeout after 3 seconds. expectTimeout(CLIENT(0), SESSION(0), 2); } TEST_F(TranscodingSessionControllerTest, TestTranscoderWatchdogHeartbeat) { // Test 2: No timeout as long as keep-alive coming; timeout after keep-alive stops. mController->submit(CLIENT(0), SESSION(1), UID(0), UID(0), mRealtimeRequest, mClientCallback0); EXPECT_EQ(mTranscoder->popEvent(), TestTranscoder::Start(CLIENT(0), SESSION(1))); for (int i = 0; i < 5; i++) { EXPECT_EQ(mTranscoder->popEvent(1000000), TestTranscoder::NoEvent); mController->onHeartBeat(CLIENT(0), SESSION(1)); } expectTimeout(CLIENT(0), SESSION(1), 2); } TEST_F(TranscodingSessionControllerTest, TestTranscoderWatchdogDuringPause) { int expectedGen = 2; // Test 3a: No timeout for paused session even if no keep-alive is sent. mController->submit(CLIENT(0), SESSION(2), UID(0), UID(0), mOfflineRequest, mClientCallback0); EXPECT_EQ(mTranscoder->popEvent(), TestTranscoder::Start(CLIENT(0), SESSION(2))); // Trigger a pause by sending a resource lost. mController->onResourceLost(CLIENT(0), SESSION(2)); EXPECT_EQ(mTranscoder->popEvent(3100000), TestTranscoder::NoEvent); mController->onResourceAvailable(); EXPECT_EQ(mTranscoder->popEvent(100000), TestTranscoder::Resume(CLIENT(0), SESSION(2))); expectTimeout(CLIENT(0), SESSION(2), expectedGen++); // Test 3b: No timeout for paused session even if no keep-alive is sent. mController->submit(CLIENT(0), SESSION(3), UID(0), UID(0), mOfflineRequest, mClientCallback0); EXPECT_EQ(mTranscoder->popEvent(), TestTranscoder::Start(CLIENT(0), SESSION(3))); // Let the session run almost to timeout, to test timeout reset after pause. EXPECT_EQ(mTranscoder->popEvent(2900000), TestTranscoder::NoEvent); // Trigger a pause by submitting a higher-priority request. mController->submit(CLIENT(0), SESSION(4), UID(0), UID(0), mRealtimeRequest, mClientCallback0); EXPECT_EQ(mTranscoder->popEvent(), TestTranscoder::Pause(CLIENT(0), SESSION(3))); EXPECT_EQ(mTranscoder->popEvent(), TestTranscoder::Start(CLIENT(0), SESSION(4))); // Finish the higher-priority session, lower-priority session should resume, // and the timeout should reset to full value. mController->onFinish(CLIENT(0), SESSION(4)); EXPECT_EQ(mTranscoder->popEvent(), TestTranscoder::Finished(CLIENT(0), SESSION(4))); EXPECT_EQ(mTranscoder->popEvent(), TestTranscoder::Resume(CLIENT(0), SESSION(3))); expectTimeout(CLIENT(0), SESSION(3), expectedGen++); } TEST_F(TranscodingSessionControllerTest, TestTranscoderPacerOverCountOnly) { ALOGD("TestTranscoderPacerOverCountOnly"); testPacerHelper(12 /*numSubmits*/, 100 /*sessionDurationMs*/, 12 /*expectedSuccess*/); } TEST_F(TranscodingSessionControllerTest, TestTranscoderPacerOverTimeOnly) { ALOGD("TestTranscoderPacerOverTimeOnly"); testPacerHelper(5 /*numSubmits*/, 1000 /*sessionDurationMs*/, 5 /*expectedSuccess*/); } TEST_F(TranscodingSessionControllerTest, TestTranscoderPacerOverQuota) { ALOGD("TestTranscoderPacerOverQuota"); testPacerHelper(12 /*numSubmits*/, 400 /*sessionDurationMs*/, 10 /*expectedSuccess*/); } TEST_F(TranscodingSessionControllerTest, TestTranscoderPacerWithPause) { ALOGD("TestTranscoderPacerDuringPause"); testPacerHelperWithPause(12 /*numSubmits*/, 400 /*sessionDurationMs*/, 10 /*expectedSuccess*/); } /* * 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. testPacerHelperWithMultipleUids(12 /*numSubmits*/, 400 /*sessionDurationMs*/, 10 /*expectedSuccess*/, mClientCallback0, {}); // Make UID(0) block on Client1's sessions too, Client1's quota should not be affected. testPacerHelperWithMultipleUids(12 /*numSubmits*/, 400 /*sessionDurationMs*/, 10 /*expectedSuccess*/, mClientCallback1, {UID(0)}); // 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. testPacerHelperWithMultipleUids(12 /*numSubmits*/, 400 /*sessionDurationMs*/, 11 /*expectedSuccess*/, mClientCallback2, {UID(10)}); } /* * Use same uid for clientUid and callingUid, should not be limited by quota. */ TEST_F(TranscodingSessionControllerTest, TestTranscoderPacerSelfUid) { ALOGD("TestTranscoderPacerSelfUid"); testPacerHelperWithSelfUid(12 /*numSubmits*/, 400 /*sessionDurationMs*/, 12 /*expectedSuccess*/); } } // namespace android