blob: c513b96784192d0bcaf642b43254e62f5db5d448 [file] [log] [blame]
// Copyright 2019 The Fuchsia Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
use anyhow::{Context as _, Error};
use async_trait::async_trait;
use fidl_fuchsia_hardware_backlight::{
DeviceMarker as BacklightMarker, DeviceProxy as BacklightProxy, State as BacklightCommand,
};
use fuchsia_syslog::fx_log_info;
const MINIMUM_BRIGHTNESS: f64 = 0.0004;
fn open_backlight() -> Result<BacklightProxy, Error> {
fx_log_info!("Opening backlight");
let (proxy, server) = fidl::endpoints::create_proxy::<BacklightMarker>()
.context("Failed to create backlight proxy")?;
// TODO(kpt): Don't hardcode this path b/138666351
fdio::service_connect("/dev/class/backlight/000", server.into_channel())
.context("Failed to connect built-in service")?;
Ok(proxy)
}
pub struct Backlight {
proxy: BacklightProxy,
}
impl Backlight {
pub async fn new() -> Result<Backlight, Error> {
let proxy = open_backlight()?;
Ok(Backlight { proxy })
}
pub async fn get_max_absolute_brightness(&self) -> Result<f64, Error> {
let connection_result = self.proxy.get_max_absolute_brightness().await;
let connection = connection_result
.map_err(|e| anyhow::format_err!("Didn't connect correctly, got err {}", e))?;
let max_brightness: f64 = connection.map_err(|e| {
anyhow::format_err!("Didn't get the max_brightness back, got err {}", e)
})?;
Ok(max_brightness)
}
async fn get(&self) -> Result<f64, Error> {
let result = self.proxy.get_state_normalized().await?;
let backlight_info =
result.map_err(|e| anyhow::format_err!("Failed to get state: {:?}", e))?;
assert!(backlight_info.brightness >= 0.0);
assert!(backlight_info.brightness <= 1.0);
Ok(if backlight_info.backlight_on { backlight_info.brightness } else { 0.0 })
}
fn set(&mut self, value: f64) -> Result<(), Error> {
// TODO(fxbug.dev/36302): Handle error here as well, similar to get_brightness above. Might involve
let regulated_value = num_traits::clamp(value, MINIMUM_BRIGHTNESS, 1.0);
let _result = self.proxy.set_state_normalized(&mut BacklightCommand {
backlight_on: value > 0.0,
brightness: regulated_value,
});
Ok(())
}
}
#[async_trait]
pub trait BacklightControl: Send {
async fn get_brightness(&self) -> Result<f64, Error>;
fn set_brightness(&mut self, value: f64) -> Result<(), Error>;
async fn get_max_absolute_brightness(&self) -> Result<f64, Error>;
}
#[async_trait]
impl BacklightControl for Backlight {
async fn get_brightness(&self) -> Result<f64, Error> {
self.get().await
}
fn set_brightness(&mut self, value: f64) -> Result<(), Error> {
self.set(value)
}
async fn get_max_absolute_brightness(&self) -> Result<f64, Error> {
self.get_max_absolute_brightness().await
}
}
#[cfg(test)]
mod tests {
use super::*;
use fidl_fuchsia_hardware_backlight::DeviceRequestStream as BacklightRequestStream;
use fuchsia_async as fasync;
use futures::prelude::future;
use futures_util::stream::StreamExt;
fn mock_backlight() -> (Backlight, BacklightRequestStream) {
let (proxy, backlight_stream) =
fidl::endpoints::create_proxy_and_stream::<BacklightMarker>().unwrap();
(Backlight { proxy }, backlight_stream)
}
async fn mock_device_set(mut reqs: BacklightRequestStream) -> BacklightCommand {
match reqs.next().await.unwrap() {
Ok(fidl_fuchsia_hardware_backlight::DeviceRequest::SetStateNormalized {
state: command,
..
}) => {
return command;
}
request => panic!("Unexpected request: {:?}", request),
}
}
async fn mock_device_get(
mut reqs: BacklightRequestStream,
backlight_command: BacklightCommand,
) {
match reqs.next().await.unwrap() {
Ok(fidl_fuchsia_hardware_backlight::DeviceRequest::GetStateNormalized {
responder,
}) => {
fx_log_info!("====== got GetStateNormalized");
let response = backlight_command;
let _ = responder.send(&mut Ok(response));
}
Ok(fidl_fuchsia_hardware_backlight::DeviceRequest::GetMaxAbsoluteBrightness {
responder,
}) => {
fx_log_info!("====== GetMaxAbsoluteBrightness");
if let Err(e) = responder.send(&mut Ok(250.0)) {
panic!("Failed to reply to GetMaxAbsoluteBrightness: {}", e);
}
}
request => panic!("====== Unexpected request: {:?}", request),
}
}
#[fasync::run_singlethreaded(test)]
async fn test_brightness_returns_zero_if_screen_off() {
// Setup
let (mock, backlight_stream) = mock_backlight();
let backlight_fut = mock_device_get(
backlight_stream,
BacklightCommand { backlight_on: false, brightness: 0.04 },
);
// Act
let mock_fut = mock.get();
let (brightness, _) = future::join(mock_fut, backlight_fut).await;
// Assert
assert_eq!(brightness.unwrap(), 0.0);
}
#[fasync::run_singlethreaded(test)]
async fn test_brightness_returns_non_zero_if_screen_on() {
// Setup
let (mock, backlight_stream) = mock_backlight();
let backlight_fut = mock_device_get(
backlight_stream,
BacklightCommand { backlight_on: true, brightness: 0.04 },
);
// Act
let mock_fut = mock.get();
let (brightness, _) = future::join(mock_fut, backlight_fut).await;
// Assert
assert_eq!(brightness.unwrap(), 0.04);
}
#[fasync::run_singlethreaded(test)]
async fn test_zero_brightness_turns_screen_off() {
// Setup
let (mut mock, backlight_stream) = mock_backlight();
let backlight_fut = mock_device_set(backlight_stream);
// Act
mock.set(0.0).expect("set failed");
let backlight_command = backlight_fut.await;
// Assert
assert_eq!(backlight_command.backlight_on, false);
}
#[fasync::run_singlethreaded(test)]
async fn test_negative_brightness_turns_screen_off() {
// Setup
let (mut mock, backlight_stream) = mock_backlight();
let backlight_fut = mock_device_set(backlight_stream);
// Act
mock.set(-0.01).expect("set failed");
let backlight_command = backlight_fut.await;
// Assert
assert_eq!(backlight_command.backlight_on, false);
}
#[fasync::run_singlethreaded(test)]
async fn test_brightness_turns_screen_on() {
// Setup
let (mut mock, backlight_stream) = mock_backlight();
let backlight_fut = mock_device_set(backlight_stream);
// Act
mock.set(0.55).expect("set failed");
let backlight_command = backlight_fut.await;
// Assert
assert_eq!(backlight_command.backlight_on, true);
assert_eq!(backlight_command.brightness, 0.55);
}
#[fasync::run_singlethreaded(test)]
async fn test_get_max_absolute_brightness() {
// Setup
let (mock, backlight_stream) = mock_backlight();
let backlight_fut = mock_device_get(
backlight_stream,
BacklightCommand { backlight_on: false, brightness: 0.04 },
);
// Act
let mock_fut = mock.get_max_absolute_brightness();
let (max_brightness, _) = future::join(mock_fut, backlight_fut).await;
// Assert
assert_eq!(max_brightness.unwrap(), 250.0);
}
}