text: // Copyright (c) 2012 The Chromium OS Authors. All rights reserved.
  // Use of this source code is governed by a BSD-style license that can be
  // found in the LICENSE file.
  
  #include <stdint.h>
  #include <unistd.h>
  
  #include <cmath>
  #include <cstdio>
  #include <cstdlib>
  #include <memory>
  
  #include <base/at_exit.h>
  #include <base/command_line.h>
  #include <base/format_macros.h>
  #include <base/logging.h>
  #include <base/message_loop/message_pump_type.h>
  #include <base/task/single_thread_task_executor.h>
  #include <base/time/time.h>
  #include <brillo/flag_helper.h>
  
  #include "power_manager/common/battery_percentage_converter.h"
  #include "power_manager/common/power_constants.h"
  #include "power_manager/common/prefs.h"
  #include "power_manager/common/util.h"
  #include "power_manager/powerd/policy/backlight_controller.h"
  #include "power_manager/powerd/policy/internal_backlight_controller.h"
  #include "power_manager/powerd/policy/keyboard_backlight_controller.h"
  #include "power_manager/powerd/system/ambient_light_sensor_manager.h"
  #include "power_manager/powerd/system/ambient_light_sensor_stub.h"
  #include "power_manager/powerd/system/backlight_stub.h"
  #include "power_manager/powerd/system/dbus_wrapper_stub.h"
  #include "power_manager/powerd/system/display/display_power_setter_stub.h"
  #include "power_manager/powerd/system/internal_backlight.h"
  #include "power_manager/powerd/system/power_supply.h"
  #include "power_manager/powerd/system/udev_stub.h"
  
  using power_manager::BatteryPercentageConverter;
  using power_manager::LidState;
  using power_manager::PowerSource;
  using power_manager::Prefs;
  using power_manager::TabletMode;
  using power_manager::policy::BacklightController;
  using power_manager::policy::InternalBacklightController;
  using power_manager::policy::KeyboardBacklightController;
  using power_manager::system::AmbientLightSensorInterface;
  using power_manager::system::AmbientLightSensorManager;
  using power_manager::system::AmbientLightSensorStub;
  using power_manager::system::BacklightStub;
  using power_manager::system::DBusWrapperStub;
  using power_manager::system::DisplayPowerSetterStub;
  using power_manager::system::InternalBacklight;
  using power_manager::system::PowerSupply;
  using power_manager::system::UdevStub;
  using power_manager::util::ClampPercent;
  
  namespace {
  
  // Prints |message| to stderr with a trailing newline and exits.
  void Abort(const std::string& message) {
    fprintf(stderr, "%s\n", message.c_str());
    exit(1);
  }
  
  // Converter instantiates several internal powerd classes to perform conversions
  // between hardware backlight levels, nonlinear percents that powerd uses (which
  // are dependent on the powerd prefs that have been set for the device), and
  // linear percents. It also supports getting the initial level that powerd would
  // choose.
  class Converter {
   public:
    Converter(int64_t current_level,
              int64_t max_level,
              power_manager::system::BacklightInterface::BrightnessScale scale,
              int64_t lux,
              bool keyboard,
              bool force_battery)
        : backlight_(max_level, current_level, scale) {
      CHECK(prefs_.Init(Prefs::GetDefaultStore(), Prefs::GetDefaultSources()));
  
      bool has_als = false;
      if (prefs_.GetBool(power_manager::kHasAmbientLightSensorPref, &has_als) &&
          has_als) {
        light_sensor_ = std::make_unique<AmbientLightSensorStub>(lux);
      }
  
      if (keyboard) {
        auto controller = std::make_unique<KeyboardBacklightController>();
        controller->Init(&backlight_, &prefs_, light_sensor_.get(),
                         &dbus_wrapper_,
                         nullptr /* display_backlight_controller */,
                         LidState::NOT_PRESENT, TabletMode::UNSUPPORTED);
        controller->HandleHoverStateChange(true /* hovering */);
        controller_ = std::move(controller);
      } else {
        auto controller = std::make_unique<InternalBacklightController>();
        controller->Init(&backlight_, &prefs_, light_sensor_.get(),
                         &display_power_setter_, &dbus_wrapper_,
                         LidState::NOT_PRESENT);
        controller_ = std::move(controller);
      }
  
      if (light_sensor_.get())
        light_sensor_->NotifyObservers();
  
      PowerSource power_source = PowerSource::BATTERY;
      if (!force_battery) {
        UdevStub udev;
  
        auto battery_percentage_converter =
            BatteryPercentageConverter::CreateFromPrefs(&prefs_);
  
        PowerSupply power_supply;
        power_supply.Init(base::FilePath(power_manager::kPowerStatusPath),
                          &prefs_, &udev, &dbus_wrapper_,
                          battery_percentage_converter.get());
        if (!power_supply.RefreshImmediately()) {
          LOG(ERROR) << "Failed to read power supply information; using battery";
        } else {
          power_source = power_supply.GetPowerStatus().line_power_on
                             ? PowerSource::AC
                             : PowerSource::BATTERY;
        }
      }
      controller_->HandlePowerSourceChange(power_source);
    }
  
    // Converts a brightness level to a nonlinear percent in [0.0, 100.0].
    double LevelToNonlinearPercent(int64_t level) {
      return controller_->LevelToPercent(level);
    }
  
    // Converts a nonlinear percent in [0.0, 100.0] to a brightness level.
    int64_t NonlinearPercentToLevel(double percent) {
      return controller_->PercentToLevel(percent);
    }
  
    // Converts a brightness level to a linear percent in [0.0, 100.0].
    double LevelToLinearPercent(int64_t level) {
      return level * 100.0 / backlight_.GetMaxBrightnessLevel();
    }
  
    // Converts a linear percent in [0.0, 100.0] to a brightness level.
    int64_t LinearPercentToLevel(double percent) {
      return static_cast<int64_t>(roundl(
          ClampPercent(percent) * backlight_.GetMaxBrightnessLevel() / 100.0));
    }
  
    // Returns the initial brightness level requested by |controller_|.
    int64_t GetInitialLevel() { return backlight_.GetCurrentBrightnessLevel(); }
  
   private:
    // A stub is used so |controller_| won't change the actual brightness.
    BacklightStub backlight_;
    Prefs prefs_;
    std::unique_ptr<AmbientLightSensorStub> light_sensor_;
    DisplayPowerSetterStub display_power_setter_;
    DBusWrapperStub dbus_wrapper_;
    std::unique_ptr<BacklightController> controller_;
  
    DISALLOW_COPY_AND_ASSIGN(Converter);
  };
  
  // Prints the path to the ambient light sensor illuminance file that powerd
  // would monitor and a trailing newline to stdout. Prints an error and aborts
  // with status code 1 if the ALS has been disabled or no path was found.
  void PrintAmbientLightPath(bool keyboard) {
    Prefs prefs;
    CHECK(prefs.Init(Prefs::GetDefaultStore(), Prefs::GetDefaultSources()));
    int64_t num_als = 0;
    if (!prefs.GetInt64(power_manager::kHasAmbientLightSensorPref, &num_als) ||
        !num_als) {
      Abort("Ambient light sensor not enabled");
    }
  
    AmbientLightSensorManager als_manager;
    als_manager.Init(&prefs);
    als_manager.Run(true /* read_immediately */);
  
    AmbientLightSensorInterface* sensor;
    sensor = keyboard ? als_manager.GetSensorForKeyboardBacklight()
                      : als_manager.GetSensorForInternalBacklight();
    if (!sensor)
      Abort("Ambient light sensor not found");
  
    base::FilePath path = sensor->GetIlluminancePath();
    if (path.empty())
      Abort("Ambient light sensor illuminance file not found");
  
    printf("%s\n", path.value().c_str());
  }
  
  }  // namespace
  
  int main(int argc, char* argv[]) {
    // Flags that print the brightness.
    DEFINE_bool(get_brightness, false, "Print current brightness level");
    DEFINE_bool(get_brightness_percent, false,
                "Print current brightness as linear percent");
    DEFINE_bool(get_max_brightness, false, "Print max brightness level");
    DEFINE_bool(get_initial_brightness, false,
                "Print brightness level used "
                "by powerd at boot");
  
    // Flags that convert between units.
    DEFINE_double(nonlinear_to_level, -1.0,
                  "Convert supplied nonlinear brightness percent to level");
    DEFINE_int64(level_to_nonlinear, -1,
                 "Convert supplied brightness level to nonlinear percent");
    DEFINE_double(linear_to_level, -1.0,
                  "Convert supplied linear brightness percent to level");
    DEFINE_int64(level_to_linear, -1,
                 "Convert supplied brightness level to linear percent");
    DEFINE_double(linear_to_nonlinear, -1.0,
                  "Convert supplied linear brightness percent to nonlinear");
    DEFINE_double(nonlinear_to_linear, -1.0,
                  "Convert supplied nonlinear brightness percent to linear");
  
    // Flags that set the brightness.
    DEFINE_int64(set_brightness, -1, "Set brightness level");
    DEFINE_double(set_brightness_percent, -1.0,
                  "Set brightness as "
                  "linearly-calculated percent in [0.0, 100.0]");
  
    // Other flags.
    DEFINE_bool(get_ambient_light_path, false,
                "Print path to ambient light sensor illuminance file");
    DEFINE_bool(force_battery, false,
                "Act as if on battery even if currently on AC (for "
                "-get_initial_brightness)");
    DEFINE_bool(keyboard, false, "Use keyboard (rather than panel) backlight");
    DEFINE_int32(lux, 0,
                 "Ambient light sensor reading (for -get_initial_brightness)");
  
    brillo::FlagHelper::Init(
        argc, argv,
        "Print or set the internal panel or keyboard backlight's brightness.");
  
    base::AtExitManager at_exit_manager;
    base::SingleThreadTaskExecutor task_executor(base::MessagePumpType::IO);
    logging::SetMinLogLevel(logging::LOG_WARNING);
  
    if (FLAGS_get_brightness + FLAGS_get_max_brightness +
            FLAGS_get_initial_brightness + FLAGS_get_brightness_percent +
            FLAGS_get_ambient_light_path + (FLAGS_nonlinear_to_level >= 0.0) +
            (FLAGS_level_to_nonlinear >= 0) + (FLAGS_linear_to_level >= 0.0) +
            (FLAGS_level_to_linear >= 0) + (FLAGS_linear_to_nonlinear >= 0.0) +
            (FLAGS_nonlinear_to_linear >= 0.0) >
        1) {
      Abort("At most one flag that prints a level or percent may be passed.");
    }
    if (FLAGS_set_brightness >= 0 && FLAGS_set_brightness_percent >= 0.0)
      Abort("At most one of -set_brightness* may be passed.");
  
    if (FLAGS_get_ambient_light_path) {
      PrintAmbientLightPath(FLAGS_keyboard);
      return 0;
    }
  
    InternalBacklight backlight;
    base::FilePath path(FLAGS_keyboard ? power_manager::kKeyboardBacklightPath
                                       : power_manager::kInternalBacklightPath);
    std::string pattern = FLAGS_keyboard
                              ? power_manager::kKeyboardBacklightPattern
                              : power_manager::kInternalBacklightPattern;
    if (!backlight.Init(path, pattern))
      Abort("No backlight in " + path.value() + " matched by " + pattern + ".");
  
    const int64_t current_level = backlight.GetCurrentBrightnessLevel();
    Converter converter(current_level, backlight.GetMaxBrightnessLevel(),
                        backlight.GetBrightnessScale(), FLAGS_lux, FLAGS_keyboard,
                        FLAGS_force_battery);
  
    // Print brightness.
    if (FLAGS_get_brightness)
      printf("%" PRIi64 "\n", current_level);
    if (FLAGS_get_max_brightness)
      printf("%" PRIi64 "\n", backlight.GetMaxBrightnessLevel());
    if (FLAGS_get_brightness_percent)
      printf("%f\n", converter.LevelToLinearPercent(current_level));
    if (FLAGS_get_initial_brightness)
      printf("%" PRIi64 "\n", converter.GetInitialLevel());
  
    // Convert between units.
    if (FLAGS_nonlinear_to_level >= 0.0) {
      printf("%" PRIi64 "\n",
             converter.NonlinearPercentToLevel(FLAGS_nonlinear_to_level));
    }
    if (FLAGS_level_to_nonlinear >= 0) {
      printf("%f\n", converter.LevelToNonlinearPercent(FLAGS_level_to_nonlinear));
    }
    if (FLAGS_linear_to_level >= 0.0) {
      printf("%" PRIi64 "\n",
             converter.LinearPercentToLevel(FLAGS_linear_to_level));
    }
    if (FLAGS_level_to_linear >= 0) {
      printf("%f\n", converter.LevelToLinearPercent(FLAGS_level_to_linear));
    }
    if (FLAGS_linear_to_nonlinear >= 0.0) {
      printf("%f\n",
             converter.LevelToNonlinearPercent(
                 converter.LinearPercentToLevel(FLAGS_linear_to_nonlinear)));
    }
    if (FLAGS_nonlinear_to_linear >= 0.0) {
      printf("%f\n",
             converter.LevelToLinearPercent(
                 converter.NonlinearPercentToLevel(FLAGS_nonlinear_to_linear)));
    }
  
    // Change the brightness.
    if (FLAGS_set_brightness >= 0) {
      CHECK(
          backlight.SetBrightnessLevel(FLAGS_set_brightness, base::TimeDelta()));
    }
    if (FLAGS_set_brightness_percent >= 0.0) {
      CHECK(backlight.SetBrightnessLevel(
          converter.LinearPercentToLevel(FLAGS_set_brightness_percent),
          base::TimeDelta()));
    }
  
    return 0;
  }
  