Loading core/java/android/hardware/display/DisplayManagerInternal.java +13 −0 Original line number Diff line number Diff line Loading @@ -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. * Loading core/res/AndroidManifest.xml +2 −0 Original line number Diff line number Diff line Loading @@ -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 --> <!-- ====================================== --> Loading core/res/res/values/config.xml +19 −0 Original line number Diff line number Diff line Loading @@ -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> core/res/res/values/symbols.xml +6 −0 Original line number Diff line number Diff line Loading @@ -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" /> Loading Loading @@ -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" /> Loading policy/src/com/android/internal/policy/impl/BurnInProtectionHelper.java 0 → 100644 +203 −0 Original line number Diff line number Diff line 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(); } } } } Loading
core/java/android/hardware/display/DisplayManagerInternal.java +13 −0 Original line number Diff line number Diff line Loading @@ -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. * Loading
core/res/AndroidManifest.xml +2 −0 Original line number Diff line number Diff line Loading @@ -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 --> <!-- ====================================== --> Loading
core/res/res/values/config.xml +19 −0 Original line number Diff line number Diff line Loading @@ -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>
core/res/res/values/symbols.xml +6 −0 Original line number Diff line number Diff line Loading @@ -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" /> Loading Loading @@ -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" /> Loading
policy/src/com/android/internal/policy/impl/BurnInProtectionHelper.java 0 → 100644 +203 −0 Original line number Diff line number Diff line 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(); } } } }