blob: d20a177324db772e72eafc37bec78646e5a0668b [file] [log] [blame]
/*
* Copyright (C) 2016 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.car.systeminterface;
import static com.android.settingslib.display.BrightnessUtils.GAMMA_SPACE_MAX;
import static com.android.settingslib.display.BrightnessUtils.convertGammaToLinear;
import static com.android.settingslib.display.BrightnessUtils.convertLinearToGamma;
import android.app.ActivityManager;
import android.car.userlib.CarUserManagerHelper;
import android.car.userlib.CarUserManagerHelper.OnUsersUpdateListener;
import android.content.ContentResolver;
import android.content.Context;
import android.database.ContentObserver;
import android.hardware.display.DisplayManager;
import android.hardware.display.DisplayManager.DisplayListener;
import android.hardware.input.InputManager;
import android.os.Handler;
import android.os.Looper;
import android.os.PowerManager;
import android.os.RemoteException;
import android.os.ServiceManager;
import android.os.SystemClock;
import android.os.UserHandle;
import android.provider.Settings.SettingNotFoundException;
import android.provider.Settings.System;
import android.util.Log;
import android.view.Display;
import android.view.DisplayAddress;
import android.view.IWindowManager;
import android.view.InputDevice;
import com.android.car.CarLog;
import com.android.car.CarPowerManagementService;
/**
* Interface that abstracts display operations
*/
public interface DisplayInterface {
/**
* @param brightness Level from 0 to 100%
*/
void setDisplayBrightness(int brightness);
void setDisplayState(boolean on);
void startDisplayStateMonitoring(CarPowerManagementService service);
void stopDisplayStateMonitoring();
/**
* Refreshing display brightness. Used when user is switching and car turned on.
*/
void refreshDisplayBrightness();
/**
* Reconfigure all secondary displays due to b/131909551
*/
void reconfigureSecondaryDisplays();
/**
* Default implementation of display operations
*/
class DefaultImpl implements DisplayInterface, OnUsersUpdateListener {
static final String TAG = DisplayInterface.class.getSimpleName();
private final ActivityManager mActivityManager;
private final ContentResolver mContentResolver;
private final Context mContext;
private final DisplayManager mDisplayManager;
private final InputManager mInputManager;
private final int mMaximumBacklight;
private final int mMinimumBacklight;
private final PowerManager mPowerManager;
private final WakeLockInterface mWakeLockInterface;
private CarPowerManagementService mService;
private boolean mDisplayStateSet;
private CarUserManagerHelper mCarUserManagerHelper;
private int mLastBrightnessLevel = -1;
private ContentObserver mBrightnessObserver =
new ContentObserver(new Handler(Looper.getMainLooper())) {
@Override
public void onChange(boolean selfChange) {
refreshDisplayBrightness();
}
};
private final DisplayManager.DisplayListener mDisplayListener = new DisplayListener() {
@Override
public void onDisplayAdded(int displayId) {
//ignore
}
@Override
public void onDisplayRemoved(int displayId) {
//ignore
}
@Override
public void onDisplayChanged(int displayId) {
if (displayId == Display.DEFAULT_DISPLAY) {
handleMainDisplayChanged();
}
}
};
DefaultImpl(Context context, WakeLockInterface wakeLockInterface) {
mActivityManager = (ActivityManager) context.getSystemService(Context.ACTIVITY_SERVICE);
mContext = context;
mContentResolver = mContext.getContentResolver();
mDisplayManager = (DisplayManager) context.getSystemService(Context.DISPLAY_SERVICE);
mInputManager = (InputManager) mContext.getSystemService(Context.INPUT_SERVICE);
mPowerManager = (PowerManager) context.getSystemService(Context.POWER_SERVICE);
mMaximumBacklight = mPowerManager.getMaximumScreenBrightnessSetting();
mMinimumBacklight = mPowerManager.getMinimumScreenBrightnessSetting();
mWakeLockInterface = wakeLockInterface;
mCarUserManagerHelper = new CarUserManagerHelper(context);
mCarUserManagerHelper.registerOnUsersUpdateListener(this);
}
@Override
public synchronized void refreshDisplayBrightness() {
int gamma = GAMMA_SPACE_MAX;
try {
int linear = System.getIntForUser(
mContentResolver,
System.SCREEN_BRIGHTNESS,
mActivityManager.getCurrentUser());
gamma = convertLinearToGamma(linear, mMinimumBacklight, mMaximumBacklight);
} catch (SettingNotFoundException e) {
Log.e(CarLog.TAG_POWER, "Could not get SCREEN_BRIGHTNESS: " + e);
}
int percentBright = (gamma * 100 + ((GAMMA_SPACE_MAX + 1) / 2)) / GAMMA_SPACE_MAX;
mService.sendDisplayBrightness(percentBright);
}
private void handleMainDisplayChanged() {
boolean isOn = isMainDisplayOn();
CarPowerManagementService service;
synchronized (this) {
if (mDisplayStateSet == isOn) { // same as what is set
return;
}
service = mService;
}
service.handleMainDisplayChanged(isOn);
}
private boolean isMainDisplayOn() {
Display disp = mDisplayManager.getDisplay(Display.DEFAULT_DISPLAY);
return disp.getState() == Display.STATE_ON;
}
@Override
public void setDisplayBrightness(int percentBright) {
if (percentBright == mLastBrightnessLevel) {
// We have already set the value last time. Skipping
return;
}
mLastBrightnessLevel = percentBright;
int gamma = (percentBright * GAMMA_SPACE_MAX + 50) / 100;
int linear = convertGammaToLinear(gamma, mMinimumBacklight, mMaximumBacklight);
System.putIntForUser(
mContentResolver,
System.SCREEN_BRIGHTNESS,
linear,
mActivityManager.getCurrentUser());
}
@Override
public void startDisplayStateMonitoring(CarPowerManagementService service) {
synchronized (this) {
mService = service;
mDisplayStateSet = isMainDisplayOn();
}
mContentResolver.registerContentObserver(
System.getUriFor(System.SCREEN_BRIGHTNESS),
false,
mBrightnessObserver,
UserHandle.USER_ALL);
mDisplayManager.registerDisplayListener(mDisplayListener, service.getHandler());
refreshDisplayBrightness();
}
@Override
public void stopDisplayStateMonitoring() {
mDisplayManager.unregisterDisplayListener(mDisplayListener);
mContentResolver.unregisterContentObserver(mBrightnessObserver);
}
@Override
public void setDisplayState(boolean on) {
synchronized (this) {
mDisplayStateSet = on;
}
if (on) {
mWakeLockInterface.switchToFullWakeLock();
Log.i(CarLog.TAG_POWER, "on display");
mPowerManager.wakeUp(SystemClock.uptimeMillis());
} else {
mWakeLockInterface.switchToPartialWakeLock();
Log.i(CarLog.TAG_POWER, "off display");
mPowerManager.goToSleep(SystemClock.uptimeMillis());
}
// Turn touchscreen input devices on or off, the same as the display
for (int deviceId : mInputManager.getInputDeviceIds()) {
InputDevice inputDevice = mInputManager.getInputDevice(deviceId);
if (inputDevice != null
&& (inputDevice.getSources() & InputDevice.SOURCE_TOUCHSCREEN)
== InputDevice.SOURCE_TOUCHSCREEN) {
if (on) {
mInputManager.enableInputDevice(deviceId);
} else {
mInputManager.disableInputDevice(deviceId);
}
}
}
}
@Override
public void onUsersUpdate() {
if (mService == null) {
// CarPowerManagementService is not connected yet
return;
}
// We need to reset last value
mLastBrightnessLevel = -1;
refreshDisplayBrightness();
}
@Override
public void reconfigureSecondaryDisplays() {
IWindowManager wm = IWindowManager.Stub
.asInterface(ServiceManager.getService(Context.WINDOW_SERVICE));
if (wm == null) {
Log.e(TAG, "reconfigureSecondaryDisplays IWindowManager not available");
return;
}
Display[] displays = mDisplayManager.getDisplays();
for (Display display : displays) {
if (display.getDisplayId() == Display.DEFAULT_DISPLAY) { // skip main
continue;
}
// Only use physical secondary displays
if (display.getAddress() instanceof DisplayAddress.Physical) {
int displayId = display.getDisplayId();
try {
// Do not change the mode but this triggers reconfiguring.
int windowingMode = wm.getWindowingMode(displayId);
wm.setWindowingMode(displayId, windowingMode);
} catch (RemoteException e) {
Log.e(CarLog.TAG_SERVICE, "cannot access IWindowManager", e);
}
}
}
}
}
}