Use the battery design capacity exposed by the kernel

Add support for battery packs whose design capacity is exposed by the
kernel. Needed if multiple battery packs with different capacities are
available for the platform as the power profile is statically set with
a fixed value.

FPIIM-2410

Change-Id: Id93d13fb8de63a2375f2948449a380ebb746a166
diff --git a/core/java/com/android/internal/os/PowerProfile.java b/core/java/com/android/internal/os/PowerProfile.java
index aaa9f73..64e76a6 100644
--- a/core/java/com/android/internal/os/PowerProfile.java
+++ b/core/java/com/android/internal/os/PowerProfile.java
@@ -1,5 +1,6 @@
 /*
  * Copyright (C) 2009 The Android Open Source Project
+ * Copyright (C) 2018 Fairphone B.V.
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -20,12 +21,16 @@
 import android.content.Context;
 import android.content.res.Resources;
 import android.content.res.XmlResourceParser;
+import android.annotation.Nullable;
+import android.util.Log;
 
 import com.android.internal.util.XmlUtils;
 
 import org.xmlpull.v1.XmlPullParser;
 import org.xmlpull.v1.XmlPullParserException;
 
+import java.io.BufferedReader;
+import java.io.FileReader;
 import java.io.IOException;
 import java.util.ArrayList;
 import java.util.HashMap;
@@ -37,6 +42,8 @@
  */
 public class PowerProfile {
 
+    private static final String TAG = "PowerProfile";
+
     /**
      * No power consumption, or accounted for elsewhere.
      */
@@ -187,6 +194,9 @@
     private static final String TAG_ARRAYITEM = "value";
     private static final String ATTR_NAME = "name";
 
+    private static final String FILENAME_BATTERY_DESIGN_CAPACITY =
+            "/sys/class/power_supply/bms/charge_full_design";
+
     public PowerProfile(Context context) {
         // Read the XML file for the given profile (normally only one per
         // device)
@@ -280,6 +290,43 @@
                 sPowerMap.put(configResIdKeys[i], (double) value);
             }
         }
+
+        /*
+         * There is no way to set the battery design capacity automatically here from what the
+         * kernel exposes in the sysfs. Several battery packs can be available for the device and
+         * they might have different capacities. So do not rely on the XML power profile and always
+         * use what the kernel exposes instead.
+         */
+        try {
+            // Read the battery design capacity from the sysfs, in microampere-hour
+            int batteryDesignCapacityMicro =
+                    Integer.parseInt(readLine(FILENAME_BATTERY_DESIGN_CAPACITY));
+            // Unconditionally store the sysfs capacity, in milliampere-hour
+            sPowerMap.put(POWER_BATTERY_CAPACITY, ((double) batteryDesignCapacityMicro) / 1000);
+        } catch (IOException ioe) {
+            Log.e(TAG, "Cannot read battery capacity from "
+                    + FILENAME_BATTERY_DESIGN_CAPACITY, ioe);
+        } catch (NumberFormatException nfe) {
+            Log.e(TAG, "Read a badly formatted battery capacity from "
+                    + FILENAME_BATTERY_DESIGN_CAPACITY, nfe);
+        }
+    }
+
+    /**
+    * Reads a line from the specified file.
+    *
+    * @param filename The file to read from.
+    * @return The first line up to 256 characters, or <code>null</code> if file is empty.
+    * @throws IOException If the file couldn't be read.
+    */
+    @Nullable
+    private static String readLine(String filename) throws IOException {
+        final BufferedReader reader = new BufferedReader(new FileReader(filename), 256);
+        try {
+            return reader.readLine();
+        } finally {
+            reader.close();
+        }
     }
 
     private CpuClusterKey[] mCpuClusters;
@@ -373,7 +420,7 @@
     public double getAveragePower(String type) {
         return getAveragePowerOrDefault(type, 0);
     }
-    
+
     /**
      * Returns the average current in mA consumed by the subsystem for the given level.
      * @param type the subsystem type