blob: 40c598ca36971b77bfc1ea81576595368de36f78 [file] [log] [blame]
/*
* Copyright (C) 2022 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.android.clockwork.displayoffload;
import android.content.ContentResolver;
import android.content.res.Resources;
import android.database.ContentObserver;
import android.hardware.Sensor;
import android.hardware.SensorManager;
import android.net.Uri;
import android.os.Build;
import android.os.Handler;
import android.os.RemoteException;
import android.provider.Settings;
import android.text.TextUtils;
import android.util.Log;
import com.android.clockwork.ArrayUtils;
import com.android.clockwork.power.AmbientConfig;
import com.android.internal.R;
import com.android.internal.annotations.VisibleForTesting;
import java.util.ArrayList;
import java.util.List;
/**
* Helper class that handles all brightness offload related logic.
*/
public class BrightnessOffloadController implements AmbientConfig.Listener {
private static final String TAG = "DOBrightness";
private static final boolean DEBUG = Build.IS_DEBUGGABLE;
private static final float DEFAULT_BRIGHTNESS_DOZE_SCREEN_FACTOR = 1f;
private final Object mBrightnessOperationLock = new Object();
private final ContentResolver mContentResolver;
private final SensorManager mSensorManager;
private final HalAdapter mHalAdapter;
private final Handler mBackgroundHandler;
private final ContentObserver mSettingsObserver;
private final int[] mAutoBrightnessLevels;
private final int[] mAutoBrightnessLcdBacklightValuesNormal;
private final int[] mAutoBrightnessLcdBacklightValuesDoze;
private final int mScreenBrightnessSettingDefault;
private final int mScreenBrightnessDozeDefault;
private final float mScreenAutoBrightnessDozeScaleFactor;
private final int mMaxBrightness;
// AmbientConfig related
private AmbientConfig mAmbientConfig;
private boolean mBrightenOnWristTilt;
private boolean mAmbientEnabled;
private float mBrightnessDozeScreenFactor;
BrightnessOffloadController(ContentResolver contentResolver, Resources baseResources,
SensorManager sensorManager, Handler backgroundHandler, HalAdapter halAdapter) {
this.mContentResolver = contentResolver;
this.mBackgroundHandler = backgroundHandler;
this.mHalAdapter = halAdapter;
this.mSensorManager = sensorManager;
// Read values from frameworks/base resources (possibly set per OEM via overlay)
mAutoBrightnessLevels = baseResources.getIntArray(R.array.config_autoBrightnessLevels);
mAutoBrightnessLcdBacklightValuesNormal = baseResources.getIntArray(
R.array.config_autoBrightnessLcdBacklightValues);
mAutoBrightnessLcdBacklightValuesDoze = baseResources.getIntArray(
R.array.config_autoBrightnessLcdBacklightValues_doze);
mScreenBrightnessSettingDefault = baseResources.getInteger(
R.integer.config_screenBrightnessSettingDefault);
mScreenBrightnessDozeDefault = baseResources.getInteger(
R.integer.config_screenBrightnessDoze);
mScreenAutoBrightnessDozeScaleFactor = baseResources.getFraction(
R.fraction.config_screenAutoBrightnessDozeScaleFactor, 1, 1);
mMaxBrightness = baseResources.getInteger(R.integer.config_screenBrightnessSettingMaximum);
mBrightnessDozeScreenFactor = DEFAULT_BRIGHTNESS_DOZE_SCREEN_FACTOR;
mSettingsObserver = new ContentObserver(mBackgroundHandler) {
// Always run on background handler
@Override
public void onChange(boolean selfChange, Uri uri) {
if (DEBUG) {
Log.d(TAG, "Brightness settings changed, reloading brightness.");
}
loadBrightness();
}
};
}
/** Load and initialize IDisplayOffload with "bright" and "dim" sets of brightnesses */
void loadBrightness() {
synchronized (mBrightnessOperationLock) {
loadBrightnessLocked();
}
}
/** Load and initialize IDisplayOffload with "bright" and "dim" sets of brightnesses */
void loadBrightnessLocked() {
Log.i(TAG, "loadBrightnessLocked()");
int screenBrightnessDoze = mScreenBrightnessDozeDefault;
boolean screenBrightnessLockActive = false;
// Read user configured preferences
ContentResolver resolver = mContentResolver;
int screenBrightness = Settings.System.getInt(resolver, Settings.System.SCREEN_BRIGHTNESS,
mScreenBrightnessSettingDefault);
int screenBrightnessMode = Settings.System.getInt(resolver,
Settings.System.SCREEN_BRIGHTNESS_MODE, 0);
int[] autoBrightnessLcdBacklightValues = mAutoBrightnessLcdBacklightValuesNormal;
// Determine whether ALS is supported and should be used. Assume it should be used,
// then check for conditions indicating it should be disabled.
Sensor lightSensor = mSensorManager.getDefaultSensor(Sensor.TYPE_LIGHT);
final boolean shouldUseAls;
if (lightSensor == null) {
Log.d(TAG, "No ALS sensor. Offload ALS won't be used.");
shouldUseAls = false;
} else if (autoBrightnessLcdBacklightValues == null
|| autoBrightnessLcdBacklightValues.length <= 1) {
Log.d(TAG, "Resource configuration doesn't include valid brightness values. "
+ "Offload ALS won't be used.");
shouldUseAls = false;
} else if (mAutoBrightnessLevels == null
|| autoBrightnessLcdBacklightValues.length != mAutoBrightnessLevels.length + 1) {
Log.d(TAG, "Resource configuration doesn't include valid brightness thresholds. "
+ "Offload ALS won't be used.");
shouldUseAls = false;
} else if (screenBrightnessMode != Settings.System.SCREEN_BRIGHTNESS_MODE_AUTOMATIC) {
Log.d(TAG, "User has configured manual brightness. Offload ALS won't be used.");
shouldUseAls = false;
} else {
shouldUseAls = true;
}
// If we are using ALS then don't listen for brightness changes
mBackgroundHandler.post(() -> registerForBrightnessConfigChanges(
/* shouldListenForBrightnessChange= */ !shouldUseAls));
Log.d(TAG, "screenBrightness = " + screenBrightness);
ArrayList<Short> brightnessValuesBright;
ArrayList<Short> brightnessValuesDim;
final boolean isAmbientEnabled = mAmbientEnabled;
// Populate rising/falling thresholds as well as the bright/dim brightness values
ArrayList<Short> alsThresholds;
if (shouldUseAls) {
float factor = screenBrightnessLockActive ? 1 : mScreenAutoBrightnessDozeScaleFactor;
alsThresholds = ArrayUtils.intArrayToShortArrayList(mAutoBrightnessLevels);
brightnessValuesBright = ArrayUtils.intArrayToShortArrayList(
autoBrightnessLcdBacklightValues);
if (!isAmbientEnabled) {
brightnessValuesDim = ArrayUtils.createMonotonicShortArray(
brightnessValuesBright.size(), 0);
} else if (mAutoBrightnessLcdBacklightValuesDoze.length == brightnessValuesBright.size()
&& !screenBrightnessLockActive) {
brightnessValuesDim = ArrayUtils.intArrayToShortArrayList(
mAutoBrightnessLcdBacklightValuesDoze);
} else {
brightnessValuesDim = new ArrayList<>(autoBrightnessLcdBacklightValues.length);
for (int value : autoBrightnessLcdBacklightValues) {
brightnessValuesDim.add((short) (value * factor));
}
}
} else {
int brightnessValueDim =
screenBrightnessLockActive ? screenBrightness
: screenBrightnessDoze;
brightnessValueDim = (int) Math.min(
brightnessValueDim * mBrightnessDozeScreenFactor,
mMaxBrightness
);
screenBrightness = (int) Math.min(
screenBrightness * mBrightnessDozeScreenFactor,
mMaxBrightness
);
alsThresholds = ArrayUtils.intArrayToShortArrayList(new int[]{});
brightnessValuesBright = ArrayUtils.intArrayToShortArrayList(
new int[]{screenBrightness});
brightnessValuesDim = ArrayUtils.intArrayToShortArrayList(
new int[]{isAmbientEnabled ? brightnessValueDim : 0});
}
try {
mHalAdapter.setBrightnessConfiguration(mBrightenOnWristTilt, alsThresholds,
brightnessValuesDim, brightnessValuesBright);
} catch (RemoteException e) {
Log.e(TAG, "Unable to send updated user settings.", e);
}
}
void setBrightnessDozeScreenFactor(float factor) {
mBrightnessDozeScreenFactor = factor;
}
private Sensor findLightSensor(String sensorType) {
if (!TextUtils.isEmpty(sensorType)) {
List<Sensor> sensors = mSensorManager.getSensorList(Sensor.TYPE_ALL);
for (Sensor sensor : sensors) {
if (sensorType.equals(sensor.getStringType())) {
return sensor;
}
}
}
return mSensorManager.getDefaultSensor(Sensor.TYPE_LIGHT);
}
// TODO(b/240342455): Refactor this call into 2 parts to avoid re-registering necessary
// listeners
private void registerForBrightnessConfigChanges(boolean shouldListenForBrightnessChange) {
mContentResolver.unregisterContentObserver(mSettingsObserver);
mContentResolver.registerContentObserver(
Settings.System.getUriFor(Settings.System.SCREEN_BRIGHTNESS_MODE), true,
mSettingsObserver);
if (shouldListenForBrightnessChange) {
mContentResolver.registerContentObserver(
Settings.System.getUriFor(Settings.System.SCREEN_BRIGHTNESS), true,
mSettingsObserver);
}
}
@Override
public void onAmbientConfigChanged() {
mAmbientEnabled = mAmbientConfig.isAmbientEnabled();
mBrightenOnWristTilt = mAmbientConfig.isUserTiltToBright();
loadBrightness();
}
/** Notify brightness offload controller when HAL restarts. */
public void onHalRestart() {
loadBrightness();
}
public void onBootComplete() {
onBootComplete(new AmbientConfig(mContentResolver));
}
@VisibleForTesting
void onBootComplete(AmbientConfig ambientConfig) {
mAmbientConfig = ambientConfig;
mAmbientConfig.register();
onAmbientConfigChanged();
mAmbientConfig.addListener(this);
}
}