Add MultiStateCounter

This native object is used to track values per-state. For example,
if the state changes from 0 to 1 between two updateValue calls,
the delta between the values is distributed to the states 0 and 1
in accordance with the time spent in those states.

Bug: 197162116
Test: atest libbattery_test

Change-Id: Ie304db5c93f4aa9676d12d0a8ab53b6867b24fff
diff --git a/libs/battery/MultiStateCounterTest.cpp b/libs/battery/MultiStateCounterTest.cpp
new file mode 100644
index 0000000..942d5ca
--- /dev/null
+++ b/libs/battery/MultiStateCounterTest.cpp
@@ -0,0 +1,105 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ * Android BPF library - public API
+ *
+ * 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.
+ */
+
+#include <gtest/gtest.h>
+#include "MultiStateCounter.h"
+
+namespace android {
+namespace battery {
+
+typedef MultiStateCounter<double> DoubleMultiStateCounter;
+
+template <>
+bool DoubleMultiStateCounter::delta(const double& previousValue, const double& newValue,
+                                    double* outValue) const {
+    *outValue = newValue - previousValue;
+    return *outValue >= 0;
+}
+
+template <>
+void DoubleMultiStateCounter::add(double* value1, const double& value2, const uint64_t numerator,
+                                  const uint64_t denominator) const {
+    if (numerator != denominator) {
+        // The caller ensures that denominator != 0
+        *value1 += value2 * numerator / denominator;
+    } else {
+        *value1 += value2;
+    }
+}
+
+template <>
+std::string DoubleMultiStateCounter::valueToString(const double& v) const {
+    return std::to_string(v);
+}
+
+class MultiStateCounterTest : public testing::Test {};
+
+TEST_F(MultiStateCounterTest, constructor) {
+    DoubleMultiStateCounter testCounter(3, 1, 0, 1000);
+    testCounter.setState(1, 2000);
+    testCounter.updateValue(3.14, 3000);
+
+    EXPECT_DOUBLE_EQ(0, testCounter.getCount(0));
+    EXPECT_DOUBLE_EQ(3.14, testCounter.getCount(1));
+    EXPECT_DOUBLE_EQ(0, testCounter.getCount(2));
+}
+
+TEST_F(MultiStateCounterTest, stateChange) {
+    DoubleMultiStateCounter testCounter(3, 1, 0, 0);
+    testCounter.setState(2, 1000);
+    testCounter.updateValue(6.0, 3000);
+
+    EXPECT_DOUBLE_EQ(0, testCounter.getCount(0));
+    EXPECT_DOUBLE_EQ(2.0, testCounter.getCount(1));
+    EXPECT_DOUBLE_EQ(4.0, testCounter.getCount(2));
+}
+
+TEST_F(MultiStateCounterTest, timeAdjustment_setState) {
+    DoubleMultiStateCounter testCounter(3, 1, 0, 0);
+    testCounter.setState(2, 2000);
+
+    // Time moves back
+    testCounter.setState(1, 1000);
+    testCounter.updateValue(6.0, 3000);
+
+    EXPECT_DOUBLE_EQ(0, testCounter.getCount(0));
+
+    // We were in state 1 from 0 to 2000, which was erased because the time moved back.
+    // Then from 1000 to 3000, so we expect the count to be 6 * (2000/3000)
+    EXPECT_DOUBLE_EQ(4.0, testCounter.getCount(1));
+
+    // No time was effectively accumulated for state 2, because the timestamp moved back
+    // while we were in state 2.
+    EXPECT_DOUBLE_EQ(0, testCounter.getCount(2));
+}
+
+TEST_F(MultiStateCounterTest, timeAdjustment_updateValue) {
+    DoubleMultiStateCounter testCounter(1, 0, 0, 0);
+    testCounter.updateValue(6.0, 2000);
+
+    // Time moves back. The negative delta from 2000 to 1000 is ignored
+    testCounter.updateValue(8.0, 1000);
+    testCounter.updateValue(11.0, 3000);
+
+    // The total accumulated count is:
+    //  6.0          // For the period 0-2000
+    //  +(11.0-8.0)  // For the period 1000-3000
+    EXPECT_DOUBLE_EQ(9.0, testCounter.getCount(0));
+}
+
+} // namespace battery
+} // namespace android