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