chromium/ash/system/power/backlights_forced_off_setter.cc

// Copyright 2017 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include "ash/system/power/backlights_forced_off_setter.h"

#include "ash/constants/ash_switches.h"
#include "ash/shell.h"
#include "ash/system/power/scoped_backlights_forced_off.h"
#include "ash/touch/touch_devices_controller.h"
#include "base/check_op.h"
#include "base/command_line.h"
#include "base/functional/bind.h"
#include "chromeos/dbus/power_manager/backlight.pb.h"
#include "ui/display/manager/touch_device_manager.h"

namespace ash {

BacklightsForcedOffSetter::BacklightsForcedOffSetter() {
  InitDisableTouchscreenWhileScreenOff();

  power_manager_observation_.Observe(chromeos::PowerManagerClient::Get());
  GetInitialBacklightsForcedOff();
}

BacklightsForcedOffSetter::~BacklightsForcedOffSetter() {
  if (active_backlights_forced_off_count_ > 0) {
    chromeos::PowerManagerClient::Get()->SetBacklightsForcedOff(false);
  }
}

void BacklightsForcedOffSetter::AddObserver(ScreenBacklightObserver* observer) {
  observers_.AddObserver(observer);
}

void BacklightsForcedOffSetter::RemoveObserver(
    ScreenBacklightObserver* observer) {
  observers_.RemoveObserver(observer);
}

ScreenBacklightState BacklightsForcedOffSetter::GetScreenBacklightState()
    const {
  return screen_backlight_state_;
}

std::unique_ptr<ScopedBacklightsForcedOff>
BacklightsForcedOffSetter::ForceBacklightsOff() {
  auto scoped_backlights_forced_off =
      std::make_unique<ScopedBacklightsForcedOff>(base::BindOnce(
          &BacklightsForcedOffSetter::OnScopedBacklightsForcedOffDestroyed,
          weak_ptr_factory_.GetWeakPtr()));
  ++active_backlights_forced_off_count_;
  SetBacklightsForcedOff(true);
  return scoped_backlights_forced_off;
}

void BacklightsForcedOffSetter::ScreenBrightnessChanged(
    const power_manager::BacklightBrightnessChange& change) {
  const bool user_initiated =
      change.cause() ==
      power_manager::BacklightBrightnessChange_Cause_USER_REQUEST;

  const ScreenBacklightState old_state = screen_backlight_state_;
  if (change.percent() > 0.0)
    screen_backlight_state_ = ScreenBacklightState::ON;
  else
    screen_backlight_state_ = user_initiated ? ScreenBacklightState::OFF
                                             : ScreenBacklightState::OFF_AUTO;

  if (screen_backlight_state_ != old_state) {
    for (auto& observer : observers_)
      observer.OnScreenBacklightStateChanged(screen_backlight_state_);
  }

  // Disable the touchscreen when the screen is turned off due to inactivity:
  // https://crbug.com/743291
  if ((screen_backlight_state_ == ScreenBacklightState::OFF_AUTO) !=
          (old_state == ScreenBacklightState::OFF_AUTO) &&
      disable_touchscreen_while_screen_off_) {
    UpdateTouchscreenStatus();
  }
}

void BacklightsForcedOffSetter::PowerManagerRestarted() {
  if (backlights_forced_off_.has_value()) {
    chromeos::PowerManagerClient::Get()->SetBacklightsForcedOff(
        backlights_forced_off_.value());
  } else {
    GetInitialBacklightsForcedOff();
  }
}

void BacklightsForcedOffSetter::ResetForTest() {
  if (active_backlights_forced_off_count_ > 0) {
    chromeos::PowerManagerClient::Get()->SetBacklightsForcedOff(false);
  }

  // Cancel all backlights forced off requests.
  weak_ptr_factory_.InvalidateWeakPtrs();
  active_backlights_forced_off_count_ = 0;

  InitDisableTouchscreenWhileScreenOff();

  backlights_forced_off_.reset();
  GetInitialBacklightsForcedOff();
}

void BacklightsForcedOffSetter::InitDisableTouchscreenWhileScreenOff() {
  disable_touchscreen_while_screen_off_ =
      !base::CommandLine::ForCurrentProcess()->HasSwitch(
          switches::kTouchscreenUsableWhileScreenOff);
}

void BacklightsForcedOffSetter::GetInitialBacklightsForcedOff() {
  chromeos::PowerManagerClient::Get()->GetBacklightsForcedOff(base::BindOnce(
      &BacklightsForcedOffSetter::OnGotInitialBacklightsForcedOff,
      weak_ptr_factory_.GetWeakPtr()));
}

void BacklightsForcedOffSetter::OnGotInitialBacklightsForcedOff(
    std::optional<bool> is_forced_off) {
  if (backlights_forced_off_.has_value() || !is_forced_off.has_value())
    return;

  backlights_forced_off_ = is_forced_off;

  UpdateTouchscreenStatus();

  for (auto& observer : observers_)
    observer.OnBacklightsForcedOffChanged(backlights_forced_off_.value());
}

void BacklightsForcedOffSetter::OnScopedBacklightsForcedOffDestroyed() {
  DCHECK_GT(active_backlights_forced_off_count_, 0);

  --active_backlights_forced_off_count_;
  SetBacklightsForcedOff(active_backlights_forced_off_count_ > 0);
}

void BacklightsForcedOffSetter::SetBacklightsForcedOff(bool forced_off) {
  if (backlights_forced_off_.has_value() &&
      backlights_forced_off_.value() == forced_off) {
    return;
  }

  backlights_forced_off_ = forced_off;
  chromeos::PowerManagerClient::Get()->SetBacklightsForcedOff(forced_off);

  UpdateTouchscreenStatus();

  for (auto& observer : observers_)
    observer.OnBacklightsForcedOffChanged(backlights_forced_off_.value());
}

void BacklightsForcedOffSetter::UpdateTouchscreenStatus() {
  // If there is an external touch display connected to the device, then we want
  // to allow users to wake up the device using this external touch device. The
  // kernel blocks wake up events from internal input devices when the screen is
  // off or is in the suspended state.
  // See https://crbug/797411 for more details.
  const bool disable_touchscreen =
      backlights_forced_off_.value_or(false) ||
      (screen_backlight_state_ == ScreenBacklightState::OFF_AUTO &&
       disable_touchscreen_while_screen_off_ &&
       !display::HasExternalTouchscreenDevice());
  Shell::Get()->touch_devices_controller()->SetTouchscreenEnabled(
      !disable_touchscreen, TouchDeviceEnabledSource::GLOBAL);
}

}  // namespace ash