Merge "Add more tests for battery checker code" into qt-qpr1-dev
am: 141f9a9c83
Change-Id: I8741bdb299d1b6aa8596d832b85d4452b5ae7c9e
diff --git a/media/libstagefright/MediaCodec.cpp b/media/libstagefright/MediaCodec.cpp
index ae0fa3c..eceb84e 100644
--- a/media/libstagefright/MediaCodec.cpp
+++ b/media/libstagefright/MediaCodec.cpp
@@ -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 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 @@
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 @@
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 @@
return;
}
- scheduleBatteryCheckerIfNeeded();
+ if (mBatteryChecker != nullptr) {
+ mBatteryChecker->onCodecActivity([this] () {
+ addResource(MediaResource::kBattery, MediaResource::kVideoCodec, 1);
+ });
+ }
BufferFlightTiming_t startdata;
bool valid = false;
@@ -981,6 +986,10 @@
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::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::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 @@
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 @@
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 @@
case kWhatCheckBatteryStats:
{
- onBatteryChecker(msg);
+ if (mBatteryChecker != nullptr) {
+ mBatteryChecker->onCheckBatteryTimer(msg, [this] () {
+ removeResource(MediaResource::kBattery, MediaResource::kVideoCodec, 1);
+ });
+ }
break;
}
@@ -3197,6 +3226,10 @@
mState = newState;
+ if (mBatteryChecker != nullptr) {
+ mBatteryChecker->setExecuting(isExecuting());
+ }
+
cancelPendingDequeueOperations();
}
diff --git a/media/libstagefright/TEST_MAPPING b/media/libstagefright/TEST_MAPPING
index 96818eb..c1b270c 100644
--- a/media/libstagefright/TEST_MAPPING
+++ b/media/libstagefright/TEST_MAPPING
@@ -7,6 +7,9 @@
"include-annotation": "android.platform.test.annotations.RequiresDevice"
}
]
+ },
+ {
+ "name": "BatteryChecker_test"
}
]
}
diff --git a/media/libstagefright/include/media/stagefright/BatteryChecker.h b/media/libstagefright/include/media/stagefright/BatteryChecker.h
new file mode 100644
index 0000000..2ec4ac0
--- /dev/null
+++ b/media/libstagefright/include/media/stagefright/BatteryChecker.h
@@ -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_
diff --git a/media/libstagefright/include/media/stagefright/MediaCodec.h b/media/libstagefright/include/media/stagefright/MediaCodec.h
index 462eb84e..cd30347 100644
--- a/media/libstagefright/include/media/stagefright/MediaCodec.h
+++ b/media/libstagefright/include/media/stagefright/MediaCodec.h
@@ -36,6 +36,7 @@
struct AMessage;
struct AReplyToken;
struct AString;
+struct BatteryChecker;
class BufferChannelBase;
struct CodecBase;
class IBatteryStats;
@@ -463,11 +464,7 @@
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);
diff --git a/media/libstagefright/tests/Android.bp b/media/libstagefright/tests/Android.bp
index be10fdc..a7f94c1 100644
--- a/media/libstagefright/tests/Android.bp
+++ b/media/libstagefright/tests/Android.bp
@@ -27,3 +27,21 @@
"-Wall",
],
}
+
+cc_test {
+ name: "BatteryChecker_test",
+ srcs: ["BatteryChecker_test.cpp"],
+ test_suites: ["device-tests"],
+
+ shared_libs: [
+ "libstagefright",
+ "libstagefright_foundation",
+ "libutils",
+ "liblog",
+ ],
+
+ cflags: [
+ "-Werror",
+ "-Wall",
+ ],
+}
\ No newline at end of file
diff --git a/media/libstagefright/tests/BatteryChecker_test.cpp b/media/libstagefright/tests/BatteryChecker_test.cpp
new file mode 100644
index 0000000..0c5ee9b
--- /dev/null
+++ b/media/libstagefright/tests/BatteryChecker_test.cpp
@@ -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