blob: acd9df12f9446410aa7e64818497a744bd8904d4 [file] [log] [blame]
/*
* 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 <iostream>
#include <list>
#include <aidl/android/media/BnResourceObserver.h>
#include <utils/Log.h>
#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<MediaObservableParcel> 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<MediaObservableParcel>& observables) {
return { Event::Busy, uid, pid, observables };
}
static Event Idle(int uid, int pid, const std::vector<MediaObservableParcel>& 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<Event> mEventQueue;
};
const EventTracker::Event EventTracker::NoEvent;
// 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<MediaObservableParcel>& 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(),
mObserverService(::ndk::SharedRefBase::make<ResourceObserverService>()),
mTestObserver1(::ndk::SharedRefBase::make<TestObserver>("observer1")),
mTestObserver2(::ndk::SharedRefBase::make<TestObserver>("observer2")),
mTestObserver3(::ndk::SharedRefBase::make<TestObserver>("observer3")) {
mService->setObserverService(mObserverService);
}
void registerObservers(MediaObservableEvent filter = ALL) {
std::vector<MediaObservableFilter> 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<ResourceObserverService> mObserverService;
std::shared_ptr<TestObserver> mTestObserver1;
std::shared_ptr<TestObserver> mTestObserver2;
std::shared_ptr<TestObserver> mTestObserver3;
};
TEST_F(ResourceObserverServiceTest, testRegisterObserver) {
std::vector<MediaObservableFilter> 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<MediaObservableFilter> 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<MediaObservableParcel> observables1, observables2, observables3;
observables1 = {{MediaObservableType::kVideoSecureCodec, 1}};
observables2 = {{MediaObservableType::kVideoNonSecureCodec, 1}};
observables3 = {{MediaObservableType::kVideoSecureCodec, 1},
{MediaObservableType::kVideoNonSecureCodec, 1}};
std::vector<MediaResourceParcel> resources;
// Add secure video codec.
resources = {MediaResource::CodecResource(1 /*secure*/, 1 /*video*/)};
mService->addResource(kTestPid1, kTestUid1, getId(mTestClient1), 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 = {MediaResource::CodecResource(0 /*secure*/, 1 /*video*/)};
mService->addResource(kTestPid2, kTestUid2, getId(mTestClient2), 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 = {MediaResource::CodecResource(1 /*secure*/, 1 /*video*/),
MediaResource::CodecResource(0 /*secure*/, 1 /*video*/)};
mService->addResource(kTestPid2, kTestUid2, getId(mTestClient3), 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(MediaResource::CodecResource(1 /*secure*/, 0 /*video*/));
resources.push_back(MediaResource::CodecResource(0 /*secure*/, 0 /*video*/));
mService->addResource(kTestPid1, kTestUid1, getId(mTestClient1), 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<MediaObservableParcel> observables1, observables2, observables3;
observables1 = {{MediaObservableType::kVideoSecureCodec, 1}};
observables2 = {{MediaObservableType::kVideoNonSecureCodec, 1}};
observables3 = {{MediaObservableType::kVideoSecureCodec, 1},
{MediaObservableType::kVideoNonSecureCodec, 1}};
std::vector<MediaResourceParcel> resources;
// Add multiple secure & non-secure video codecs.
// Multiple entries of the same type should be merged, count should be propagated correctly.
resources = {MediaResource::CodecResource(1 /*secure*/, 1 /*video*/),
MediaResource::CodecResource(1 /*secure*/, 1 /*video*/),
MediaResource::CodecResource(0 /*secure*/, 1 /*video*/, 3 /*count*/)};
observables1 = {{MediaObservableType::kVideoSecureCodec, 2}};
observables2 = {{MediaObservableType::kVideoNonSecureCodec, 3}};
observables3 = {{MediaObservableType::kVideoSecureCodec, 2},
{MediaObservableType::kVideoNonSecureCodec, 3}};
mService->addResource(kTestPid2, kTestUid2, getId(mTestClient3), 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<MediaObservableParcel> observables1, observables2, observables3;
observables1 = {{MediaObservableType::kVideoSecureCodec, 1}};
observables2 = {{MediaObservableType::kVideoNonSecureCodec, 1}};
observables3 = {{MediaObservableType::kVideoSecureCodec, 1},
{MediaObservableType::kVideoNonSecureCodec, 1}};
std::vector<MediaResourceParcel> resources;
// Add secure video codec to client1.
resources = {MediaResource::CodecResource(1 /*secure*/, 1 /*video*/)};
mService->addResource(kTestPid1, kTestUid1, getId(mTestClient1), 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(kTestPid1, getId(mTestClient1), 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(kTestPid1, getId(mTestClient1), 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(kTestPid1, getId(mTestClient1));
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 = {MediaResource::CodecResource(0 /*secure*/, 1 /*video*/)};
mService->addResource(kTestPid2, kTestUid2, getId(mTestClient2), 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(kTestPid2, getId(mTestClient2));
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(kTestPid2, getId(mTestClient2), 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(kTestPid2, getId(mTestClient2));
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 = {MediaResource::CodecResource(1 /*secure*/, 1 /*video*/),
MediaResource::CodecResource(0 /*secure*/, 1 /*video*/),
MediaResource::CodecResource(1 /*secure*/, 0 /*video*/),
MediaResource::CodecResource(0 /*secure*/, 0 /*video*/)};
mService->addResource(kTestPid2, kTestUid2, getId(mTestClient3), 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 = {MediaResource::CodecResource(1 /*secure*/, 0 /*video*/)};
mService->removeResource(kTestPid2, getId(mTestClient3), 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 = {MediaResource::CodecResource(0 /*secure*/, 0 /*video*/),
MediaResource::CodecResource(1 /*secure*/, 1 /*video*/)};
mService->removeResource(kTestPid2, getId(mTestClient3), 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(kTestPid2, getId(mTestClient3));
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<MediaObservableParcel> observables1, observables2, observables3;
observables1 = {{MediaObservableType::kVideoSecureCodec, 1}};
observables2 = {{MediaObservableType::kVideoNonSecureCodec, 1}};
observables3 = {{MediaObservableType::kVideoSecureCodec, 1},
{MediaObservableType::kVideoNonSecureCodec, 1}};
std::vector<MediaResourceParcel> resources;
// Add multiple secure & non-secure video codecs, plus audio codecs (that's ignored).
// (ResourceManager will merge these internally.)
resources = {MediaResource::CodecResource(1 /*secure*/, 1 /*video*/),
MediaResource::CodecResource(0 /*secure*/, 1 /*video*/, 4 /*count*/),
MediaResource::CodecResource(1 /*secure*/, 0 /*video*/),
MediaResource::CodecResource(0 /*secure*/, 0 /*video*/)};
mService->addResource(kTestPid2, kTestUid2, getId(mTestClient3), 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 = {MediaResource::CodecResource(0 /*secure*/, 0 /*video*/),
MediaResource::CodecResource(1 /*secure*/, 1 /*video*/),
MediaResource::CodecResource(1 /*secure*/, 1 /*video*/),
MediaResource::CodecResource(0 /*secure*/, 1 /*video*/, 2 /*count*/)};
mService->removeResource(kTestPid2, getId(mTestClient3), 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(kTestPid2, getId(mTestClient3));
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<MediaObservableFilter> 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<MediaObservableParcel> observables1, observables2;
observables1 = {{MediaObservableType::kVideoSecureCodec, 1}};
observables2 = {{MediaObservableType::kVideoNonSecureCodec, 1}};
std::vector<MediaResourceParcel> resources;
// Add secure & non-secure video codecs.
resources = {MediaResource::CodecResource(1 /*secure*/, 1 /*video*/),
MediaResource::CodecResource(0 /*secure*/, 1 /*video*/)};
mService->addResource(kTestPid2, kTestUid2, getId(mTestClient3), 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(kTestPid2, getId(mTestClient3), 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