Merge "Burn in protection." into lmp-mr1-modular-dev

This commit is contained in:
Filip Gruszczynski
2015-02-25 21:30:07 +00:00
committed by Android (Google) Code Review
8 changed files with 306 additions and 1 deletions

View File

@@ -131,6 +131,19 @@ public abstract class DisplayManagerInternal {
public abstract void setDisplayProperties(int displayId, boolean hasContent,
float requestedRefreshRate, boolean inTraversal);
/**
* Applies an offset to the contents of a display, for example to avoid burn-in.
* <p>
* TODO: Technically this should be associated with a physical rather than logical
* display but this is good enough for now.
* </p>
*
* @param displayId The logical display id to update.
* @param x The X offset by which to shift the contents of the display.
* @param y The Y offset by which to shift the contents of the display.
*/
public abstract void setDisplayOffsets(int displayId, int x, int y);
/**
* Describes the requested power state of the display.
*

View File

@@ -303,6 +303,8 @@
<protected-broadcast android:name="android.intent.action.ACTION_SET_RADIO_CAPABILITY_DONE" />
<protected-broadcast android:name="android.intent.action.ACTION_SET_RADIO_CAPABILITY_FAILED" />
<protected-broadcast android:name="android.internal.policy.action.BURN_IN_PROTECTION" />
<!-- ====================================== -->
<!-- Permissions for things that cost money -->
<!-- ====================================== -->

View File

@@ -2066,4 +2066,23 @@
<!-- Scale factor threshold used by the screen magnifier to determine when to switch from
panning to scaling the magnification viewport. -->
<item name="config_screen_magnification_scaling_threshold" format="float" type="dimen">0.3</item>
<!-- If true, the display will be shifted around in ambient mode. -->
<bool name="config_enableBurnInProtection">false</bool>
<!-- Specifies the maximum burn-in offset displacement from the center. If -1, no maximum value
will be used. -->
<integer name="config_burnInProtectionMaxRadius">-1</integer>
<!-- Specifies the minimum burn-in offset horizontally. -->
<integer name="config_burnInProtectionMinHorizontalOffset">0</integer>
<!-- Specifies the maximum burn-in offset horizontally. -->
<integer name="config_burnInProtectionMaxHorizontalOffset">0</integer>
<!-- Specifies the minimum burn-in offset vertically. -->
<integer name="config_burnInProtectionMinVerticalOffset">0</integer>
<!-- Specifies the maximum burn-in offset vertically. -->
<integer name="config_burnInProtectionMaxVerticalOffset">0</integer>
</resources>

View File

@@ -254,6 +254,7 @@
<java-symbol type="bool" name="config_duplicate_port_omadm_wappush" />
<java-symbol type="bool" name="config_enable_emergency_call_while_sim_locked" />
<java-symbol type="bool" name="config_enable_puk_unlock_screen" />
<java-symbol type="bool" name="config_enableBurnInProtection" />
<java-symbol type="bool" name="config_hotswapCapable" />
<java-symbol type="bool" name="config_mms_content_disposition_support" />
<java-symbol type="bool" name="config_networkSamplingWakesDevice" />
@@ -344,6 +345,11 @@
<java-symbol type="integer" name="config_wifi_framework_current_network_boost" />
<java-symbol type="integer" name="config_bluetooth_max_advertisers" />
<java-symbol type="integer" name="config_bluetooth_max_scan_filters" />
<java-symbol type="integer" name="config_burnInProtectionMinHorizontalOffset" />
<java-symbol type="integer" name="config_burnInProtectionMaxHorizontalOffset" />
<java-symbol type="integer" name="config_burnInProtectionMinVerticalOffset" />
<java-symbol type="integer" name="config_burnInProtectionMaxVerticalOffset" />
<java-symbol type="integer" name="config_burnInProtectionMaxRadius" />
<java-symbol type="integer" name="config_cursorWindowSize" />
<java-symbol type="integer" name="config_drawLockTimeoutMillis" />
<java-symbol type="integer" name="config_doublePressOnPowerBehavior" />

View File

@@ -0,0 +1,203 @@
package com.android.internal.policy.impl;
import android.app.AlarmManager;
import android.app.PendingIntent;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.content.res.Resources;
import android.hardware.display.DisplayManager;
import android.hardware.display.DisplayManagerInternal;
import android.os.Handler;
import android.os.SystemClock;
import android.util.Log;
import android.view.Display;
import com.android.server.LocalServices;
import java.io.PrintWriter;
import java.util.concurrent.TimeUnit;
public class BurnInProtectionHelper implements DisplayManager.DisplayListener {
private static final String TAG = "BurnInProtection";
// Default value when max burnin radius is not set.
public static final int BURN_IN_RADIUS_MAX_DEFAULT = -1;
private static final long BURNIN_PROTECTION_WAKEUP_INTERVAL_MS = TimeUnit.MINUTES.toMillis(1);
private static final long BURNIN_PROTECTION_MINIMAL_INTERVAL_MS = TimeUnit.SECONDS.toMillis(10);
private static final String ACTION_BURN_IN_PROTECTION =
"android.internal.policy.action.BURN_IN_PROTECTION";
private static final int BURN_IN_SHIFT_STEP = 2;
private boolean mBurnInProtectionActive;
private final int mMinHorizontalBurnInOffset;
private final int mMaxHorizontalBurnInOffset;
private final int mMinVerticalBurnInOffset;
private final int mMaxVerticalBurnInOffset;
private final int mBurnInRadiusMaxSquared;
private int mLastBurnInXOffset = 0;
/* 1 means increasing, -1 means decreasing */
private int mXOffsetDirection = 1;
private int mLastBurnInYOffset = 0;
/* 1 means increasing, -1 means decreasing */
private int mYOffsetDirection = 1;
private final AlarmManager mAlarmManager;
private final PendingIntent mBurnInProtectionIntent;
private final DisplayManagerInternal mDisplayManagerInternal;
private final Display mDisplay;
private BroadcastReceiver mBurnInProtectionReceiver = new BroadcastReceiver() {
@Override
public void onReceive(Context context, Intent intent) {
updateBurnInProtection();
}
};
public BurnInProtectionHelper(Context context) {
final Resources resources = context.getResources();
mMinHorizontalBurnInOffset = resources.getInteger(
com.android.internal.R.integer.config_burnInProtectionMinHorizontalOffset);
mMaxHorizontalBurnInOffset = resources.getInteger(
com.android.internal.R.integer.config_burnInProtectionMaxHorizontalOffset);
mMinVerticalBurnInOffset = resources.getInteger(
com.android.internal.R.integer.config_burnInProtectionMinVerticalOffset);
mMaxVerticalBurnInOffset = resources.getInteger(
com.android.internal.R.integer.config_burnInProtectionMaxVerticalOffset);
int burnInRadiusMax = resources.getInteger(
com.android.internal.R.integer.config_burnInProtectionMaxRadius);
if (burnInRadiusMax != BURN_IN_RADIUS_MAX_DEFAULT) {
mBurnInRadiusMaxSquared = burnInRadiusMax * burnInRadiusMax;
} else {
mBurnInRadiusMaxSquared = BURN_IN_RADIUS_MAX_DEFAULT;
}
mDisplayManagerInternal = LocalServices.getService(DisplayManagerInternal.class);
mAlarmManager = (AlarmManager) context.getSystemService(Context.ALARM_SERVICE);
context.registerReceiver(mBurnInProtectionReceiver,
new IntentFilter(ACTION_BURN_IN_PROTECTION));
Intent intent = new Intent(ACTION_BURN_IN_PROTECTION);
intent.setPackage(context.getPackageName());
intent.setFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY);
mBurnInProtectionIntent = PendingIntent.getBroadcast(context, 0,
intent, PendingIntent.FLAG_UPDATE_CURRENT);
DisplayManager displayManager =
(DisplayManager) context.getSystemService(Context.DISPLAY_SERVICE);
mDisplay = displayManager.getDisplay(Display.DEFAULT_DISPLAY);
displayManager.registerDisplayListener(this, null /* handler */);
}
public void startBurnInProtection() {
if (!mBurnInProtectionActive) {
mBurnInProtectionActive = true;
updateBurnInProtection();
}
}
private void updateBurnInProtection() {
if (mBurnInProtectionActive) {
adjustOffsets();
mDisplayManagerInternal.setDisplayOffsets(mDisplay.getDisplayId(),
mLastBurnInXOffset, mLastBurnInYOffset);
// Next adjustment at least ten seconds in the future.
long next = SystemClock.elapsedRealtime() + BURNIN_PROTECTION_MINIMAL_INTERVAL_MS;
// And aligned to the minute.
next = next - next % BURNIN_PROTECTION_WAKEUP_INTERVAL_MS
+ BURNIN_PROTECTION_WAKEUP_INTERVAL_MS;
mAlarmManager.set(AlarmManager.ELAPSED_REALTIME, next, mBurnInProtectionIntent);
} else {
mAlarmManager.cancel(mBurnInProtectionIntent);
mDisplayManagerInternal.setDisplayOffsets(mDisplay.getDisplayId(), 0, 0);
}
}
public void cancelBurnInProtection() {
if (mBurnInProtectionActive) {
mBurnInProtectionActive = false;
updateBurnInProtection();
}
}
/**
* Gently shifts current burn-in offsets, minimizing the change for the user.
*
* Shifts are applied in following fashion:
* 1) shift horizontally from minimum to the maximum;
* 2) shift vertically by one from minimum to the maximum;
* 3) shift horizontally from maximum to the minimum;
* 4) shift vertically by one from minimum to the maximum.
* 5) if you reach the maximum vertically, start shifting back by one from maximum to minimum.
*
* On top of that, stay within specified radius. If the shift distance from the center is
* higher than the radius, skip these values and go the next position that is within the radius.
*/
private void adjustOffsets() {
do {
// By default, let's just shift the X offset.
final int xChange = mXOffsetDirection * BURN_IN_SHIFT_STEP;
mLastBurnInXOffset += xChange;
if (mLastBurnInXOffset > mMaxHorizontalBurnInOffset
|| mLastBurnInXOffset < mMinHorizontalBurnInOffset) {
// Whoops, we went too far horizontally. Let's retract..
mLastBurnInXOffset -= xChange;
// change horizontal direction..
mXOffsetDirection *= -1;
// and let's shift the Y offset.
final int yChange = mYOffsetDirection * BURN_IN_SHIFT_STEP;
mLastBurnInYOffset += yChange;
if (mLastBurnInYOffset > mMaxVerticalBurnInOffset
|| mLastBurnInYOffset < mMinVerticalBurnInOffset) {
// Whoops, we went to far vertically. Let's retract..
mLastBurnInYOffset -= yChange;
// and change vertical direction.
mYOffsetDirection *= -1;
}
}
// If we are outside of the radius, let's try again.
} while (mBurnInRadiusMaxSquared != BURN_IN_RADIUS_MAX_DEFAULT
&& mLastBurnInXOffset * mLastBurnInXOffset + mLastBurnInYOffset * mLastBurnInYOffset
> mBurnInRadiusMaxSquared);
}
public void dump(String prefix, PrintWriter pw) {
pw.println(prefix + TAG);
prefix += " ";
pw.println(prefix + "mBurnInProtectionActive=" + mBurnInProtectionActive);
pw.println(prefix + "mHorizontalBurnInOffsetsBounds=(" + mMinHorizontalBurnInOffset + ", "
+ mMaxHorizontalBurnInOffset + ")");
pw.println(prefix + "mVerticalBurnInOffsetsBounds=(" + mMinVerticalBurnInOffset + ", "
+ mMaxVerticalBurnInOffset + ")");
pw.println(prefix + "mBurnInRadiusMaxSquared=" + mBurnInRadiusMaxSquared);
pw.println(prefix + "mLastBurnInOffset=(" + mLastBurnInXOffset + ", "
+ mLastBurnInYOffset + ")");
pw.println(prefix + "mOfsetChangeDirections=(" + mXOffsetDirection + ", "
+ mYOffsetDirection + ")");
}
@Override
public void onDisplayAdded(int i) {
}
@Override
public void onDisplayRemoved(int i) {
}
@Override
public void onDisplayChanged(int displayId) {
if (displayId == mDisplay.getDisplayId()) {
if (mDisplay.getState() == Display.STATE_DOZE
|| mDisplay.getState() == Display.STATE_DOZE_SUSPEND) {
startBurnInProtection();
} else {
cancelBurnInProtection();
}
}
}
}

View File

@@ -252,6 +252,7 @@ public class PhoneWindowManager implements WindowManagerPolicy {
Vibrator mVibrator; // Vibrator for giving feedback of orientation changes
SearchManager mSearchManager;
AccessibilityManager mAccessibilityManager;
BurnInProtectionHelper mBurnInProtectionHelper;
// Vibrator pattern for haptic feedback of a long press.
long[] mLongPressVibePattern;
@@ -1183,6 +1184,10 @@ public class PhoneWindowManager implements WindowManagerPolicy {
mWindowManagerFuncs = windowManagerFuncs;
mWindowManagerInternal = LocalServices.getService(WindowManagerInternal.class);
mDreamManagerInternal = LocalServices.getService(DreamManagerInternal.class);
if (context.getResources().getBoolean(
com.android.internal.R.bool.config_enableBurnInProtection)){
mBurnInProtectionHelper = new BurnInProtectionHelper(context);
}
mHandler = new PolicyHandler();
mWakeGestureListener = new MyWakeGestureListener(mContext, mHandler);
@@ -6398,5 +6403,8 @@ public class PhoneWindowManager implements WindowManagerPolicy {
if (mOrientationListener != null) {
mOrientationListener.dump(pw, prefix);
}
if (mBurnInProtectionHelper != null) {
mBurnInProtectionHelper.dump(prefix, pw);
}
}
}

View File

@@ -46,7 +46,6 @@ import android.os.ServiceManager;
import android.os.SystemClock;
import android.os.SystemProperties;
import android.text.TextUtils;
import android.util.Log;
import android.util.Slog;
import android.util.SparseArray;
import android.view.Display;
@@ -832,6 +831,24 @@ public final class DisplayManagerService extends SystemService {
}
}
private void setDisplayOffsetsInternal(int displayId, int x, int y) {
synchronized (mSyncRoot) {
LogicalDisplay display = mLogicalDisplays.get(displayId);
if (display == null) {
return;
}
if (display.getDisplayOffsetXLocked() != x
|| display.getDisplayOffsetYLocked() != y) {
if (DEBUG) {
Slog.d(TAG, "Display " + displayId + " burn-in offset set to ("
+ x + ", " + y + ")");
}
display.setDisplayOffsetsLocked(x, y);
scheduleTraversalLocked(false);
}
}
}
private void clearViewportsLocked() {
mDefaultViewport.valid = false;
mExternalTouchViewport.valid = false;
@@ -1513,5 +1530,10 @@ public final class DisplayManagerService extends SystemService {
float requestedRefreshRate, boolean inTraversal) {
setDisplayPropertiesInternal(displayId, hasContent, requestedRefreshRate, inTraversal);
}
@Override
public void setDisplayOffsets(int displayId, int x, int y) {
setDisplayOffsetsInternal(displayId, x, y);
}
}
}

View File

@@ -76,6 +76,10 @@ final class LogicalDisplay {
// The pending requested refresh rate. 0 if no request is pending.
private float mRequestedRefreshRate;
// The display offsets to apply to the display projection.
private int mDisplayOffsetX;
private int mDisplayOffsetY;
// Temporary rectangle used when needed.
private final Rect mTempLayerStackRect = new Rect();
private final Rect mTempDisplayRect = new Rect();
@@ -313,6 +317,10 @@ final class LogicalDisplay {
mTempDisplayRect.set(displayRectLeft, displayRectTop,
displayRectLeft + displayRectWidth, displayRectTop + displayRectHeight);
mTempDisplayRect.left += mDisplayOffsetX;
mTempDisplayRect.right += mDisplayOffsetX;
mTempDisplayRect.top += mDisplayOffsetY;
mTempDisplayRect.bottom += mDisplayOffsetY;
device.setProjectionInTransactionLocked(orientation, mTempLayerStackRect, mTempDisplayRect);
}
@@ -356,10 +364,34 @@ final class LogicalDisplay {
return mRequestedRefreshRate;
}
/**
* Gets the burn-in offset in X.
*/
public int getDisplayOffsetXLocked() {
return mDisplayOffsetX;
}
/**
* Gets the burn-in offset in Y.
*/
public int getDisplayOffsetYLocked() {
return mDisplayOffsetY;
}
/**
* Sets the burn-in offsets.
*/
public void setDisplayOffsetsLocked(int x, int y) {
mDisplayOffsetX = x;
mDisplayOffsetY = y;
}
public void dumpLocked(PrintWriter pw) {
pw.println("mDisplayId=" + mDisplayId);
pw.println("mLayerStack=" + mLayerStack);
pw.println("mHasContent=" + mHasContent);
pw.println("mRequestedRefreshRate=" + mRequestedRefreshRate);
pw.println("mDisplayOffset=(" + mDisplayOffsetX + ", " + mDisplayOffsetY + ")");
pw.println("mPrimaryDisplayDevice=" + (mPrimaryDisplayDevice != null ?
mPrimaryDisplayDevice.getNameLocked() : "null"));
pw.println("mBaseDisplayInfo=" + mBaseDisplayInfo);