/* * Copyright 2020 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ //#define LOG_NDEBUG 0 #define LOG_TAG "ResourceObserverService_test" #include #include #include #include #include "ResourceObserverService.h" #include "ResourceManagerServiceTestUtils.h" namespace android { using ::aidl::android::media::BnResourceObserver; using ::aidl::android::media::MediaObservableParcel; using ::aidl::android::media::MediaObservableType; #define BUSY ::aidl::android::media::MediaObservableEvent::kBusy #define IDLE ::aidl::android::media::MediaObservableEvent::kIdle #define ALL ::aidl::android::media::MediaObservableEvent::kAll struct EventTracker { struct Event { enum { NoEvent, Busy, Idle } type = NoEvent; int uid = 0; int pid = 0; std::vector observables; }; static const Event NoEvent; static std::string toString(const MediaObservableParcel& observable) { return "{" + ::aidl::android::media::toString(observable.type) + ", " + std::to_string(observable.value) + "}"; } static std::string toString(const Event& event) { std::string eventStr; switch (event.type) { case Event::Busy: eventStr = "Busy"; break; case Event::Idle: eventStr = "Idle"; break; default: return "NoEvent"; } std::string observableStr; for (auto &observable : event.observables) { if (!observableStr.empty()) { observableStr += ", "; } observableStr += toString(observable); } return "{" + eventStr + ", " + std::to_string(event.uid) + ", " + std::to_string(event.pid) + ", {" + observableStr + "}}"; } static Event Busy(int uid, int pid, const std::vector& observables) { return { Event::Busy, uid, pid, observables }; } static Event Idle(int uid, int pid, const std::vector& observables) { return { Event::Idle, uid, pid, observables }; } // Pop 1 event from front, wait for up to timeoutUs if empty. const Event& pop(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; } // Push 1 event to back. void append(const Event& event) { ALOGD("%s", toString(event).c_str()); std::unique_lock lock(mLock); mEventQueue.push_back(event); mCondition.notify_one(); } private: std::mutex mLock; std::condition_variable mCondition; Event mPoppedEvent; std::list mEventQueue; }; const EventTracker::Event EventTracker::NoEvent; static MediaResource createSecureVideoCodecResource(int amount = 1) { return MediaResource(MediaResource::Type::kSecureCodec, MediaResource::SubType::kHwVideoCodec, amount); } static MediaResource createNonSecureVideoCodecResource(int amount = 1) { return MediaResource(MediaResource::Type::kNonSecureCodec, MediaResource::SubType::kHwVideoCodec, amount); } static MediaResource createSecureAudioCodecResource(int amount = 1) { return MediaResource(MediaResource::Type::kSecureCodec, MediaResource::SubType::kHwAudioCodec, amount); } static MediaResource createNonSecureAudioCodecResource(int amount = 1) { return MediaResource(MediaResource::Type::kNonSecureCodec, MediaResource::SubType::kHwAudioCodec, amount); } // Operators for GTest macros. bool operator==(const EventTracker::Event& lhs, const EventTracker::Event& rhs) { return lhs.type == rhs.type && lhs.uid == rhs.uid && lhs.pid == rhs.pid && lhs.observables == rhs.observables; } std::ostream& operator<<(std::ostream& str, const EventTracker::Event& v) { str << EventTracker::toString(v); return str; } struct TestObserver : public BnResourceObserver, public EventTracker { TestObserver(const char *name) : mName(name) {} ~TestObserver() = default; Status onStatusChanged(MediaObservableEvent event, int32_t uid, int32_t pid, const std::vector& observables) override { ALOGD("%s: %s", mName.c_str(), __FUNCTION__); if (event == MediaObservableEvent::kBusy) { append(Busy(uid, pid, observables)); } else { append(Idle(uid, pid, observables)); } return Status::ok(); } std::string mName; }; class ResourceObserverServiceTest : public ResourceManagerServiceTestBase { public: ResourceObserverServiceTest() : ResourceManagerServiceTestBase() {} void SetUp() override { ResourceManagerServiceTestBase::SetUp(); mObserverService = ::ndk::SharedRefBase::make(); mTestObserver1 = ::ndk::SharedRefBase::make("observer1"); mTestObserver2 = ::ndk::SharedRefBase::make("observer2"); mTestObserver3 = ::ndk::SharedRefBase::make("observer3"); mService->setObserverService(mObserverService); } void registerObservers(MediaObservableEvent filter = ALL) { std::vector filters1, filters2, filters3; filters1 = {{MediaObservableType::kVideoSecureCodec, filter}}; filters2 = {{MediaObservableType::kVideoNonSecureCodec, filter}}; filters3 = {{MediaObservableType::kVideoSecureCodec, filter}, {MediaObservableType::kVideoNonSecureCodec, filter}}; // mTestObserver1 monitors secure video codecs. EXPECT_TRUE(mObserverService->registerObserver(mTestObserver1, filters1).isOk()); // mTestObserver2 monitors non-secure video codecs. EXPECT_TRUE(mObserverService->registerObserver(mTestObserver2, filters2).isOk()); // mTestObserver3 monitors both secure & non-secure video codecs. EXPECT_TRUE(mObserverService->registerObserver(mTestObserver3, filters3).isOk()); } protected: std::shared_ptr mObserverService; std::shared_ptr mTestObserver1; std::shared_ptr mTestObserver2; std::shared_ptr mTestObserver3; }; TEST_F(ResourceObserverServiceTest, testRegisterObserver) { std::vector filters1; Status status; // Register with null observer should fail. status = mObserverService->registerObserver(nullptr, filters1); EXPECT_FALSE(status.isOk()); EXPECT_EQ(status.getServiceSpecificError(), BAD_VALUE); // Register with empty observables should fail. status = mObserverService->registerObserver(mTestObserver1, filters1); EXPECT_FALSE(status.isOk()); EXPECT_EQ(status.getServiceSpecificError(), BAD_VALUE); // mTestObserver1 monitors secure video codecs. filters1 = {{MediaObservableType::kVideoSecureCodec, ALL}}; EXPECT_TRUE(mObserverService->registerObserver(mTestObserver1, filters1).isOk()); // Register duplicates should fail. status = mObserverService->registerObserver(mTestObserver1, filters1); EXPECT_FALSE(status.isOk()); EXPECT_EQ(status.getServiceSpecificError(), ALREADY_EXISTS); } TEST_F(ResourceObserverServiceTest, testUnregisterObserver) { std::vector filters1; Status status; // Unregister without registering first should fail. status = mObserverService->unregisterObserver(mTestObserver1); EXPECT_FALSE(status.isOk()); EXPECT_EQ(status.getServiceSpecificError(), NAME_NOT_FOUND); // mTestObserver1 monitors secure video codecs. filters1 = {{MediaObservableType::kVideoSecureCodec, ALL}}; EXPECT_TRUE(mObserverService->registerObserver(mTestObserver1, filters1).isOk()); EXPECT_TRUE(mObserverService->unregisterObserver(mTestObserver1).isOk()); // Unregister again should fail. status = mObserverService->unregisterObserver(mTestObserver1); EXPECT_FALSE(status.isOk()); EXPECT_EQ(status.getServiceSpecificError(), NAME_NOT_FOUND); } TEST_F(ResourceObserverServiceTest, testAddResourceBasic) { registerObservers(); std::vector observables1, observables2, observables3; observables1 = {{MediaObservableType::kVideoSecureCodec, 1}}; observables2 = {{MediaObservableType::kVideoNonSecureCodec, 1}}; observables3 = {{MediaObservableType::kVideoSecureCodec, 1}, {MediaObservableType::kVideoNonSecureCodec, 1}}; ClientInfoParcel client1Info{.pid = static_cast(kTestPid1), .uid = static_cast(kTestUid1), .id = getId(mTestClient1), .name = "none"}; ClientInfoParcel client2Info{.pid = static_cast(kTestPid2), .uid = static_cast(kTestUid2), .id = getId(mTestClient2), .name = "none"}; ClientInfoParcel client3Info{.pid = static_cast(kTestPid2), .uid = static_cast(kTestUid2), .id = getId(mTestClient3), .name = "none"}; std::vector resources; // Add secure video codec. resources = {createSecureVideoCodecResource()}; mService->addResource(client1Info, mTestClient1, resources); EXPECT_EQ(mTestObserver1->pop(), EventTracker::Busy(kTestUid1, kTestPid1, observables1)); EXPECT_EQ(mTestObserver2->pop(), EventTracker::NoEvent); EXPECT_EQ(mTestObserver3->pop(), EventTracker::Busy(kTestUid1, kTestPid1, observables1)); // Add non-secure video codec. resources = {createNonSecureVideoCodecResource()}; mService->addResource(client2Info, mTestClient2, resources); EXPECT_EQ(mTestObserver1->pop(), EventTracker::NoEvent); EXPECT_EQ(mTestObserver2->pop(), EventTracker::Busy(kTestUid2, kTestPid2, observables2)); EXPECT_EQ(mTestObserver3->pop(), EventTracker::Busy(kTestUid2, kTestPid2, observables2)); // Add secure & non-secure video codecs. resources = {createSecureVideoCodecResource(), createNonSecureVideoCodecResource()}; mService->addResource(client3Info, mTestClient3, resources); EXPECT_EQ(mTestObserver1->pop(), EventTracker::Busy(kTestUid2, kTestPid2, observables1)); EXPECT_EQ(mTestObserver2->pop(), EventTracker::Busy(kTestUid2, kTestPid2, observables2)); EXPECT_EQ(mTestObserver3->pop(), EventTracker::Busy(kTestUid2, kTestPid2, observables3)); // Add additional audio codecs, should be ignored. resources.push_back(createSecureAudioCodecResource()); resources.push_back(createNonSecureAudioCodecResource()); mService->addResource(client1Info, mTestClient1, resources); EXPECT_EQ(mTestObserver1->pop(), EventTracker::Busy(kTestUid1, kTestPid1, observables1)); EXPECT_EQ(mTestObserver2->pop(), EventTracker::Busy(kTestUid1, kTestPid1, observables2)); EXPECT_EQ(mTestObserver3->pop(), EventTracker::Busy(kTestUid1, kTestPid1, observables3)); } TEST_F(ResourceObserverServiceTest, testAddResourceMultiple) { registerObservers(); std::vector observables1, observables2, observables3; observables1 = {{MediaObservableType::kVideoSecureCodec, 1}}; observables2 = {{MediaObservableType::kVideoNonSecureCodec, 1}}; observables3 = {{MediaObservableType::kVideoSecureCodec, 1}, {MediaObservableType::kVideoNonSecureCodec, 1}}; std::vector resources; // Add multiple secure & non-secure video codecs. // Multiple entries of the same type should be merged, count should be propagated correctly. resources = {createSecureVideoCodecResource(), createSecureVideoCodecResource(), createNonSecureVideoCodecResource(3)}; observables1 = {{MediaObservableType::kVideoSecureCodec, 2}}; observables2 = {{MediaObservableType::kVideoNonSecureCodec, 3}}; observables3 = {{MediaObservableType::kVideoSecureCodec, 2}, {MediaObservableType::kVideoNonSecureCodec, 3}}; ClientInfoParcel client3Info{.pid = static_cast(kTestPid2), .uid = static_cast(kTestUid2), .id = getId(mTestClient3), .name = "none"}; mService->addResource(client3Info, mTestClient3, resources); EXPECT_EQ(mTestObserver1->pop(), EventTracker::Busy(kTestUid2, kTestPid2, observables1)); EXPECT_EQ(mTestObserver2->pop(), EventTracker::Busy(kTestUid2, kTestPid2, observables2)); EXPECT_EQ(mTestObserver3->pop(), EventTracker::Busy(kTestUid2, kTestPid2, observables3)); } TEST_F(ResourceObserverServiceTest, testRemoveResourceBasic) { registerObservers(); std::vector observables1, observables2, observables3; observables1 = {{MediaObservableType::kVideoSecureCodec, 1}}; observables2 = {{MediaObservableType::kVideoNonSecureCodec, 1}}; observables3 = {{MediaObservableType::kVideoSecureCodec, 1}, {MediaObservableType::kVideoNonSecureCodec, 1}}; ClientInfoParcel client1Info{.pid = static_cast(kTestPid1), .uid = static_cast(kTestUid1), .id = getId(mTestClient1), .name = "none"}; ClientInfoParcel client2Info{.pid = static_cast(kTestPid2), .uid = static_cast(kTestUid2), .id = getId(mTestClient2), .name = "none"}; ClientInfoParcel client3Info{.pid = static_cast(kTestPid2), .uid = static_cast(kTestUid2), .id = getId(mTestClient3), .name = "none"}; std::vector resources; // Add secure video codec to client1. resources = {createSecureVideoCodecResource()}; mService->addResource(client1Info, mTestClient1, resources); EXPECT_EQ(mTestObserver1->pop(), EventTracker::Busy(kTestUid1, kTestPid1, observables1)); EXPECT_EQ(mTestObserver2->pop(), EventTracker::NoEvent); EXPECT_EQ(mTestObserver3->pop(), EventTracker::Busy(kTestUid1, kTestPid1, observables1)); // Remove secure video codec. observer 1&3 should receive updates. mService->removeResource(client1Info, resources); EXPECT_EQ(mTestObserver1->pop(), EventTracker::Idle(kTestUid1, kTestPid1, observables1)); EXPECT_EQ(mTestObserver2->pop(), EventTracker::NoEvent); EXPECT_EQ(mTestObserver3->pop(), EventTracker::Idle(kTestUid1, kTestPid1, observables1)); // Remove secure video codec again, should have no event. mService->removeResource(client1Info, resources); EXPECT_EQ(mTestObserver1->pop(), EventTracker::NoEvent); EXPECT_EQ(mTestObserver2->pop(), EventTracker::NoEvent); EXPECT_EQ(mTestObserver3->pop(), EventTracker::NoEvent); // Remove client1, should have no event. mService->removeClient(client1Info); EXPECT_EQ(mTestObserver1->pop(), EventTracker::NoEvent); EXPECT_EQ(mTestObserver2->pop(), EventTracker::NoEvent); EXPECT_EQ(mTestObserver3->pop(), EventTracker::NoEvent); // Add non-secure video codec to client2. resources = {createNonSecureVideoCodecResource()}; mService->addResource(client2Info, mTestClient2, resources); EXPECT_EQ(mTestObserver1->pop(), EventTracker::NoEvent); EXPECT_EQ(mTestObserver2->pop(), EventTracker::Busy(kTestUid2, kTestPid2, observables2)); EXPECT_EQ(mTestObserver3->pop(), EventTracker::Busy(kTestUid2, kTestPid2, observables2)); // Remove client2, observer 2&3 should receive updates. mService->removeClient(client2Info); EXPECT_EQ(mTestObserver1->pop(), EventTracker::NoEvent); EXPECT_EQ(mTestObserver2->pop(), EventTracker::Idle(kTestUid2, kTestPid2, observables2)); EXPECT_EQ(mTestObserver3->pop(), EventTracker::Idle(kTestUid2, kTestPid2, observables2)); // Remove non-secure codec after client2 removed, should have no event. mService->removeResource(client2Info, resources); EXPECT_EQ(mTestObserver1->pop(), EventTracker::NoEvent); EXPECT_EQ(mTestObserver2->pop(), EventTracker::NoEvent); EXPECT_EQ(mTestObserver3->pop(), EventTracker::NoEvent); // Remove client2 again, should have no event. mService->removeClient(client2Info); EXPECT_EQ(mTestObserver1->pop(), EventTracker::NoEvent); EXPECT_EQ(mTestObserver2->pop(), EventTracker::NoEvent); EXPECT_EQ(mTestObserver3->pop(), EventTracker::NoEvent); // Add secure & non-secure video codecs, plus audio codecs (that's ignored). resources = {createSecureVideoCodecResource(), createNonSecureVideoCodecResource(), createSecureAudioCodecResource(), createNonSecureAudioCodecResource()}; mService->addResource(client3Info, mTestClient3, resources); EXPECT_EQ(mTestObserver1->pop(), EventTracker::Busy(kTestUid2, kTestPid2, observables1)); EXPECT_EQ(mTestObserver2->pop(), EventTracker::Busy(kTestUid2, kTestPid2, observables2)); EXPECT_EQ(mTestObserver3->pop(), EventTracker::Busy(kTestUid2, kTestPid2, observables3)); // Remove one audio codec, should have no event. resources = {createSecureAudioCodecResource()}; mService->removeResource(client3Info, resources); EXPECT_EQ(mTestObserver1->pop(), EventTracker::NoEvent); EXPECT_EQ(mTestObserver2->pop(), EventTracker::NoEvent); EXPECT_EQ(mTestObserver3->pop(), EventTracker::NoEvent); // Remove the other audio codec and the secure video codec, only secure video codec // removal should be reported. resources = {createNonSecureAudioCodecResource(), createSecureVideoCodecResource()}; mService->removeResource(client3Info, resources); EXPECT_EQ(mTestObserver1->pop(), EventTracker::Idle(kTestUid2, kTestPid2, observables1)); EXPECT_EQ(mTestObserver2->pop(), EventTracker::NoEvent); EXPECT_EQ(mTestObserver3->pop(), EventTracker::Idle(kTestUid2, kTestPid2, observables1)); // Remove client3 entirely. Non-secure video codec removal should be reported. mService->removeClient(client3Info); EXPECT_EQ(mTestObserver1->pop(), EventTracker::NoEvent); EXPECT_EQ(mTestObserver2->pop(), EventTracker::Idle(kTestUid2, kTestPid2, observables2)); EXPECT_EQ(mTestObserver3->pop(), EventTracker::Idle(kTestUid2, kTestPid2, observables2)); } TEST_F(ResourceObserverServiceTest, testRemoveResourceMultiple) { registerObservers(); std::vector observables1, observables2, observables3; observables1 = {{MediaObservableType::kVideoSecureCodec, 1}}; observables2 = {{MediaObservableType::kVideoNonSecureCodec, 1}}; observables3 = {{MediaObservableType::kVideoSecureCodec, 1}, {MediaObservableType::kVideoNonSecureCodec, 1}}; std::vector resources; // Add multiple secure & non-secure video codecs, plus audio codecs (that's ignored). // (ResourceManager will merge these internally.) resources = {createSecureVideoCodecResource(), createNonSecureVideoCodecResource(4), createSecureAudioCodecResource(), createNonSecureAudioCodecResource()}; ClientInfoParcel client3Info{.pid = static_cast(kTestPid2), .uid = static_cast(kTestUid2), .id = getId(mTestClient3), .name = "none"}; mService->addResource(client3Info, mTestClient3, resources); observables1 = {{MediaObservableType::kVideoSecureCodec, 1}}; observables2 = {{MediaObservableType::kVideoNonSecureCodec, 4}}; observables3 = {{MediaObservableType::kVideoSecureCodec, 1}, {MediaObservableType::kVideoNonSecureCodec, 4}}; EXPECT_EQ(mTestObserver1->pop(), EventTracker::Busy(kTestUid2, kTestPid2, observables1)); EXPECT_EQ(mTestObserver2->pop(), EventTracker::Busy(kTestUid2, kTestPid2, observables2)); EXPECT_EQ(mTestObserver3->pop(), EventTracker::Busy(kTestUid2, kTestPid2, observables3)); // Remove one audio codec, 2 secure video codecs and 2 non-secure video codecs. // 1 secure video codec removal and 2 non-secure video codec removals should be reported. resources = {createNonSecureAudioCodecResource(), createSecureVideoCodecResource(), createSecureVideoCodecResource(), createNonSecureVideoCodecResource(2)}; mService->removeResource(client3Info, resources); observables1 = {{MediaObservableType::kVideoSecureCodec, 1}}; observables2 = {{MediaObservableType::kVideoNonSecureCodec, 2}}; observables3 = {{MediaObservableType::kVideoSecureCodec, 1}, {MediaObservableType::kVideoNonSecureCodec, 2}}; EXPECT_EQ(mTestObserver1->pop(), EventTracker::Idle(kTestUid2, kTestPid2, observables1)); EXPECT_EQ(mTestObserver2->pop(), EventTracker::Idle(kTestUid2, kTestPid2, observables2)); EXPECT_EQ(mTestObserver3->pop(), EventTracker::Idle(kTestUid2, kTestPid2, observables3)); // Remove client3 entirely. 2 non-secure video codecs removal should be reported. mService->removeClient(client3Info); EXPECT_EQ(mTestObserver1->pop(), EventTracker::NoEvent); EXPECT_EQ(mTestObserver2->pop(), EventTracker::Idle(kTestUid2, kTestPid2, observables2)); EXPECT_EQ(mTestObserver3->pop(), EventTracker::Idle(kTestUid2, kTestPid2, observables2)); } TEST_F(ResourceObserverServiceTest, testEventFilters) { // Register observers with different event filters. std::vector filters1, filters2, filters3; filters1 = {{MediaObservableType::kVideoSecureCodec, BUSY}}; filters2 = {{MediaObservableType::kVideoNonSecureCodec, IDLE}}; filters3 = {{MediaObservableType::kVideoSecureCodec, IDLE}, {MediaObservableType::kVideoNonSecureCodec, BUSY}}; // mTestObserver1 monitors secure video codecs. EXPECT_TRUE(mObserverService->registerObserver(mTestObserver1, filters1).isOk()); // mTestObserver2 monitors non-secure video codecs. EXPECT_TRUE(mObserverService->registerObserver(mTestObserver2, filters2).isOk()); // mTestObserver3 monitors both secure & non-secure video codecs. EXPECT_TRUE(mObserverService->registerObserver(mTestObserver3, filters3).isOk()); std::vector observables1, observables2; observables1 = {{MediaObservableType::kVideoSecureCodec, 1}}; observables2 = {{MediaObservableType::kVideoNonSecureCodec, 1}}; std::vector resources; // Add secure & non-secure video codecs. resources = {createSecureVideoCodecResource(), createNonSecureVideoCodecResource()}; ClientInfoParcel client1Info{.pid = static_cast(kTestPid1), .uid = static_cast(kTestUid1), .id = getId(mTestClient1), .name = "none"}; ClientInfoParcel client2Info{.pid = static_cast(kTestPid2), .uid = static_cast(kTestUid2), .id = getId(mTestClient2), .name = "none"}; ClientInfoParcel client3Info{.pid = static_cast(kTestPid2), .uid = static_cast(kTestUid2), .id = getId(mTestClient3), .name = "none"}; mService->addResource(client3Info, mTestClient3, resources); EXPECT_EQ(mTestObserver1->pop(), EventTracker::Busy(kTestUid2, kTestPid2, observables1)); EXPECT_EQ(mTestObserver2->pop(), EventTracker::NoEvent); EXPECT_EQ(mTestObserver3->pop(), EventTracker::Busy(kTestUid2, kTestPid2, observables2)); // Remove secure & non-secure video codecs. mService->removeResource(client3Info, resources); EXPECT_EQ(mTestObserver1->pop(), EventTracker::NoEvent); EXPECT_EQ(mTestObserver2->pop(), EventTracker::Idle(kTestUid2, kTestPid2, observables2)); EXPECT_EQ(mTestObserver3->pop(), EventTracker::Idle(kTestUid2, kTestPid2, observables1)); } } // namespace android