Add more tests for battery checker code

There is no change on functionality, only refactoring
to allow unit testing.

bug: 138381810
bug: 139440234

Change-Id: Id927c091607d9ea5ea84b6e292d53154ef5171a6
gugelfrei
Chong Zhang 5 years ago
parent 49851fcb64
commit ad2ceb8560

@ -48,6 +48,7 @@
#include <media/stagefright/foundation/avc_utils.h>
#include <media/stagefright/foundation/hexdump.h>
#include <media/stagefright/ACodec.h>
#include <media/stagefright/BatteryChecker.h>
#include <media/stagefright/BufferProducerWrapper.h>
#include <media/stagefright/MediaCodec.h>
#include <media/stagefright/MediaCodecList.h>
@ -116,7 +117,6 @@ static bool isResourceError(status_t err) {
static const int kMaxRetry = 2;
static const int kMaxReclaimWaitTimeInUs = 500000; // 0.5s
static const int kNumBuffersAlign = 16;
static const int kBatteryStatsTimeoutUs = 3000000ll; // 3 seconds
////////////////////////////////////////////////////////////////////////////////
@ -539,10 +539,7 @@ MediaCodec::MediaCodec(const sp<ALooper> &looper, pid_t pid, uid_t uid)
mHaveInputSurface(false),
mHavePendingInputBuffers(false),
mCpuBoostRequested(false),
mLatencyUnknown(0),
mLastActivityTimeUs(-1ll),
mBatteryStatNotified(false),
mBatteryCheckerGeneration(0) {
mLatencyUnknown(0) {
if (uid == kNoUid) {
mUid = IPCThreadState::self()->getCallingUid();
} else {
@ -755,7 +752,11 @@ void MediaCodec::statsBufferSent(int64_t presentationUs) {
return;
}
scheduleBatteryCheckerIfNeeded();
if (mBatteryChecker != nullptr) {
mBatteryChecker->onCodecActivity([this] () {
addResource(MediaResource::kBattery, MediaResource::kVideoCodec, 1);
});
}
const int64_t nowNs = systemTime(SYSTEM_TIME_MONOTONIC);
BufferFlightTiming_t startdata = { presentationUs, nowNs };
@ -791,7 +792,11 @@ void MediaCodec::statsBufferReceived(int64_t presentationUs) {
return;
}
scheduleBatteryCheckerIfNeeded();
if (mBatteryChecker != nullptr) {
mBatteryChecker->onCodecActivity([this] () {
addResource(MediaResource::kBattery, MediaResource::kVideoCodec, 1);
});
}
BufferFlightTiming_t startdata;
bool valid = false;
@ -981,6 +986,10 @@ status_t MediaCodec::init(const AString &name) {
mAnalyticsItem->setCString(kCodecMode, mIsVideo ? kCodecModeVideo : kCodecModeAudio);
}
if (mIsVideo) {
mBatteryChecker = new BatteryChecker(new AMessage(kWhatCheckBatteryStats, this));
}
status_t err;
Vector<MediaResource> resources;
MediaResource::Type type =
@ -1706,19 +1715,27 @@ void MediaCodec::requestCpuBoostIfNeeded() {
}
}
void MediaCodec::scheduleBatteryCheckerIfNeeded() {
if (!mIsVideo || !isExecuting()) {
BatteryChecker::BatteryChecker(const sp<AMessage> &msg, int64_t timeoutUs)
: mTimeoutUs(timeoutUs)
, mLastActivityTimeUs(-1ll)
, mBatteryStatNotified(false)
, mBatteryCheckerGeneration(0)
, mIsExecuting(false)
, mBatteryCheckerMsg(msg) {}
void BatteryChecker::onCodecActivity(std::function<void()> batteryOnCb) {
if (!isExecuting()) {
// ignore if not executing
return;
}
if (!mBatteryStatNotified) {
addResource(MediaResource::kBattery, MediaResource::kVideoCodec, 1);
batteryOnCb();
mBatteryStatNotified = true;
sp<AMessage> msg = new AMessage(kWhatCheckBatteryStats, this);
sp<AMessage> msg = mBatteryCheckerMsg->dup();
msg->setInt32("generation", mBatteryCheckerGeneration);
// post checker and clear last activity time
msg->post(kBatteryStatsTimeoutUs);
msg->post(mTimeoutUs);
mLastActivityTimeUs = -1ll;
} else {
// update last activity time
@ -1726,7 +1743,8 @@ void MediaCodec::scheduleBatteryCheckerIfNeeded() {
}
}
void MediaCodec::onBatteryChecker(const sp<AMessage> &msg) {
void BatteryChecker::onCheckBatteryTimer(
const sp<AMessage> &msg, std::function<void()> batteryOffCb) {
// ignore if this checker already expired because the client resource was removed
int32_t generation;
if (!msg->findInt32("generation", &generation)
@ -1736,15 +1754,20 @@ void MediaCodec::onBatteryChecker(const sp<AMessage> &msg) {
if (mLastActivityTimeUs < 0ll) {
// timed out inactive, do not repost checker
removeResource(MediaResource::kBattery, MediaResource::kVideoCodec, 1);
batteryOffCb();
mBatteryStatNotified = false;
} else {
// repost checker and clear last activity time
msg->post(kBatteryStatsTimeoutUs + mLastActivityTimeUs - ALooper::GetNowUs());
msg->post(mTimeoutUs + mLastActivityTimeUs - ALooper::GetNowUs());
mLastActivityTimeUs = -1ll;
}
}
void BatteryChecker::onClientRemoved() {
mBatteryStatNotified = false;
mBatteryCheckerGeneration++;
}
////////////////////////////////////////////////////////////////////////////////
void MediaCodec::cancelPendingDequeueOperations() {
@ -2382,8 +2405,10 @@ void MediaCodec::onMessageReceived(const sp<AMessage> &msg) {
mFlags &= ~kFlagIsComponentAllocated;
// off since we're removing all resources including the battery on
mBatteryStatNotified = false;
mBatteryCheckerGeneration++;
if (mBatteryChecker != nullptr) {
mBatteryChecker->onClientRemoved();
}
mResourceManagerService->removeClient(getId(mResourceManagerClient));
(new AMessage)->postReply(mReplyID);
@ -3097,7 +3122,11 @@ void MediaCodec::onMessageReceived(const sp<AMessage> &msg) {
case kWhatCheckBatteryStats:
{
onBatteryChecker(msg);
if (mBatteryChecker != nullptr) {
mBatteryChecker->onCheckBatteryTimer(msg, [this] () {
removeResource(MediaResource::kBattery, MediaResource::kVideoCodec, 1);
});
}
break;
}
@ -3197,6 +3226,10 @@ void MediaCodec::setState(State newState) {
mState = newState;
if (mBatteryChecker != nullptr) {
mBatteryChecker->setExecuting(isExecuting());
}
cancelPendingDequeueOperations();
}

@ -7,6 +7,9 @@
"include-annotation": "android.platform.test.annotations.RequiresDevice"
}
]
},
{
"name": "BatteryChecker_test"
}
]
}

@ -0,0 +1,47 @@
/*
* Copyright (C) 2019 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.
*/
#ifndef BATTERY_CHECKER_H_
#define BATTERY_CHECKER_H_
#include <media/stagefright/foundation/AMessage.h>
namespace android {
struct BatteryChecker : public RefBase {
BatteryChecker(const sp<AMessage> &msg, int64_t timeout = 3000000ll);
void setExecuting(bool executing) { mIsExecuting = executing; }
void onCodecActivity(std::function<void()> batteryOnCb);
void onCheckBatteryTimer(const sp<AMessage>& msg, std::function<void()> batteryOffCb);
void onClientRemoved();
private:
const int64_t mTimeoutUs;
int64_t mLastActivityTimeUs;
bool mBatteryStatNotified;
int32_t mBatteryCheckerGeneration;
bool mIsExecuting;
sp<AMessage> mBatteryCheckerMsg;
bool isExecuting() { return mIsExecuting; }
DISALLOW_EVIL_CONSTRUCTORS(BatteryChecker);
};
} // namespace android
#endif // BATTERY_CHECKER_H_

@ -36,6 +36,7 @@ struct ABuffer;
struct AMessage;
struct AReplyToken;
struct AString;
struct BatteryChecker;
class BufferChannelBase;
struct CodecBase;
class IBatteryStats;
@ -463,11 +464,7 @@ private:
Mutex mLatencyLock;
int64_t mLatencyUnknown; // buffers for which we couldn't calculate latency
int64_t mLastActivityTimeUs;
bool mBatteryStatNotified;
int32_t mBatteryCheckerGeneration;
void onBatteryChecker(const sp<AMessage>& msg);
void scheduleBatteryCheckerIfNeeded();
sp<BatteryChecker> mBatteryChecker;
void statsBufferSent(int64_t presentationUs);
void statsBufferReceived(int64_t presentationUs);

@ -27,3 +27,21 @@ cc_test {
"-Wall",
],
}
cc_test {
name: "BatteryChecker_test",
srcs: ["BatteryChecker_test.cpp"],
test_suites: ["device-tests"],
shared_libs: [
"libstagefright",
"libstagefright_foundation",
"libutils",
"liblog",
],
cflags: [
"-Werror",
"-Wall",
],
}

@ -0,0 +1,242 @@
/*
* Copyright 2019 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 "BatteryChecker_test"
#include <utils/Log.h>
#include <gtest/gtest.h>
#include <media/stagefright/BatteryChecker.h>
#include <media/stagefright/foundation/ADebug.h>
#include <media/stagefright/foundation/AHandler.h>
#include <vector>
namespace android {
static const int kBatteryTimeoutUs = 1000000ll; // 1 seconds
static const int kTestMarginUs = 50000ll; // 50ms
static const int kWaitStatusChangeUs = kBatteryTimeoutUs + kTestMarginUs;
static const int kSparseFrameIntervalUs = kBatteryTimeoutUs - kTestMarginUs;
class BatteryCheckerTestHandler : public AHandler {
enum EventType {
// Events simulating MediaCodec
kWhatStart = 0, // codec entering executing state
kWhatStop, // codec exiting executing state
kWhatActivity, // codec queue input or dequeue output
kWhatReleased, // codec released
kWhatCheckpoint, // test checkpoing with expected values on On/Off
// Message for battery checker monitor (not for testing through runTest())
kWhatBatteryChecker,
};
struct Operation {
int32_t event;
int64_t delay = 0;
uint32_t repeatCount = 0;
int32_t expectedOnCounter = 0;
int32_t expectedOffCounter = 0;
};
std::vector<Operation> mOps;
sp<BatteryChecker> mBatteryChecker;
int32_t mOnCounter;
int32_t mOffCounter;
Condition mDone;
Mutex mLock;
BatteryCheckerTestHandler() : mOnCounter(0), mOffCounter(0) {}
void runTest(const std::vector<Operation> &ops, int64_t timeoutUs) {
mOps = ops;
mBatteryChecker = new BatteryChecker(
new AMessage(kWhatBatteryChecker, this), kBatteryTimeoutUs);
(new AMessage(ops[0].event, this))->post();
// wait for done
AutoMutex lock(mLock);
EXPECT_NE(TIMED_OUT, mDone.waitRelative(mLock, timeoutUs * 1000ll));
}
virtual void onMessageReceived(const sp<AMessage> &msg);
friend class BatteryCheckerTest;
};
class BatteryCheckerTest : public ::testing::Test {
public:
BatteryCheckerTest()
: mLooper(new ALooper)
, mHandler(new BatteryCheckerTestHandler()) {
mLooper->setName("BatterCheckerLooper");
mLooper->start(false, false, ANDROID_PRIORITY_AUDIO);
mLooper->registerHandler(mHandler);
}
protected:
using EventType = BatteryCheckerTestHandler::EventType;
using Operation = BatteryCheckerTestHandler::Operation;
virtual ~BatteryCheckerTest() {
mLooper->stop();
mLooper->unregisterHandler(mHandler->id());
}
void runTest(const std::vector<Operation> &ops, int64_t timeoutUs) {
mHandler->runTest(ops, timeoutUs);
}
sp<ALooper> mLooper;
sp<BatteryCheckerTestHandler> mHandler;
};
void BatteryCheckerTestHandler::onMessageReceived(const sp<AMessage> &msg) {
switch(msg->what()) {
case kWhatStart:
mBatteryChecker->setExecuting(true);
break;
case kWhatStop:
mBatteryChecker->setExecuting(false);
break;
case kWhatActivity:
mBatteryChecker->onCodecActivity([this] () { mOnCounter++; });
break;
case kWhatReleased:
mBatteryChecker->onClientRemoved();
break;
case kWhatBatteryChecker:
mBatteryChecker->onCheckBatteryTimer(msg, [this] () { mOffCounter++; });
break;
case kWhatCheckpoint:
// verify ON/OFF state and total events
EXPECT_EQ(mOnCounter, mOps[0].expectedOnCounter);
EXPECT_EQ(mOffCounter, mOps[0].expectedOffCounter);
break;
default:
TRESPASS();
}
if (msg->what() != kWhatBatteryChecker) {
EXPECT_EQ(msg->what(), mOps[0].event);
// post next message
if (!mOps[0].repeatCount) {
mOps.erase(mOps.begin());
} else {
mOps[0].repeatCount--;
}
int64_t duration = mOps[0].delay;
if (!mOps.empty()) {
(new AMessage(mOps[0].event, this))->post(duration);
} else {
AutoMutex lock(mLock);
mDone.signal();
}
}
}
TEST_F(BatteryCheckerTest, testNormalOperations) {
runTest({
{EventType::kWhatStart, 0ll},
{EventType::kWhatCheckpoint, 0ll, 0, 0, 0},
{EventType::kWhatActivity, 33333ll},
{EventType::kWhatCheckpoint, 0ll, 0, 1, 0}, // ON
{EventType::kWhatActivity, 33333ll, 2*kWaitStatusChangeUs/33333ll},
{EventType::kWhatCheckpoint, 0ll, 0, 1, 0},
{EventType::kWhatCheckpoint, kWaitStatusChangeUs, 0, 1, 1}, // OFF
}, 10000000ll);
}
TEST_F(BatteryCheckerTest, testPauseResume) {
runTest({
{EventType::kWhatStart, 0ll},
{EventType::kWhatCheckpoint, 0ll, 0, 0, 0},
{EventType::kWhatActivity, 33333ll},
{EventType::kWhatCheckpoint, 0ll, 0, 1, 0}, // ON
{EventType::kWhatCheckpoint, kWaitStatusChangeUs, 0, 1, 1}, // OFF
{EventType::kWhatActivity, 33333ll},
{EventType::kWhatCheckpoint, 0ll, 0, 2, 1}, // ON
{EventType::kWhatCheckpoint, kWaitStatusChangeUs, 0, 2, 2}, // OFF
}, 10000000ll);
}
TEST_F(BatteryCheckerTest, testClientRemovedAndRestart) {
runTest({
{EventType::kWhatStart, 0ll},
{EventType::kWhatActivity, 33333ll, kWaitStatusChangeUs/33333ll},
{EventType::kWhatCheckpoint, 0ll, 0, 1, 0},
// stop executing state itself shouldn't trigger any calls
{EventType::kWhatStop, 0ll},
{EventType::kWhatCheckpoint, 0ll, 0, 1, 0},
// release shouldn't trigger any calls either,
// client resource will be removed entirely
{EventType::kWhatReleased, 0ll},
{EventType::kWhatCheckpoint, 0ll, 0, 1, 0},
{EventType::kWhatCheckpoint, kWaitStatusChangeUs, 0, 1, 0},
// start pushing buffers again, On should be received without any Off
{EventType::kWhatStart, 0ll},
{EventType::kWhatActivity, 33333ll},
{EventType::kWhatCheckpoint, 0ll, 0, 2, 0},
// double check that only new checker msg triggers OFF,
// left-over checker msg from stale generate discarded
{EventType::kWhatCheckpoint, kWaitStatusChangeUs, 0, 2, 1},
}, 10000000ll);
}
TEST_F(BatteryCheckerTest, testActivityWhileNotExecuting) {
runTest({
// activity before start shouldn't trigger
{EventType::kWhatActivity, 0ll},
{EventType::kWhatCheckpoint, 0ll, 0, 0, 0},
{EventType::kWhatStart, 0ll},
{EventType::kWhatCheckpoint, 0ll, 0, 0, 0},
// activity after start before stop should trigger
{EventType::kWhatActivity, 33333ll},
{EventType::kWhatCheckpoint, 0ll, 0, 1, 0},
// stop executing state itself shouldn't trigger any calls
{EventType::kWhatStop, 0ll},
{EventType::kWhatCheckpoint, 0ll, 0, 1, 0},
// keep pushing another 3 seconds after stop, expected to OFF
{EventType::kWhatActivity, 33333ll, kWaitStatusChangeUs/33333ll},
{EventType::kWhatCheckpoint, 0ll, 0, 1, 1},
}, 10000000ll);
}
TEST_F(BatteryCheckerTest, testSparseActivity) {
runTest({
{EventType::kWhatStart, 0ll},
{EventType::kWhatCheckpoint, 0ll, 0, 0, 0},
// activity arrives sparsely with interval only slightly small than timeout
// should only trigger 1 ON
{EventType::kWhatActivity, kSparseFrameIntervalUs, 2},
{EventType::kWhatCheckpoint, 0ll, 0, 1, 0},
{EventType::kWhatCheckpoint, kSparseFrameIntervalUs, 0, 1, 0},
{EventType::kWhatCheckpoint, kTestMarginUs, 0, 1, 1}, // OFF
}, 10000000ll);
}
} // namespace android
Loading…
Cancel
Save