diff options
author | Filip Gruszczynski <gruszczy@google.com> | 2015-02-25 21:40:38 +0000 |
---|---|---|
committer | Android Git Automerger <android-git-automerger@android.com> | 2015-02-25 21:40:38 +0000 |
commit | 0fbcc7086fe742e641915ed5f097119d01613cb5 (patch) | |
tree | 2a9c155aefb7515613e5ba75196f93bba9bfc550 | |
parent | 892609ad34145e6418e42c683428f1a880b4c874 (diff) | |
parent | 1e74b5d6b0e964b7a9e58e64b45f50aec15b1178 (diff) | |
download | frameworks_base-0fbcc7086fe742e641915ed5f097119d01613cb5.zip frameworks_base-0fbcc7086fe742e641915ed5f097119d01613cb5.tar.gz frameworks_base-0fbcc7086fe742e641915ed5f097119d01613cb5.tar.bz2 |
am 1e74b5d6: Merge "Burn in protection." into lmp-mr1-modular-dev
* commit '1e74b5d6b0e964b7a9e58e64b45f50aec15b1178':
Burn in protection.
8 files changed, 306 insertions, 1 deletions
diff --git a/core/java/android/hardware/display/DisplayManagerInternal.java b/core/java/android/hardware/display/DisplayManagerInternal.java index bb162153..adab9be 100644 --- a/core/java/android/hardware/display/DisplayManagerInternal.java +++ b/core/java/android/hardware/display/DisplayManagerInternal.java @@ -132,6 +132,19 @@ public abstract class DisplayManagerInternal { 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. * * This object is intended to describe the general characteristics of the diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml index ccdb5db..59dafed 100644 --- a/core/res/AndroidManifest.xml +++ b/core/res/AndroidManifest.xml @@ -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 --> <!-- ====================================== --> diff --git a/core/res/res/values/config.xml b/core/res/res/values/config.xml index 739ff1e..ac616c9 100755 --- a/core/res/res/values/config.xml +++ b/core/res/res/values/config.xml @@ -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> diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml index 25a1462..01c49bd 100755 --- a/core/res/res/values/symbols.xml +++ b/core/res/res/values/symbols.xml @@ -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" /> diff --git a/policy/src/com/android/internal/policy/impl/BurnInProtectionHelper.java b/policy/src/com/android/internal/policy/impl/BurnInProtectionHelper.java new file mode 100644 index 0000000..19fe262 --- /dev/null +++ b/policy/src/com/android/internal/policy/impl/BurnInProtectionHelper.java @@ -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(); + } + } + } +} diff --git a/policy/src/com/android/internal/policy/impl/PhoneWindowManager.java b/policy/src/com/android/internal/policy/impl/PhoneWindowManager.java index 69a4ac0..a426ac2 100644 --- a/policy/src/com/android/internal/policy/impl/PhoneWindowManager.java +++ b/policy/src/com/android/internal/policy/impl/PhoneWindowManager.java @@ -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); + } } } diff --git a/services/core/java/com/android/server/display/DisplayManagerService.java b/services/core/java/com/android/server/display/DisplayManagerService.java index 09dc477..5f0ad9f 100644 --- a/services/core/java/com/android/server/display/DisplayManagerService.java +++ b/services/core/java/com/android/server/display/DisplayManagerService.java @@ -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); + } } } diff --git a/services/core/java/com/android/server/display/LogicalDisplay.java b/services/core/java/com/android/server/display/LogicalDisplay.java index 6c57eec..3bb7818 100644 --- a/services/core/java/com/android/server/display/LogicalDisplay.java +++ b/services/core/java/com/android/server/display/LogicalDisplay.java @@ -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); |