power: Support max-level-dependent initial brightness.

To make it possible to use a single image in conjunction
with different backlight hardware, extend the format of
powerd's internal_backlight_no_als_ac_brightness and
internal_backlight_no_als_battery_brightness prefs to
support defining multiple initial brightness percentages.

For example, using:

60.0 250
50.0 500

indicates that 60% should be used when the backlight's
maximum brightness level is reported as 250, while 50%
should be used for 500.

Note that the former single-percentage format is still
allowed (and should be used in almost all cases).

Also note that when defining multiple percentages, all
possible maximum levels must be supplied.

BUG=chrome-os-partner:28715
TEST=added unit tests; manually checked that powerd starts
     up happily on lumpy

Change-Id: Icfb901c91ca7b25ee36a1c5d1f2d6aa5875fcae0
Reviewed-on: https://chromium-review.googlesource.com/199159
Commit-Queue: Daniel Erat <derat@chromium.org>
Tested-by: Daniel Erat <derat@chromium.org>
Reviewed-by: Chris Masone <cmasone@chromium.org>
diff --git a/common/power_constants.h b/common/power_constants.h
index 17b38e6..f0667ee 100644
--- a/common/power_constants.h
+++ b/common/power_constants.h
@@ -66,7 +66,8 @@
 extern const char kInternalBacklightAlsStepsPref[];
 
 // Starting internal backlight brightness while on line and battery power for
-// systems lacking an ambient light sensor.
+// systems lacking an ambient light sensor. See
+// powerd/policy/internal_backlight_controller.cc for details.
 extern const char kInternalBacklightNoAlsAcBrightnessPref[];
 extern const char kInternalBacklightNoAlsBatteryBrightnessPref[];
 
diff --git a/powerd/policy/internal_backlight_controller.cc b/powerd/policy/internal_backlight_controller.cc
index 8e53f13..ece4ab8 100644
--- a/powerd/policy/internal_backlight_controller.cc
+++ b/powerd/policy/internal_backlight_controller.cc
@@ -11,6 +11,8 @@
 #include <string>
 
 #include <base/logging.h>
+#include <base/strings/string_number_conversions.h>
+#include <base/strings/string_split.h>
 #include <base/strings/string_util.h>
 #include <base/strings/stringprintf.h>
 
@@ -74,6 +76,62 @@
       std::max(InternalBacklightController::kMinVisiblePercent, percent));
 }
 
+// Reads |pref_name| from |prefs| and returns the desired initial brightness
+// percent corresponding to |max_brightness_level|, the backlight's actual
+// maximum brightness. Crashes on failure.
+//
+// The pref's value should consist of one or more lines, each containing either
+// a single double brightness percentage or a space-separated "<double-percent>
+// <int64-max-level>" pair. The percentage from the first line either using the
+// single-value format or matching |max_brightness_level| will be returned.
+//
+// For example,
+//
+// 60.0 300
+// 50.0 400
+//
+// indicates that 60% should be used if the maximum brightness level is 300,
+// while 50% should be used if it's 400.
+//
+// Note that when using the two-value format, all possible maximum levels for
+// the current system must be covered by the pref.
+double GetInitialBrightnessPercent(PrefsInterface* prefs,
+                                   const std::string& pref_name,
+                                   int64 max_brightness_level) {
+  DCHECK(prefs);
+  std::string pref_value;
+  CHECK(prefs->GetString(pref_name, &pref_value))
+      << "Unable to read pref " << pref_name;
+
+  std::vector<std::string> lines;
+  base::SplitString(pref_value, '\n', &lines);
+  for (size_t i = 0; i < lines.size(); ++i) {
+    std::vector<std::string> parts;
+    base::SplitStringAlongWhitespace(lines[i], &parts);
+    CHECK(parts.size() == 1U || parts.size() == 2U)
+        << "Unable to parse \"" << lines[i] << "\" from pref " << pref_name;
+
+    double percent = 0.0;
+    CHECK(base::StringToDouble(parts[0], &percent) &&
+          percent >= 0.0 && percent <= 100.0)
+        << "Unable to parse \"" << parts[0] << "\" from pref " << pref_name
+        << " as double in [0.0, 100.0]";
+
+    int64 max_level = -1;
+    CHECK(parts.size() == 1U ||
+          (base::StringToInt64(parts[1], &max_level) && max_level > 0))
+        << "Unable to parse \"" << parts[1] << "\" from pref " << pref_name;
+
+    if (max_level < 0 || max_level == max_brightness_level)
+      return percent;
+  }
+
+  LOG(FATAL) << "Unable to find initial brightness level in pref "
+             << pref_name << " for max brightness level "
+             << max_brightness_level;
+  return kMaxPercent;
+}
+
 }  // namespace
 
 const int64 InternalBacklightController::kMaxBrightnessSteps = 16;
@@ -138,12 +196,10 @@
 
   const double initial_percent = LevelToPercent(current_level_);
   ambient_light_brightness_percent_ = initial_percent;
-  plugged_explicit_brightness_percent_ = initial_percent;
-  unplugged_explicit_brightness_percent_ = initial_percent;
-  prefs_->GetDouble(kInternalBacklightNoAlsAcBrightnessPref,
-                    &plugged_explicit_brightness_percent_);
-  prefs_->GetDouble(kInternalBacklightNoAlsBatteryBrightnessPref,
-                    &unplugged_explicit_brightness_percent_);
+  plugged_explicit_brightness_percent_ = GetInitialBrightnessPercent(
+      prefs_, kInternalBacklightNoAlsAcBrightnessPref, max_level_);
+  unplugged_explicit_brightness_percent_ = GetInitialBrightnessPercent(
+      prefs_, kInternalBacklightNoAlsBatteryBrightnessPref, max_level_);
 
   prefs_->GetBool(kInstantTransitionsBelowMinLevelPref,
                   &instant_transitions_below_min_level_);
diff --git a/powerd/policy/internal_backlight_controller_unittest.cc b/powerd/policy/internal_backlight_controller_unittest.cc
index 1e21714..ef167e7 100644
--- a/powerd/policy/internal_backlight_controller_unittest.cc
+++ b/powerd/policy/internal_backlight_controller_unittest.cc
@@ -40,8 +40,8 @@
         report_initial_power_source_(true),
         default_min_visible_level_(1),
         default_als_steps_("50.0 -1 400\n90.0 100 -1"),
-        default_no_als_ac_brightness_(80.0),
-        default_no_als_battery_brightness_(60.0),
+        default_no_als_ac_brightness_("80.0"),
+        default_no_als_battery_brightness_("60.0"),
         backlight_(max_backlight_level_, initial_backlight_level_),
         light_sensor_(initial_als_lux_) {
   }
@@ -52,9 +52,9 @@
   virtual void Init(PowerSource power_source) {
     prefs_.SetInt64(kMinVisibleBacklightLevelPref, default_min_visible_level_);
     prefs_.SetString(kInternalBacklightAlsStepsPref, default_als_steps_);
-    prefs_.SetDouble(kInternalBacklightNoAlsAcBrightnessPref,
+    prefs_.SetString(kInternalBacklightNoAlsAcBrightnessPref,
                      default_no_als_ac_brightness_);
-    prefs_.SetDouble(kInternalBacklightNoAlsBatteryBrightnessPref,
+    prefs_.SetString(kInternalBacklightNoAlsBatteryBrightnessPref,
                      default_no_als_battery_brightness_);
     backlight_.set_max_level(max_backlight_level_);
     backlight_.set_current_level(initial_backlight_level_);
@@ -102,8 +102,8 @@
   // Default values for prefs.  Applied when Init() is called.
   int64 default_min_visible_level_;
   std::string default_als_steps_;
-  double default_no_als_ac_brightness_;
-  double default_no_als_battery_brightness_;
+  std::string default_no_als_ac_brightness_;
+  std::string default_no_als_battery_brightness_;
 
   FakePrefs prefs_;
   system::BacklightStub backlight_;
@@ -547,20 +547,40 @@
   pass_light_sensor_ = false;
   report_initial_power_source_ = false;
   report_initial_als_reading_ = false;
-  default_no_als_ac_brightness_ = 95.0;
-  default_no_als_battery_brightness_ = 75.0;
+  default_no_als_ac_brightness_ = "95.0";
+  default_no_als_battery_brightness_ = "75.0";
   Init(POWER_AC);
   EXPECT_EQ(initial_backlight_level_, backlight_.current_level());
 
   // The brightness percentages from the "no ALS" prefs should be used as
   // starting points when there's no ALS.
   controller_->HandlePowerSourceChange(POWER_AC);
-  EXPECT_EQ(PercentToLevel(default_no_als_ac_brightness_),
-            backlight_.current_level());
+  EXPECT_EQ(PercentToLevel(95.0), backlight_.current_level());
 
   controller_->HandlePowerSourceChange(POWER_BATTERY);
-  EXPECT_EQ(PercentToLevel(default_no_als_battery_brightness_),
-            backlight_.current_level());
+  EXPECT_EQ(PercentToLevel(75.0), backlight_.current_level());
+}
+
+TEST_F(InternalBacklightControllerTest, NoAmbientLightSensorMultipleDefaults) {
+  // Test that different default brightness percentages can be specified for
+  // different maximum brightness levels (http://crosbug.com/p/28715).
+  pass_light_sensor_ = false;
+  report_initial_power_source_ = false;
+  report_initial_als_reading_ = false;
+  max_backlight_level_ = 400;
+  default_no_als_ac_brightness_ = "40.0 300\n50.0 400";
+  default_no_als_battery_brightness_ = "20.0 400\n30.0 300";
+  Init(POWER_AC);
+  EXPECT_EQ(initial_backlight_level_, backlight_.current_level());
+
+  // The default percentages corresponding to a maximum level of 400 should be
+  // used on both AC and battery.
+  controller_->HandlePowerSourceChange(POWER_AC);
+  EXPECT_EQ(PercentToLevel(50.0), backlight_.current_level());
+
+  controller_->HandlePowerSourceChange(POWER_BATTERY);
+  EXPECT_EQ(PercentToLevel(20.0), backlight_.current_level());
+
 }
 
 TEST_F(InternalBacklightControllerTest, ForceBacklightOn) {