Merge "Calculate battery charge from energy and voltage"
diff --git a/protos/perfetto/trace/perfetto_trace.proto b/protos/perfetto/trace/perfetto_trace.proto
index a215592..fc47541 100644
--- a/protos/perfetto/trace/perfetto_trace.proto
+++ b/protos/perfetto/trace/perfetto_trace.proto
@@ -10204,6 +10204,12 @@
 
   // Battery name, emitted only on multiple batteries.
   optional string name = 5;
+
+  // Battery capacity in microwatt-hours(µWh).
+  optional int64 energy_counter_uwh = 6;
+
+  // Battery voltage in microvolts(µV).
+  optional int64 voltage_uv = 7;
 }
 
 // End of protos/perfetto/trace/power/battery_counters.proto
diff --git a/protos/perfetto/trace/power/battery_counters.proto b/protos/perfetto/trace/power/battery_counters.proto
index ab1aaa8..bb1a23c 100644
--- a/protos/perfetto/trace/power/battery_counters.proto
+++ b/protos/perfetto/trace/power/battery_counters.proto
@@ -35,4 +35,10 @@
 
   // Battery name, emitted only on multiple batteries.
   optional string name = 5;
+
+  // Battery capacity in microwatt-hours(µWh).
+  optional int64 energy_counter_uwh = 6;
+
+  // Battery voltage in microvolts(µV).
+  optional int64 voltage_uv = 7;
 }
diff --git a/src/trace_processor/importers/proto/android_probes_parser.cc b/src/trace_processor/importers/proto/android_probes_parser.cc
index d4018e3..f85db0b 100644
--- a/src/trace_processor/importers/proto/android_probes_parser.cc
+++ b/src/trace_processor/importers/proto/android_probes_parser.cc
@@ -110,7 +110,18 @@
         context_->track_tracker->InternGlobalCounterTrack(batt_charge_id);
     context_->event_tracker->PushCounter(
         ts, static_cast<double>(evt.charge_counter_uah()), track);
+  } else if (evt.has_energy_counter_uwh() && evt.has_voltage_uv()) {
+    // Calculate charge counter from energy counter and voltage.
+    TrackId track =
+        context_->track_tracker->InternGlobalCounterTrack(batt_charge_id);
+    auto energy = evt.energy_counter_uwh();
+    auto voltage = evt.voltage_uv();
+    if (voltage > 0) {
+      context_->event_tracker->PushCounter(
+          ts, static_cast<double>(energy * 1000000 / voltage), track);
+    }
   }
+
   if (evt.has_capacity_percent()) {
     TrackId track =
         context_->track_tracker->InternGlobalCounterTrack(batt_capacity_id);
diff --git a/src/traced/probes/power/linux_power_sysfs_data_source.cc b/src/traced/probes/power/linux_power_sysfs_data_source.cc
index b4bdec6..9d4365b 100644
--- a/src/traced/probes/power/linux_power_sysfs_data_source.cc
+++ b/src/traced/probes/power/linux_power_sysfs_data_source.cc
@@ -85,6 +85,21 @@
 }
 
 base::Optional<int64_t>
+LinuxPowerSysfsDataSource::BatteryInfo::GetEnergyCounterUah(
+    size_t battery_idx) {
+  PERFETTO_CHECK(battery_idx < sysfs_battery_subdirs_.size());
+  return ReadFileAsInt64(power_supply_dir_path_ + "/" +
+                         sysfs_battery_subdirs_[battery_idx] + "/energy_now");
+}
+
+base::Optional<int64_t> LinuxPowerSysfsDataSource::BatteryInfo::GetVoltageUv(
+    size_t battery_idx) {
+  PERFETTO_CHECK(battery_idx < sysfs_battery_subdirs_.size());
+  return ReadFileAsInt64(power_supply_dir_path_ + "/" +
+                         sysfs_battery_subdirs_[battery_idx] + "/voltage_now");
+}
+
+base::Optional<int64_t>
 LinuxPowerSysfsDataSource::BatteryInfo::GetCapacityPercent(size_t battery_idx) {
   PERFETTO_CHECK(battery_idx < sysfs_battery_subdirs_.size());
   return ReadFileAsInt64(power_supply_dir_path_ + "/" +
@@ -174,6 +189,12 @@
     value = battery_info_->GetAverageCurrentUa(battery_idx);
     if (value)
       counters_proto->set_current_ua(*value);
+    value = battery_info_->GetEnergyCounterUah(battery_idx);
+    if (value)
+      counters_proto->set_energy_counter_uwh(*value);
+    value = battery_info_->GetVoltageUv(battery_idx);
+    if (value)
+      counters_proto->set_voltage_uv(*value);
     // On systems with multiple batteries, disambiguate with battery names.
     if (battery_info_->num_batteries() > 1)
       counters_proto->set_name(battery_info_->GetBatteryName(battery_idx));
diff --git a/src/traced/probes/power/linux_power_sysfs_data_source.h b/src/traced/probes/power/linux_power_sysfs_data_source.h
index d757b38..058ccd6 100644
--- a/src/traced/probes/power/linux_power_sysfs_data_source.h
+++ b/src/traced/probes/power/linux_power_sysfs_data_source.h
@@ -41,6 +41,12 @@
     // The current coloumb counter value in µAh.
     base::Optional<int64_t> GetChargeCounterUah(size_t battery_idx);
 
+    // The current energy counter in µWh.
+    base::Optional<int64_t> GetEnergyCounterUah(size_t battery_idx);
+
+    // The voltage in µV.
+    base::Optional<int64_t> GetVoltageUv(size_t battery_idx);
+
     // The battery capacity in percent.
     base::Optional<int64_t> GetCapacityPercent(size_t battery_idx);
 
diff --git a/src/traced/probes/power/linux_power_sysfs_data_source_unittest.cc b/src/traced/probes/power/linux_power_sysfs_data_source_unittest.cc
index ff98604..2c1c8aa 100644
--- a/src/traced/probes/power/linux_power_sysfs_data_source_unittest.cc
+++ b/src/traced/probes/power/linux_power_sysfs_data_source_unittest.cc
@@ -104,5 +104,41 @@
   EXPECT_EQ(*battery_info_->GetChargeCounterUah(main_battery_idx), 3074000);
 }
 
+TEST(LinuxPowerSysfsDataSourceTest, EnergyNow) {
+  base::TmpDirTree tmpdir;
+  std::unique_ptr<LinuxPowerSysfsDataSource::BatteryInfo> battery_info_;
+
+  tmpdir.AddDir("BAT0");
+  tmpdir.AddFile("BAT0/type", "Battery\n");
+  tmpdir.AddFile("BAT0/present", "1\n");
+  tmpdir.AddFile("BAT0/capacity", "95\n");          // 95 percent.
+  tmpdir.AddFile("BAT0/energy_now", "56680000\n");  // 56680000 µWh.
+
+  battery_info_.reset(
+      new LinuxPowerSysfsDataSource::BatteryInfo(tmpdir.path().c_str()));
+
+  EXPECT_EQ(battery_info_->num_batteries(), 1u);
+  EXPECT_EQ(*battery_info_->GetCapacityPercent(0), 95);
+  EXPECT_EQ(*battery_info_->GetEnergyCounterUah(0), 56680000);
+}
+
+TEST(LinuxPowerSysfsDataSourceTest, EnergyVoltageNow) {
+  base::TmpDirTree tmpdir;
+  std::unique_ptr<LinuxPowerSysfsDataSource::BatteryInfo> battery_info_;
+
+  tmpdir.AddDir("BAT0");
+  tmpdir.AddFile("BAT0/type", "Battery\n");
+  tmpdir.AddFile("BAT0/present", "1\n");
+  tmpdir.AddFile("BAT0/capacity", "95\n");           // 95 percent.
+  tmpdir.AddFile("BAT0/voltage_now", "17356000\n");  // Now at 17.356 µV.
+
+  battery_info_.reset(
+      new LinuxPowerSysfsDataSource::BatteryInfo(tmpdir.path().c_str()));
+
+  EXPECT_EQ(battery_info_->num_batteries(), 1u);
+  EXPECT_EQ(*battery_info_->GetCapacityPercent(0), 95);
+  EXPECT_EQ(*battery_info_->GetVoltageUv(0), 17356000);
+}
+
 }  // namespace
 }  // namespace perfetto
diff --git a/test/trace_processor/diff_tests/include_index.py b/test/trace_processor/diff_tests/include_index.py
index 09e084e..4367919 100644
--- a/test/trace_processor/diff_tests/include_index.py
+++ b/test/trace_processor/diff_tests/include_index.py
@@ -56,6 +56,7 @@
 from diff_tests.performance.tests import Performance
 from diff_tests.power.tests import Power
 from diff_tests.power.tests_energy_breakdown import PowerEnergyBreakdown
+from diff_tests.power.tests_linux_sysfs_power import LinuxSysfsPower
 from diff_tests.power.tests_power_rails import PowerPowerRails
 from diff_tests.power.tests_voltage_and_scaling import PowerVoltageAndScaling
 from diff_tests.process_tracking.tests import ProcessTracking
@@ -113,6 +114,7 @@
       *GraphicsDrmRelatedFtraceEvents(index_path, 'graphics',
                                       'GraphicsDrmRelatedFtraceEvents').fetch(),
       *Ufs(index_path, 'ufs', 'Ufs').fetch(),
+      *LinuxSysfsPower(index_path, 'power', 'LinuxSysfsPower').fetch(),
       *Memory(index_path, 'memory', 'Memory').fetch(),
       *MemoryMetrics(index_path, 'memory', 'MemoryMetrics').fetch(),
       *Network(index_path, 'network', 'Network').fetch(),
diff --git a/test/trace_processor/diff_tests/power/tests_linux_sysfs_power.py b/test/trace_processor/diff_tests/power/tests_linux_sysfs_power.py
new file mode 100644
index 0000000..fce058e
--- /dev/null
+++ b/test/trace_processor/diff_tests/power/tests_linux_sysfs_power.py
@@ -0,0 +1,134 @@
+#!/usr/bin/env python3
+# Copyright (C) 2023 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 a
+#
+#      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.
+
+from python.generators.diff_tests.testing import Path, DataPath, Metric
+from python.generators.diff_tests.testing import Csv, Json, TextProto
+from python.generators.diff_tests.testing import DiffTestBlueprint
+from python.generators.diff_tests.testing import TestSuite
+
+
+class LinuxSysfsPower(TestSuite):
+
+  # Test basic battery counters.
+  def test_counters(self):
+    return DiffTestBlueprint(
+        trace=TextProto("""
+        packet {
+          timestamp: 3000000
+          battery {
+            charge_counter_uah: 3005000
+            capacity_percent: 100.000000
+            current_ua: 0
+          }
+        }
+        """),
+        query="""
+        SELECT * FROM (
+          (SELECT AVG(value) AS capacity_percent FROM counters
+           WHERE name='batt.capacity_pct'),
+          (SELECT AVG(value) AS charge_uah FROM counters
+           WHERE name='batt.charge_uah'),
+          (SELECT AVG(value) AS current_ua FROM counters
+           WHERE name='batt.current_ua')
+        );
+        """,
+        out=Csv("""
+        "capacity_percent","charge_uah","current_ua"
+        100.000000,3005000.000000,0.000000
+        """))
+
+  # Test multiple batteries.
+  def test_multiple_batteries(self):
+    return DiffTestBlueprint(
+        trace=TextProto("""
+        packet {
+          timestamp: 3000000
+          battery {
+            charge_counter_uah: 3005000
+            capacity_percent: 100.000000
+            current_ua: 0
+            name: "BAT0"
+          }
+        }
+        packet {
+          timestamp: 3000000
+          battery {
+            capacity_percent: 90.000000
+            name: "BAT1"
+          }
+        }
+        """),
+        query="""
+        SELECT name, value FROM counters WHERE name like "batt.%" ORDER BY name
+        """,
+        out=Csv("""
+        "name","value"
+        "batt.BAT0.capacity_pct",100.000000
+        "batt.BAT0.charge_uah",3005000.000000
+        "batt.BAT0.current_ua",0.000000
+        "batt.BAT1.capacity_pct",90.000000
+        """))
+
+  # Test convertion to charge counter from energy and voltage.
+  def test_charge_from_energy_and_voltage(self):
+    return DiffTestBlueprint(
+        trace=TextProto("""
+        packet {
+          timestamp: 3000000
+          battery {
+            energy_counter_uwh: 56680000
+            voltage_uv: 17356000
+          }
+        }
+        packet {
+          timestamp: 4000000
+          battery {
+            energy_counter_uwh: 56600000
+            voltage_uv: 17356000
+          }
+        }
+        """),
+        query="""
+        SELECT value
+        FROM counters
+        WHERE name = "batt.charge_uah"
+        """,
+        out=Csv("""
+        "value"
+        3265729.000000
+        3261120.000000
+        """))
+
+  # Test convertion to charge counter from energy and voltage: bad voltage
+  # value.
+  def test_charge_from_energy_and_bad_voltage(self):
+    return DiffTestBlueprint(
+        trace=TextProto("""
+        packet {
+          timestamp: 3000000
+          battery {
+            energy_counter_uwh: 56680000
+            voltage_uv: 0
+          }
+        }
+        """),
+        query="""
+        SELECT value
+        FROM counters
+        WHERE name = "batt.charge_uah"
+        """,
+        out=Csv("""
+        "value"
+        """))