/*
|
* 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);
|
}
|
}
|
|
}
|
|
}
|