am 1e74b5d6: Merge "Burn in protection." into lmp-mr1-modular-dev
* commit '1e74b5d6b0e964b7a9e58e64b45f50aec15b1178': Burn in protection.
This commit is contained in:
@@ -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.
|
||||
*
|
||||
|
||||
@@ -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 -->
|
||||
<!-- ====================================== -->
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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" />
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
@@ -6400,5 +6405,8 @@ public class PhoneWindowManager implements WindowManagerPolicy {
|
||||
if (mOrientationListener != null) {
|
||||
mOrientationListener.dump(pw, prefix);
|
||||
}
|
||||
if (mBurnInProtectionHelper != null) {
|
||||
mBurnInProtectionHelper.dump(prefix, pw);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
|
||||
Reference in New Issue
Block a user