blob: 1707a62f805ac7cb219c533befb07bffc7ba9b2a [file] [log] [blame]
/*
* Copyright (C) 2019 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.server.display.whitebalance;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.hardware.Sensor;
import android.hardware.SensorEvent;
import android.hardware.SensorEventListener;
import android.hardware.SensorManager;
import android.os.Handler;
import android.util.Slog;
import com.android.internal.util.Preconditions;
import com.android.server.display.utils.History;
import java.io.PrintWriter;
/**
* The DisplayWhiteBalanceController uses the AmbientSensor to detect changes in the ambient
* brightness and color temperature.
*
* The AmbientSensor listens on an actual sensor, derives the ambient brightness or color
* temperature from its events, and calls back into the DisplayWhiteBalanceController to report it.
*/
abstract class AmbientSensor {
protected String mTag;
protected boolean mLoggingEnabled;
private final Handler mHandler;
protected final SensorManager mSensorManager;
protected Sensor mSensor;
private boolean mEnabled;
private int mRate; // Milliseconds
// The total events count and the most recent events are kept for debugging purposes.
private int mEventsCount;
private static final int HISTORY_SIZE = 50;
private History mEventsHistory;
/**
* @param tag
* The tag used for dumping and logging.
* @param handler
* The handler used to determine which thread to run on.
* @param sensorManager
* The sensor manager used to acquire necessary sensors.
* @param rate
* The sensor rate.
*
* @throws IllegalArgumentException
* - rate is not positive.
* @throws NullPointerException
* - handler is null;
* - sensorManager is null.
* @throws IllegalStateException
* - Cannot find the necessary sensor.
*/
AmbientSensor(String tag, @NonNull Handler handler, @NonNull SensorManager sensorManager,
int rate) {
validateArguments(handler, sensorManager, rate);
mTag = tag;
mLoggingEnabled = false;
mHandler = handler;
mSensorManager = sensorManager;
mEnabled = false;
mRate = rate;
mEventsCount = 0;
mEventsHistory = new History(HISTORY_SIZE);
}
/**
* Enable/disable the sensor.
*
* @param enabled
* Whether the sensor should be on/off.
*
* @return Whether the method succeeded or not.
*/
public boolean setEnabled(boolean enabled) {
if (enabled) {
return enable();
} else {
return disable();
}
}
/**
* Enable/disable logging.
*
* @param loggingEnabled
* Whether logging should be on/off.
*
* @return Whether the method succeeded or not.
*/
public boolean setLoggingEnabled(boolean loggingEnabled) {
if (mLoggingEnabled == loggingEnabled) {
return false;
}
mLoggingEnabled = loggingEnabled;
return true;
}
/**
* Dump the state.
*
* @param writer
* The PrintWriter used to dump the state.
*/
public void dump(PrintWriter writer) {
writer.println(" " + mTag);
writer.println(" mLoggingEnabled=" + mLoggingEnabled);
writer.println(" mHandler=" + mHandler);
writer.println(" mSensorManager=" + mSensorManager);
writer.println(" mSensor=" + mSensor);
writer.println(" mEnabled=" + mEnabled);
writer.println(" mRate=" + mRate);
writer.println(" mEventsCount=" + mEventsCount);
writer.println(" mEventsHistory=" + mEventsHistory);
}
private static void validateArguments(Handler handler, SensorManager sensorManager, int rate) {
Preconditions.checkNotNull(handler, "handler cannot be null");
Preconditions.checkNotNull(sensorManager, "sensorManager cannot be null");
if (rate <= 0) {
throw new IllegalArgumentException("rate must be positive");
}
}
protected abstract void update(float value);
private boolean enable() {
if (mEnabled) {
return false;
}
if (mLoggingEnabled) {
Slog.d(mTag, "enabling");
}
mEnabled = true;
startListening();
return true;
}
private boolean disable() {
if (!mEnabled) {
return false;
}
if (mLoggingEnabled) {
Slog.d(mTag, "disabling");
}
mEnabled = false;
mEventsCount = 0;
stopListening();
return true;
}
private void startListening() {
if (mSensorManager == null) {
return;
}
mSensorManager.registerListener(mListener, mSensor, mRate * 1000, mHandler);
}
private void stopListening() {
if (mSensorManager == null) {
return;
}
mSensorManager.unregisterListener(mListener);
}
private void handleNewEvent(float value) {
// This shouldn't really happen, except for the race condition where the sensor is disabled
// with an event already in the handler queue, in which case we discard that event.
if (!mEnabled) {
return;
}
if (mLoggingEnabled) {
Slog.d(mTag, "handle new event: " + value);
}
mEventsCount++;
mEventsHistory.add(value);
update(value);
}
private SensorEventListener mListener = new SensorEventListener() {
@Override
public void onSensorChanged(SensorEvent event) {
final float value = event.values[0];
handleNewEvent(value);
}
@Override
public void onAccuracyChanged(Sensor sensor, int accuracy) {
// Not used.
}
};
/**
* A sensor that reports the ambient brightness.
*/
static class AmbientBrightnessSensor extends AmbientSensor {
private static final String TAG = "AmbientBrightnessSensor";
// To decouple the DisplayWhiteBalanceController from the AmbientBrightnessSensor, the
// DWBC implements Callbacks and passes itself to the ABS so it can call back into it
// without knowing about it.
@Nullable
private Callbacks mCallbacks;
/**
* @param handler
* The handler used to determine which thread to run on.
* @param sensorManager
* The sensor manager used to acquire necessary sensors.
* @param rate
* The sensor rate.
*
* @throws IllegalArgumentException
* - rate is not positive.
* @throws NullPointerException
* - handler is null;
* - sensorManager is null.
* @throws IllegalStateException
* - Cannot find the light sensor.
*/
AmbientBrightnessSensor(@NonNull Handler handler, @NonNull SensorManager sensorManager,
int rate) {
super(TAG, handler, sensorManager, rate);
mSensor = mSensorManager.getDefaultSensor(Sensor.TYPE_LIGHT);
if (mSensor == null) {
throw new IllegalStateException("cannot find light sensor");
}
mCallbacks = null;
}
/**
* Set an object to call back to when the ambient brightness changes.
*
* @param callbacks
* The object to call back to.
*
* @return Whether the method succeeded or not.
*/
public boolean setCallbacks(Callbacks callbacks) {
if (mCallbacks == callbacks) {
return false;
}
mCallbacks = callbacks;
return true;
}
/**
* See {@link AmbientSensor#dump base class}.
*/
@Override
public void dump(PrintWriter writer) {
super.dump(writer);
writer.println(" mCallbacks=" + mCallbacks);
}
interface Callbacks {
void onAmbientBrightnessChanged(float value);
}
@Override
protected void update(float value) {
if (mCallbacks != null) {
mCallbacks.onAmbientBrightnessChanged(value);
}
}
}
/**
* A sensor that reports the ambient color temperature.
*/
static class AmbientColorTemperatureSensor extends AmbientSensor {
private static final String TAG = "AmbientColorTemperatureSensor";
// To decouple the DisplayWhiteBalanceController from the
// AmbientColorTemperatureSensor, the DWBC implements Callbacks and passes itself to the
// ACTS so it can call back into it without knowing about it.
@Nullable
private Callbacks mCallbacks;
/**
* @param handler
* The handler used to determine which thread to run on.
* @param sensorManager
* The sensor manager used to acquire necessary sensors.
* @param name
* The color sensor name.
* @param rate
* The sensor rate.
*
* @throws IllegalArgumentException
* - rate is not positive.
* @throws NullPointerException
* - handler is null;
* - sensorManager is null.
* @throws IllegalStateException
* - Cannot find the color sensor.
*/
AmbientColorTemperatureSensor(@NonNull Handler handler,
@NonNull SensorManager sensorManager, String name, int rate) {
super(TAG, handler, sensorManager, rate);
mSensor = null;
for (Sensor sensor : mSensorManager.getSensorList(Sensor.TYPE_ALL)) {
if (sensor.getStringType().equals(name)) {
mSensor = sensor;
break;
}
}
if (mSensor == null) {
throw new IllegalStateException("cannot find sensor " + name);
}
mCallbacks = null;
}
/**
* Set an object to call back to when the ambient color temperature changes.
*
* @param callbacks
* The object to call back to.
*
* @return Whether the method succeeded or not.
*/
public boolean setCallbacks(Callbacks callbacks) {
if (mCallbacks == callbacks) {
return false;
}
mCallbacks = callbacks;
return true;
}
/**
* See {@link AmbientSensor#dump base class}.
*/
@Override
public void dump(PrintWriter writer) {
super.dump(writer);
writer.println(" mCallbacks=" + mCallbacks);
}
interface Callbacks {
void onAmbientColorTemperatureChanged(float value);
}
@Override
protected void update(float value) {
if (mCallbacks != null) {
mCallbacks.onAmbientColorTemperatureChanged(value);
}
}
}
}