Merge "Fix a lot of OVERLAY_CHANGED broadcasts"
diff --git a/apex/jobscheduler/service/java/com/android/server/job/JobSchedulerService.java b/apex/jobscheduler/service/java/com/android/server/job/JobSchedulerService.java
index 4d5eef2..1e13dbf 100644
--- a/apex/jobscheduler/service/java/com/android/server/job/JobSchedulerService.java
+++ b/apex/jobscheduler/service/java/com/android/server/job/JobSchedulerService.java
@@ -1404,8 +1404,31 @@
}
mChangedJobList.remove(cancelled);
// Cancel if running.
- mConcurrencyManager.stopJobOnServiceContextLocked(
+ final boolean wasRunning = mConcurrencyManager.stopJobOnServiceContextLocked(
cancelled, reason, internalReasonCode, debugReason);
+ // If the job was running, the JobServiceContext should log with state FINISHED.
+ if (!wasRunning) {
+ FrameworkStatsLog.write_non_chained(FrameworkStatsLog.SCHEDULED_JOB_STATE_CHANGED,
+ cancelled.getSourceUid(), null, cancelled.getBatteryName(),
+ FrameworkStatsLog.SCHEDULED_JOB_STATE_CHANGED__STATE__CANCELLED,
+ internalReasonCode, cancelled.getStandbyBucket(),
+ cancelled.getJobId(),
+ cancelled.hasChargingConstraint(),
+ cancelled.hasBatteryNotLowConstraint(),
+ cancelled.hasStorageNotLowConstraint(),
+ cancelled.hasTimingDelayConstraint(),
+ cancelled.hasDeadlineConstraint(),
+ cancelled.hasIdleConstraint(),
+ cancelled.hasConnectivityConstraint(),
+ cancelled.hasContentTriggerConstraint(),
+ cancelled.isRequestedExpeditedJob(),
+ /* isRunningAsExpeditedJob */ false,
+ reason,
+ cancelled.getJob().isPrefetch(),
+ cancelled.getJob().getPriority(),
+ cancelled.getEffectivePriority(),
+ cancelled.getNumFailures());
+ }
// If this is a replacement, bring in the new version of the job
if (incomingJob != null) {
if (DEBUG) Slog.i(TAG, "Tracking replacement job " + incomingJob.toShortString());
diff --git a/core/api/test-current.txt b/core/api/test-current.txt
index 5967233..99005a4 100644
--- a/core/api/test-current.txt
+++ b/core/api/test-current.txt
@@ -1223,6 +1223,7 @@
method @Nullable public android.view.Display.Mode getGlobalUserPreferredDisplayMode();
method @NonNull public int[] getUserDisabledHdrTypes();
method public boolean isMinimalPostProcessingRequested(int);
+ method @RequiresPermission(android.Manifest.permission.ACCESS_SURFACE_FLINGER) public void overrideHdrTypes(int, @NonNull int[]);
method @RequiresPermission(android.Manifest.permission.WRITE_SECURE_SETTINGS) public void setAreUserDisabledHdrTypesAllowed(boolean);
method @RequiresPermission(android.Manifest.permission.MODIFY_USER_PREFERRED_DISPLAY_MODE) public void setGlobalUserPreferredDisplayMode(@NonNull android.view.Display.Mode);
method @RequiresPermission(android.Manifest.permission.MODIFY_REFRESH_RATE_SWITCHING_TYPE) public void setRefreshRateSwitchingType(int);
@@ -2885,7 +2886,6 @@
ctor public SurfaceControl(@NonNull android.view.SurfaceControl, @NonNull String);
method @NonNull public static android.os.IBinder getInternalDisplayToken();
method public boolean isSameSurface(@NonNull android.view.SurfaceControl);
- method public static void overrideHdrTypes(@NonNull android.os.IBinder, @NonNull int[]);
}
public class SurfaceControlViewHost {
diff --git a/core/java/android/hardware/display/DisplayManager.java b/core/java/android/hardware/display/DisplayManager.java
index 8bc11cb..dfb4236 100644
--- a/core/java/android/hardware/display/DisplayManager.java
+++ b/core/java/android/hardware/display/DisplayManager.java
@@ -855,6 +855,16 @@
return mGlobal.getUserDisabledHdrTypes();
}
+ /**
+ * Overrides HDR modes for a display device.
+ *
+ * @hide
+ */
+ @RequiresPermission(Manifest.permission.ACCESS_SURFACE_FLINGER)
+ @TestApi
+ public void overrideHdrTypes(int displayId, @NonNull int[] modes) {
+ mGlobal.overrideHdrTypes(displayId, modes);
+ }
/**
* Creates a virtual display.
diff --git a/core/java/android/hardware/display/DisplayManagerGlobal.java b/core/java/android/hardware/display/DisplayManagerGlobal.java
index e2f8592..9294dea 100644
--- a/core/java/android/hardware/display/DisplayManagerGlobal.java
+++ b/core/java/android/hardware/display/DisplayManagerGlobal.java
@@ -20,9 +20,11 @@
import static android.hardware.display.DisplayManager.EventsMask;
import static android.view.Display.HdrCapabilities.HdrType;
+import android.Manifest;
import android.annotation.IntDef;
import android.annotation.NonNull;
import android.annotation.Nullable;
+import android.annotation.RequiresPermission;
import android.app.PropertyInvalidatedCache;
import android.compat.annotation.UnsupportedAppUsage;
import android.content.Context;
@@ -577,6 +579,20 @@
}
}
+ /**
+ * Overrides HDR modes for a display device.
+ *
+ */
+ @RequiresPermission(Manifest.permission.ACCESS_SURFACE_FLINGER)
+ public void overrideHdrTypes(int displayId, int[] modes) {
+ try {
+ mDm.overrideHdrTypes(displayId, modes);
+ } catch (RemoteException ex) {
+ throw ex.rethrowFromSystemServer();
+ }
+ }
+
+
public void requestColorMode(int displayId, int colorMode) {
try {
mDm.requestColorMode(displayId, colorMode);
diff --git a/core/java/android/hardware/display/IDisplayManager.aidl b/core/java/android/hardware/display/IDisplayManager.aidl
index fa3c450..b166e21 100644
--- a/core/java/android/hardware/display/IDisplayManager.aidl
+++ b/core/java/android/hardware/display/IDisplayManager.aidl
@@ -83,6 +83,9 @@
// No permissions required.
int[] getUserDisabledHdrTypes();
+ // Requires ACCESS_SURFACE_FLINGER permission.
+ void overrideHdrTypes(int displayId, in int[] modes);
+
// Requires CONFIGURE_DISPLAY_COLOR_MODE
void requestColorMode(int displayId, int colorMode);
diff --git a/core/java/android/hardware/radio/TunerAdapter.java b/core/java/android/hardware/radio/TunerAdapter.java
index aa5480a..4a18333 100644
--- a/core/java/android/hardware/radio/TunerAdapter.java
+++ b/core/java/android/hardware/radio/TunerAdapter.java
@@ -16,12 +16,13 @@
package android.hardware.radio;
-import android.annotation.NonNull;
import android.annotation.Nullable;
import android.graphics.Bitmap;
import android.os.RemoteException;
import android.util.Log;
+import com.android.internal.annotations.GuardedBy;
+
import java.util.List;
import java.util.Map;
import java.util.Objects;
@@ -29,28 +30,36 @@
/**
* Implements the RadioTuner interface by forwarding calls to radio service.
*/
-class TunerAdapter extends RadioTuner {
+final class TunerAdapter extends RadioTuner {
private static final String TAG = "BroadcastRadio.TunerAdapter";
- @NonNull private final ITuner mTuner;
- @NonNull private final TunerCallbackAdapter mCallback;
- private boolean mIsClosed = false;
+ private final ITuner mTuner;
+ private final TunerCallbackAdapter mCallback;
+ private final Object mLock = new Object();
- private @RadioManager.Band int mBand;
+ @GuardedBy("mLock")
+ private boolean mIsClosed;
+ @GuardedBy("mLock")
+ @RadioManager.Band
+ private int mBand;
+
+ @GuardedBy("mLock")
private ProgramList mLegacyListProxy;
+
+ @GuardedBy("mLock")
private Map<String, String> mLegacyListFilter;
- TunerAdapter(@NonNull ITuner tuner, @NonNull TunerCallbackAdapter callback,
+ TunerAdapter(ITuner tuner, TunerCallbackAdapter callback,
@RadioManager.Band int band) {
- mTuner = Objects.requireNonNull(tuner);
- mCallback = Objects.requireNonNull(callback);
+ mTuner = Objects.requireNonNull(tuner, "Tuner cannot be null");
+ mCallback = Objects.requireNonNull(callback, "Callback cannot be null");
mBand = band;
}
@Override
public void close() {
- synchronized (mTuner) {
+ synchronized (mLock) {
if (mIsClosed) {
Log.v(TAG, "Tuner is already closed");
return;
@@ -60,8 +69,8 @@
mLegacyListProxy.close();
mLegacyListProxy = null;
}
- mCallback.close();
}
+ mCallback.close();
try {
mTuner.close();
} catch (RemoteException e) {
@@ -71,16 +80,20 @@
@Override
public int setConfiguration(RadioManager.BandConfig config) {
- if (config == null) return RadioManager.STATUS_BAD_VALUE;
+ if (config == null) {
+ return RadioManager.STATUS_BAD_VALUE;
+ }
try {
mTuner.setConfiguration(config);
- mBand = config.getType();
+ synchronized (mLock) {
+ mBand = config.getType();
+ }
return RadioManager.STATUS_OK;
} catch (IllegalArgumentException e) {
Log.e(TAG, "Can't set configuration", e);
return RadioManager.STATUS_BAD_VALUE;
} catch (RemoteException e) {
- Log.e(TAG, "service died", e);
+ Log.e(TAG, "Service died", e);
return RadioManager.STATUS_DEAD_OBJECT;
}
}
@@ -94,7 +107,7 @@
config[0] = mTuner.getConfiguration();
return RadioManager.STATUS_OK;
} catch (RemoteException e) {
- Log.e(TAG, "service died", e);
+ Log.e(TAG, "Service died", e);
return RadioManager.STATUS_DEAD_OBJECT;
}
}
@@ -107,7 +120,7 @@
Log.e(TAG, "Can't set muted", e);
return RadioManager.STATUS_ERROR;
} catch (RemoteException e) {
- Log.e(TAG, "service died", e);
+ Log.e(TAG, "Service died", e);
return RadioManager.STATUS_DEAD_OBJECT;
}
return RadioManager.STATUS_OK;
@@ -118,7 +131,7 @@
try {
return mTuner.isMuted();
} catch (RemoteException e) {
- Log.e(TAG, "service died", e);
+ Log.e(TAG, "Service died", e);
return true;
}
}
@@ -126,12 +139,13 @@
@Override
public int step(int direction, boolean skipSubChannel) {
try {
- mTuner.step(direction == RadioTuner.DIRECTION_DOWN, skipSubChannel);
+ mTuner.step(/* directionDown= */ direction == RadioTuner.DIRECTION_DOWN,
+ skipSubChannel);
} catch (IllegalStateException e) {
Log.e(TAG, "Can't step", e);
return RadioManager.STATUS_INVALID_OPERATION;
} catch (RemoteException e) {
- Log.e(TAG, "service died", e);
+ Log.e(TAG, "Service died", e);
return RadioManager.STATUS_DEAD_OBJECT;
}
return RadioManager.STATUS_OK;
@@ -140,12 +154,13 @@
@Override
public int scan(int direction, boolean skipSubChannel) {
try {
- mTuner.scan(direction == RadioTuner.DIRECTION_DOWN, skipSubChannel);
+ mTuner.scan(/* directionDown= */ direction == RadioTuner.DIRECTION_DOWN,
+ skipSubChannel);
} catch (IllegalStateException e) {
Log.e(TAG, "Can't scan", e);
return RadioManager.STATUS_INVALID_OPERATION;
} catch (RemoteException e) {
- Log.e(TAG, "service died", e);
+ Log.e(TAG, "Service died", e);
return RadioManager.STATUS_DEAD_OBJECT;
}
return RadioManager.STATUS_OK;
@@ -154,7 +169,11 @@
@Override
public int tune(int channel, int subChannel) {
try {
- mTuner.tune(ProgramSelector.createAmFmSelector(mBand, channel, subChannel));
+ int band;
+ synchronized (mLock) {
+ band = mBand;
+ }
+ mTuner.tune(ProgramSelector.createAmFmSelector(band, channel, subChannel));
} catch (IllegalStateException e) {
Log.e(TAG, "Can't tune", e);
return RadioManager.STATUS_INVALID_OPERATION;
@@ -162,18 +181,18 @@
Log.e(TAG, "Can't tune", e);
return RadioManager.STATUS_BAD_VALUE;
} catch (RemoteException e) {
- Log.e(TAG, "service died", e);
+ Log.e(TAG, "Service died", e);
return RadioManager.STATUS_DEAD_OBJECT;
}
return RadioManager.STATUS_OK;
}
@Override
- public void tune(@NonNull ProgramSelector selector) {
+ public void tune(ProgramSelector selector) {
try {
mTuner.tune(selector);
} catch (RemoteException e) {
- throw new RuntimeException("service died", e);
+ throw new RuntimeException("Service died", e);
}
}
@@ -185,7 +204,7 @@
Log.e(TAG, "Can't cancel", e);
return RadioManager.STATUS_INVALID_OPERATION;
} catch (RemoteException e) {
- Log.e(TAG, "service died", e);
+ Log.e(TAG, "Service died", e);
return RadioManager.STATUS_DEAD_OBJECT;
}
return RadioManager.STATUS_OK;
@@ -196,7 +215,7 @@
try {
mTuner.cancelAnnouncement();
} catch (RemoteException e) {
- throw new RuntimeException("service died", e);
+ throw new RuntimeException("Service died", e);
}
}
@@ -217,11 +236,12 @@
}
@Override
- public @Nullable Bitmap getMetadataImage(int id) {
+ @Nullable
+ public Bitmap getMetadataImage(int id) {
try {
return mTuner.getImage(id);
} catch (RemoteException e) {
- throw new RuntimeException("service died", e);
+ throw new RuntimeException("Service died", e);
}
}
@@ -230,66 +250,72 @@
try {
return mTuner.startBackgroundScan();
} catch (RemoteException e) {
- throw new RuntimeException("service died", e);
+ throw new RuntimeException("Service died", e);
}
}
@Override
- public @NonNull List<RadioManager.ProgramInfo>
+ public List<RadioManager.ProgramInfo>
getProgramList(@Nullable Map<String, String> vendorFilter) {
- synchronized (mTuner) {
+ synchronized (mLock) {
if (mLegacyListProxy == null || !Objects.equals(mLegacyListFilter, vendorFilter)) {
Log.i(TAG, "Program list filter has changed, requesting new list");
mLegacyListProxy = new ProgramList();
mLegacyListFilter = vendorFilter;
-
mCallback.clearLastCompleteList();
- mCallback.setProgramListObserver(mLegacyListProxy, () -> { });
- try {
- mTuner.startProgramListUpdates(new ProgramList.Filter(vendorFilter));
- } catch (RemoteException ex) {
- throw new RuntimeException("service died", ex);
- }
+ mCallback.setProgramListObserver(mLegacyListProxy, () -> {
+ Log.i(TAG, "Empty closeListener in programListObserver");
+ });
}
-
- List<RadioManager.ProgramInfo> list = mCallback.getLastCompleteList();
- if (list == null) throw new IllegalStateException("Program list is not ready yet");
- return list;
}
+ try {
+ mTuner.startProgramListUpdates(new ProgramList.Filter(vendorFilter));
+ } catch (RemoteException ex) {
+ throw new RuntimeException("Service died", ex);
+ }
+
+ List<RadioManager.ProgramInfo> list = mCallback.getLastCompleteList();
+ if (list == null) {
+ throw new IllegalStateException("Program list is not ready yet");
+ }
+ return list;
}
@Override
- public @Nullable ProgramList getDynamicProgramList(@Nullable ProgramList.Filter filter) {
- synchronized (mTuner) {
+ @Nullable
+ public ProgramList getDynamicProgramList(@Nullable ProgramList.Filter filter) {
+ synchronized (mLock) {
if (mLegacyListProxy != null) {
mLegacyListProxy.close();
mLegacyListProxy = null;
}
mLegacyListFilter = null;
-
- ProgramList list = new ProgramList();
- mCallback.setProgramListObserver(list, () -> {
- try {
- mTuner.stopProgramListUpdates();
- } catch (IllegalStateException ex) {
- // it's fine to not stop updates if tuner is already closed
- } catch (RemoteException ex) {
- Log.e(TAG, "Couldn't stop program list updates", ex);
- }
- });
-
- try {
- mTuner.startProgramListUpdates(filter);
- } catch (UnsupportedOperationException ex) {
- Log.i(TAG, "Program list is not supported with this hardware");
- return null;
- } catch (RemoteException ex) {
- mCallback.setProgramListObserver(null, () -> { });
- throw new RuntimeException("service died", ex);
- }
-
- return list;
}
+ ProgramList list = new ProgramList();
+ mCallback.setProgramListObserver(list, () -> {
+ try {
+ mTuner.stopProgramListUpdates();
+ } catch (IllegalStateException ex) {
+ // it's fine to not stop updates if tuner is already closed
+ Log.e(TAG, "Tuner may already be closed", ex);
+ } catch (RemoteException ex) {
+ Log.e(TAG, "Couldn't stop program list updates", ex);
+ }
+ });
+
+ try {
+ mTuner.startProgramListUpdates(filter);
+ } catch (UnsupportedOperationException ex) {
+ Log.i(TAG, "Program list is not supported with this hardware");
+ return null;
+ } catch (RemoteException ex) {
+ mCallback.setProgramListObserver(null, () -> {
+ Log.i(TAG, "Empty closeListener in programListObserver");
+ });
+ throw new RuntimeException("Service died", ex);
+ }
+
+ return list;
}
@Override
@@ -315,7 +341,7 @@
try {
return mTuner.isConfigFlagSupported(flag);
} catch (RemoteException e) {
- throw new RuntimeException("service died", e);
+ throw new RuntimeException("Service died", e);
}
}
@@ -324,7 +350,7 @@
try {
return mTuner.isConfigFlagSet(flag);
} catch (RemoteException e) {
- throw new RuntimeException("service died", e);
+ throw new RuntimeException("Service died", e);
}
}
@@ -333,25 +359,26 @@
try {
mTuner.setConfigFlag(flag, value);
} catch (RemoteException e) {
- throw new RuntimeException("service died", e);
+ throw new RuntimeException("Service died", e);
}
}
@Override
- public @NonNull Map<String, String> setParameters(@NonNull Map<String, String> parameters) {
+ public Map<String, String> setParameters(Map<String, String> parameters) {
try {
- return mTuner.setParameters(Objects.requireNonNull(parameters));
+ return mTuner.setParameters(Objects.requireNonNull(parameters,
+ "Parameters cannot be null"));
} catch (RemoteException e) {
- throw new RuntimeException("service died", e);
+ throw new RuntimeException("Service died", e);
}
}
@Override
- public @NonNull Map<String, String> getParameters(@NonNull List<String> keys) {
+ public Map<String, String> getParameters(List<String> keys) {
try {
- return mTuner.getParameters(Objects.requireNonNull(keys));
+ return mTuner.getParameters(Objects.requireNonNull(keys, "Keys cannot be null"));
} catch (RemoteException e) {
- throw new RuntimeException("service died", e);
+ throw new RuntimeException("Service died", e);
}
}
diff --git a/core/java/android/hardware/radio/TunerCallbackAdapter.java b/core/java/android/hardware/radio/TunerCallbackAdapter.java
index e3f7001..b9782a8 100644
--- a/core/java/android/hardware/radio/TunerCallbackAdapter.java
+++ b/core/java/android/hardware/radio/TunerCallbackAdapter.java
@@ -16,12 +16,13 @@
package android.hardware.radio;
-import android.annotation.NonNull;
import android.annotation.Nullable;
import android.os.Handler;
import android.os.Looper;
import android.util.Log;
+import com.android.internal.annotations.GuardedBy;
+
import java.util.List;
import java.util.Map;
import java.util.Objects;
@@ -29,23 +30,31 @@
/**
* Implements the ITunerCallback interface by forwarding calls to RadioTuner.Callback.
*/
-class TunerCallbackAdapter extends ITunerCallback.Stub {
+final class TunerCallbackAdapter extends ITunerCallback.Stub {
private static final String TAG = "BroadcastRadio.TunerCallbackAdapter";
private final Object mLock = new Object();
- @NonNull private final RadioTuner.Callback mCallback;
- @NonNull private final Handler mHandler;
+ private final RadioTuner.Callback mCallback;
+ private final Handler mHandler;
+ @GuardedBy("mLock")
@Nullable ProgramList mProgramList;
// cache for deprecated methods
+ @GuardedBy("mLock")
boolean mIsAntennaConnected = true;
+
+ @GuardedBy("mLock")
@Nullable List<RadioManager.ProgramInfo> mLastCompleteList;
- private boolean mDelayedCompleteCallback = false;
+
+ @GuardedBy("mLock")
+ private boolean mDelayedCompleteCallback;
+
+ @GuardedBy("mLock")
@Nullable RadioManager.ProgramInfo mCurrentProgramInfo;
- TunerCallbackAdapter(@NonNull RadioTuner.Callback callback, @Nullable Handler handler) {
- mCallback = callback;
+ TunerCallbackAdapter(RadioTuner.Callback callback, @Nullable Handler handler) {
+ mCallback = Objects.requireNonNull(callback, "Callback cannot be null");
if (handler == null) {
mHandler = new Handler(Looper.getMainLooper());
} else {
@@ -55,31 +64,39 @@
void close() {
synchronized (mLock) {
- if (mProgramList != null) mProgramList.close();
+ if (mProgramList != null) {
+ mProgramList.close();
+ }
}
}
void setProgramListObserver(@Nullable ProgramList programList,
- @NonNull ProgramList.OnCloseListener closeListener) {
- Objects.requireNonNull(closeListener);
+ ProgramList.OnCloseListener closeListener) {
+ Objects.requireNonNull(closeListener, "CloseListener cannot be null");
synchronized (mLock) {
if (mProgramList != null) {
Log.w(TAG, "Previous program list observer wasn't properly closed, closing it...");
mProgramList.close();
}
mProgramList = programList;
- if (programList == null) return;
+ if (programList == null) {
+ return;
+ }
programList.setOnCloseListener(() -> {
synchronized (mLock) {
- if (mProgramList != programList) return;
+ if (mProgramList != programList) {
+ return;
+ }
mProgramList = null;
mLastCompleteList = null;
- closeListener.onClose();
}
+ closeListener.onClose();
});
programList.addOnCompleteListener(() -> {
synchronized (mLock) {
- if (mProgramList != programList) return;
+ if (mProgramList != programList) {
+ return;
+ }
mLastCompleteList = programList.toList();
if (mDelayedCompleteCallback) {
Log.d(TAG, "Sending delayed onBackgroundScanComplete callback");
@@ -109,7 +126,11 @@
}
boolean isAntennaConnected() {
- return mIsAntennaConnected;
+ boolean isConnected;
+ synchronized (mLock) {
+ isConnected = mIsAntennaConnected;
+ }
+ return isConnected;
}
@Override
@@ -177,7 +198,9 @@
@Override
public void onAntennaState(boolean connected) {
- mIsAntennaConnected = connected;
+ synchronized (mLock) {
+ mIsAntennaConnected = connected;
+ }
mHandler.post(() -> mCallback.onAntennaState(connected));
}
@@ -186,6 +209,7 @@
mHandler.post(() -> mCallback.onBackgroundScanAvailabilityChange(isAvailable));
}
+ @GuardedBy("mLock")
private void sendBackgroundScanCompleteLocked() {
mDelayedCompleteCallback = false;
mHandler.post(() -> mCallback.onBackgroundScanComplete());
@@ -213,8 +237,10 @@
public void onProgramListUpdated(ProgramList.Chunk chunk) {
mHandler.post(() -> {
synchronized (mLock) {
- if (mProgramList == null) return;
- mProgramList.apply(Objects.requireNonNull(chunk));
+ if (mProgramList == null) {
+ return;
+ }
+ mProgramList.apply(Objects.requireNonNull(chunk, "Chunk cannot be null"));
}
});
}
diff --git a/core/java/android/inputmethodservice/InputMethodService.java b/core/java/android/inputmethodservice/InputMethodService.java
index 7436601..9ed55ff 100644
--- a/core/java/android/inputmethodservice/InputMethodService.java
+++ b/core/java/android/inputmethodservice/InputMethodService.java
@@ -967,7 +967,7 @@
Log.d(TAG, "Input should have started before starting Stylus handwriting.");
return;
}
- maybeCreateInkWindow();
+ maybeCreateAndInitInkWindow();
if (!mOnPreparedStylusHwCalled) {
// prepare hasn't been called by Stylus HOVER.
onPrepareStylusHandwriting();
@@ -1027,8 +1027,7 @@
*/
@Override
public void initInkWindow() {
- maybeCreateInkWindow();
- mInkWindow.initOnly();
+ maybeCreateAndInitInkWindow();
onPrepareStylusHandwriting();
mOnPreparedStylusHwCalled = true;
}
@@ -1036,11 +1035,13 @@
/**
* Create and attach token to Ink window if it wasn't already created.
*/
- private void maybeCreateInkWindow() {
+ private void maybeCreateAndInitInkWindow() {
if (mInkWindow == null) {
mInkWindow = new InkWindow(mWindow.getContext());
mInkWindow.setToken(mToken);
}
+ mInkWindow.initOnly();
+ setInkViewVisibilityListener();
// TODO(b/243571274): set an idle-timeout after which InkWindow is removed.
}
@@ -2469,13 +2470,19 @@
* @param motionEvent {@link MotionEvent} from stylus.
*/
public void onStylusHandwritingMotionEvent(@NonNull MotionEvent motionEvent) {
- if (mInkWindow.isInkViewVisible()) {
+ if (mInkWindow != null && mInkWindow.isInkViewVisible()) {
mInkWindow.getDecorView().dispatchTouchEvent(motionEvent);
} else {
if (mPendingEvents == null) {
mPendingEvents = new RingBuffer(MotionEvent.class, MAX_EVENTS_BUFFER);
}
mPendingEvents.append(motionEvent);
+ setInkViewVisibilityListener();
+ }
+ }
+
+ private void setInkViewVisibilityListener() {
+ if (mInkWindow != null) {
mInkWindow.setInkViewVisibilityListener(() -> {
if (mPendingEvents != null && !mPendingEvents.isEmpty()) {
for (MotionEvent event : mPendingEvents.toArray()) {
@@ -2539,6 +2546,7 @@
mHandler.removeCallbacks(mFinishHwRunnable);
}
mFinishHwRunnable = null;
+ mPendingEvents.clear();
final int requestId = mHandwritingRequestId.getAsInt();
mHandwritingRequestId = OptionalInt.empty();
diff --git a/core/java/android/view/SurfaceControl.java b/core/java/android/view/SurfaceControl.java
index 9322bf6..2f7ae49 100644
--- a/core/java/android/view/SurfaceControl.java
+++ b/core/java/android/view/SurfaceControl.java
@@ -225,8 +225,6 @@
private static native void nativeSetDimmingEnabled(long transactionObj, long nativeObject,
boolean dimmingEnabled);
- private static native void nativeOverrideHdrTypes(IBinder displayToken, int[] modes);
-
private static native void nativeSetInputWindowInfo(long transactionObj, long nativeObject,
InputWindowHandle handle);
@@ -2038,18 +2036,6 @@
}
/**
- * Overrides HDR modes for a display device.
- *
- * If the caller does not have ACCESS_SURFACE_FLINGER permission, this will throw a Security
- * Exception.
- * @hide
- */
- @TestApi
- public static void overrideHdrTypes(@NonNull IBinder displayToken, @NonNull int[] modes) {
- nativeOverrideHdrTypes(displayToken, modes);
- }
-
- /**
* @hide
*/
public static long[] getPhysicalDisplayIds() {
diff --git a/core/jni/android_util_AssetManager.cpp b/core/jni/android_util_AssetManager.cpp
index 8c23b21..764338f 100644
--- a/core/jni/android_util_AssetManager.cpp
+++ b/core/jni/android_util_AssetManager.cpp
@@ -39,6 +39,7 @@
#include "androidfw/AssetManager2.h"
#include "androidfw/AttributeResolution.h"
#include "androidfw/MutexGuard.h"
+#include <androidfw/ResourceTimer.h>
#include "androidfw/ResourceTypes.h"
#include "androidfw/ResourceUtils.h"
@@ -630,6 +631,7 @@
jshort density, jobject typed_value,
jboolean resolve_references) {
ScopedLock<AssetManager2> assetmanager(AssetManagerFromLong(ptr));
+ ResourceTimer _tag(ResourceTimer::Counter::GetResourceValue);
auto value = assetmanager->GetResource(static_cast<uint32_t>(resid), false /*may_be_bag*/,
static_cast<uint16_t>(density));
if (!value.has_value()) {
@@ -1232,6 +1234,7 @@
}
ScopedLock<AssetManager2> assetmanager(AssetManagerFromLong(ptr));
+ ResourceTimer _tag(ResourceTimer::Counter::RetrieveAttributes);
ResXMLParser* xml_parser = reinterpret_cast<ResXMLParser*>(xml_parser_ptr);
auto result =
RetrieveAttributes(assetmanager.get(), xml_parser, reinterpret_cast<uint32_t*>(attrs),
diff --git a/core/jni/android_view_SurfaceControl.cpp b/core/jni/android_view_SurfaceControl.cpp
index 4ad995a..b11f22a 100644
--- a/core/jni/android_view_SurfaceControl.cpp
+++ b/core/jni/android_view_SurfaceControl.cpp
@@ -1528,27 +1528,6 @@
transaction->reparent(ctrl, newParent);
}
-static void nativeOverrideHdrTypes(JNIEnv* env, jclass clazz, jobject tokenObject,
- jintArray jHdrTypes) {
- sp<IBinder> token(ibinderForJavaObject(env, tokenObject));
- if (token == nullptr || jHdrTypes == nullptr) return;
-
- int* hdrTypes = env->GetIntArrayElements(jHdrTypes, 0);
- int numHdrTypes = env->GetArrayLength(jHdrTypes);
-
- std::vector<ui::Hdr> hdrTypesVector;
- for (int i = 0; i < numHdrTypes; i++) {
- hdrTypesVector.push_back(static_cast<ui::Hdr>(hdrTypes[i]));
- }
- env->ReleaseIntArrayElements(jHdrTypes, hdrTypes, 0);
-
- status_t error = SurfaceComposerClient::overrideHdrTypes(token, hdrTypesVector);
- if (error != NO_ERROR) {
- jniThrowExceptionFmt(env, "java/lang/SecurityException",
- "ACCESS_SURFACE_FLINGER is missing");
- }
-}
-
static jboolean nativeGetBootDisplayModeSupport(JNIEnv* env, jclass clazz) {
bool isBootDisplayModeSupported = false;
SurfaceComposerClient::getBootDisplayModeSupport(&isBootDisplayModeSupported);
@@ -2056,8 +2035,6 @@
(void*)nativeSetGameContentType },
{"nativeGetCompositionDataspaces", "()[I",
(void*)nativeGetCompositionDataspaces},
- {"nativeOverrideHdrTypes", "(Landroid/os/IBinder;[I)V",
- (void*)nativeOverrideHdrTypes },
{"nativeClearContentFrameStats", "(J)Z",
(void*)nativeClearContentFrameStats },
{"nativeGetContentFrameStats", "(JLandroid/view/WindowContentFrameStats;)Z",
diff --git a/services/core/java/com/android/server/TEST_MAPPING b/services/core/java/com/android/server/TEST_MAPPING
index 5c84a62..ae65dcb 100644
--- a/services/core/java/com/android/server/TEST_MAPPING
+++ b/services/core/java/com/android/server/TEST_MAPPING
@@ -1,6 +1,9 @@
{
"presubmit": [
{
+ "name": "CtsLocationFineTestCases"
+ },
+ {
"name": "CtsLocationCoarseTestCases"
},
{
@@ -66,10 +69,5 @@
],
"file_patterns": ["ClipboardService\\.java"]
}
- ],
- "postsubmit": [
- {
- "name": "CtsLocationFineTestCases"
- }
]
}
diff --git a/services/core/java/com/android/server/VcnManagementService.java b/services/core/java/com/android/server/VcnManagementService.java
index f26d9f9..76cac93 100644
--- a/services/core/java/com/android/server/VcnManagementService.java
+++ b/services/core/java/com/android/server/VcnManagementService.java
@@ -24,6 +24,7 @@
import static android.net.vcn.VcnManager.VCN_STATUS_CODE_INACTIVE;
import static android.net.vcn.VcnManager.VCN_STATUS_CODE_NOT_CONFIGURED;
import static android.net.vcn.VcnManager.VCN_STATUS_CODE_SAFE_MODE;
+import static android.telephony.SubscriptionManager.isValidSubscriptionId;
import static com.android.server.vcn.TelephonySubscriptionTracker.TelephonySubscriptionSnapshot;
import static com.android.server.vcn.TelephonySubscriptionTracker.TelephonySubscriptionTrackerCallback;
@@ -167,6 +168,10 @@
static final String VCN_CONFIG_FILE =
new File(Environment.getDataSystemDirectory(), "vcn/configs.xml").getPath();
+ // TODO(b/176956496): Directly use CarrierServiceBindHelper.UNBIND_DELAY_MILLIS
+ @VisibleForTesting(visibility = Visibility.PRIVATE)
+ static final long CARRIER_PRIVILEGES_LOST_TEARDOWN_DELAY_MS = TimeUnit.SECONDS.toMillis(30);
+
/* Binder context for this service */
@NonNull private final Context mContext;
@NonNull private final Dependencies mDeps;
@@ -360,15 +365,12 @@
/** Notifies the VcnManagementService that external dependencies can be set up. */
public void systemReady() {
- // Always run on the handler thread to ensure consistency.
- mHandler.post(() -> {
- mNetworkProvider.register();
- mContext.getSystemService(ConnectivityManager.class)
- .registerNetworkCallback(
- new NetworkRequest.Builder().clearCapabilities().build(),
- mTrackingNetworkCallback);
- mTelephonySubscriptionTracker.register();
- });
+ mNetworkProvider.register();
+ mContext.getSystemService(ConnectivityManager.class)
+ .registerNetworkCallback(
+ new NetworkRequest.Builder().clearCapabilities().build(),
+ mTrackingNetworkCallback);
+ mTelephonySubscriptionTracker.register();
}
private void enforcePrimaryUser() {
@@ -509,15 +511,22 @@
if (!mVcns.containsKey(subGrp)) {
startVcnLocked(subGrp, entry.getValue());
}
+
+ // Cancel any scheduled teardowns for active subscriptions
+ mHandler.removeCallbacksAndMessages(mVcns.get(subGrp));
}
}
- // Schedule teardown of any VCN instances that have lost carrier privileges
+ // Schedule teardown of any VCN instances that have lost carrier privileges (after a
+ // delay)
for (Entry<ParcelUuid, Vcn> entry : mVcns.entrySet()) {
final ParcelUuid subGrp = entry.getKey();
final VcnConfig config = mConfigs.get(subGrp);
final boolean isActiveSubGrp = isActiveSubGroup(subGrp, snapshot);
+ final boolean isValidActiveDataSubIdNotInVcnSubGrp =
+ isValidSubscriptionId(snapshot.getActiveDataSubscriptionId())
+ && !isActiveSubGroup(subGrp, snapshot);
// TODO(b/193687515): Support multiple VCNs active at the same time
if (config == null
@@ -527,12 +536,31 @@
final ParcelUuid uuidToTeardown = subGrp;
final Vcn instanceToTeardown = entry.getValue();
- stopVcnLocked(uuidToTeardown);
+ // TODO(b/193687515): Support multiple VCNs active at the same time
+ // If directly switching to a subscription not in the current group,
+ // teardown immediately to prevent other subscription's network from being
+ // outscored by the VCN. Otherwise, teardown after a delay to ensure that
+ // SIM profile switches do not trigger the VCN to cycle.
+ final long teardownDelayMs =
+ isValidActiveDataSubIdNotInVcnSubGrp
+ ? 0
+ : CARRIER_PRIVILEGES_LOST_TEARDOWN_DELAY_MS;
+ mHandler.postDelayed(() -> {
+ synchronized (mLock) {
+ // Guard against case where this is run after a old instance was
+ // torn down, and a new instance was started. Verify to ensure
+ // correct instance is torn down. This could happen as a result of a
+ // Carrier App manually removing/adding a VcnConfig.
+ if (mVcns.get(uuidToTeardown) == instanceToTeardown) {
+ stopVcnLocked(uuidToTeardown);
- // TODO(b/181789060): invoke asynchronously after Vcn notifies
- // through VcnCallback
- notifyAllPermissionedStatusCallbacksLocked(
- uuidToTeardown, VCN_STATUS_CODE_INACTIVE);
+ // TODO(b/181789060): invoke asynchronously after Vcn notifies
+ // through VcnCallback
+ notifyAllPermissionedStatusCallbacksLocked(
+ uuidToTeardown, VCN_STATUS_CODE_INACTIVE);
+ }
+ }
+ }, instanceToTeardown, teardownDelayMs);
} else {
// If this VCN's status has not changed, update it with the new snapshot
entry.getValue().updateSubscriptionSnapshot(mLastSnapshot);
diff --git a/services/core/java/com/android/server/display/DisplayControl.java b/services/core/java/com/android/server/display/DisplayControl.java
index e9640cf..a060f07 100644
--- a/services/core/java/com/android/server/display/DisplayControl.java
+++ b/services/core/java/com/android/server/display/DisplayControl.java
@@ -16,6 +16,9 @@
package com.android.server.display;
+import android.Manifest;
+import android.annotation.NonNull;
+import android.annotation.RequiresPermission;
import android.os.IBinder;
import java.util.Objects;
@@ -26,6 +29,7 @@
public class DisplayControl {
private static native IBinder nativeCreateDisplay(String name, boolean secure);
private static native void nativeDestroyDisplay(IBinder displayToken);
+ private static native void nativeOverrideHdrTypes(IBinder displayToken, int[] modes);
/**
* Create a display in SurfaceFlinger.
@@ -52,4 +56,11 @@
nativeDestroyDisplay(displayToken);
}
+ /**
+ * Overrides HDR modes for a display device.
+ */
+ @RequiresPermission(Manifest.permission.ACCESS_SURFACE_FLINGER)
+ public static void overrideHdrTypes(@NonNull IBinder displayToken, @NonNull int[] modes) {
+ nativeOverrideHdrTypes(displayToken, modes);
+ }
}
diff --git a/services/core/java/com/android/server/display/DisplayManagerService.java b/services/core/java/com/android/server/display/DisplayManagerService.java
index 9ab78bc..2cde526 100644
--- a/services/core/java/com/android/server/display/DisplayManagerService.java
+++ b/services/core/java/com/android/server/display/DisplayManagerService.java
@@ -2562,10 +2562,10 @@
final BrightnessSetting brightnessSetting = new BrightnessSetting(mPersistentDataStore,
display, mSyncRoot);
- final DisplayPowerController displayPowerController;
+ final DisplayPowerControllerInterface displayPowerController;
if (SystemProperties.getInt(PROP_USE_NEW_DISPLAY_POWER_CONTROLLER, 0) == 1) {
- displayPowerController = new DisplayPowerController(
+ displayPowerController = new DisplayPowerController2(
mContext, /* injector= */ null, mDisplayPowerCallbacks, mPowerHandler,
mSensorManager, mDisplayBlanker, display, mBrightnessTracker, brightnessSetting,
() -> handleBrightnessChange(display));
@@ -3000,6 +3000,19 @@
}
}
+ @Override
+ public void overrideHdrTypes(int displayId, int[] modes) {
+ IBinder displayToken;
+ synchronized (mSyncRoot) {
+ displayToken = getDisplayToken(displayId);
+ if (displayToken == null) {
+ throw new IllegalArgumentException("Invalid display: " + displayId);
+ }
+ }
+
+ DisplayControl.overrideHdrTypes(displayToken, modes);
+ }
+
@Override // Binder call
public void setAreUserDisabledHdrTypesAllowed(boolean areUserDisabledHdrTypesAllowed) {
mContext.enforceCallingOrSelfPermission(
diff --git a/services/core/java/com/android/server/display/DisplayPowerController.java b/services/core/java/com/android/server/display/DisplayPowerController.java
index 5a65afe..61225d7 100644
--- a/services/core/java/com/android/server/display/DisplayPowerController.java
+++ b/services/core/java/com/android/server/display/DisplayPowerController.java
@@ -798,8 +798,9 @@
* Notified when the display is changed. We use this to apply any changes that might be needed
* when displays get swapped on foldable devices. For example, different brightness properties
* of each display need to be properly reflected in AutomaticBrightnessController.
+ *
+ * Make sure DisplayManagerService.mSyncRoot is held when this is called
*/
- @GuardedBy("DisplayManagerService.mSyncRoot")
@Override
public void onDisplayChanged() {
final DisplayDevice device = mLogicalDisplay.getPrimaryDisplayDeviceLocked();
@@ -1896,7 +1897,7 @@
}
},
() -> {
- sendUpdatePowerStateLocked();
+ sendUpdatePowerState();
postBrightnessChangeRunnable();
// TODO(b/192258832): Switch the HBMChangeCallback to a listener pattern.
if (mAutomaticBrightnessController != null) {
@@ -1912,7 +1913,7 @@
ddConfig != null ? ddConfig.getBrightnessThrottlingData() : null;
return new BrightnessThrottler(mHandler, data,
() -> {
- sendUpdatePowerStateLocked();
+ sendUpdatePowerState();
postBrightnessChangeRunnable();
}, mUniqueDisplayId);
}
diff --git a/services/core/java/com/android/server/display/DisplayPowerController2.java b/services/core/java/com/android/server/display/DisplayPowerController2.java
new file mode 100644
index 0000000..dc7db10
--- /dev/null
+++ b/services/core/java/com/android/server/display/DisplayPowerController2.java
@@ -0,0 +1,3071 @@
+/*
+ * Copyright (C) 2022 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.server.display;
+
+import android.animation.Animator;
+import android.animation.ObjectAnimator;
+import android.annotation.Nullable;
+import android.annotation.UserIdInt;
+import android.app.ActivityManager;
+import android.content.Context;
+import android.content.pm.ParceledListSlice;
+import android.content.res.Resources;
+import android.database.ContentObserver;
+import android.hardware.Sensor;
+import android.hardware.SensorEvent;
+import android.hardware.SensorEventListener;
+import android.hardware.SensorManager;
+import android.hardware.display.AmbientBrightnessDayStats;
+import android.hardware.display.BrightnessChangeEvent;
+import android.hardware.display.BrightnessConfiguration;
+import android.hardware.display.BrightnessInfo;
+import android.hardware.display.DisplayManagerInternal.DisplayPowerCallbacks;
+import android.hardware.display.DisplayManagerInternal.DisplayPowerRequest;
+import android.metrics.LogMaker;
+import android.net.Uri;
+import android.os.Handler;
+import android.os.IBinder;
+import android.os.Looper;
+import android.os.Message;
+import android.os.PowerManager;
+import android.os.RemoteException;
+import android.os.SystemClock;
+import android.os.SystemProperties;
+import android.os.Trace;
+import android.os.UserHandle;
+import android.provider.Settings;
+import android.util.FloatProperty;
+import android.util.Log;
+import android.util.MathUtils;
+import android.util.MutableFloat;
+import android.util.MutableInt;
+import android.util.Slog;
+import android.util.TimeUtils;
+import android.view.Display;
+
+import com.android.internal.R;
+import com.android.internal.annotations.GuardedBy;
+import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.app.IBatteryStats;
+import com.android.internal.display.BrightnessSynchronizer;
+import com.android.internal.logging.MetricsLogger;
+import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
+import com.android.internal.util.FrameworkStatsLog;
+import com.android.internal.util.RingBuffer;
+import com.android.server.LocalServices;
+import com.android.server.am.BatteryStatsService;
+import com.android.server.display.RampAnimator.DualRampAnimator;
+import com.android.server.display.brightness.BrightnessEvent;
+import com.android.server.display.brightness.BrightnessReason;
+import com.android.server.display.color.ColorDisplayService.ColorDisplayServiceInternal;
+import com.android.server.display.color.ColorDisplayService.ReduceBrightColorsListener;
+import com.android.server.display.utils.SensorUtils;
+import com.android.server.display.whitebalance.DisplayWhiteBalanceController;
+import com.android.server.display.whitebalance.DisplayWhiteBalanceFactory;
+import com.android.server.display.whitebalance.DisplayWhiteBalanceSettings;
+import com.android.server.policy.WindowManagerPolicy;
+
+import java.io.PrintWriter;
+
+/**
+ * Controls the power state of the display.
+ *
+ * Handles the proximity sensor, light sensor, and animations between states
+ * including the screen off animation.
+ *
+ * This component acts independently of the rest of the power manager service.
+ * In particular, it does not share any state and it only communicates
+ * via asynchronous callbacks to inform the power manager that something has
+ * changed.
+ *
+ * Everything this class does internally is serialized on its handler although
+ * it may be accessed by other threads from the outside.
+ *
+ * Note that the power manager service guarantees that it will hold a suspend
+ * blocker as long as the display is not ready. So most of the work done here
+ * does not need to worry about holding a suspend blocker unless it happens
+ * independently of the display ready signal.
+ *
+ * For debugging, you can make the color fade and brightness animations run
+ * slower by changing the "animator duration scale" option in Development Settings.
+ */
+final class DisplayPowerController2 implements AutomaticBrightnessController.Callbacks,
+ DisplayWhiteBalanceController.Callbacks, DisplayPowerControllerInterface {
+ private static final String SCREEN_ON_BLOCKED_TRACE_NAME = "Screen on blocked";
+ private static final String SCREEN_OFF_BLOCKED_TRACE_NAME = "Screen off blocked";
+
+ private static final boolean DEBUG = false;
+ private static final boolean DEBUG_PRETEND_PROXIMITY_SENSOR_ABSENT = false;
+
+ // If true, uses the color fade on animation.
+ // We might want to turn this off if we cannot get a guarantee that the screen
+ // actually turns on and starts showing new content after the call to set the
+ // screen state returns. Playing the animation can also be somewhat slow.
+ private static final boolean USE_COLOR_FADE_ON_ANIMATION = false;
+
+ private static final float SCREEN_ANIMATION_RATE_MINIMUM = 0.0f;
+
+ private static final int COLOR_FADE_ON_ANIMATION_DURATION_MILLIS = 250;
+ private static final int COLOR_FADE_OFF_ANIMATION_DURATION_MILLIS = 400;
+
+ private static final int MSG_UPDATE_POWER_STATE = 1;
+ private static final int MSG_PROXIMITY_SENSOR_DEBOUNCED = 2;
+ private static final int MSG_SCREEN_ON_UNBLOCKED = 3;
+ private static final int MSG_SCREEN_OFF_UNBLOCKED = 4;
+ private static final int MSG_CONFIGURE_BRIGHTNESS = 5;
+ private static final int MSG_SET_TEMPORARY_BRIGHTNESS = 6;
+ private static final int MSG_SET_TEMPORARY_AUTO_BRIGHTNESS_ADJUSTMENT = 7;
+ private static final int MSG_IGNORE_PROXIMITY = 8;
+ private static final int MSG_STOP = 9;
+ private static final int MSG_UPDATE_BRIGHTNESS = 10;
+ private static final int MSG_UPDATE_RBC = 11;
+ private static final int MSG_BRIGHTNESS_RAMP_DONE = 12;
+ private static final int MSG_STATSD_HBM_BRIGHTNESS = 13;
+
+ private static final int PROXIMITY_UNKNOWN = -1;
+ private static final int PROXIMITY_NEGATIVE = 0;
+ private static final int PROXIMITY_POSITIVE = 1;
+
+ // Proximity sensor debounce delay in milliseconds for positive or negative transitions.
+ private static final int PROXIMITY_SENSOR_POSITIVE_DEBOUNCE_DELAY = 0;
+ private static final int PROXIMITY_SENSOR_NEGATIVE_DEBOUNCE_DELAY = 250;
+
+ private static final int BRIGHTNESS_CHANGE_STATSD_REPORT_INTERVAL_MS = 500;
+
+ // Trigger proximity if distance is less than 5 cm.
+ private static final float TYPICAL_PROXIMITY_THRESHOLD = 5.0f;
+
+ // State machine constants for tracking initial brightness ramp skipping when enabled.
+ private static final int RAMP_STATE_SKIP_NONE = 0;
+ private static final int RAMP_STATE_SKIP_INITIAL = 1;
+ private static final int RAMP_STATE_SKIP_AUTOBRIGHT = 2;
+
+ private static final int REPORTED_TO_POLICY_UNREPORTED = -1;
+ private static final int REPORTED_TO_POLICY_SCREEN_OFF = 0;
+ private static final int REPORTED_TO_POLICY_SCREEN_TURNING_ON = 1;
+ private static final int REPORTED_TO_POLICY_SCREEN_ON = 2;
+ private static final int REPORTED_TO_POLICY_SCREEN_TURNING_OFF = 3;
+
+ private static final int RINGBUFFER_MAX = 100;
+
+ private final String mTag;
+
+ private final Object mLock = new Object();
+
+ private final Context mContext;
+
+ // Our handler.
+ private final DisplayControllerHandler mHandler;
+
+ // Asynchronous callbacks into the power manager service.
+ // Only invoked from the handler thread while no locks are held.
+ private final DisplayPowerCallbacks mCallbacks;
+
+ // Battery stats.
+ @Nullable
+ private final IBatteryStats mBatteryStats;
+
+ // The sensor manager.
+ private final SensorManager mSensorManager;
+
+ // The window manager policy.
+ private final WindowManagerPolicy mWindowManagerPolicy;
+
+ // The display blanker.
+ private final DisplayBlanker mBlanker;
+
+ // The LogicalDisplay tied to this DisplayPowerController2.
+ private final LogicalDisplay mLogicalDisplay;
+
+ // The ID of the LogicalDisplay tied to this DisplayPowerController2.
+ private final int mDisplayId;
+
+ // The unique ID of the primary display device currently tied to this logical display
+ private String mUniqueDisplayId;
+
+ // Tracker for brightness changes.
+ @Nullable
+ private final BrightnessTracker mBrightnessTracker;
+
+ // Tracker for brightness settings changes.
+ private final SettingsObserver mSettingsObserver;
+
+ // The proximity sensor, or null if not available or needed.
+ private Sensor mProximitySensor;
+
+ // The doze screen brightness.
+ private final float mScreenBrightnessDozeConfig;
+
+ // The dim screen brightness.
+ private final float mScreenBrightnessDimConfig;
+
+ // The minimum dim amount to use if the screen brightness is already below
+ // mScreenBrightnessDimConfig.
+ private final float mScreenBrightnessMinimumDimAmount;
+
+ private final float mScreenBrightnessDefault;
+
+ // The minimum allowed brightness while in VR.
+ private final float mScreenBrightnessForVrRangeMinimum;
+
+ // The maximum allowed brightness while in VR.
+ private final float mScreenBrightnessForVrRangeMaximum;
+
+ // The default screen brightness for VR.
+ private final float mScreenBrightnessForVrDefault;
+
+ // True if auto-brightness should be used.
+ private boolean mUseSoftwareAutoBrightnessConfig;
+
+ // True if should use light sensor to automatically determine doze screen brightness.
+ private final boolean mAllowAutoBrightnessWhileDozingConfig;
+
+ // Whether or not the color fade on screen on / off is enabled.
+ private final boolean mColorFadeEnabled;
+
+ @GuardedBy("mCachedBrightnessInfo")
+ private final CachedBrightnessInfo mCachedBrightnessInfo = new CachedBrightnessInfo();
+
+ private DisplayDevice mDisplayDevice;
+
+ // True if we should fade the screen while turning it off, false if we should play
+ // a stylish color fade animation instead.
+ private final boolean mColorFadeFadesConfig;
+
+ // True if we need to fake a transition to off when coming out of a doze state.
+ // Some display hardware will blank itself when coming out of doze in order to hide
+ // artifacts. For these displays we fake a transition into OFF so that policy can appropriately
+ // blank itself and begin an appropriate power on animation.
+ private final boolean mDisplayBlanksAfterDozeConfig;
+
+ // True if there are only buckets of brightness values when the display is in the doze state,
+ // rather than a full range of values. If this is true, then we'll avoid animating the screen
+ // brightness since it'd likely be multiple jarring brightness transitions instead of just one
+ // to reach the final state.
+ private final boolean mBrightnessBucketsInDozeConfig;
+
+ private final Clock mClock;
+ private final Injector mInjector;
+
+ // Maximum time a ramp animation can take.
+ private long mBrightnessRampIncreaseMaxTimeMillis;
+ private long mBrightnessRampDecreaseMaxTimeMillis;
+
+ // The pending power request.
+ // Initially null until the first call to requestPowerState.
+ @GuardedBy("mLock")
+ private DisplayPowerRequest mPendingRequestLocked;
+
+ // True if a request has been made to wait for the proximity sensor to go negative.
+ @GuardedBy("mLock")
+ private boolean mPendingWaitForNegativeProximityLocked;
+
+ // True if the pending power request or wait for negative proximity flag
+ // has been changed since the last update occurred.
+ @GuardedBy("mLock")
+ private boolean mPendingRequestChangedLocked;
+
+ // Set to true when the important parts of the pending power request have been applied.
+ // The important parts are mainly the screen state. Brightness changes may occur
+ // concurrently.
+ @GuardedBy("mLock")
+ private boolean mDisplayReadyLocked;
+
+ // Set to true if a power state update is required.
+ @GuardedBy("mLock")
+ private boolean mPendingUpdatePowerStateLocked;
+
+ /* The following state must only be accessed by the handler thread. */
+
+ // The currently requested power state.
+ // The power controller will progressively update its internal state to match
+ // the requested power state. Initially null until the first update.
+ private DisplayPowerRequest mPowerRequest;
+
+ // The current power state.
+ // Must only be accessed on the handler thread.
+ private DisplayPowerState mPowerState;
+
+ // True if the device should wait for negative proximity sensor before
+ // waking up the screen. This is set to false as soon as a negative
+ // proximity sensor measurement is observed or when the device is forced to
+ // go to sleep by the user. While true, the screen remains off.
+ private boolean mWaitingForNegativeProximity;
+
+ // True if the device should not take into account the proximity sensor
+ // until either the proximity sensor state changes, or there is no longer a
+ // request to listen to proximity sensor.
+ private boolean mIgnoreProximityUntilChanged;
+
+ // The actual proximity sensor threshold value.
+ private float mProximityThreshold;
+
+ // Set to true if the proximity sensor listener has been registered
+ // with the sensor manager.
+ private boolean mProximitySensorEnabled;
+
+ // The debounced proximity sensor state.
+ private int mProximity = PROXIMITY_UNKNOWN;
+
+ // The raw non-debounced proximity sensor state.
+ private int mPendingProximity = PROXIMITY_UNKNOWN;
+ private long mPendingProximityDebounceTime = -1; // -1 if fully debounced
+
+ // True if the screen was turned off because of the proximity sensor.
+ // When the screen turns on again, we report user activity to the power manager.
+ private boolean mScreenOffBecauseOfProximity;
+
+ // The currently active screen on unblocker. This field is non-null whenever
+ // we are waiting for a callback to release it and unblock the screen.
+ private ScreenOnUnblocker mPendingScreenOnUnblocker;
+ private ScreenOffUnblocker mPendingScreenOffUnblocker;
+
+ // True if we were in the process of turning off the screen.
+ // This allows us to recover more gracefully from situations where we abort
+ // turning off the screen.
+ private boolean mPendingScreenOff;
+
+ // True if we have unfinished business and are holding a suspend blocker.
+ private boolean mUnfinishedBusiness;
+
+ // The elapsed real time when the screen on was blocked.
+ private long mScreenOnBlockStartRealTime;
+ private long mScreenOffBlockStartRealTime;
+
+ // Screen state we reported to policy. Must be one of REPORTED_TO_POLICY_* fields.
+ private int mReportedScreenStateToPolicy = REPORTED_TO_POLICY_UNREPORTED;
+
+ // If the last recorded screen state was dozing or not.
+ private boolean mDozing;
+
+ // Remembers whether certain kinds of brightness adjustments
+ // were recently applied so that we can decide how to transition.
+ private boolean mAppliedAutoBrightness;
+ private boolean mAppliedDimming;
+ private boolean mAppliedLowPower;
+ private boolean mAppliedScreenBrightnessOverride;
+ private boolean mAppliedTemporaryBrightness;
+ private boolean mAppliedTemporaryAutoBrightnessAdjustment;
+ private boolean mAppliedBrightnessBoost;
+ private boolean mAppliedThrottling;
+
+ // Reason for which the brightness was last changed. See {@link BrightnessReason} for more
+ // information.
+ // At the time of this writing, this value is changed within updatePowerState() only, which is
+ // limited to the thread used by DisplayControllerHandler.
+ private final BrightnessReason mBrightnessReason = new BrightnessReason();
+ private final BrightnessReason mBrightnessReasonTemp = new BrightnessReason();
+
+ // Brightness animation ramp rates in brightness units per second
+ private float mBrightnessRampRateFastDecrease;
+ private float mBrightnessRampRateFastIncrease;
+ private float mBrightnessRampRateSlowDecrease;
+ private float mBrightnessRampRateSlowIncrease;
+
+ // Report HBM brightness change to StatsD
+ private int mDisplayStatsId;
+ private float mLastStatsBrightness = PowerManager.BRIGHTNESS_MIN;
+
+ // Whether or not to skip the initial brightness ramps into STATE_ON.
+ private final boolean mSkipScreenOnBrightnessRamp;
+
+ // Display white balance components.
+ @Nullable
+ private final DisplayWhiteBalanceSettings mDisplayWhiteBalanceSettings;
+ @Nullable
+ private final DisplayWhiteBalanceController mDisplayWhiteBalanceController;
+
+ @Nullable
+ private final ColorDisplayServiceInternal mCdsi;
+ private float[] mNitsRange;
+
+ private final HighBrightnessModeController mHbmController;
+
+ private final BrightnessThrottler mBrightnessThrottler;
+
+ private final BrightnessSetting mBrightnessSetting;
+
+ private final Runnable mOnBrightnessChangeRunnable;
+
+ private final BrightnessEvent mLastBrightnessEvent;
+ private final BrightnessEvent mTempBrightnessEvent;
+
+ // Keeps a record of brightness changes for dumpsys.
+ private RingBuffer<BrightnessEvent> mBrightnessEventRingBuffer;
+
+ // A record of state for skipping brightness ramps.
+ private int mSkipRampState = RAMP_STATE_SKIP_NONE;
+
+ // The first autobrightness value set when entering RAMP_STATE_SKIP_INITIAL.
+ private float mInitialAutoBrightness;
+
+ // The controller for the automatic brightness level.
+ @Nullable
+ private AutomaticBrightnessController mAutomaticBrightnessController;
+
+ private Sensor mLightSensor;
+
+ // The mappers between ambient lux, display backlight values, and display brightness.
+ // We will switch between the idle mapper and active mapper in AutomaticBrightnessController.
+ // Mapper used for active (normal) screen brightness mode
+ @Nullable
+ private BrightnessMappingStrategy mInteractiveModeBrightnessMapper;
+ // Mapper used for idle screen brightness mode
+ @Nullable
+ private BrightnessMappingStrategy mIdleModeBrightnessMapper;
+
+ // The current brightness configuration.
+ @Nullable
+ private BrightnessConfiguration mBrightnessConfiguration;
+
+ // The last brightness that was set by the user and not temporary. Set to
+ // PowerManager.BRIGHTNESS_INVALID_FLOAT when a brightness has yet to be recorded.
+ private float mLastUserSetScreenBrightness = Float.NaN;
+
+ // The screen brightness setting has changed but not taken effect yet. If this is different
+ // from the current screen brightness setting then this is coming from something other than us
+ // and should be considered a user interaction.
+ private float mPendingScreenBrightnessSetting;
+
+ // The last observed screen brightness setting, either set by us or by the settings app on
+ // behalf of the user.
+ private float mCurrentScreenBrightnessSetting;
+
+ // The temporary screen brightness. Typically set when a user is interacting with the
+ // brightness slider but hasn't settled on a choice yet. Set to
+ // PowerManager.BRIGHTNESS_INVALID_FLOAT when there's no temporary brightness set.
+ private float mTemporaryScreenBrightness;
+
+ // The current screen brightness while in VR mode.
+ private float mScreenBrightnessForVr;
+
+ // The last auto brightness adjustment that was set by the user and not temporary. Set to
+ // Float.NaN when an auto-brightness adjustment hasn't been recorded yet.
+ private float mAutoBrightnessAdjustment;
+
+ // The pending auto brightness adjustment that will take effect on the next power state update.
+ private float mPendingAutoBrightnessAdjustment;
+
+ // The temporary auto brightness adjustment. Typically set when a user is interacting with the
+ // adjustment slider but hasn't settled on a choice yet. Set to
+ // PowerManager.BRIGHTNESS_INVALID_FLOAT when there's no temporary adjustment set.
+ private float mTemporaryAutoBrightnessAdjustment;
+
+ private boolean mIsRbcActive;
+
+ // Whether there's a callback to tell listeners the display has changed scheduled to run. When
+ // true it implies a wakelock is being held to guarantee the update happens before we collapse
+ // into suspend and so needs to be cleaned up if the thread is exiting.
+ // Should only be accessed on the Handler thread.
+ private boolean mOnStateChangedPending;
+
+ // Count of proximity messages currently on this DPC's Handler. Used to keep track of how many
+ // suspend blocker acquisitions are pending when shutting down this DPC.
+ // Should only be accessed on the Handler thread.
+ private int mOnProximityPositiveMessages;
+ private int mOnProximityNegativeMessages;
+
+ // Animators.
+ private ObjectAnimator mColorFadeOnAnimator;
+ private ObjectAnimator mColorFadeOffAnimator;
+ private DualRampAnimator<DisplayPowerState> mScreenBrightnessRampAnimator;
+ private BrightnessSetting.BrightnessSettingListener mBrightnessSettingListener;
+
+ // True if this DisplayPowerController2 has been stopped and should no longer be running.
+ private boolean mStopped;
+
+ private DisplayDeviceConfig mDisplayDeviceConfig;
+
+ // Identifiers for suspend blocker acuisition requests
+ private final String mSuspendBlockerIdUnfinishedBusiness;
+ private final String mSuspendBlockerIdOnStateChanged;
+ private final String mSuspendBlockerIdProxPositive;
+ private final String mSuspendBlockerIdProxNegative;
+ private final String mSuspendBlockerIdProxDebounce;
+
+ /**
+ * Creates the display power controller.
+ */
+ DisplayPowerController2(Context context, Injector injector,
+ DisplayPowerCallbacks callbacks, Handler handler,
+ SensorManager sensorManager, DisplayBlanker blanker, LogicalDisplay logicalDisplay,
+ BrightnessTracker brightnessTracker, BrightnessSetting brightnessSetting,
+ Runnable onBrightnessChangeRunnable) {
+
+ mInjector = injector != null ? injector : new Injector();
+ mClock = mInjector.getClock();
+ mLogicalDisplay = logicalDisplay;
+ mDisplayId = mLogicalDisplay.getDisplayIdLocked();
+ mTag = "DisplayPowerController2[" + mDisplayId + "]";
+ mSuspendBlockerIdUnfinishedBusiness = getSuspendBlockerUnfinishedBusinessId(mDisplayId);
+ mSuspendBlockerIdOnStateChanged = getSuspendBlockerOnStateChangedId(mDisplayId);
+ mSuspendBlockerIdProxPositive = getSuspendBlockerProxPositiveId(mDisplayId);
+ mSuspendBlockerIdProxNegative = getSuspendBlockerProxNegativeId(mDisplayId);
+ mSuspendBlockerIdProxDebounce = getSuspendBlockerProxDebounceId(mDisplayId);
+
+ mDisplayDevice = mLogicalDisplay.getPrimaryDisplayDeviceLocked();
+ mUniqueDisplayId = logicalDisplay.getPrimaryDisplayDeviceLocked().getUniqueId();
+ mDisplayStatsId = mUniqueDisplayId.hashCode();
+ mHandler = new DisplayControllerHandler(handler.getLooper());
+ mLastBrightnessEvent = new BrightnessEvent(mDisplayId);
+ mTempBrightnessEvent = new BrightnessEvent(mDisplayId);
+
+ if (mDisplayId == Display.DEFAULT_DISPLAY) {
+ mBatteryStats = BatteryStatsService.getService();
+ } else {
+ mBatteryStats = null;
+ }
+
+ mSettingsObserver = new SettingsObserver(mHandler);
+ mCallbacks = callbacks;
+ mSensorManager = sensorManager;
+ mWindowManagerPolicy = LocalServices.getService(WindowManagerPolicy.class);
+ mBlanker = blanker;
+ mContext = context;
+ mBrightnessTracker = brightnessTracker;
+ // TODO: b/186428377 update brightness setting when display changes
+ mBrightnessSetting = brightnessSetting;
+ mOnBrightnessChangeRunnable = onBrightnessChangeRunnable;
+
+ PowerManager pm = context.getSystemService(PowerManager.class);
+
+ final Resources resources = context.getResources();
+
+ // DOZE AND DIM SETTINGS
+ mScreenBrightnessDozeConfig = clampAbsoluteBrightness(
+ pm.getBrightnessConstraint(PowerManager.BRIGHTNESS_CONSTRAINT_TYPE_DOZE));
+ mScreenBrightnessDimConfig = clampAbsoluteBrightness(
+ pm.getBrightnessConstraint(PowerManager.BRIGHTNESS_CONSTRAINT_TYPE_DIM));
+ mScreenBrightnessMinimumDimAmount = resources.getFloat(
+ R.dimen.config_screenBrightnessMinimumDimAmountFloat);
+
+
+ // NORMAL SCREEN SETTINGS
+ mScreenBrightnessDefault = clampAbsoluteBrightness(
+ mLogicalDisplay.getDisplayInfoLocked().brightnessDefault);
+
+ // VR SETTINGS
+ mScreenBrightnessForVrDefault = clampAbsoluteBrightness(
+ pm.getBrightnessConstraint(PowerManager.BRIGHTNESS_CONSTRAINT_TYPE_DEFAULT_VR));
+ mScreenBrightnessForVrRangeMaximum = clampAbsoluteBrightness(
+ pm.getBrightnessConstraint(PowerManager.BRIGHTNESS_CONSTRAINT_TYPE_MAXIMUM_VR));
+ mScreenBrightnessForVrRangeMinimum = clampAbsoluteBrightness(
+ pm.getBrightnessConstraint(PowerManager.BRIGHTNESS_CONSTRAINT_TYPE_MINIMUM_VR));
+
+ // Check the setting, but also verify that it is the default display. Only the default
+ // display has an automatic brightness controller running.
+ // TODO: b/179021925 - Fix to work with multiple displays
+ mUseSoftwareAutoBrightnessConfig = resources.getBoolean(
+ R.bool.config_automatic_brightness_available)
+ && mDisplayId == Display.DEFAULT_DISPLAY;
+
+ mAllowAutoBrightnessWhileDozingConfig = resources.getBoolean(
+ R.bool.config_allowAutoBrightnessWhileDozing);
+
+ mDisplayDeviceConfig = logicalDisplay.getPrimaryDisplayDeviceLocked()
+ .getDisplayDeviceConfig();
+
+ loadBrightnessRampRates();
+ mSkipScreenOnBrightnessRamp = resources.getBoolean(
+ R.bool.config_skipScreenOnBrightnessRamp);
+
+ mHbmController = createHbmControllerLocked();
+
+ mBrightnessThrottler = createBrightnessThrottlerLocked();
+
+ // Seed the cached brightness
+ saveBrightnessInfo(getScreenBrightnessSetting());
+
+ DisplayWhiteBalanceSettings displayWhiteBalanceSettings = null;
+ DisplayWhiteBalanceController displayWhiteBalanceController = null;
+ if (mDisplayId == Display.DEFAULT_DISPLAY) {
+ try {
+ displayWhiteBalanceSettings = new DisplayWhiteBalanceSettings(mContext, mHandler);
+ displayWhiteBalanceController = DisplayWhiteBalanceFactory.create(mHandler,
+ mSensorManager, resources);
+ displayWhiteBalanceSettings.setCallbacks(this);
+ displayWhiteBalanceController.setCallbacks(this);
+ } catch (Exception e) {
+ Slog.e(mTag, "failed to set up display white-balance: " + e);
+ }
+ }
+ mDisplayWhiteBalanceSettings = displayWhiteBalanceSettings;
+ mDisplayWhiteBalanceController = displayWhiteBalanceController;
+
+ loadNitsRange(resources);
+
+ if (mDisplayId == Display.DEFAULT_DISPLAY) {
+ mCdsi = LocalServices.getService(ColorDisplayServiceInternal.class);
+ boolean active = mCdsi.setReduceBrightColorsListener(new ReduceBrightColorsListener() {
+ @Override
+ public void onReduceBrightColorsActivationChanged(boolean activated,
+ boolean userInitiated) {
+ applyReduceBrightColorsSplineAdjustment();
+
+ }
+
+ @Override
+ public void onReduceBrightColorsStrengthChanged(int strength) {
+ applyReduceBrightColorsSplineAdjustment();
+ }
+ });
+ if (active) {
+ applyReduceBrightColorsSplineAdjustment();
+ }
+ } else {
+ mCdsi = null;
+ }
+
+ setUpAutoBrightness(resources, handler);
+
+ mColorFadeEnabled = !ActivityManager.isLowRamDeviceStatic();
+ mColorFadeFadesConfig = resources.getBoolean(
+ R.bool.config_animateScreenLights);
+
+ mDisplayBlanksAfterDozeConfig = resources.getBoolean(
+ R.bool.config_displayBlanksAfterDoze);
+
+ mBrightnessBucketsInDozeConfig = resources.getBoolean(
+ R.bool.config_displayBrightnessBucketsInDoze);
+
+ loadProximitySensor();
+
+ mCurrentScreenBrightnessSetting = getScreenBrightnessSetting();
+ mScreenBrightnessForVr = getScreenBrightnessForVrSetting();
+ mAutoBrightnessAdjustment = getAutoBrightnessAdjustmentSetting();
+ mTemporaryScreenBrightness = PowerManager.BRIGHTNESS_INVALID_FLOAT;
+ mPendingScreenBrightnessSetting = PowerManager.BRIGHTNESS_INVALID_FLOAT;
+ mTemporaryAutoBrightnessAdjustment = PowerManager.BRIGHTNESS_INVALID_FLOAT;
+ mPendingAutoBrightnessAdjustment = PowerManager.BRIGHTNESS_INVALID_FLOAT;
+
+ }
+
+ private void applyReduceBrightColorsSplineAdjustment() {
+ mHandler.obtainMessage(MSG_UPDATE_RBC).sendToTarget();
+ sendUpdatePowerState();
+ }
+
+ private void handleRbcChanged() {
+ if (mAutomaticBrightnessController == null) {
+ return;
+ }
+ if ((!mAutomaticBrightnessController.isInIdleMode()
+ && mInteractiveModeBrightnessMapper == null)
+ || (mAutomaticBrightnessController.isInIdleMode()
+ && mIdleModeBrightnessMapper == null)) {
+ Log.w(mTag, "No brightness mapping available to recalculate splines for this mode");
+ return;
+ }
+
+ float[] adjustedNits = new float[mNitsRange.length];
+ for (int i = 0; i < mNitsRange.length; i++) {
+ adjustedNits[i] = mCdsi.getReduceBrightColorsAdjustedBrightnessNits(mNitsRange[i]);
+ }
+ mIsRbcActive = mCdsi.isReduceBrightColorsActivated();
+ mAutomaticBrightnessController.recalculateSplines(mIsRbcActive, adjustedNits);
+ }
+
+ /**
+ * Returns true if the proximity sensor screen-off function is available.
+ */
+ @Override
+ public boolean isProximitySensorAvailable() {
+ return mProximitySensor != null;
+ }
+
+ /**
+ * Get the {@link BrightnessChangeEvent}s for the specified user.
+ *
+ * @param userId userId to fetch data for
+ * @param includePackage if false will null out the package name in events
+ */
+ @Nullable
+ @Override
+ public ParceledListSlice<BrightnessChangeEvent> getBrightnessEvents(
+ @UserIdInt int userId, boolean includePackage) {
+ if (mBrightnessTracker == null) {
+ return null;
+ }
+ return mBrightnessTracker.getEvents(userId, includePackage);
+ }
+
+ @Override
+ public void onSwitchUser(@UserIdInt int newUserId) {
+ handleSettingsChange(true /* userSwitch */);
+ if (mBrightnessTracker != null) {
+ mBrightnessTracker.onSwitchUser(newUserId);
+ }
+ }
+
+ @Nullable
+ @Override
+ public ParceledListSlice<AmbientBrightnessDayStats> getAmbientBrightnessStats(
+ @UserIdInt int userId) {
+ if (mBrightnessTracker == null) {
+ return null;
+ }
+ return mBrightnessTracker.getAmbientBrightnessStats(userId);
+ }
+
+ /**
+ * Persist the brightness slider events and ambient brightness stats to disk.
+ */
+ @Override
+ public void persistBrightnessTrackerState() {
+ if (mBrightnessTracker != null) {
+ mBrightnessTracker.persistBrightnessTrackerState();
+ }
+ }
+
+ /**
+ * Requests a new power state.
+ * The controller makes a copy of the provided object and then
+ * begins adjusting the power state to match what was requested.
+ *
+ * @param request The requested power state.
+ * @param waitForNegativeProximity If true, issues a request to wait for
+ * negative proximity before turning the screen back on,
+ * assuming the screen
+ * was turned off by the proximity sensor.
+ * @return True if display is ready, false if there are important changes that must
+ * be made asynchronously (such as turning the screen on), in which case the caller
+ * should grab a wake lock, watch for {@link DisplayPowerCallbacks#onStateChanged()}
+ * then try the request again later until the state converges.
+ */
+ public boolean requestPowerState(DisplayPowerRequest request,
+ boolean waitForNegativeProximity) {
+ if (DEBUG) {
+ Slog.d(mTag, "requestPowerState: "
+ + request + ", waitForNegativeProximity=" + waitForNegativeProximity);
+ }
+
+ synchronized (mLock) {
+ if (mStopped) {
+ return true;
+ }
+
+ boolean changed = false;
+
+ if (waitForNegativeProximity
+ && !mPendingWaitForNegativeProximityLocked) {
+ mPendingWaitForNegativeProximityLocked = true;
+ changed = true;
+ }
+
+ if (mPendingRequestLocked == null) {
+ mPendingRequestLocked = new DisplayPowerRequest(request);
+ changed = true;
+ } else if (!mPendingRequestLocked.equals(request)) {
+ mPendingRequestLocked.copyFrom(request);
+ changed = true;
+ }
+
+ if (changed) {
+ mDisplayReadyLocked = false;
+ if (!mPendingRequestChangedLocked) {
+ mPendingRequestChangedLocked = true;
+ sendUpdatePowerStateLocked();
+ }
+ }
+
+ return mDisplayReadyLocked;
+ }
+ }
+
+ @Override
+ public BrightnessConfiguration getDefaultBrightnessConfiguration() {
+ if (mAutomaticBrightnessController == null) {
+ return null;
+ }
+ return mAutomaticBrightnessController.getDefaultConfig();
+ }
+
+ /**
+ * Notified when the display is changed. We use this to apply any changes that might be needed
+ * when displays get swapped on foldable devices. For example, different brightness properties
+ * of each display need to be properly reflected in AutomaticBrightnessController.
+ *
+ * Make sure DisplayManagerService.mSyncRoot lock is held when this is called
+ */
+ @Override
+ public void onDisplayChanged() {
+ final DisplayDevice device = mLogicalDisplay.getPrimaryDisplayDeviceLocked();
+ if (device == null) {
+ Slog.wtf(mTag, "Display Device is null in DisplayPowerController2 for display: "
+ + mLogicalDisplay.getDisplayIdLocked());
+ return;
+ }
+
+ final String uniqueId = device.getUniqueId();
+ final DisplayDeviceConfig config = device.getDisplayDeviceConfig();
+ final IBinder token = device.getDisplayTokenLocked();
+ final DisplayDeviceInfo info = device.getDisplayDeviceInfoLocked();
+ mHandler.post(() -> {
+ if (mDisplayDevice != device) {
+ mDisplayDevice = device;
+ mUniqueDisplayId = uniqueId;
+ mDisplayStatsId = mUniqueDisplayId.hashCode();
+ mDisplayDeviceConfig = config;
+ loadFromDisplayDeviceConfig(token, info);
+ updatePowerState();
+ }
+ });
+ }
+
+ /**
+ * Called when the displays are preparing to transition from one device state to another.
+ * This process involves turning off some displays so we need updatePowerState() to run and
+ * calculate the new state.
+ */
+ @Override
+ public void onDeviceStateTransition() {
+ sendUpdatePowerState();
+ }
+
+ /**
+ * Unregisters all listeners and interrupts all running threads; halting future work.
+ *
+ * This method should be called when the DisplayPowerController2 is no longer in use; i.e. when
+ * the {@link #mDisplayId display} has been removed.
+ */
+ @Override
+ public void stop() {
+ synchronized (mLock) {
+ mStopped = true;
+ Message msg = mHandler.obtainMessage(MSG_STOP);
+ mHandler.sendMessageAtTime(msg, mClock.uptimeMillis());
+
+ if (mDisplayWhiteBalanceController != null) {
+ mDisplayWhiteBalanceController.setEnabled(false);
+ }
+
+ if (mAutomaticBrightnessController != null) {
+ mAutomaticBrightnessController.stop();
+ }
+
+ if (mBrightnessSetting != null) {
+ mBrightnessSetting.unregisterListener(mBrightnessSettingListener);
+ }
+
+ mContext.getContentResolver().unregisterContentObserver(mSettingsObserver);
+ }
+ }
+
+ private void loadFromDisplayDeviceConfig(IBinder token, DisplayDeviceInfo info) {
+ // All properties that depend on the associated DisplayDevice and the DDC must be
+ // updated here.
+ loadBrightnessRampRates();
+ loadProximitySensor();
+ loadNitsRange(mContext.getResources());
+ setUpAutoBrightness(mContext.getResources(), mHandler);
+ reloadReduceBrightColours();
+ if (mScreenBrightnessRampAnimator != null) {
+ mScreenBrightnessRampAnimator.setAnimationTimeLimits(
+ mBrightnessRampIncreaseMaxTimeMillis,
+ mBrightnessRampDecreaseMaxTimeMillis);
+ }
+ mHbmController.resetHbmData(info.width, info.height, token, info.uniqueId,
+ mDisplayDeviceConfig.getHighBrightnessModeData(),
+ new HighBrightnessModeController.HdrBrightnessDeviceConfig() {
+ @Override
+ public float getHdrBrightnessFromSdr(float sdrBrightness) {
+ return mDisplayDeviceConfig.getHdrBrightnessFromSdr(sdrBrightness);
+ }
+ });
+ mBrightnessThrottler.resetThrottlingData(
+ mDisplayDeviceConfig.getBrightnessThrottlingData(), mUniqueDisplayId);
+ }
+
+ private void sendUpdatePowerState() {
+ synchronized (mLock) {
+ sendUpdatePowerStateLocked();
+ }
+ }
+
+ @GuardedBy("mLock")
+ private void sendUpdatePowerStateLocked() {
+ if (!mStopped && !mPendingUpdatePowerStateLocked) {
+ mPendingUpdatePowerStateLocked = true;
+ Message msg = mHandler.obtainMessage(MSG_UPDATE_POWER_STATE);
+ mHandler.sendMessageAtTime(msg, mClock.uptimeMillis());
+ }
+ }
+
+ private void initialize(int displayState) {
+ mPowerState = mInjector.getDisplayPowerState(mBlanker,
+ mColorFadeEnabled ? new ColorFade(mDisplayId) : null, mDisplayId, displayState);
+
+ if (mColorFadeEnabled) {
+ mColorFadeOnAnimator = ObjectAnimator.ofFloat(
+ mPowerState, DisplayPowerState.COLOR_FADE_LEVEL, 0.0f, 1.0f);
+ mColorFadeOnAnimator.setDuration(COLOR_FADE_ON_ANIMATION_DURATION_MILLIS);
+ mColorFadeOnAnimator.addListener(mAnimatorListener);
+
+ mColorFadeOffAnimator = ObjectAnimator.ofFloat(
+ mPowerState, DisplayPowerState.COLOR_FADE_LEVEL, 1.0f, 0.0f);
+ mColorFadeOffAnimator.setDuration(COLOR_FADE_OFF_ANIMATION_DURATION_MILLIS);
+ mColorFadeOffAnimator.addListener(mAnimatorListener);
+ }
+
+ mScreenBrightnessRampAnimator = mInjector.getDualRampAnimator(mPowerState,
+ DisplayPowerState.SCREEN_BRIGHTNESS_FLOAT,
+ DisplayPowerState.SCREEN_SDR_BRIGHTNESS_FLOAT);
+ mScreenBrightnessRampAnimator.setAnimationTimeLimits(
+ mBrightnessRampIncreaseMaxTimeMillis,
+ mBrightnessRampDecreaseMaxTimeMillis);
+ mScreenBrightnessRampAnimator.setListener(mRampAnimatorListener);
+
+ noteScreenState(mPowerState.getScreenState());
+ noteScreenBrightness(mPowerState.getScreenBrightness());
+
+ // Initialize all of the brightness tracking state
+ final float brightness = convertToNits(mPowerState.getScreenBrightness());
+ if (brightness >= PowerManager.BRIGHTNESS_MIN) {
+ mBrightnessTracker.start(brightness);
+ }
+ mBrightnessSettingListener = brightnessValue -> {
+ Message msg = mHandler.obtainMessage(MSG_UPDATE_BRIGHTNESS, brightnessValue);
+ mHandler.sendMessage(msg);
+ };
+
+ mBrightnessSetting.registerListener(mBrightnessSettingListener);
+ mContext.getContentResolver().registerContentObserver(
+ Settings.System.getUriFor(Settings.System.SCREEN_BRIGHTNESS_FOR_VR_FLOAT),
+ false /*notifyForDescendants*/, mSettingsObserver, UserHandle.USER_ALL);
+ mContext.getContentResolver().registerContentObserver(
+ Settings.System.getUriFor(Settings.System.SCREEN_AUTO_BRIGHTNESS_ADJ),
+ false /*notifyForDescendants*/, mSettingsObserver, UserHandle.USER_ALL);
+ }
+
+ private void setUpAutoBrightness(Resources resources, Handler handler) {
+ if (!mUseSoftwareAutoBrightnessConfig) {
+ return;
+ }
+
+ final boolean isIdleScreenBrightnessEnabled = resources.getBoolean(
+ R.bool.config_enableIdleScreenBrightnessMode);
+ mInteractiveModeBrightnessMapper = BrightnessMappingStrategy.create(resources,
+ mDisplayDeviceConfig, mDisplayWhiteBalanceController);
+ if (isIdleScreenBrightnessEnabled) {
+ mIdleModeBrightnessMapper = BrightnessMappingStrategy.createForIdleMode(resources,
+ mDisplayDeviceConfig, mDisplayWhiteBalanceController);
+ }
+
+ if (mInteractiveModeBrightnessMapper != null) {
+ final float dozeScaleFactor = resources.getFraction(
+ R.fraction.config_screenAutoBrightnessDozeScaleFactor,
+ 1, 1);
+
+ int[] ambientBrighteningThresholds = resources.getIntArray(
+ R.array.config_ambientBrighteningThresholds);
+ int[] ambientDarkeningThresholds = resources.getIntArray(
+ R.array.config_ambientDarkeningThresholds);
+ int[] ambientThresholdLevels = resources.getIntArray(
+ R.array.config_ambientThresholdLevels);
+ float ambientDarkeningMinThreshold =
+ mDisplayDeviceConfig.getAmbientLuxDarkeningMinThreshold();
+ float ambientBrighteningMinThreshold =
+ mDisplayDeviceConfig.getAmbientLuxBrighteningMinThreshold();
+ HysteresisLevels ambientBrightnessThresholds = new HysteresisLevels(
+ ambientBrighteningThresholds, ambientDarkeningThresholds,
+ ambientThresholdLevels, ambientDarkeningMinThreshold,
+ ambientBrighteningMinThreshold);
+
+ int[] screenBrighteningThresholds = resources.getIntArray(
+ R.array.config_screenBrighteningThresholds);
+ int[] screenDarkeningThresholds = resources.getIntArray(
+ R.array.config_screenDarkeningThresholds);
+ int[] screenThresholdLevels = resources.getIntArray(
+ R.array.config_screenThresholdLevels);
+ float screenDarkeningMinThreshold =
+ mDisplayDeviceConfig.getScreenDarkeningMinThreshold();
+ float screenBrighteningMinThreshold =
+ mDisplayDeviceConfig.getScreenBrighteningMinThreshold();
+ HysteresisLevels screenBrightnessThresholds = new HysteresisLevels(
+ screenBrighteningThresholds, screenDarkeningThresholds, screenThresholdLevels,
+ screenDarkeningMinThreshold, screenBrighteningMinThreshold);
+
+ // Idle screen thresholds
+ float screenDarkeningMinThresholdIdle =
+ mDisplayDeviceConfig.getScreenDarkeningMinThresholdIdle();
+ float screenBrighteningMinThresholdIdle =
+ mDisplayDeviceConfig.getScreenBrighteningMinThresholdIdle();
+ HysteresisLevels screenBrightnessThresholdsIdle = new HysteresisLevels(
+ screenBrighteningThresholds, screenDarkeningThresholds, screenThresholdLevels,
+ screenDarkeningMinThresholdIdle, screenBrighteningMinThresholdIdle);
+
+ // Idle ambient thresholds
+ float ambientDarkeningMinThresholdIdle =
+ mDisplayDeviceConfig.getAmbientLuxDarkeningMinThresholdIdle();
+ float ambientBrighteningMinThresholdIdle =
+ mDisplayDeviceConfig.getAmbientLuxBrighteningMinThresholdIdle();
+ HysteresisLevels ambientBrightnessThresholdsIdle = new HysteresisLevels(
+ ambientBrighteningThresholds, ambientDarkeningThresholds,
+ ambientThresholdLevels, ambientDarkeningMinThresholdIdle,
+ ambientBrighteningMinThresholdIdle);
+
+ long brighteningLightDebounce = mDisplayDeviceConfig
+ .getAutoBrightnessBrighteningLightDebounce();
+ long darkeningLightDebounce = mDisplayDeviceConfig
+ .getAutoBrightnessDarkeningLightDebounce();
+ boolean autoBrightnessResetAmbientLuxAfterWarmUp = resources.getBoolean(
+ R.bool.config_autoBrightnessResetAmbientLuxAfterWarmUp);
+
+ int lightSensorWarmUpTimeConfig = resources.getInteger(
+ R.integer.config_lightSensorWarmupTime);
+ int lightSensorRate = resources.getInteger(
+ R.integer.config_autoBrightnessLightSensorRate);
+ int initialLightSensorRate = resources.getInteger(
+ R.integer.config_autoBrightnessInitialLightSensorRate);
+ if (initialLightSensorRate == -1) {
+ initialLightSensorRate = lightSensorRate;
+ } else if (initialLightSensorRate > lightSensorRate) {
+ Slog.w(mTag, "Expected config_autoBrightnessInitialLightSensorRate ("
+ + initialLightSensorRate + ") to be less than or equal to "
+ + "config_autoBrightnessLightSensorRate (" + lightSensorRate + ").");
+ }
+
+ loadAmbientLightSensor();
+ if (mBrightnessTracker != null) {
+ mBrightnessTracker.setLightSensor(mLightSensor);
+ }
+
+ if (mAutomaticBrightnessController != null) {
+ mAutomaticBrightnessController.stop();
+ }
+ mAutomaticBrightnessController = new AutomaticBrightnessController(this,
+ handler.getLooper(), mSensorManager, mLightSensor,
+ mInteractiveModeBrightnessMapper, lightSensorWarmUpTimeConfig,
+ PowerManager.BRIGHTNESS_MIN, PowerManager.BRIGHTNESS_MAX, dozeScaleFactor,
+ lightSensorRate, initialLightSensorRate, brighteningLightDebounce,
+ darkeningLightDebounce, autoBrightnessResetAmbientLuxAfterWarmUp,
+ ambientBrightnessThresholds, screenBrightnessThresholds,
+ ambientBrightnessThresholdsIdle, screenBrightnessThresholdsIdle, mContext,
+ mHbmController, mBrightnessThrottler, mIdleModeBrightnessMapper,
+ mDisplayDeviceConfig.getAmbientHorizonShort(),
+ mDisplayDeviceConfig.getAmbientHorizonLong());
+
+ mBrightnessEventRingBuffer =
+ new RingBuffer<>(BrightnessEvent.class, RINGBUFFER_MAX);
+ } else {
+ mUseSoftwareAutoBrightnessConfig = false;
+ }
+ }
+
+ private void loadBrightnessRampRates() {
+ mBrightnessRampRateFastDecrease = mDisplayDeviceConfig.getBrightnessRampFastDecrease();
+ mBrightnessRampRateFastIncrease = mDisplayDeviceConfig.getBrightnessRampFastIncrease();
+ mBrightnessRampRateSlowDecrease = mDisplayDeviceConfig.getBrightnessRampSlowDecrease();
+ mBrightnessRampRateSlowIncrease = mDisplayDeviceConfig.getBrightnessRampSlowIncrease();
+ mBrightnessRampDecreaseMaxTimeMillis =
+ mDisplayDeviceConfig.getBrightnessRampDecreaseMaxMillis();
+ mBrightnessRampIncreaseMaxTimeMillis =
+ mDisplayDeviceConfig.getBrightnessRampIncreaseMaxMillis();
+ }
+
+ private void loadNitsRange(Resources resources) {
+ if (mDisplayDeviceConfig != null && mDisplayDeviceConfig.getNits() != null) {
+ mNitsRange = mDisplayDeviceConfig.getNits();
+ } else {
+ Slog.w(mTag, "Screen brightness nits configuration is unavailable; falling back");
+ mNitsRange = BrightnessMappingStrategy.getFloatArray(resources
+ .obtainTypedArray(R.array.config_screenBrightnessNits));
+ }
+ }
+
+ private void reloadReduceBrightColours() {
+ if (mCdsi != null && mCdsi.isReduceBrightColorsActivated()) {
+ applyReduceBrightColorsSplineAdjustment();
+ }
+ }
+
+ @Override
+ public void setAutomaticScreenBrightnessMode(boolean isIdle) {
+ if (mAutomaticBrightnessController != null) {
+ if (isIdle) {
+ mAutomaticBrightnessController.switchToIdleMode();
+ } else {
+ mAutomaticBrightnessController.switchToInteractiveScreenBrightnessMode();
+ }
+ }
+ if (mDisplayWhiteBalanceController != null) {
+ mDisplayWhiteBalanceController.setStrongModeEnabled(isIdle);
+ }
+ }
+
+ private final Animator.AnimatorListener mAnimatorListener = new Animator.AnimatorListener() {
+ @Override
+ public void onAnimationStart(Animator animation) {
+ }
+
+ @Override
+ public void onAnimationEnd(Animator animation) {
+ sendUpdatePowerState();
+ }
+
+ @Override
+ public void onAnimationRepeat(Animator animation) {
+ }
+
+ @Override
+ public void onAnimationCancel(Animator animation) {
+ }
+ };
+
+ private final RampAnimator.Listener mRampAnimatorListener = new RampAnimator.Listener() {
+ @Override
+ public void onAnimationEnd() {
+ sendUpdatePowerState();
+ Message msg = mHandler.obtainMessage(MSG_BRIGHTNESS_RAMP_DONE);
+ mHandler.sendMessage(msg);
+ }
+ };
+
+ /** Clean up all resources that are accessed via the {@link #mHandler} thread. */
+ private void cleanupHandlerThreadAfterStop() {
+ setProximitySensorEnabled(false);
+ mHbmController.stop();
+ mBrightnessThrottler.stop();
+ mHandler.removeCallbacksAndMessages(null);
+
+ // Release any outstanding wakelocks we're still holding because of pending messages.
+ if (mUnfinishedBusiness) {
+ mCallbacks.releaseSuspendBlocker(mSuspendBlockerIdUnfinishedBusiness);
+ mUnfinishedBusiness = false;
+ }
+ if (mOnStateChangedPending) {
+ mCallbacks.releaseSuspendBlocker(mSuspendBlockerIdOnStateChanged);
+ mOnStateChangedPending = false;
+ }
+ for (int i = 0; i < mOnProximityPositiveMessages; i++) {
+ mCallbacks.releaseSuspendBlocker(mSuspendBlockerIdProxPositive);
+ }
+ mOnProximityPositiveMessages = 0;
+ for (int i = 0; i < mOnProximityNegativeMessages; i++) {
+ mCallbacks.releaseSuspendBlocker(mSuspendBlockerIdProxNegative);
+ }
+ mOnProximityNegativeMessages = 0;
+
+ final float brightness = mPowerState != null
+ ? mPowerState.getScreenBrightness()
+ : PowerManager.BRIGHTNESS_MIN;
+ reportStats(brightness);
+
+ if (mPowerState != null) {
+ mPowerState.stop();
+ mPowerState = null;
+ }
+ }
+
+ private void updatePowerState() {
+ if (DEBUG) {
+ Trace.beginSection("DisplayPowerController#updatePowerState");
+ }
+ updatePowerStateInternal();
+ if (DEBUG) {
+ Trace.endSection();
+ }
+ }
+
+ private void updatePowerStateInternal() {
+ // Update the power state request.
+ final boolean mustNotify;
+ final int previousPolicy;
+ boolean mustInitialize = false;
+ int brightnessAdjustmentFlags = 0;
+ mBrightnessReasonTemp.set(null);
+ mTempBrightnessEvent.reset();
+ synchronized (mLock) {
+ if (mStopped) {
+ return;
+ }
+ mPendingUpdatePowerStateLocked = false;
+ if (mPendingRequestLocked == null) {
+ return; // wait until first actual power request
+ }
+
+ if (mPowerRequest == null) {
+ mPowerRequest = new DisplayPowerRequest(mPendingRequestLocked);
+ updatePendingProximityRequestsLocked();
+ mPendingRequestChangedLocked = false;
+ mustInitialize = true;
+ // Assume we're on and bright until told otherwise, since that's the state we turn
+ // on in.
+ previousPolicy = DisplayPowerRequest.POLICY_BRIGHT;
+ } else if (mPendingRequestChangedLocked) {
+ previousPolicy = mPowerRequest.policy;
+ mPowerRequest.copyFrom(mPendingRequestLocked);
+ updatePendingProximityRequestsLocked();
+ mPendingRequestChangedLocked = false;
+ mDisplayReadyLocked = false;
+ } else {
+ previousPolicy = mPowerRequest.policy;
+ }
+
+ mustNotify = !mDisplayReadyLocked;
+ }
+
+ // Compute the basic display state using the policy.
+ // We might override this below based on other factors.
+ // Initialise brightness as invalid.
+ int state;
+ float brightnessState = PowerManager.BRIGHTNESS_INVALID_FLOAT;
+ boolean performScreenOffTransition = false;
+ switch (mPowerRequest.policy) {
+ case DisplayPowerRequest.POLICY_OFF:
+ state = Display.STATE_OFF;
+ performScreenOffTransition = true;
+ break;
+ case DisplayPowerRequest.POLICY_DOZE:
+ if (mPowerRequest.dozeScreenState != Display.STATE_UNKNOWN) {
+ state = mPowerRequest.dozeScreenState;
+ } else {
+ state = Display.STATE_DOZE;
+ }
+ if (!mAllowAutoBrightnessWhileDozingConfig) {
+ brightnessState = mPowerRequest.dozeScreenBrightness;
+ mBrightnessReasonTemp.setReason(BrightnessReason.REASON_DOZE);
+ }
+ break;
+ case DisplayPowerRequest.POLICY_VR:
+ state = Display.STATE_VR;
+ break;
+ case DisplayPowerRequest.POLICY_DIM:
+ case DisplayPowerRequest.POLICY_BRIGHT:
+ default:
+ state = Display.STATE_ON;
+ break;
+ }
+ assert (state != Display.STATE_UNKNOWN);
+
+ boolean skipRampBecauseOfProximityChangeToNegative = false;
+ // Apply the proximity sensor.
+ if (mProximitySensor != null) {
+ if (mPowerRequest.useProximitySensor && state != Display.STATE_OFF) {
+ // At this point the policy says that the screen should be on, but we've been
+ // asked to listen to the prox sensor to adjust the display state, so lets make
+ // sure the sensor is on.
+ setProximitySensorEnabled(true);
+ if (!mScreenOffBecauseOfProximity
+ && mProximity == PROXIMITY_POSITIVE
+ && !mIgnoreProximityUntilChanged) {
+ // Prox sensor already reporting "near" so we should turn off the screen.
+ // Also checked that we aren't currently set to ignore the proximity sensor
+ // temporarily.
+ mScreenOffBecauseOfProximity = true;
+ sendOnProximityPositiveWithWakelock();
+ }
+ } else if (mWaitingForNegativeProximity
+ && mScreenOffBecauseOfProximity
+ && mProximity == PROXIMITY_POSITIVE
+ && state != Display.STATE_OFF) {
+ // The policy says that we should have the screen on, but it's off due to the prox
+ // and we've been asked to wait until the screen is far from the user to turn it
+ // back on. Let keep the prox sensor on so we can tell when it's far again.
+ setProximitySensorEnabled(true);
+ } else {
+ // We haven't been asked to use the prox sensor and we're not waiting on the screen
+ // to turn back on...so lets shut down the prox sensor.
+ setProximitySensorEnabled(false);
+ mWaitingForNegativeProximity = false;
+ }
+
+ if (mScreenOffBecauseOfProximity
+ && (mProximity != PROXIMITY_POSITIVE || mIgnoreProximityUntilChanged)) {
+ // The screen *was* off due to prox being near, but now it's "far" so lets turn
+ // the screen back on. Also turn it back on if we've been asked to ignore the
+ // prox sensor temporarily.
+ mScreenOffBecauseOfProximity = false;
+ skipRampBecauseOfProximityChangeToNegative = true;
+ sendOnProximityNegativeWithWakelock();
+ }
+ } else {
+ mWaitingForNegativeProximity = false;
+ mIgnoreProximityUntilChanged = false;
+ }
+
+ if (!mLogicalDisplay.isEnabled()
+ || mLogicalDisplay.getPhase() == LogicalDisplay.DISPLAY_PHASE_LAYOUT_TRANSITION
+ || mScreenOffBecauseOfProximity) {
+ state = Display.STATE_OFF;
+ }
+
+ // Initialize things the first time the power state is changed.
+ if (mustInitialize) {
+ initialize(state);
+ }
+
+ // Animate the screen state change unless already animating.
+ // The transition may be deferred, so after this point we will use the
+ // actual state instead of the desired one.
+ final int oldState = mPowerState.getScreenState();
+ animateScreenStateChange(state, performScreenOffTransition);
+ state = mPowerState.getScreenState();
+
+ if (state == Display.STATE_OFF) {
+ brightnessState = PowerManager.BRIGHTNESS_OFF_FLOAT;
+ mBrightnessReasonTemp.setReason(BrightnessReason.REASON_SCREEN_OFF);
+ }
+
+ // Always use the VR brightness when in the VR state.
+ if (state == Display.STATE_VR) {
+ brightnessState = mScreenBrightnessForVr;
+ mBrightnessReasonTemp.setReason(BrightnessReason.REASON_VR);
+ }
+
+ if ((Float.isNaN(brightnessState))
+ && isValidBrightnessValue(mPowerRequest.screenBrightnessOverride)) {
+ brightnessState = mPowerRequest.screenBrightnessOverride;
+ mBrightnessReasonTemp.setReason(BrightnessReason.REASON_OVERRIDE);
+ mAppliedScreenBrightnessOverride = true;
+ } else {
+ mAppliedScreenBrightnessOverride = false;
+ }
+
+ final boolean autoBrightnessEnabledInDoze =
+ mAllowAutoBrightnessWhileDozingConfig && Display.isDozeState(state);
+ final boolean autoBrightnessEnabled = mPowerRequest.useAutoBrightness
+ && (state == Display.STATE_ON || autoBrightnessEnabledInDoze)
+ && Float.isNaN(brightnessState)
+ && mAutomaticBrightnessController != null;
+ final boolean autoBrightnessDisabledDueToDisplayOff = mPowerRequest.useAutoBrightness
+ && !(state == Display.STATE_ON || autoBrightnessEnabledInDoze);
+ final int autoBrightnessState = autoBrightnessEnabled
+ ? AutomaticBrightnessController.AUTO_BRIGHTNESS_ENABLED
+ : autoBrightnessDisabledDueToDisplayOff
+ ? AutomaticBrightnessController.AUTO_BRIGHTNESS_OFF_DUE_TO_DISPLAY_STATE
+ : AutomaticBrightnessController.AUTO_BRIGHTNESS_DISABLED;
+
+ final boolean userSetBrightnessChanged = updateUserSetScreenBrightness();
+
+ // Use the temporary screen brightness if there isn't an override, either from
+ // WindowManager or based on the display state.
+ if (isValidBrightnessValue(mTemporaryScreenBrightness)) {
+ brightnessState = mTemporaryScreenBrightness;
+ mAppliedTemporaryBrightness = true;
+ mBrightnessReasonTemp.setReason(BrightnessReason.REASON_TEMPORARY);
+ } else {
+ mAppliedTemporaryBrightness = false;
+ }
+
+ final boolean autoBrightnessAdjustmentChanged = updateAutoBrightnessAdjustment();
+
+ // Use the autobrightness adjustment override if set.
+ final float autoBrightnessAdjustment;
+ if (!Float.isNaN(mTemporaryAutoBrightnessAdjustment)) {
+ autoBrightnessAdjustment = mTemporaryAutoBrightnessAdjustment;
+ brightnessAdjustmentFlags = BrightnessReason.ADJUSTMENT_AUTO_TEMP;
+ mAppliedTemporaryAutoBrightnessAdjustment = true;
+ } else {
+ autoBrightnessAdjustment = mAutoBrightnessAdjustment;
+ brightnessAdjustmentFlags = BrightnessReason.ADJUSTMENT_AUTO;
+ mAppliedTemporaryAutoBrightnessAdjustment = false;
+ }
+ // Apply brightness boost.
+ // We do this here after deciding whether auto-brightness is enabled so that we don't
+ // disable the light sensor during this temporary state. That way when boost ends we will
+ // be able to resume normal auto-brightness behavior without any delay.
+ if (mPowerRequest.boostScreenBrightness
+ && brightnessState != PowerManager.BRIGHTNESS_OFF_FLOAT) {
+ brightnessState = PowerManager.BRIGHTNESS_MAX;
+ mBrightnessReasonTemp.setReason(BrightnessReason.REASON_BOOST);
+ mAppliedBrightnessBoost = true;
+ } else {
+ mAppliedBrightnessBoost = false;
+ }
+
+ // If the brightness is already set then it's been overridden by something other than the
+ // user, or is a temporary adjustment.
+ boolean userInitiatedChange = (Float.isNaN(brightnessState))
+ && (autoBrightnessAdjustmentChanged || userSetBrightnessChanged);
+ boolean hadUserBrightnessPoint = false;
+ // Configure auto-brightness.
+ if (mAutomaticBrightnessController != null) {
+ hadUserBrightnessPoint = mAutomaticBrightnessController.hasUserDataPoints();
+ mAutomaticBrightnessController.configure(autoBrightnessState,
+ mBrightnessConfiguration,
+ mLastUserSetScreenBrightness,
+ userSetBrightnessChanged, autoBrightnessAdjustment,
+ autoBrightnessAdjustmentChanged, mPowerRequest.policy);
+ }
+
+ if (mBrightnessTracker != null) {
+ mBrightnessTracker.setBrightnessConfiguration(mBrightnessConfiguration);
+ }
+
+ boolean updateScreenBrightnessSetting = false;
+
+ // Apply auto-brightness.
+ boolean slowChange = false;
+ if (Float.isNaN(brightnessState)) {
+ float newAutoBrightnessAdjustment = autoBrightnessAdjustment;
+ if (autoBrightnessEnabled) {
+ brightnessState = mAutomaticBrightnessController.getAutomaticScreenBrightness(
+ mTempBrightnessEvent);
+ newAutoBrightnessAdjustment =
+ mAutomaticBrightnessController.getAutomaticScreenBrightnessAdjustment();
+ }
+ if (isValidBrightnessValue(brightnessState)
+ || brightnessState == PowerManager.BRIGHTNESS_OFF_FLOAT) {
+ // Use current auto-brightness value and slowly adjust to changes.
+ brightnessState = clampScreenBrightness(brightnessState);
+ if (mAppliedAutoBrightness && !autoBrightnessAdjustmentChanged) {
+ slowChange = true; // slowly adapt to auto-brightness
+ }
+ updateScreenBrightnessSetting = mCurrentScreenBrightnessSetting != brightnessState;
+ mAppliedAutoBrightness = true;
+ mBrightnessReasonTemp.setReason(BrightnessReason.REASON_AUTOMATIC);
+ } else {
+ mAppliedAutoBrightness = false;
+ }
+ if (autoBrightnessAdjustment != newAutoBrightnessAdjustment) {
+ // If the autobrightness controller has decided to change the adjustment value
+ // used, make sure that's reflected in settings.
+ putAutoBrightnessAdjustmentSetting(newAutoBrightnessAdjustment);
+ } else {
+ // Adjustment values resulted in no change
+ brightnessAdjustmentFlags = 0;
+ }
+ } else {
+ // Any non-auto-brightness values such as override or temporary should still be subject
+ // to clamping so that they don't go beyond the current max as specified by HBM
+ // Controller.
+ brightnessState = clampScreenBrightness(brightnessState);
+ mAppliedAutoBrightness = false;
+ brightnessAdjustmentFlags = 0;
+ }
+
+ // Use default brightness when dozing unless overridden.
+ if ((Float.isNaN(brightnessState))
+ && Display.isDozeState(state)) {
+ brightnessState = clampScreenBrightness(mScreenBrightnessDozeConfig);
+ mBrightnessReasonTemp.setReason(BrightnessReason.REASON_DOZE_DEFAULT);
+ }
+
+ // Apply manual brightness.
+ if (Float.isNaN(brightnessState)) {
+ brightnessState = clampScreenBrightness(mCurrentScreenBrightnessSetting);
+ if (brightnessState != mCurrentScreenBrightnessSetting) {
+ // The manually chosen screen brightness is outside of the currently allowed
+ // range (i.e., high-brightness-mode), make sure we tell the rest of the system
+ // by updating the setting.
+ updateScreenBrightnessSetting = true;
+ }
+ mBrightnessReasonTemp.setReason(BrightnessReason.REASON_MANUAL);
+ }
+
+ // Now that a desired brightness has been calculated, apply brightness throttling. The
+ // dimming and low power transformations that follow can only dim brightness further.
+ //
+ // We didn't do this earlier through brightness clamping because we need to know both
+ // unthrottled (unclamped/ideal) and throttled brightness levels for subsequent operations.
+ // Note throttling effectively changes the allowed brightness range, so, similarly to HBM,
+ // we broadcast this change through setting.
+ final float unthrottledBrightnessState = brightnessState;
+ if (mBrightnessThrottler.isThrottled()) {
+ mTempBrightnessEvent.setThermalMax(mBrightnessThrottler.getBrightnessCap());
+ brightnessState = Math.min(brightnessState, mBrightnessThrottler.getBrightnessCap());
+ mBrightnessReasonTemp.addModifier(BrightnessReason.MODIFIER_THROTTLED);
+ if (!mAppliedThrottling) {
+ // Brightness throttling is needed, so do so quickly.
+ // Later, when throttling is removed, we let other mechanisms decide on speed.
+ slowChange = false;
+ }
+ mAppliedThrottling = true;
+ } else if (mAppliedThrottling) {
+ mAppliedThrottling = false;
+ }
+
+ if (updateScreenBrightnessSetting) {
+ // Tell the rest of the system about the new brightness in case we had to change it
+ // for things like auto-brightness or high-brightness-mode. Note that we do this
+ // before applying the low power or dim transformations so that the slider
+ // accurately represents the full possible range, even if they range changes what
+ // it means in absolute terms.
+ updateScreenBrightnessSetting(brightnessState);
+ }
+
+ // Apply dimming by at least some minimum amount when user activity
+ // timeout is about to expire.
+ if (mPowerRequest.policy == DisplayPowerRequest.POLICY_DIM) {
+ if (brightnessState > PowerManager.BRIGHTNESS_MIN) {
+ brightnessState = Math.max(
+ Math.min(brightnessState - mScreenBrightnessMinimumDimAmount,
+ mScreenBrightnessDimConfig),
+ PowerManager.BRIGHTNESS_MIN);
+ mBrightnessReasonTemp.addModifier(BrightnessReason.MODIFIER_DIMMED);
+ }
+ if (!mAppliedDimming) {
+ slowChange = false;
+ }
+ mAppliedDimming = true;
+ } else if (mAppliedDimming) {
+ slowChange = false;
+ mAppliedDimming = false;
+ }
+ // If low power mode is enabled, scale brightness by screenLowPowerBrightnessFactor
+ // as long as it is above the minimum threshold.
+ if (mPowerRequest.lowPowerMode) {
+ if (brightnessState > PowerManager.BRIGHTNESS_MIN) {
+ final float brightnessFactor =
+ Math.min(mPowerRequest.screenLowPowerBrightnessFactor, 1);
+ final float lowPowerBrightnessFloat = (brightnessState * brightnessFactor);
+ brightnessState = Math.max(lowPowerBrightnessFloat, PowerManager.BRIGHTNESS_MIN);
+ mBrightnessReasonTemp.addModifier(BrightnessReason.MODIFIER_LOW_POWER);
+ }
+ if (!mAppliedLowPower) {
+ slowChange = false;
+ }
+ mAppliedLowPower = true;
+ } else if (mAppliedLowPower) {
+ slowChange = false;
+ mAppliedLowPower = false;
+ }
+
+ // The current brightness to use has been calculated at this point, and HbmController should
+ // be notified so that it can accurately calculate HDR or HBM levels. We specifically do it
+ // here instead of having HbmController listen to the brightness setting because certain
+ // brightness sources (such as an app override) are not saved to the setting, but should be
+ // reflected in HBM calculations.
+ mHbmController.onBrightnessChanged(brightnessState, unthrottledBrightnessState,
+ mBrightnessThrottler.getBrightnessMaxReason());
+
+ // Animate the screen brightness when the screen is on or dozing.
+ // Skip the animation when the screen is off or suspended or transition to/from VR.
+ boolean brightnessAdjusted = false;
+ final boolean brightnessIsTemporary =
+ mAppliedTemporaryBrightness || mAppliedTemporaryAutoBrightnessAdjustment;
+ if (!mPendingScreenOff) {
+ if (mSkipScreenOnBrightnessRamp) {
+ if (state == Display.STATE_ON) {
+ if (mSkipRampState == RAMP_STATE_SKIP_NONE && mDozing) {
+ mInitialAutoBrightness = brightnessState;
+ mSkipRampState = RAMP_STATE_SKIP_INITIAL;
+ } else if (mSkipRampState == RAMP_STATE_SKIP_INITIAL
+ && mUseSoftwareAutoBrightnessConfig
+ && !BrightnessSynchronizer.floatEquals(brightnessState,
+ mInitialAutoBrightness)) {
+ mSkipRampState = RAMP_STATE_SKIP_AUTOBRIGHT;
+ } else if (mSkipRampState == RAMP_STATE_SKIP_AUTOBRIGHT) {
+ mSkipRampState = RAMP_STATE_SKIP_NONE;
+ }
+ } else {
+ mSkipRampState = RAMP_STATE_SKIP_NONE;
+ }
+ }
+
+ final boolean wasOrWillBeInVr =
+ (state == Display.STATE_VR || oldState == Display.STATE_VR);
+ final boolean initialRampSkip = (state == Display.STATE_ON && mSkipRampState
+ != RAMP_STATE_SKIP_NONE) || skipRampBecauseOfProximityChangeToNegative;
+ // While dozing, sometimes the brightness is split into buckets. Rather than animating
+ // through the buckets, which is unlikely to be smooth in the first place, just jump
+ // right to the suggested brightness.
+ final boolean hasBrightnessBuckets =
+ Display.isDozeState(state) && mBrightnessBucketsInDozeConfig;
+ // If the color fade is totally covering the screen then we can change the backlight
+ // level without it being a noticeable jump since any actual content isn't yet visible.
+ final boolean isDisplayContentVisible =
+ mColorFadeEnabled && mPowerState.getColorFadeLevel() == 1.0f;
+ // We only want to animate the brightness if it is between 0.0f and 1.0f.
+ // brightnessState can contain the values -1.0f and NaN, which we do not want to
+ // animate to. To avoid this, we check the value first.
+ // If the brightnessState is off (-1.0f) we still want to animate to the minimum
+ // brightness (0.0f) to accommodate for LED displays, which can appear bright to the
+ // user even when the display is all black. We also clamp here in case some
+ // transformations to the brightness have pushed it outside of the currently
+ // allowed range.
+ float animateValue = clampScreenBrightness(brightnessState);
+
+ // If there are any HDR layers on the screen, we have a special brightness value that we
+ // use instead. We still preserve the calculated brightness for Standard Dynamic Range
+ // (SDR) layers, but the main brightness value will be the one for HDR.
+ float sdrAnimateValue = animateValue;
+ // TODO(b/216365040): The decision to prevent HBM for HDR in low power mode should be
+ // done in HighBrightnessModeController.
+ if (mHbmController.getHighBrightnessMode() == BrightnessInfo.HIGH_BRIGHTNESS_MODE_HDR
+ && ((mBrightnessReason.getModifier() & BrightnessReason.MODIFIER_DIMMED) == 0
+ || (mBrightnessReason.getModifier() & BrightnessReason.MODIFIER_LOW_POWER)
+ == 0)) {
+ // We want to scale HDR brightness level with the SDR level
+ animateValue = mHbmController.getHdrBrightnessValue();
+ }
+
+ final float currentBrightness = mPowerState.getScreenBrightness();
+ final float currentSdrBrightness = mPowerState.getSdrScreenBrightness();
+ if (isValidBrightnessValue(animateValue)
+ && (animateValue != currentBrightness
+ || sdrAnimateValue != currentSdrBrightness)) {
+ if (initialRampSkip || hasBrightnessBuckets
+ || wasOrWillBeInVr || !isDisplayContentVisible || brightnessIsTemporary) {
+ animateScreenBrightness(animateValue, sdrAnimateValue,
+ SCREEN_ANIMATION_RATE_MINIMUM);
+ } else {
+ boolean isIncreasing = animateValue > currentBrightness;
+ final float rampSpeed;
+ if (isIncreasing && slowChange) {
+ rampSpeed = mBrightnessRampRateSlowIncrease;
+ } else if (isIncreasing && !slowChange) {
+ rampSpeed = mBrightnessRampRateFastIncrease;
+ } else if (!isIncreasing && slowChange) {
+ rampSpeed = mBrightnessRampRateSlowDecrease;
+ } else {
+ rampSpeed = mBrightnessRampRateFastDecrease;
+ }
+ animateScreenBrightness(animateValue, sdrAnimateValue, rampSpeed);
+ }
+ }
+
+ // Report brightness to brightnesstracker:
+ // If brightness is not temporary (ie the slider has been released)
+ // AND if we are not in idle screen brightness mode.
+ if (!brightnessIsTemporary
+ && (mAutomaticBrightnessController != null
+ && !mAutomaticBrightnessController.isInIdleMode())) {
+ if (userInitiatedChange && (mAutomaticBrightnessController == null
+ || !mAutomaticBrightnessController.hasValidAmbientLux())) {
+ // If we don't have a valid lux reading we can't report a valid
+ // slider event so notify as if the system changed the brightness.
+ userInitiatedChange = false;
+ }
+ notifyBrightnessTrackerChanged(brightnessState, userInitiatedChange,
+ hadUserBrightnessPoint);
+ }
+
+ // We save the brightness info *after* the brightness setting has been changed and
+ // adjustments made so that the brightness info reflects the latest value.
+ brightnessAdjusted = saveBrightnessInfo(getScreenBrightnessSetting(), animateValue);
+ } else {
+ brightnessAdjusted = saveBrightnessInfo(getScreenBrightnessSetting());
+ }
+
+ // Only notify if the brightness adjustment is not temporary (i.e. slider has been released)
+ if (brightnessAdjusted && !brightnessIsTemporary) {
+ postBrightnessChangeRunnable();
+ }
+
+ // Log any changes to what is currently driving the brightness setting.
+ if (!mBrightnessReasonTemp.equals(mBrightnessReason) || brightnessAdjustmentFlags != 0) {
+ Slog.v(mTag, "Brightness [" + brightnessState + "] reason changing to: '"
+ + mBrightnessReasonTemp.toString(brightnessAdjustmentFlags)
+ + "', previous reason: '" + mBrightnessReason + "'.");
+ mBrightnessReason.set(mBrightnessReasonTemp);
+ } else if (mBrightnessReasonTemp.getReason() == BrightnessReason.REASON_MANUAL
+ && userSetBrightnessChanged) {
+ Slog.v(mTag, "Brightness [" + brightnessState + "] manual adjustment.");
+ }
+
+
+ // Log brightness events when a detail of significance has changed. Generally this is the
+ // brightness itself changing, but also includes data like HBM cap, thermal throttling
+ // brightness cap, RBC state, etc.
+ mTempBrightnessEvent.setTime(System.currentTimeMillis());
+ mTempBrightnessEvent.setBrightness(brightnessState);
+ mTempBrightnessEvent.setPhysicalDisplayId(mUniqueDisplayId);
+ mTempBrightnessEvent.setReason(mBrightnessReason);
+ mTempBrightnessEvent.setHbmMax(mHbmController.getCurrentBrightnessMax());
+ mTempBrightnessEvent.setHbmMode(mHbmController.getHighBrightnessMode());
+ mTempBrightnessEvent.setFlags(mTempBrightnessEvent.getFlags()
+ | (mIsRbcActive ? BrightnessEvent.FLAG_RBC : 0)
+ | (mPowerRequest.lowPowerMode ? BrightnessEvent.FLAG_LOW_POWER_MODE : 0));
+ mTempBrightnessEvent.setRbcStrength(mCdsi != null
+ ? mCdsi.getReduceBrightColorsStrength() : -1);
+ mTempBrightnessEvent.setPowerFactor(mPowerRequest.screenLowPowerBrightnessFactor);
+ // Temporary is what we use during slider interactions. We avoid logging those so that
+ // we don't spam logcat when the slider is being used.
+ boolean tempToTempTransition =
+ mTempBrightnessEvent.getReason().getReason() == BrightnessReason.REASON_TEMPORARY
+ && mLastBrightnessEvent.getReason().getReason()
+ == BrightnessReason.REASON_TEMPORARY;
+ if ((!mTempBrightnessEvent.equalsMainData(mLastBrightnessEvent) && !tempToTempTransition)
+ || brightnessAdjustmentFlags != 0) {
+ float lastBrightness = mLastBrightnessEvent.getBrightness();
+ mTempBrightnessEvent.setInitialBrightness(lastBrightness);
+ mTempBrightnessEvent.setFastAmbientLux(
+ mAutomaticBrightnessController == null
+ ? -1f : mAutomaticBrightnessController.getFastAmbientLux());
+ mTempBrightnessEvent.setSlowAmbientLux(
+ mAutomaticBrightnessController == null
+ ? -1f : mAutomaticBrightnessController.getSlowAmbientLux());
+ mTempBrightnessEvent.setAutomaticBrightnessEnabled(mPowerRequest.useAutoBrightness);
+ mLastBrightnessEvent.copyFrom(mTempBrightnessEvent);
+ BrightnessEvent newEvent = new BrightnessEvent(mTempBrightnessEvent);
+ // Adjustment flags (and user-set flag) only get added after the equality checks since
+ // they are transient.
+ newEvent.setAdjustmentFlags(brightnessAdjustmentFlags);
+ newEvent.setFlags(newEvent.getFlags() | (userSetBrightnessChanged
+ ? BrightnessEvent.FLAG_USER_SET : 0));
+ Slog.i(mTag, newEvent.toString(/* includeTime= */ false));
+
+ if (userSetBrightnessChanged) {
+ logManualBrightnessEvent(newEvent);
+ }
+ if (mBrightnessEventRingBuffer != null) {
+ mBrightnessEventRingBuffer.append(newEvent);
+ }
+ }
+
+ // Update display white-balance.
+ if (mDisplayWhiteBalanceController != null) {
+ if (state == Display.STATE_ON && mDisplayWhiteBalanceSettings.isEnabled()) {
+ mDisplayWhiteBalanceController.setEnabled(true);
+ mDisplayWhiteBalanceController.updateDisplayColorTemperature();
+ } else {
+ mDisplayWhiteBalanceController.setEnabled(false);
+ }
+ }
+
+ // Determine whether the display is ready for use in the newly requested state.
+ // Note that we do not wait for the brightness ramp animation to complete before
+ // reporting the display is ready because we only need to ensure the screen is in the
+ // right power state even as it continues to converge on the desired brightness.
+ final boolean ready = mPendingScreenOnUnblocker == null
+ && (!mColorFadeEnabled || (!mColorFadeOnAnimator.isStarted()
+ && !mColorFadeOffAnimator.isStarted()))
+ && mPowerState.waitUntilClean(mCleanListener);
+ final boolean finished = ready
+ && !mScreenBrightnessRampAnimator.isAnimating();
+
+ // Notify policy about screen turned on.
+ if (ready && state != Display.STATE_OFF
+ && mReportedScreenStateToPolicy == REPORTED_TO_POLICY_SCREEN_TURNING_ON) {
+ setReportedScreenState(REPORTED_TO_POLICY_SCREEN_ON);
+ mWindowManagerPolicy.screenTurnedOn(mDisplayId);
+ }
+
+ // Grab a wake lock if we have unfinished business.
+ if (!finished && !mUnfinishedBusiness) {
+ if (DEBUG) {
+ Slog.d(mTag, "Unfinished business...");
+ }
+ mCallbacks.acquireSuspendBlocker(mSuspendBlockerIdUnfinishedBusiness);
+ mUnfinishedBusiness = true;
+ }
+
+ // Notify the power manager when ready.
+ if (ready && mustNotify) {
+ // Send state change.
+ synchronized (mLock) {
+ if (!mPendingRequestChangedLocked) {
+ mDisplayReadyLocked = true;
+
+ if (DEBUG) {
+ Slog.d(mTag, "Display ready!");
+ }
+ }
+ }
+ sendOnStateChangedWithWakelock();
+ }
+
+ // Release the wake lock when we have no unfinished business.
+ if (finished && mUnfinishedBusiness) {
+ if (DEBUG) {
+ Slog.d(mTag, "Finished business...");
+ }
+ mUnfinishedBusiness = false;
+ mCallbacks.releaseSuspendBlocker(mSuspendBlockerIdUnfinishedBusiness);
+ }
+
+ // Record if dozing for future comparison.
+ mDozing = state != Display.STATE_ON;
+
+ if (previousPolicy != mPowerRequest.policy) {
+ logDisplayPolicyChanged(mPowerRequest.policy);
+ }
+ }
+
+ @Override
+ public void updateBrightness() {
+ sendUpdatePowerState();
+ }
+
+ /**
+ * Ignores the proximity sensor until the sensor state changes, but only if the sensor is
+ * currently enabled and forcing the screen to be dark.
+ */
+ @Override
+ public void ignoreProximitySensorUntilChanged() {
+ mHandler.sendEmptyMessage(MSG_IGNORE_PROXIMITY);
+ }
+
+ @Override
+ public void setBrightnessConfiguration(BrightnessConfiguration c) {
+ Message msg = mHandler.obtainMessage(MSG_CONFIGURE_BRIGHTNESS, c);
+ msg.sendToTarget();
+ }
+
+ @Override
+ public void setTemporaryBrightness(float brightness) {
+ Message msg = mHandler.obtainMessage(MSG_SET_TEMPORARY_BRIGHTNESS,
+ Float.floatToIntBits(brightness), 0 /*unused*/);
+ msg.sendToTarget();
+ }
+
+ @Override
+ public void setTemporaryAutoBrightnessAdjustment(float adjustment) {
+ Message msg = mHandler.obtainMessage(MSG_SET_TEMPORARY_AUTO_BRIGHTNESS_ADJUSTMENT,
+ Float.floatToIntBits(adjustment), 0 /*unused*/);
+ msg.sendToTarget();
+ }
+
+ @Override
+ public BrightnessInfo getBrightnessInfo() {
+ synchronized (mCachedBrightnessInfo) {
+ return new BrightnessInfo(
+ mCachedBrightnessInfo.brightness.value,
+ mCachedBrightnessInfo.adjustedBrightness.value,
+ mCachedBrightnessInfo.brightnessMin.value,
+ mCachedBrightnessInfo.brightnessMax.value,
+ mCachedBrightnessInfo.hbmMode.value,
+ mCachedBrightnessInfo.hbmTransitionPoint.value,
+ mCachedBrightnessInfo.brightnessMaxReason.value);
+ }
+ }
+
+ private boolean saveBrightnessInfo(float brightness) {
+ return saveBrightnessInfo(brightness, brightness);
+ }
+
+ private boolean saveBrightnessInfo(float brightness, float adjustedBrightness) {
+ synchronized (mCachedBrightnessInfo) {
+ final float minBrightness = Math.min(mHbmController.getCurrentBrightnessMin(),
+ mBrightnessThrottler.getBrightnessCap());
+ final float maxBrightness = Math.min(mHbmController.getCurrentBrightnessMax(),
+ mBrightnessThrottler.getBrightnessCap());
+ boolean changed = false;
+
+ changed |=
+ mCachedBrightnessInfo.checkAndSetFloat(mCachedBrightnessInfo.brightness,
+ brightness);
+ changed |=
+ mCachedBrightnessInfo.checkAndSetFloat(mCachedBrightnessInfo.adjustedBrightness,
+ adjustedBrightness);
+ changed |=
+ mCachedBrightnessInfo.checkAndSetFloat(mCachedBrightnessInfo.brightnessMin,
+ minBrightness);
+ changed |=
+ mCachedBrightnessInfo.checkAndSetFloat(mCachedBrightnessInfo.brightnessMax,
+ maxBrightness);
+ changed |=
+ mCachedBrightnessInfo.checkAndSetInt(mCachedBrightnessInfo.hbmMode,
+ mHbmController.getHighBrightnessMode());
+ changed |=
+ mCachedBrightnessInfo.checkAndSetFloat(mCachedBrightnessInfo.hbmTransitionPoint,
+ mHbmController.getTransitionPoint());
+ changed |=
+ mCachedBrightnessInfo.checkAndSetInt(mCachedBrightnessInfo.brightnessMaxReason,
+ mBrightnessThrottler.getBrightnessMaxReason());
+
+ return changed;
+ }
+ }
+
+ void postBrightnessChangeRunnable() {
+ mHandler.post(mOnBrightnessChangeRunnable);
+ }
+
+ private HighBrightnessModeController createHbmControllerLocked() {
+ final DisplayDevice device = mLogicalDisplay.getPrimaryDisplayDeviceLocked();
+ final DisplayDeviceConfig ddConfig = device.getDisplayDeviceConfig();
+ final IBinder displayToken =
+ mLogicalDisplay.getPrimaryDisplayDeviceLocked().getDisplayTokenLocked();
+ final String displayUniqueId =
+ mLogicalDisplay.getPrimaryDisplayDeviceLocked().getUniqueId();
+ final DisplayDeviceConfig.HighBrightnessModeData hbmData =
+ ddConfig != null ? ddConfig.getHighBrightnessModeData() : null;
+ final DisplayDeviceInfo info = device.getDisplayDeviceInfoLocked();
+ return new HighBrightnessModeController(mHandler, info.width, info.height, displayToken,
+ displayUniqueId, PowerManager.BRIGHTNESS_MIN, PowerManager.BRIGHTNESS_MAX, hbmData,
+ new HighBrightnessModeController.HdrBrightnessDeviceConfig() {
+ @Override
+ public float getHdrBrightnessFromSdr(float sdrBrightness) {
+ return mDisplayDeviceConfig.getHdrBrightnessFromSdr(sdrBrightness);
+ }
+ },
+ () -> {
+ sendUpdatePowerState();
+ postBrightnessChangeRunnable();
+ // TODO(b/192258832): Switch the HBMChangeCallback to a listener pattern.
+ if (mAutomaticBrightnessController != null) {
+ mAutomaticBrightnessController.update();
+ }
+ }, mContext);
+ }
+
+ private BrightnessThrottler createBrightnessThrottlerLocked() {
+ final DisplayDevice device = mLogicalDisplay.getPrimaryDisplayDeviceLocked();
+ final DisplayDeviceConfig ddConfig = device.getDisplayDeviceConfig();
+ final DisplayDeviceConfig.BrightnessThrottlingData data =
+ ddConfig != null ? ddConfig.getBrightnessThrottlingData() : null;
+ return new BrightnessThrottler(mHandler, data,
+ () -> {
+ sendUpdatePowerState();
+ postBrightnessChangeRunnable();
+ }, mUniqueDisplayId);
+ }
+
+ private void blockScreenOn() {
+ if (mPendingScreenOnUnblocker == null) {
+ Trace.asyncTraceBegin(Trace.TRACE_TAG_POWER, SCREEN_ON_BLOCKED_TRACE_NAME, 0);
+ mPendingScreenOnUnblocker = new ScreenOnUnblocker();
+ mScreenOnBlockStartRealTime = SystemClock.elapsedRealtime();
+ Slog.i(mTag, "Blocking screen on until initial contents have been drawn.");
+ }
+ }
+
+ private void unblockScreenOn() {
+ if (mPendingScreenOnUnblocker != null) {
+ mPendingScreenOnUnblocker = null;
+ long delay = SystemClock.elapsedRealtime() - mScreenOnBlockStartRealTime;
+ Slog.i(mTag, "Unblocked screen on after " + delay + " ms");
+ Trace.asyncTraceEnd(Trace.TRACE_TAG_POWER, SCREEN_ON_BLOCKED_TRACE_NAME, 0);
+ }
+ }
+
+ private void blockScreenOff() {
+ if (mPendingScreenOffUnblocker == null) {
+ Trace.asyncTraceBegin(Trace.TRACE_TAG_POWER, SCREEN_OFF_BLOCKED_TRACE_NAME, 0);
+ mPendingScreenOffUnblocker = new ScreenOffUnblocker();
+ mScreenOffBlockStartRealTime = SystemClock.elapsedRealtime();
+ Slog.i(mTag, "Blocking screen off");
+ }
+ }
+
+ private void unblockScreenOff() {
+ if (mPendingScreenOffUnblocker != null) {
+ mPendingScreenOffUnblocker = null;
+ long delay = SystemClock.elapsedRealtime() - mScreenOffBlockStartRealTime;
+ Slog.i(mTag, "Unblocked screen off after " + delay + " ms");
+ Trace.asyncTraceEnd(Trace.TRACE_TAG_POWER, SCREEN_OFF_BLOCKED_TRACE_NAME, 0);
+ }
+ }
+
+ private boolean setScreenState(int state) {
+ return setScreenState(state, false /*reportOnly*/);
+ }
+
+ private boolean setScreenState(int state, boolean reportOnly) {
+ final boolean isOff = (state == Display.STATE_OFF);
+
+ if (mPowerState.getScreenState() != state
+ || mReportedScreenStateToPolicy == REPORTED_TO_POLICY_UNREPORTED) {
+ // If we are trying to turn screen off, give policy a chance to do something before we
+ // actually turn the screen off.
+ if (isOff && !mScreenOffBecauseOfProximity) {
+ if (mReportedScreenStateToPolicy == REPORTED_TO_POLICY_SCREEN_ON
+ || mReportedScreenStateToPolicy == REPORTED_TO_POLICY_UNREPORTED) {
+ setReportedScreenState(REPORTED_TO_POLICY_SCREEN_TURNING_OFF);
+ blockScreenOff();
+ mWindowManagerPolicy.screenTurningOff(mDisplayId, mPendingScreenOffUnblocker);
+ unblockScreenOff();
+ } else if (mPendingScreenOffUnblocker != null) {
+ // Abort doing the state change until screen off is unblocked.
+ return false;
+ }
+ }
+
+ if (!reportOnly && mPowerState.getScreenState() != state) {
+ Trace.traceCounter(Trace.TRACE_TAG_POWER, "ScreenState", state);
+ // TODO(b/153319140) remove when we can get this from the above trace invocation
+ SystemProperties.set("debug.tracing.screen_state", String.valueOf(state));
+ mPowerState.setScreenState(state);
+ // Tell battery stats about the transition.
+ noteScreenState(state);
+ }
+ }
+
+ // Tell the window manager policy when the screen is turned off or on unless it's due
+ // to the proximity sensor. We temporarily block turning the screen on until the
+ // window manager is ready by leaving a black surface covering the screen.
+ // This surface is essentially the final state of the color fade animation and
+ // it is only removed once the window manager tells us that the activity has
+ // finished drawing underneath.
+ if (isOff && mReportedScreenStateToPolicy != REPORTED_TO_POLICY_SCREEN_OFF
+ && !mScreenOffBecauseOfProximity) {
+ setReportedScreenState(REPORTED_TO_POLICY_SCREEN_OFF);
+ unblockScreenOn();
+ mWindowManagerPolicy.screenTurnedOff(mDisplayId);
+ } else if (!isOff
+ && mReportedScreenStateToPolicy == REPORTED_TO_POLICY_SCREEN_TURNING_OFF) {
+
+ // We told policy already that screen was turning off, but now we changed our minds.
+ // Complete the full state transition on -> turningOff -> off.
+ unblockScreenOff();
+ mWindowManagerPolicy.screenTurnedOff(mDisplayId);
+ setReportedScreenState(REPORTED_TO_POLICY_SCREEN_OFF);
+ }
+ if (!isOff
+ && (mReportedScreenStateToPolicy == REPORTED_TO_POLICY_SCREEN_OFF
+ || mReportedScreenStateToPolicy == REPORTED_TO_POLICY_UNREPORTED)) {
+ setReportedScreenState(REPORTED_TO_POLICY_SCREEN_TURNING_ON);
+ if (mPowerState.getColorFadeLevel() == 0.0f) {
+ blockScreenOn();
+ } else {
+ unblockScreenOn();
+ }
+ mWindowManagerPolicy.screenTurningOn(mDisplayId, mPendingScreenOnUnblocker);
+ }
+
+ // Return true if the screen isn't blocked.
+ return mPendingScreenOnUnblocker == null;
+ }
+
+ private void setReportedScreenState(int state) {
+ Trace.traceCounter(Trace.TRACE_TAG_POWER, "ReportedScreenStateToPolicy", state);
+ mReportedScreenStateToPolicy = state;
+ }
+
+ private void loadAmbientLightSensor() {
+ DisplayDeviceConfig.SensorData lightSensor = mDisplayDeviceConfig.getAmbientLightSensor();
+ final int fallbackType = mDisplayId == Display.DEFAULT_DISPLAY
+ ? Sensor.TYPE_LIGHT : SensorUtils.NO_FALLBACK;
+ mLightSensor = SensorUtils.findSensor(mSensorManager, lightSensor.type, lightSensor.name,
+ fallbackType);
+ }
+
+ private void loadProximitySensor() {
+ if (DEBUG_PRETEND_PROXIMITY_SENSOR_ABSENT) {
+ return;
+ }
+ final DisplayDeviceConfig.SensorData proxSensor =
+ mDisplayDeviceConfig.getProximitySensor();
+ final int fallbackType = mDisplayId == Display.DEFAULT_DISPLAY
+ ? Sensor.TYPE_PROXIMITY : SensorUtils.NO_FALLBACK;
+ mProximitySensor = SensorUtils.findSensor(mSensorManager, proxSensor.type, proxSensor.name,
+ fallbackType);
+ if (mProximitySensor != null) {
+ mProximityThreshold = Math.min(mProximitySensor.getMaximumRange(),
+ TYPICAL_PROXIMITY_THRESHOLD);
+ }
+ }
+
+ private float clampScreenBrightnessForVr(float value) {
+ return MathUtils.constrain(
+ value, mScreenBrightnessForVrRangeMinimum,
+ mScreenBrightnessForVrRangeMaximum);
+ }
+
+ private float clampScreenBrightness(float value) {
+ if (Float.isNaN(value)) {
+ value = PowerManager.BRIGHTNESS_MIN;
+ }
+ return MathUtils.constrain(value,
+ mHbmController.getCurrentBrightnessMin(), mHbmController.getCurrentBrightnessMax());
+ }
+
+ // Checks whether the brightness is within the valid brightness range, not including off.
+ private boolean isValidBrightnessValue(float brightness) {
+ return brightness >= PowerManager.BRIGHTNESS_MIN
+ && brightness <= PowerManager.BRIGHTNESS_MAX;
+ }
+
+ private void animateScreenBrightness(float target, float sdrTarget, float rate) {
+ if (DEBUG) {
+ Slog.d(mTag, "Animating brightness: target=" + target + ", sdrTarget=" + sdrTarget
+ + ", rate=" + rate);
+ }
+ if (mScreenBrightnessRampAnimator.animateTo(target, sdrTarget, rate)) {
+ Trace.traceCounter(Trace.TRACE_TAG_POWER, "TargetScreenBrightness", (int) target);
+ // TODO(b/153319140) remove when we can get this from the above trace invocation
+ SystemProperties.set("debug.tracing.screen_brightness", String.valueOf(target));
+ noteScreenBrightness(target);
+ }
+ }
+
+ private void animateScreenStateChange(int target, boolean performScreenOffTransition) {
+ // If there is already an animation in progress, don't interfere with it.
+ if (mColorFadeEnabled
+ && (mColorFadeOnAnimator.isStarted() || mColorFadeOffAnimator.isStarted())) {
+ if (target != Display.STATE_ON) {
+ return;
+ }
+ // If display state changed to on, proceed and stop the color fade and turn screen on.
+ mPendingScreenOff = false;
+ }
+
+ if (mDisplayBlanksAfterDozeConfig
+ && Display.isDozeState(mPowerState.getScreenState())
+ && !Display.isDozeState(target)) {
+ // Skip the screen off animation and add a black surface to hide the
+ // contents of the screen.
+ mPowerState.prepareColorFade(mContext,
+ mColorFadeFadesConfig ? ColorFade.MODE_FADE : ColorFade.MODE_WARM_UP);
+ if (mColorFadeOffAnimator != null) {
+ mColorFadeOffAnimator.end();
+ }
+ // Some display hardware will blank itself on the transition between doze and non-doze
+ // but still on display states. In this case we want to report to policy that the
+ // display has turned off so it can prepare the appropriate power on animation, but we
+ // don't want to actually transition to the fully off state since that takes
+ // significantly longer to transition from.
+ setScreenState(Display.STATE_OFF, target != Display.STATE_OFF /*reportOnly*/);
+ }
+
+ // If we were in the process of turning off the screen but didn't quite
+ // finish. Then finish up now to prevent a jarring transition back
+ // to screen on if we skipped blocking screen on as usual.
+ if (mPendingScreenOff && target != Display.STATE_OFF) {
+ setScreenState(Display.STATE_OFF);
+ mPendingScreenOff = false;
+ mPowerState.dismissColorFadeResources();
+ }
+
+ if (target == Display.STATE_ON) {
+ // Want screen on. The contents of the screen may not yet
+ // be visible if the color fade has not been dismissed because
+ // its last frame of animation is solid black.
+ if (!setScreenState(Display.STATE_ON)) {
+ return; // screen on blocked
+ }
+ if (USE_COLOR_FADE_ON_ANIMATION && mColorFadeEnabled && mPowerRequest.isBrightOrDim()) {
+ // Perform screen on animation.
+ if (mPowerState.getColorFadeLevel() == 1.0f) {
+ mPowerState.dismissColorFade();
+ } else if (mPowerState.prepareColorFade(mContext,
+ mColorFadeFadesConfig
+ ? ColorFade.MODE_FADE : ColorFade.MODE_WARM_UP)) {
+ mColorFadeOnAnimator.start();
+ } else {
+ mColorFadeOnAnimator.end();
+ }
+ } else {
+ // Skip screen on animation.
+ mPowerState.setColorFadeLevel(1.0f);
+ mPowerState.dismissColorFade();
+ }
+ } else if (target == Display.STATE_VR) {
+ // Wait for brightness animation to complete beforehand when entering VR
+ // from screen on to prevent a perceptible jump because brightness may operate
+ // differently when the display is configured for dozing.
+ if (mScreenBrightnessRampAnimator.isAnimating()
+ && mPowerState.getScreenState() == Display.STATE_ON) {
+ return;
+ }
+
+ // Set screen state.
+ if (!setScreenState(Display.STATE_VR)) {
+ return; // screen on blocked
+ }
+
+ // Dismiss the black surface without fanfare.
+ mPowerState.setColorFadeLevel(1.0f);
+ mPowerState.dismissColorFade();
+ } else if (target == Display.STATE_DOZE) {
+ // Want screen dozing.
+ // Wait for brightness animation to complete beforehand when entering doze
+ // from screen on to prevent a perceptible jump because brightness may operate
+ // differently when the display is configured for dozing.
+ if (mScreenBrightnessRampAnimator.isAnimating()
+ && mPowerState.getScreenState() == Display.STATE_ON) {
+ return;
+ }
+
+ // Set screen state.
+ if (!setScreenState(Display.STATE_DOZE)) {
+ return; // screen on blocked
+ }
+
+ // Dismiss the black surface without fanfare.
+ mPowerState.setColorFadeLevel(1.0f);
+ mPowerState.dismissColorFade();
+ } else if (target == Display.STATE_DOZE_SUSPEND) {
+ // Want screen dozing and suspended.
+ // Wait for brightness animation to complete beforehand unless already
+ // suspended because we may not be able to change it after suspension.
+ if (mScreenBrightnessRampAnimator.isAnimating()
+ && mPowerState.getScreenState() != Display.STATE_DOZE_SUSPEND) {
+ return;
+ }
+
+ // If not already suspending, temporarily set the state to doze until the
+ // screen on is unblocked, then suspend.
+ if (mPowerState.getScreenState() != Display.STATE_DOZE_SUSPEND) {
+ if (!setScreenState(Display.STATE_DOZE)) {
+ return; // screen on blocked
+ }
+ setScreenState(Display.STATE_DOZE_SUSPEND); // already on so can't block
+ }
+
+ // Dismiss the black surface without fanfare.
+ mPowerState.setColorFadeLevel(1.0f);
+ mPowerState.dismissColorFade();
+ } else if (target == Display.STATE_ON_SUSPEND) {
+ // Want screen full-power and suspended.
+ // Wait for brightness animation to complete beforehand unless already
+ // suspended because we may not be able to change it after suspension.
+ if (mScreenBrightnessRampAnimator.isAnimating()
+ && mPowerState.getScreenState() != Display.STATE_ON_SUSPEND) {
+ return;
+ }
+
+ // If not already suspending, temporarily set the state to on until the
+ // screen on is unblocked, then suspend.
+ if (mPowerState.getScreenState() != Display.STATE_ON_SUSPEND) {
+ if (!setScreenState(Display.STATE_ON)) {
+ return;
+ }
+ setScreenState(Display.STATE_ON_SUSPEND);
+ }
+
+ // Dismiss the black surface without fanfare.
+ mPowerState.setColorFadeLevel(1.0f);
+ mPowerState.dismissColorFade();
+ } else {
+ // Want screen off.
+ mPendingScreenOff = true;
+ if (!mColorFadeEnabled) {
+ mPowerState.setColorFadeLevel(0.0f);
+ }
+
+ if (mPowerState.getColorFadeLevel() == 0.0f) {
+ // Turn the screen off.
+ // A black surface is already hiding the contents of the screen.
+ setScreenState(Display.STATE_OFF);
+ mPendingScreenOff = false;
+ mPowerState.dismissColorFadeResources();
+ } else if (performScreenOffTransition
+ && mPowerState.prepareColorFade(mContext,
+ mColorFadeFadesConfig
+ ? ColorFade.MODE_FADE : ColorFade.MODE_COOL_DOWN)
+ && mPowerState.getScreenState() != Display.STATE_OFF) {
+ // Perform the screen off animation.
+ mColorFadeOffAnimator.start();
+ } else {
+ // Skip the screen off animation and add a black surface to hide the
+ // contents of the screen.
+ mColorFadeOffAnimator.end();
+ }
+ }
+ }
+
+ private final Runnable mCleanListener = this::sendUpdatePowerState;
+
+ private void setProximitySensorEnabled(boolean enable) {
+ if (enable) {
+ if (!mProximitySensorEnabled) {
+ // Register the listener.
+ // Proximity sensor state already cleared initially.
+ mProximitySensorEnabled = true;
+ mIgnoreProximityUntilChanged = false;
+ mSensorManager.registerListener(mProximitySensorListener, mProximitySensor,
+ SensorManager.SENSOR_DELAY_NORMAL, mHandler);
+ }
+ } else {
+ if (mProximitySensorEnabled) {
+ // Unregister the listener.
+ // Clear the proximity sensor state for next time.
+ mProximitySensorEnabled = false;
+ mProximity = PROXIMITY_UNKNOWN;
+ mIgnoreProximityUntilChanged = false;
+ mPendingProximity = PROXIMITY_UNKNOWN;
+ mHandler.removeMessages(MSG_PROXIMITY_SENSOR_DEBOUNCED);
+ mSensorManager.unregisterListener(mProximitySensorListener);
+ clearPendingProximityDebounceTime(); // release wake lock (must be last)
+ }
+ }
+ }
+
+ private void handleProximitySensorEvent(long time, boolean positive) {
+ if (mProximitySensorEnabled) {
+ if (mPendingProximity == PROXIMITY_NEGATIVE && !positive) {
+ return; // no change
+ }
+ if (mPendingProximity == PROXIMITY_POSITIVE && positive) {
+ return; // no change
+ }
+
+ // Only accept a proximity sensor reading if it remains
+ // stable for the entire debounce delay. We hold a wake lock while
+ // debouncing the sensor.
+ mHandler.removeMessages(MSG_PROXIMITY_SENSOR_DEBOUNCED);
+ if (positive) {
+ mPendingProximity = PROXIMITY_POSITIVE;
+ setPendingProximityDebounceTime(
+ time + PROXIMITY_SENSOR_POSITIVE_DEBOUNCE_DELAY); // acquire wake lock
+ } else {
+ mPendingProximity = PROXIMITY_NEGATIVE;
+ setPendingProximityDebounceTime(
+ time + PROXIMITY_SENSOR_NEGATIVE_DEBOUNCE_DELAY); // acquire wake lock
+ }
+
+ // Debounce the new sensor reading.
+ debounceProximitySensor();
+ }
+ }
+
+ private void debounceProximitySensor() {
+ if (mProximitySensorEnabled
+ && mPendingProximity != PROXIMITY_UNKNOWN
+ && mPendingProximityDebounceTime >= 0) {
+ final long now = mClock.uptimeMillis();
+ if (mPendingProximityDebounceTime <= now) {
+ if (mProximity != mPendingProximity) {
+ // if the status of the sensor changed, stop ignoring.
+ mIgnoreProximityUntilChanged = false;
+ Slog.i(mTag, "No longer ignoring proximity [" + mPendingProximity + "]");
+ }
+ // Sensor reading accepted. Apply the change then release the wake lock.
+ mProximity = mPendingProximity;
+ updatePowerState();
+ clearPendingProximityDebounceTime(); // release wake lock (must be last)
+ } else {
+ // Need to wait a little longer.
+ // Debounce again later. We continue holding a wake lock while waiting.
+ Message msg = mHandler.obtainMessage(MSG_PROXIMITY_SENSOR_DEBOUNCED);
+ mHandler.sendMessageAtTime(msg, mPendingProximityDebounceTime);
+ }
+ }
+ }
+
+ private void clearPendingProximityDebounceTime() {
+ if (mPendingProximityDebounceTime >= 0) {
+ mPendingProximityDebounceTime = -1;
+ mCallbacks.releaseSuspendBlocker(mSuspendBlockerIdProxDebounce);
+ }
+ }
+
+ private void setPendingProximityDebounceTime(long debounceTime) {
+ if (mPendingProximityDebounceTime < 0) {
+ mCallbacks.acquireSuspendBlocker(mSuspendBlockerIdProxDebounce);
+ }
+ mPendingProximityDebounceTime = debounceTime;
+ }
+
+ private void sendOnStateChangedWithWakelock() {
+ if (!mOnStateChangedPending) {
+ mOnStateChangedPending = true;
+ mCallbacks.acquireSuspendBlocker(mSuspendBlockerIdOnStateChanged);
+ mHandler.post(mOnStateChangedRunnable);
+ }
+ }
+
+ private void logDisplayPolicyChanged(int newPolicy) {
+ LogMaker log = new LogMaker(MetricsEvent.DISPLAY_POLICY);
+ log.setType(MetricsEvent.TYPE_UPDATE);
+ log.setSubtype(newPolicy);
+ MetricsLogger.action(log);
+ }
+
+ private void handleSettingsChange(boolean userSwitch) {
+ mPendingScreenBrightnessSetting = getScreenBrightnessSetting();
+ mPendingAutoBrightnessAdjustment = getAutoBrightnessAdjustmentSetting();
+ if (userSwitch) {
+ // Don't treat user switches as user initiated change.
+ setCurrentScreenBrightness(mPendingScreenBrightnessSetting);
+ updateAutoBrightnessAdjustment();
+ if (mAutomaticBrightnessController != null) {
+ mAutomaticBrightnessController.resetShortTermModel();
+ }
+ }
+ // We don't bother with a pending variable for VR screen brightness since we just
+ // immediately adapt to it.
+ mScreenBrightnessForVr = getScreenBrightnessForVrSetting();
+ sendUpdatePowerState();
+ }
+
+ private float getAutoBrightnessAdjustmentSetting() {
+ final float adj = Settings.System.getFloatForUser(mContext.getContentResolver(),
+ Settings.System.SCREEN_AUTO_BRIGHTNESS_ADJ, 0.0f, UserHandle.USER_CURRENT);
+ return Float.isNaN(adj) ? 0.0f : clampAutoBrightnessAdjustment(adj);
+ }
+
+ @Override
+ public float getScreenBrightnessSetting() {
+ float brightness = mBrightnessSetting.getBrightness();
+ if (Float.isNaN(brightness)) {
+ brightness = mScreenBrightnessDefault;
+ }
+ return clampAbsoluteBrightness(brightness);
+ }
+
+ private float getScreenBrightnessForVrSetting() {
+ final float brightnessFloat = Settings.System.getFloatForUser(mContext.getContentResolver(),
+ Settings.System.SCREEN_BRIGHTNESS_FOR_VR_FLOAT, mScreenBrightnessForVrDefault,
+ UserHandle.USER_CURRENT);
+ return clampScreenBrightnessForVr(brightnessFloat);
+ }
+
+ @Override
+ public void setBrightness(float brightnessValue) {
+ // Update the setting, which will eventually call back into DPC to have us actually update
+ // the display with the new value.
+ mBrightnessSetting.setBrightness(brightnessValue);
+ }
+
+ private void updateScreenBrightnessSetting(float brightnessValue) {
+ if (!isValidBrightnessValue(brightnessValue)
+ || brightnessValue == mCurrentScreenBrightnessSetting) {
+ return;
+ }
+ setCurrentScreenBrightness(brightnessValue);
+ mBrightnessSetting.setBrightness(brightnessValue);
+ }
+
+ private void setCurrentScreenBrightness(float brightnessValue) {
+ if (brightnessValue != mCurrentScreenBrightnessSetting) {
+ mCurrentScreenBrightnessSetting = brightnessValue;
+ postBrightnessChangeRunnable();
+ }
+ }
+
+ private void putAutoBrightnessAdjustmentSetting(float adjustment) {
+ if (mDisplayId == Display.DEFAULT_DISPLAY) {
+ mAutoBrightnessAdjustment = adjustment;
+ Settings.System.putFloatForUser(mContext.getContentResolver(),
+ Settings.System.SCREEN_AUTO_BRIGHTNESS_ADJ, adjustment,
+ UserHandle.USER_CURRENT);
+ }
+ }
+
+ private boolean updateAutoBrightnessAdjustment() {
+ if (Float.isNaN(mPendingAutoBrightnessAdjustment)) {
+ return false;
+ }
+ if (mAutoBrightnessAdjustment == mPendingAutoBrightnessAdjustment) {
+ mPendingAutoBrightnessAdjustment = Float.NaN;
+ return false;
+ }
+ mAutoBrightnessAdjustment = mPendingAutoBrightnessAdjustment;
+ mPendingAutoBrightnessAdjustment = Float.NaN;
+ mTemporaryAutoBrightnessAdjustment = Float.NaN;
+ return true;
+ }
+
+ // We want to return true if the user has set the screen brightness.
+ // RBC on, off, and intensity changes will return false.
+ // Slider interactions whilst in RBC will return true, just as when in non-rbc.
+ private boolean updateUserSetScreenBrightness() {
+ if ((Float.isNaN(mPendingScreenBrightnessSetting)
+ || mPendingScreenBrightnessSetting < 0.0f)) {
+ return false;
+ }
+ if (mCurrentScreenBrightnessSetting == mPendingScreenBrightnessSetting) {
+ mPendingScreenBrightnessSetting = PowerManager.BRIGHTNESS_INVALID_FLOAT;
+ mTemporaryScreenBrightness = PowerManager.BRIGHTNESS_INVALID_FLOAT;
+ return false;
+ }
+ setCurrentScreenBrightness(mPendingScreenBrightnessSetting);
+ mLastUserSetScreenBrightness = mPendingScreenBrightnessSetting;
+ mPendingScreenBrightnessSetting = PowerManager.BRIGHTNESS_INVALID_FLOAT;
+ mTemporaryScreenBrightness = PowerManager.BRIGHTNESS_INVALID_FLOAT;
+ return true;
+ }
+
+ private void notifyBrightnessTrackerChanged(float brightness, boolean userInitiated,
+ boolean hadUserDataPoint) {
+ final float brightnessInNits = convertToNits(brightness);
+ if (mPowerRequest.useAutoBrightness && brightnessInNits >= 0.0f
+ && mAutomaticBrightnessController != null) {
+ // We only want to track changes on devices that can actually map the display backlight
+ // values into a physical brightness unit since the value provided by the API is in
+ // nits and not using the arbitrary backlight units.
+ final float powerFactor = mPowerRequest.lowPowerMode
+ ? mPowerRequest.screenLowPowerBrightnessFactor
+ : 1.0f;
+ mBrightnessTracker.notifyBrightnessChanged(brightnessInNits, userInitiated,
+ powerFactor, hadUserDataPoint,
+ mAutomaticBrightnessController.isDefaultConfig(), mUniqueDisplayId);
+ }
+ }
+
+ private float convertToNits(float brightness) {
+ if (mAutomaticBrightnessController == null) {
+ return -1f;
+ }
+ return mAutomaticBrightnessController.convertToNits(brightness);
+ }
+
+ @GuardedBy("mLock")
+ private void updatePendingProximityRequestsLocked() {
+ mWaitingForNegativeProximity |= mPendingWaitForNegativeProximityLocked;
+ mPendingWaitForNegativeProximityLocked = false;
+
+ if (mIgnoreProximityUntilChanged) {
+ // Also, lets stop waiting for negative proximity if we're ignoring it.
+ mWaitingForNegativeProximity = false;
+ }
+ }
+
+ private void ignoreProximitySensorUntilChangedInternal() {
+ if (!mIgnoreProximityUntilChanged
+ && mProximity == PROXIMITY_POSITIVE) {
+ // Only ignore if it is still reporting positive (near)
+ mIgnoreProximityUntilChanged = true;
+ Slog.i(mTag, "Ignoring proximity");
+ updatePowerState();
+ }
+ }
+
+ private final Runnable mOnStateChangedRunnable = new Runnable() {
+ @Override
+ public void run() {
+ mOnStateChangedPending = false;
+ mCallbacks.onStateChanged();
+ mCallbacks.releaseSuspendBlocker(mSuspendBlockerIdOnStateChanged);
+ }
+ };
+
+ private void sendOnProximityPositiveWithWakelock() {
+ mCallbacks.acquireSuspendBlocker(mSuspendBlockerIdProxPositive);
+ mHandler.post(mOnProximityPositiveRunnable);
+ mOnProximityPositiveMessages++;
+ }
+
+ private final Runnable mOnProximityPositiveRunnable = new Runnable() {
+ @Override
+ public void run() {
+ mOnProximityPositiveMessages--;
+ mCallbacks.onProximityPositive();
+ mCallbacks.releaseSuspendBlocker(mSuspendBlockerIdProxPositive);
+ }
+ };
+
+ private void sendOnProximityNegativeWithWakelock() {
+ mOnProximityNegativeMessages++;
+ mCallbacks.acquireSuspendBlocker(mSuspendBlockerIdProxNegative);
+ mHandler.post(mOnProximityNegativeRunnable);
+ }
+
+ private final Runnable mOnProximityNegativeRunnable = new Runnable() {
+ @Override
+ public void run() {
+ mOnProximityNegativeMessages--;
+ mCallbacks.onProximityNegative();
+ mCallbacks.releaseSuspendBlocker(mSuspendBlockerIdProxNegative);
+ }
+ };
+
+ @Override
+ public void dump(final PrintWriter pw) {
+ synchronized (mLock) {
+ pw.println();
+ pw.println("Display Power Controller:");
+ pw.println(" mDisplayId=" + mDisplayId);
+ pw.println(" mLightSensor=" + mLightSensor);
+
+ pw.println();
+ pw.println("Display Power Controller Locked State:");
+ pw.println(" mDisplayReadyLocked=" + mDisplayReadyLocked);
+ pw.println(" mPendingRequestLocked=" + mPendingRequestLocked);
+ pw.println(" mPendingRequestChangedLocked=" + mPendingRequestChangedLocked);
+ pw.println(" mPendingWaitForNegativeProximityLocked="
+ + mPendingWaitForNegativeProximityLocked);
+ pw.println(" mPendingUpdatePowerStateLocked=" + mPendingUpdatePowerStateLocked);
+ }
+
+ pw.println();
+ pw.println("Display Power Controller Configuration:");
+ pw.println(" mScreenBrightnessRangeDefault=" + mScreenBrightnessDefault);
+ pw.println(" mScreenBrightnessDozeConfig=" + mScreenBrightnessDozeConfig);
+ pw.println(" mScreenBrightnessDimConfig=" + mScreenBrightnessDimConfig);
+ pw.println(" mScreenBrightnessForVrRangeMinimum=" + mScreenBrightnessForVrRangeMinimum);
+ pw.println(" mScreenBrightnessForVrRangeMaximum=" + mScreenBrightnessForVrRangeMaximum);
+ pw.println(" mScreenBrightnessForVrDefault=" + mScreenBrightnessForVrDefault);
+ pw.println(" mUseSoftwareAutoBrightnessConfig=" + mUseSoftwareAutoBrightnessConfig);
+ pw.println(" mAllowAutoBrightnessWhileDozingConfig="
+ + mAllowAutoBrightnessWhileDozingConfig);
+ pw.println(" mSkipScreenOnBrightnessRamp=" + mSkipScreenOnBrightnessRamp);
+ pw.println(" mColorFadeFadesConfig=" + mColorFadeFadesConfig);
+ pw.println(" mColorFadeEnabled=" + mColorFadeEnabled);
+ synchronized (mCachedBrightnessInfo) {
+ pw.println(" mCachedBrightnessInfo.brightness="
+ + mCachedBrightnessInfo.brightness.value);
+ pw.println(" mCachedBrightnessInfo.adjustedBrightness="
+ + mCachedBrightnessInfo.adjustedBrightness.value);
+ pw.println(" mCachedBrightnessInfo.brightnessMin="
+ + mCachedBrightnessInfo.brightnessMin.value);
+ pw.println(" mCachedBrightnessInfo.brightnessMax="
+ + mCachedBrightnessInfo.brightnessMax.value);
+ pw.println(" mCachedBrightnessInfo.hbmMode=" + mCachedBrightnessInfo.hbmMode.value);
+ pw.println(" mCachedBrightnessInfo.hbmTransitionPoint="
+ + mCachedBrightnessInfo.hbmTransitionPoint.value);
+ pw.println(" mCachedBrightnessInfo.brightnessMaxReason ="
+ + mCachedBrightnessInfo.brightnessMaxReason.value);
+ }
+ pw.println(" mDisplayBlanksAfterDozeConfig=" + mDisplayBlanksAfterDozeConfig);
+ pw.println(" mBrightnessBucketsInDozeConfig=" + mBrightnessBucketsInDozeConfig);
+
+ mHandler.runWithScissors(() -> dumpLocal(pw), 1000);
+ }
+
+ private void dumpLocal(PrintWriter pw) {
+ pw.println();
+ pw.println("Display Power Controller Thread State:");
+ pw.println(" mPowerRequest=" + mPowerRequest);
+ pw.println(" mUnfinishedBusiness=" + mUnfinishedBusiness);
+ pw.println(" mWaitingForNegativeProximity=" + mWaitingForNegativeProximity);
+ pw.println(" mProximitySensor=" + mProximitySensor);
+ pw.println(" mProximitySensorEnabled=" + mProximitySensorEnabled);
+ pw.println(" mProximityThreshold=" + mProximityThreshold);
+ pw.println(" mProximity=" + proximityToString(mProximity));
+ pw.println(" mPendingProximity=" + proximityToString(mPendingProximity));
+ pw.println(" mPendingProximityDebounceTime="
+ + TimeUtils.formatUptime(mPendingProximityDebounceTime));
+ pw.println(" mScreenOffBecauseOfProximity=" + mScreenOffBecauseOfProximity);
+ pw.println(" mLastUserSetScreenBrightness=" + mLastUserSetScreenBrightness);
+ pw.println(" mPendingScreenBrightnessSetting="
+ + mPendingScreenBrightnessSetting);
+ pw.println(" mTemporaryScreenBrightness=" + mTemporaryScreenBrightness);
+ pw.println(" mAutoBrightnessAdjustment=" + mAutoBrightnessAdjustment);
+ pw.println(" mBrightnessReason=" + mBrightnessReason);
+ pw.println(" mTemporaryAutoBrightnessAdjustment=" + mTemporaryAutoBrightnessAdjustment);
+ pw.println(" mPendingAutoBrightnessAdjustment=" + mPendingAutoBrightnessAdjustment);
+ pw.println(" mScreenBrightnessForVrFloat=" + mScreenBrightnessForVr);
+ pw.println(" mAppliedAutoBrightness=" + mAppliedAutoBrightness);
+ pw.println(" mAppliedDimming=" + mAppliedDimming);
+ pw.println(" mAppliedLowPower=" + mAppliedLowPower);
+ pw.println(" mAppliedThrottling=" + mAppliedThrottling);
+ pw.println(" mAppliedScreenBrightnessOverride=" + mAppliedScreenBrightnessOverride);
+ pw.println(" mAppliedTemporaryBrightness=" + mAppliedTemporaryBrightness);
+ pw.println(" mAppliedTemporaryAutoBrightnessAdjustment="
+ + mAppliedTemporaryAutoBrightnessAdjustment);
+ pw.println(" mAppliedBrightnessBoost=" + mAppliedBrightnessBoost);
+ pw.println(" mDozing=" + mDozing);
+ pw.println(" mSkipRampState=" + skipRampStateToString(mSkipRampState));
+ pw.println(" mScreenOnBlockStartRealTime=" + mScreenOnBlockStartRealTime);
+ pw.println(" mScreenOffBlockStartRealTime=" + mScreenOffBlockStartRealTime);
+ pw.println(" mPendingScreenOnUnblocker=" + mPendingScreenOnUnblocker);
+ pw.println(" mPendingScreenOffUnblocker=" + mPendingScreenOffUnblocker);
+ pw.println(" mPendingScreenOff=" + mPendingScreenOff);
+ pw.println(" mReportedToPolicy="
+ + reportedToPolicyToString(mReportedScreenStateToPolicy));
+ pw.println(" mIsRbcActive=" + mIsRbcActive);
+ pw.println(" mOnStateChangePending=" + mOnStateChangedPending);
+ pw.println(" mOnProximityPositiveMessages=" + mOnProximityPositiveMessages);
+ pw.println(" mOnProximityNegativeMessages=" + mOnProximityNegativeMessages);
+
+ if (mScreenBrightnessRampAnimator != null) {
+ pw.println(" mScreenBrightnessRampAnimator.isAnimating()="
+ + mScreenBrightnessRampAnimator.isAnimating());
+ }
+
+ if (mColorFadeOnAnimator != null) {
+ pw.println(" mColorFadeOnAnimator.isStarted()="
+ + mColorFadeOnAnimator.isStarted());
+ }
+ if (mColorFadeOffAnimator != null) {
+ pw.println(" mColorFadeOffAnimator.isStarted()="
+ + mColorFadeOffAnimator.isStarted());
+ }
+
+ if (mPowerState != null) {
+ mPowerState.dump(pw);
+ }
+
+ if (mAutomaticBrightnessController != null) {
+ mAutomaticBrightnessController.dump(pw);
+ dumpBrightnessEvents(pw);
+ }
+
+ if (mHbmController != null) {
+ mHbmController.dump(pw);
+ }
+
+ if (mBrightnessThrottler != null) {
+ mBrightnessThrottler.dump(pw);
+ }
+
+ pw.println();
+ if (mDisplayWhiteBalanceController != null) {
+ mDisplayWhiteBalanceController.dump(pw);
+ mDisplayWhiteBalanceSettings.dump(pw);
+ }
+ }
+
+ private static String proximityToString(int state) {
+ switch (state) {
+ case PROXIMITY_UNKNOWN:
+ return "Unknown";
+ case PROXIMITY_NEGATIVE:
+ return "Negative";
+ case PROXIMITY_POSITIVE:
+ return "Positive";
+ default:
+ return Integer.toString(state);
+ }
+ }
+
+ private static String reportedToPolicyToString(int state) {
+ switch (state) {
+ case REPORTED_TO_POLICY_SCREEN_OFF:
+ return "REPORTED_TO_POLICY_SCREEN_OFF";
+ case REPORTED_TO_POLICY_SCREEN_TURNING_ON:
+ return "REPORTED_TO_POLICY_SCREEN_TURNING_ON";
+ case REPORTED_TO_POLICY_SCREEN_ON:
+ return "REPORTED_TO_POLICY_SCREEN_ON";
+ default:
+ return Integer.toString(state);
+ }
+ }
+
+ private static String skipRampStateToString(int state) {
+ switch (state) {
+ case RAMP_STATE_SKIP_NONE:
+ return "RAMP_STATE_SKIP_NONE";
+ case RAMP_STATE_SKIP_INITIAL:
+ return "RAMP_STATE_SKIP_INITIAL";
+ case RAMP_STATE_SKIP_AUTOBRIGHT:
+ return "RAMP_STATE_SKIP_AUTOBRIGHT";
+ default:
+ return Integer.toString(state);
+ }
+ }
+
+ private void dumpBrightnessEvents(PrintWriter pw) {
+ int size = mBrightnessEventRingBuffer.size();
+ if (size < 1) {
+ pw.println("No Automatic Brightness Adjustments");
+ return;
+ }
+
+ pw.println("Automatic Brightness Adjustments Last " + size + " Events: ");
+ BrightnessEvent[] eventArray = mBrightnessEventRingBuffer.toArray();
+ for (int i = 0; i < mBrightnessEventRingBuffer.size(); i++) {
+ pw.println(" " + eventArray[i].toString());
+ }
+ }
+
+ private static float clampAbsoluteBrightness(float value) {
+ return MathUtils.constrain(value, PowerManager.BRIGHTNESS_MIN,
+ PowerManager.BRIGHTNESS_MAX);
+ }
+
+ private static float clampAutoBrightnessAdjustment(float value) {
+ return MathUtils.constrain(value, -1.0f, 1.0f);
+ }
+
+ private void noteScreenState(int screenState) {
+ if (mBatteryStats != null) {
+ try {
+ // TODO(multi-display): make this multi-display
+ mBatteryStats.noteScreenState(screenState);
+ } catch (RemoteException e) {
+ // same process
+ }
+ }
+ }
+
+ private void noteScreenBrightness(float brightness) {
+ if (mBatteryStats != null) {
+ try {
+ // TODO(brightnessfloat): change BatteryStats to use float
+ mBatteryStats.noteScreenBrightness(BrightnessSynchronizer.brightnessFloatToInt(
+ brightness));
+ } catch (RemoteException e) {
+ // same process
+ }
+ }
+ }
+
+ private void reportStats(float brightness) {
+ if (mLastStatsBrightness == brightness) {
+ return;
+ }
+
+ float hbmTransitionPoint = PowerManager.BRIGHTNESS_MAX;
+ synchronized (mCachedBrightnessInfo) {
+ if (mCachedBrightnessInfo.hbmTransitionPoint == null) {
+ return;
+ }
+ hbmTransitionPoint = mCachedBrightnessInfo.hbmTransitionPoint.value;
+ }
+
+ final boolean aboveTransition = brightness > hbmTransitionPoint;
+ final boolean oldAboveTransition = mLastStatsBrightness > hbmTransitionPoint;
+
+ if (aboveTransition || oldAboveTransition) {
+ mLastStatsBrightness = brightness;
+ mHandler.removeMessages(MSG_STATSD_HBM_BRIGHTNESS);
+ if (aboveTransition != oldAboveTransition) {
+ // report immediately
+ logHbmBrightnessStats(brightness, mDisplayStatsId);
+ } else {
+ // delay for rate limiting
+ Message msg = mHandler.obtainMessage();
+ msg.what = MSG_STATSD_HBM_BRIGHTNESS;
+ msg.arg1 = Float.floatToIntBits(brightness);
+ msg.arg2 = mDisplayStatsId;
+ mHandler.sendMessageDelayed(msg, BRIGHTNESS_CHANGE_STATSD_REPORT_INTERVAL_MS);
+ }
+ }
+ }
+
+ private void logHbmBrightnessStats(float brightness, int displayStatsId) {
+ synchronized (mHandler) {
+ FrameworkStatsLog.write(
+ FrameworkStatsLog.DISPLAY_HBM_BRIGHTNESS_CHANGED, displayStatsId, brightness);
+ }
+ }
+
+ private void logManualBrightnessEvent(BrightnessEvent event) {
+ float appliedLowPowerMode = event.isLowPowerModeSet() ? event.getPowerFactor() : -1f;
+ int appliedRbcStrength = event.isRbcEnabled() ? event.getRbcStrength() : -1;
+ float appliedHbmMaxNits =
+ event.getHbmMode() == BrightnessInfo.HIGH_BRIGHTNESS_MODE_OFF
+ ? -1f : convertToNits(event.getHbmMax());
+ // thermalCapNits set to -1 if not currently capping max brightness
+ float appliedThermalCapNits =
+ event.getThermalMax() == PowerManager.BRIGHTNESS_MAX
+ ? -1f : convertToNits(event.getThermalMax());
+
+ FrameworkStatsLog.write(FrameworkStatsLog.DISPLAY_BRIGHTNESS_CHANGED,
+ convertToNits(event.getInitialBrightness()),
+ convertToNits(event.getBrightness()),
+ event.getSlowAmbientLux(),
+ event.getPhysicalDisplayId(),
+ event.isShortTermModelActive(),
+ appliedLowPowerMode,
+ appliedRbcStrength,
+ appliedHbmMaxNits,
+ appliedThermalCapNits,
+ event.isAutomaticBrightnessEnabled(),
+ FrameworkStatsLog.DISPLAY_BRIGHTNESS_CHANGED__REASON__REASON_MANUAL);
+ }
+
+ private final class DisplayControllerHandler extends Handler {
+ DisplayControllerHandler(Looper looper) {
+ super(looper, null, true /*async*/);
+ }
+
+ @Override
+ public void handleMessage(Message msg) {
+ switch (msg.what) {
+ case MSG_UPDATE_POWER_STATE:
+ updatePowerState();
+ break;
+
+ case MSG_PROXIMITY_SENSOR_DEBOUNCED:
+ debounceProximitySensor();
+ break;
+
+ case MSG_SCREEN_ON_UNBLOCKED:
+ if (mPendingScreenOnUnblocker == msg.obj) {
+ unblockScreenOn();
+ updatePowerState();
+ }
+ break;
+ case MSG_SCREEN_OFF_UNBLOCKED:
+ if (mPendingScreenOffUnblocker == msg.obj) {
+ unblockScreenOff();
+ updatePowerState();
+ }
+ break;
+ case MSG_CONFIGURE_BRIGHTNESS:
+ mBrightnessConfiguration = (BrightnessConfiguration) msg.obj;
+ updatePowerState();
+ break;
+
+ case MSG_SET_TEMPORARY_BRIGHTNESS:
+ // TODO: Should we have a a timeout for the temporary brightness?
+ mTemporaryScreenBrightness = Float.intBitsToFloat(msg.arg1);
+ updatePowerState();
+ break;
+
+ case MSG_SET_TEMPORARY_AUTO_BRIGHTNESS_ADJUSTMENT:
+ mTemporaryAutoBrightnessAdjustment = Float.intBitsToFloat(msg.arg1);
+ updatePowerState();
+ break;
+
+ case MSG_IGNORE_PROXIMITY:
+ ignoreProximitySensorUntilChangedInternal();
+ break;
+
+ case MSG_STOP:
+ cleanupHandlerThreadAfterStop();
+ break;
+
+ case MSG_UPDATE_BRIGHTNESS:
+ if (mStopped) {
+ return;
+ }
+ handleSettingsChange(false /*userSwitch*/);
+ break;
+
+ case MSG_UPDATE_RBC:
+ handleRbcChanged();
+ break;
+
+ case MSG_BRIGHTNESS_RAMP_DONE:
+ if (mPowerState != null) {
+ final float brightness = mPowerState.getScreenBrightness();
+ reportStats(brightness);
+ }
+ break;
+
+ case MSG_STATSD_HBM_BRIGHTNESS:
+ logHbmBrightnessStats(Float.intBitsToFloat(msg.arg1), msg.arg2);
+ break;
+ }
+ }
+ }
+
+ private final SensorEventListener mProximitySensorListener = new SensorEventListener() {
+ @Override
+ public void onSensorChanged(SensorEvent event) {
+ if (mProximitySensorEnabled) {
+ final long time = mClock.uptimeMillis();
+ final float distance = event.values[0];
+ boolean positive = distance >= 0.0f && distance < mProximityThreshold;
+ handleProximitySensorEvent(time, positive);
+ }
+ }
+
+ @Override
+ public void onAccuracyChanged(Sensor sensor, int accuracy) {
+ // Not used.
+ }
+ };
+
+
+ private final class SettingsObserver extends ContentObserver {
+ SettingsObserver(Handler handler) {
+ super(handler);
+ }
+
+ @Override
+ public void onChange(boolean selfChange, Uri uri) {
+ handleSettingsChange(false /* userSwitch */);
+ }
+ }
+
+ private final class ScreenOnUnblocker implements WindowManagerPolicy.ScreenOnListener {
+ @Override
+ public void onScreenOn() {
+ Message msg = mHandler.obtainMessage(MSG_SCREEN_ON_UNBLOCKED, this);
+ mHandler.sendMessage(msg);
+ }
+ }
+
+ private final class ScreenOffUnblocker implements WindowManagerPolicy.ScreenOffListener {
+ @Override
+ public void onScreenOff() {
+ Message msg = mHandler.obtainMessage(MSG_SCREEN_OFF_UNBLOCKED, this);
+ mHandler.sendMessage(msg);
+ }
+ }
+
+ @Override
+ public void setAutoBrightnessLoggingEnabled(boolean enabled) {
+ if (mAutomaticBrightnessController != null) {
+ mAutomaticBrightnessController.setLoggingEnabled(enabled);
+ }
+ }
+
+ @Override // DisplayWhiteBalanceController.Callbacks
+ public void updateWhiteBalance() {
+ sendUpdatePowerState();
+ }
+
+ @Override
+ public void setDisplayWhiteBalanceLoggingEnabled(boolean enabled) {
+ if (mDisplayWhiteBalanceController != null) {
+ mDisplayWhiteBalanceController.setLoggingEnabled(enabled);
+ mDisplayWhiteBalanceSettings.setLoggingEnabled(enabled);
+ }
+ }
+
+ @Override
+ public void setAmbientColorTemperatureOverride(float cct) {
+ if (mDisplayWhiteBalanceController != null) {
+ mDisplayWhiteBalanceController.setAmbientColorTemperatureOverride(cct);
+ // The ambient color temperature override is only applied when the ambient color
+ // temperature changes or is updated, so it doesn't necessarily change the screen color
+ // temperature immediately. So, let's make it!
+ sendUpdatePowerState();
+ }
+ }
+
+ @VisibleForTesting
+ String getSuspendBlockerUnfinishedBusinessId(int displayId) {
+ return "[" + displayId + "]unfinished business";
+ }
+
+ String getSuspendBlockerOnStateChangedId(int displayId) {
+ return "[" + displayId + "]on state changed";
+ }
+
+ String getSuspendBlockerProxPositiveId(int displayId) {
+ return "[" + displayId + "]prox positive";
+ }
+
+ String getSuspendBlockerProxNegativeId(int displayId) {
+ return "[" + displayId + "]prox negative";
+ }
+
+ @VisibleForTesting
+ String getSuspendBlockerProxDebounceId(int displayId) {
+ return "[" + displayId + "]prox debounce";
+ }
+
+ /** Functional interface for providing time. */
+ @VisibleForTesting
+ interface Clock {
+ /**
+ * Returns current time in milliseconds since boot, not counting time spent in deep sleep.
+ */
+ long uptimeMillis();
+ }
+
+ @VisibleForTesting
+ static class Injector {
+ Clock getClock() {
+ return SystemClock::uptimeMillis;
+ }
+
+ DisplayPowerState getDisplayPowerState(DisplayBlanker blanker, ColorFade colorFade,
+ int displayId, int displayState) {
+ return new DisplayPowerState(blanker, colorFade, displayId, displayState);
+ }
+
+ DualRampAnimator<DisplayPowerState> getDualRampAnimator(DisplayPowerState dps,
+ FloatProperty<DisplayPowerState> firstProperty,
+ FloatProperty<DisplayPowerState> secondProperty) {
+ return new DualRampAnimator(dps, firstProperty, secondProperty);
+ }
+ }
+
+ static class CachedBrightnessInfo {
+ public MutableFloat brightness = new MutableFloat(PowerManager.BRIGHTNESS_INVALID_FLOAT);
+ public MutableFloat adjustedBrightness =
+ new MutableFloat(PowerManager.BRIGHTNESS_INVALID_FLOAT);
+ public MutableFloat brightnessMin =
+ new MutableFloat(PowerManager.BRIGHTNESS_INVALID_FLOAT);
+ public MutableFloat brightnessMax =
+ new MutableFloat(PowerManager.BRIGHTNESS_INVALID_FLOAT);
+ public MutableInt hbmMode = new MutableInt(BrightnessInfo.HIGH_BRIGHTNESS_MODE_OFF);
+ public MutableFloat hbmTransitionPoint =
+ new MutableFloat(HighBrightnessModeController.HBM_TRANSITION_POINT_INVALID);
+ public MutableInt brightnessMaxReason =
+ new MutableInt(BrightnessInfo.BRIGHTNESS_MAX_REASON_NONE);
+
+ public boolean checkAndSetFloat(MutableFloat mf, float f) {
+ if (mf.value != f) {
+ mf.value = f;
+ return true;
+ }
+ return false;
+ }
+
+ public boolean checkAndSetInt(MutableInt mi, int i) {
+ if (mi.value != i) {
+ mi.value = i;
+ return true;
+ }
+ return false;
+ }
+ }
+}
diff --git a/services/core/java/com/android/server/location/LocationManagerService.java b/services/core/java/com/android/server/location/LocationManagerService.java
index 1d1057f..9bd48f2 100644
--- a/services/core/java/com/android/server/location/LocationManagerService.java
+++ b/services/core/java/com/android/server/location/LocationManagerService.java
@@ -115,6 +115,7 @@
import com.android.server.location.injector.LocationPermissionsHelper;
import com.android.server.location.injector.LocationPowerSaveModeHelper;
import com.android.server.location.injector.LocationUsageLogger;
+import com.android.server.location.injector.PackageResetHelper;
import com.android.server.location.injector.ScreenInteractiveHelper;
import com.android.server.location.injector.SettingsHelper;
import com.android.server.location.injector.SystemAlarmHelper;
@@ -125,6 +126,7 @@
import com.android.server.location.injector.SystemEmergencyHelper;
import com.android.server.location.injector.SystemLocationPermissionsHelper;
import com.android.server.location.injector.SystemLocationPowerSaveModeHelper;
+import com.android.server.location.injector.SystemPackageResetHelper;
import com.android.server.location.injector.SystemScreenInteractiveHelper;
import com.android.server.location.injector.SystemSettingsHelper;
import com.android.server.location.injector.SystemUserInfoHelper;
@@ -1696,11 +1698,13 @@
private final SystemDeviceStationaryHelper mDeviceStationaryHelper;
private final SystemDeviceIdleHelper mDeviceIdleHelper;
private final LocationUsageLogger mLocationUsageLogger;
+ private final PackageResetHelper mPackageResetHelper;
// lazily instantiated since they may not always be used
@GuardedBy("this")
- private @Nullable SystemEmergencyHelper mEmergencyCallHelper;
+ @Nullable
+ private SystemEmergencyHelper mEmergencyCallHelper;
@GuardedBy("this")
private boolean mSystemReady;
@@ -1721,6 +1725,7 @@
mDeviceStationaryHelper = new SystemDeviceStationaryHelper();
mDeviceIdleHelper = new SystemDeviceIdleHelper(context);
mLocationUsageLogger = new LocationUsageLogger();
+ mPackageResetHelper = new SystemPackageResetHelper(context);
}
synchronized void onSystemReady() {
@@ -1811,5 +1816,10 @@
public LocationUsageLogger getLocationUsageLogger() {
return mLocationUsageLogger;
}
+
+ @Override
+ public PackageResetHelper getPackageResetHelper() {
+ return mPackageResetHelper;
+ }
}
}
diff --git a/services/core/java/com/android/server/location/gnss/GnssListenerMultiplexer.java b/services/core/java/com/android/server/location/gnss/GnssListenerMultiplexer.java
index 82bcca2..e7f6e67 100644
--- a/services/core/java/com/android/server/location/gnss/GnssListenerMultiplexer.java
+++ b/services/core/java/com/android/server/location/gnss/GnssListenerMultiplexer.java
@@ -39,6 +39,7 @@
import com.android.server.location.injector.AppForegroundHelper;
import com.android.server.location.injector.Injector;
import com.android.server.location.injector.LocationPermissionsHelper;
+import com.android.server.location.injector.PackageResetHelper;
import com.android.server.location.injector.SettingsHelper;
import com.android.server.location.injector.UserInfoHelper;
import com.android.server.location.injector.UserInfoHelper.UserListener;
@@ -193,6 +194,7 @@
protected final LocationPermissionsHelper mLocationPermissionsHelper;
protected final AppForegroundHelper mAppForegroundHelper;
protected final LocationManagerInternal mLocationManagerInternal;
+ private final PackageResetHelper mPackageResetHelper;
private final UserListener mUserChangedListener = this::onUserChanged;
private final ProviderEnabledListener mProviderEnabledChangedListener =
@@ -218,12 +220,25 @@
};
private final AppForegroundHelper.AppForegroundListener mAppForegroundChangedListener =
this::onAppForegroundChanged;
+ private final PackageResetHelper.Responder mPackageResetResponder =
+ new PackageResetHelper.Responder() {
+ @Override
+ public void onPackageReset(String packageName) {
+ GnssListenerMultiplexer.this.onPackageReset(packageName);
+ }
+
+ @Override
+ public boolean isResetableForPackage(String packageName) {
+ return GnssListenerMultiplexer.this.isResetableForPackage(packageName);
+ }
+ };
protected GnssListenerMultiplexer(Injector injector) {
mUserInfoHelper = injector.getUserInfoHelper();
mSettingsHelper = injector.getSettingsHelper();
mLocationPermissionsHelper = injector.getLocationPermissionsHelper();
mAppForegroundHelper = injector.getAppForegroundHelper();
+ mPackageResetHelper = injector.getPackageResetHelper();
mLocationManagerInternal = Objects.requireNonNull(
LocalServices.getService(LocationManagerInternal.class));
}
@@ -357,6 +372,7 @@
mLocationPackageBlacklistChangedListener);
mLocationPermissionsHelper.addListener(mLocationPermissionsListener);
mAppForegroundHelper.addListener(mAppForegroundChangedListener);
+ mPackageResetHelper.register(mPackageResetResponder);
}
@Override
@@ -374,6 +390,7 @@
mLocationPackageBlacklistChangedListener);
mLocationPermissionsHelper.removeListener(mLocationPermissionsListener);
mAppForegroundHelper.removeListener(mAppForegroundChangedListener);
+ mPackageResetHelper.unregister(mPackageResetResponder);
}
private void onUserChanged(int userId, int change) {
@@ -407,6 +424,27 @@
updateRegistrations(registration -> registration.onForegroundChanged(uid, foreground));
}
+ private void onPackageReset(String packageName) {
+ // invoked when a package is "force quit" - move off the main thread
+ FgThread.getExecutor().execute(
+ () ->
+ updateRegistrations(
+ registration -> {
+ if (registration.getIdentity().getPackageName().equals(
+ packageName)) {
+ registration.remove();
+ }
+
+ return false;
+ }));
+ }
+
+ private boolean isResetableForPackage(String packageName) {
+ // invoked to find out if the given package has any state that can be "force quit"
+ return findRegistration(
+ registration -> registration.getIdentity().getPackageName().equals(packageName));
+ }
+
@Override
protected String getServiceState() {
if (!isSupported()) {
diff --git a/services/core/java/com/android/server/location/injector/Injector.java b/services/core/java/com/android/server/location/injector/Injector.java
index c0ce3a6..b2c8672 100644
--- a/services/core/java/com/android/server/location/injector/Injector.java
+++ b/services/core/java/com/android/server/location/injector/Injector.java
@@ -63,4 +63,7 @@
/** Returns a LocationUsageLogger. */
LocationUsageLogger getLocationUsageLogger();
+
+ /** Returns a PackageResetHelper. */
+ PackageResetHelper getPackageResetHelper();
}
diff --git a/services/core/java/com/android/server/location/injector/PackageResetHelper.java b/services/core/java/com/android/server/location/injector/PackageResetHelper.java
new file mode 100644
index 0000000..721c576
--- /dev/null
+++ b/services/core/java/com/android/server/location/injector/PackageResetHelper.java
@@ -0,0 +1,101 @@
+/*
+ * Copyright (C) 2022 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.server.location.injector;
+
+import static com.android.server.location.LocationManagerService.D;
+import static com.android.server.location.LocationManagerService.TAG;
+
+import android.util.Log;
+
+import com.android.internal.annotations.GuardedBy;
+
+import java.util.concurrent.CopyOnWriteArrayList;
+
+/** Helpers for tracking queries and resets of package state. */
+public abstract class PackageResetHelper {
+
+ /** Interface for responding to reset events. */
+ public interface Responder {
+
+ /**
+ * Called when a package's runtime state is being reset for whatever reason and any
+ * components carrying runtime state on behalf of the package should clear that state.
+ *
+ * @param packageName The name of the package.
+ */
+ void onPackageReset(String packageName);
+
+ /**
+ * Called when the system queries whether this package has any active state for the given
+ * package. Should return true if the component has some runtime state that is resetable of
+ * behalf of the given package, and false otherwise.
+ *
+ * @param packageName The name of the package.
+ * @return True if this component has resetable state for the given package.
+ */
+ boolean isResetableForPackage(String packageName);
+ }
+
+ private final CopyOnWriteArrayList<Responder> mResponders;
+
+ public PackageResetHelper() {
+ mResponders = new CopyOnWriteArrayList<>();
+ }
+
+ /** Begin listening for package reset events. */
+ public synchronized void register(Responder responder) {
+ boolean empty = mResponders.isEmpty();
+ mResponders.add(responder);
+ if (empty) {
+ onRegister();
+ }
+ }
+
+ /** Stop listening for package reset events. */
+ public synchronized void unregister(Responder responder) {
+ mResponders.remove(responder);
+ if (mResponders.isEmpty()) {
+ onUnregister();
+ }
+ }
+
+ @GuardedBy("this")
+ protected abstract void onRegister();
+
+ @GuardedBy("this")
+ protected abstract void onUnregister();
+
+ protected final void notifyPackageReset(String packageName) {
+ if (D) {
+ Log.d(TAG, "package " + packageName + " reset");
+ }
+
+ for (Responder responder : mResponders) {
+ responder.onPackageReset(packageName);
+ }
+ }
+
+ protected final boolean queryResetableForPackage(String packageName) {
+ for (Responder responder : mResponders) {
+ if (responder.isResetableForPackage(packageName)) {
+ return true;
+ }
+ }
+
+ return false;
+ }
+}
diff --git a/services/core/java/com/android/server/location/injector/SystemPackageResetHelper.java b/services/core/java/com/android/server/location/injector/SystemPackageResetHelper.java
new file mode 100644
index 0000000..91b0212
--- /dev/null
+++ b/services/core/java/com/android/server/location/injector/SystemPackageResetHelper.java
@@ -0,0 +1,140 @@
+/*
+ * Copyright (C) 2022 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.server.location.injector;
+
+import android.annotation.Nullable;
+import android.app.Activity;
+import android.content.BroadcastReceiver;
+import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.content.pm.ApplicationInfo;
+import android.content.pm.PackageManager;
+import android.net.Uri;
+
+import com.android.internal.util.Preconditions;
+
+/** Listens to appropriate broadcasts for queries and resets. */
+public class SystemPackageResetHelper extends PackageResetHelper {
+
+ private final Context mContext;
+
+ @Nullable
+ private BroadcastReceiver mReceiver;
+
+ public SystemPackageResetHelper(Context context) {
+ mContext = context;
+ }
+
+ @Override
+ protected void onRegister() {
+ Preconditions.checkState(mReceiver == null);
+ mReceiver = new Receiver();
+
+ IntentFilter filter = new IntentFilter();
+ filter.addAction(Intent.ACTION_PACKAGE_CHANGED);
+ filter.addAction(Intent.ACTION_PACKAGE_REMOVED);
+ filter.addAction(Intent.ACTION_PACKAGE_RESTARTED);
+ filter.addAction(Intent.ACTION_QUERY_PACKAGE_RESTART);
+ filter.addDataScheme("package");
+
+ // We don't filter for Intent.ACTION_PACKAGE_DATA_CLEARED as 1) it refers to persistent
+ // data, and 2) it should always be preceded by Intent.ACTION_PACKAGE_RESTARTED, which
+ // refers to runtime data. in this way we also avoid redundant callbacks.
+
+ mContext.registerReceiver(mReceiver, filter);
+ }
+
+ @Override
+ protected void onUnregister() {
+ Preconditions.checkState(mReceiver != null);
+ mContext.unregisterReceiver(mReceiver);
+ mReceiver = null;
+ }
+
+ private class Receiver extends BroadcastReceiver {
+
+ @Override
+ public void onReceive(Context context, Intent intent) {
+ String action = intent.getAction();
+ if (action == null) {
+ return;
+ }
+
+ Uri data = intent.getData();
+ if (data == null) {
+ return;
+ }
+
+ String packageName = data.getSchemeSpecificPart();
+ if (packageName == null) {
+ return;
+ }
+
+ switch (action) {
+ case Intent.ACTION_QUERY_PACKAGE_RESTART:
+ String[] packages = intent.getStringArrayExtra(Intent.EXTRA_PACKAGES);
+ if (packages != null) {
+ // it would be more efficient to pass through the whole array, but at the
+ // moment the array is always size 1, and this makes for a nicer callback.
+ for (String pkg : packages) {
+ if (queryResetableForPackage(pkg)) {
+ setResultCode(Activity.RESULT_OK);
+ break;
+ }
+ }
+ }
+ break;
+ case Intent.ACTION_PACKAGE_CHANGED:
+ // make sure this is an enabled/disabled change to the package as a whole, not
+ // just some of its components. This avoids unnecessary work in the callback.
+ boolean isPackageChange = false;
+ String[] components = intent.getStringArrayExtra(
+ Intent.EXTRA_CHANGED_COMPONENT_NAME_LIST);
+ if (components != null) {
+ for (String component : components) {
+ if (packageName.equals(component)) {
+ isPackageChange = true;
+ break;
+ }
+ }
+ }
+
+ if (isPackageChange) {
+ try {
+ ApplicationInfo appInfo =
+ context.getPackageManager().getApplicationInfo(packageName,
+ PackageManager.ApplicationInfoFlags.of(0));
+ if (!appInfo.enabled) {
+ notifyPackageReset(packageName);
+ }
+ } catch (PackageManager.NameNotFoundException e) {
+ return;
+ }
+ }
+ break;
+ case Intent.ACTION_PACKAGE_REMOVED:
+ // fall through
+ case Intent.ACTION_PACKAGE_RESTARTED:
+ notifyPackageReset(packageName);
+ break;
+ default:
+ break;
+ }
+ }
+ }
+}
diff --git a/services/core/java/com/android/server/location/provider/LocationProviderManager.java b/services/core/java/com/android/server/location/provider/LocationProviderManager.java
index a69a079..bd75251 100644
--- a/services/core/java/com/android/server/location/provider/LocationProviderManager.java
+++ b/services/core/java/com/android/server/location/provider/LocationProviderManager.java
@@ -106,6 +106,7 @@
import com.android.server.location.injector.LocationPowerSaveModeHelper;
import com.android.server.location.injector.LocationPowerSaveModeHelper.LocationPowerSaveModeChangedListener;
import com.android.server.location.injector.LocationUsageLogger;
+import com.android.server.location.injector.PackageResetHelper;
import com.android.server.location.injector.ScreenInteractiveHelper;
import com.android.server.location.injector.ScreenInteractiveHelper.ScreenInteractiveChangedListener;
import com.android.server.location.injector.SettingsHelper;
@@ -1373,6 +1374,7 @@
protected final ScreenInteractiveHelper mScreenInteractiveHelper;
protected final LocationUsageLogger mLocationUsageLogger;
protected final LocationFudger mLocationFudger;
+ private final PackageResetHelper mPackageResetHelper;
private final UserListener mUserChangedListener = this::onUserChanged;
private final LocationSettings.LocationUserSettingsListener mLocationUserSettingsListener =
@@ -1407,6 +1409,18 @@
this::onLocationPowerSaveModeChanged;
private final ScreenInteractiveChangedListener mScreenInteractiveChangedListener =
this::onScreenInteractiveChanged;
+ private final PackageResetHelper.Responder mPackageResetResponder =
+ new PackageResetHelper.Responder() {
+ @Override
+ public void onPackageReset(String packageName) {
+ LocationProviderManager.this.onPackageReset(packageName);
+ }
+
+ @Override
+ public boolean isResetableForPackage(String packageName) {
+ return LocationProviderManager.this.isResetableForPackage(packageName);
+ }
+ };
// acquiring mMultiplexerLock makes operations on mProvider atomic, but is otherwise unnecessary
protected final MockableLocationProvider mProvider;
@@ -1442,6 +1456,7 @@
mScreenInteractiveHelper = injector.getScreenInteractiveHelper();
mLocationUsageLogger = injector.getLocationUsageLogger();
mLocationFudger = new LocationFudger(mSettingsHelper.getCoarseLocationAccuracyM());
+ mPackageResetHelper = injector.getPackageResetHelper();
mProvider = new MockableLocationProvider(mMultiplexerLock);
@@ -1970,6 +1985,7 @@
mAppForegroundHelper.addListener(mAppForegroundChangedListener);
mLocationPowerSaveModeHelper.addListener(mLocationPowerSaveModeChangedListener);
mScreenInteractiveHelper.addListener(mScreenInteractiveChangedListener);
+ mPackageResetHelper.register(mPackageResetResponder);
}
@GuardedBy("mMultiplexerLock")
@@ -1988,6 +2004,7 @@
mAppForegroundHelper.removeListener(mAppForegroundChangedListener);
mLocationPowerSaveModeHelper.removeListener(mLocationPowerSaveModeChangedListener);
mScreenInteractiveHelper.removeListener(mScreenInteractiveChangedListener);
+ mPackageResetHelper.unregister(mPackageResetResponder);
}
@GuardedBy("mMultiplexerLock")
@@ -2391,6 +2408,27 @@
updateRegistrations(registration -> registration.onLocationPermissionsChanged(uid));
}
+ private void onPackageReset(String packageName) {
+ // invoked when a package is "force quit" - move off the main thread
+ FgThread.getExecutor().execute(
+ () ->
+ updateRegistrations(
+ registration -> {
+ if (registration.getIdentity().getPackageName().equals(
+ packageName)) {
+ registration.remove();
+ }
+
+ return false;
+ }));
+ }
+
+ private boolean isResetableForPackage(String packageName) {
+ // invoked to find out if the given package has any state that can be "force quit"
+ return findRegistration(
+ registration -> registration.getIdentity().getPackageName().equals(packageName));
+ }
+
@GuardedBy("mMultiplexerLock")
@Override
public void onStateChanged(
diff --git a/services/core/java/com/android/server/media/MediaRouter2ServiceImpl.java b/services/core/java/com/android/server/media/MediaRouter2ServiceImpl.java
index bfa8af9..92a6329 100644
--- a/services/core/java/com/android/server/media/MediaRouter2ServiceImpl.java
+++ b/services/core/java/com/android/server/media/MediaRouter2ServiceImpl.java
@@ -601,6 +601,26 @@
}
}
+ public void dump(@NonNull PrintWriter pw, @NonNull String prefix) {
+ pw.println(prefix + "MediaRouter2ServiceImpl");
+
+ String indent = prefix + " ";
+
+ synchronized (mLock) {
+ pw.println(indent + "mNextRouterOrManagerId=" + mNextRouterOrManagerId.get());
+ pw.println(indent + "mCurrentUserId=" + mCurrentUserId);
+
+ pw.println(indent + "UserRecords:");
+ if (mUserRecords.size() > 0) {
+ for (int i = 0; i < mUserRecords.size(); i++) {
+ mUserRecords.get(i).dump(pw, indent + " ");
+ }
+ } else {
+ pw.println(indent + "<no user records>");
+ }
+ }
+ }
+
//TODO(b/136703681): Review this is handling multi-user properly.
void switchUser() {
synchronized (mLock) {
@@ -1197,6 +1217,41 @@
}
return null;
}
+
+ public void dump(@NonNull PrintWriter pw, @NonNull String prefix) {
+ pw.println(prefix + "UserRecord");
+
+ String indent = prefix + " ";
+
+ pw.println(indent + "mUserId=" + mUserId);
+
+ pw.println(indent + "Router Records:");
+ if (!mRouterRecords.isEmpty()) {
+ for (RouterRecord routerRecord : mRouterRecords) {
+ routerRecord.dump(pw, indent + " ");
+ }
+ } else {
+ pw.println(indent + "<no router records>");
+ }
+
+ pw.println(indent + "Manager Records:");
+ if (!mManagerRecords.isEmpty()) {
+ for (ManagerRecord managerRecord : mManagerRecords) {
+ managerRecord.dump(pw, indent + " ");
+ }
+ } else {
+ pw.println(indent + "<no manager records>");
+ }
+
+ if (!mHandler.runWithScissors(new Runnable() {
+ @Override
+ public void run() {
+ mHandler.dump(pw, indent);
+ }
+ }, 1000)) {
+ pw.println(indent + "<could not dump handler state>");
+ }
+ }
}
final class RouterRecord implements IBinder.DeathRecipient {
@@ -1236,6 +1291,22 @@
public void binderDied() {
routerDied(this);
}
+
+ public void dump(@NonNull PrintWriter pw, @NonNull String prefix) {
+ pw.println(prefix + "RouterRecord");
+
+ String indent = prefix + " ";
+
+ pw.println(indent + "mPackageName=" + mPackageName);
+ pw.println(indent + "mSelectRouteSequenceNumbers=" + mSelectRouteSequenceNumbers);
+ pw.println(indent + "mUid=" + mUid);
+ pw.println(indent + "mPid=" + mPid);
+ pw.println(indent + "mHasConfigureWifiDisplayPermission="
+ + mHasConfigureWifiDisplayPermission);
+ pw.println(indent + "mHasModifyAudioRoutingPermission="
+ + mHasModifyAudioRoutingPermission);
+ pw.println(indent + "mRouterId=" + mRouterId);
+ }
}
final class ManagerRecord implements IBinder.DeathRecipient {
@@ -1267,8 +1338,20 @@
managerDied(this);
}
- public void dump(PrintWriter pw, String prefix) {
- pw.println(prefix + this);
+ public void dump(@NonNull PrintWriter pw, @NonNull String prefix) {
+ pw.println(prefix + "ManagerRecord");
+
+ String indent = prefix + " ";
+
+ pw.println(indent + "mPackageName=" + mPackageName);
+ pw.println(indent + "mManagerId=" + mManagerId);
+ pw.println(indent + "mUid=" + mUid);
+ pw.println(indent + "mPid=" + mPid);
+ pw.println(indent + "mIsScanning=" + mIsScanning);
+
+ if (mLastSessionCreationRequest != null) {
+ mLastSessionCreationRequest.dump(pw, indent);
+ }
}
public void startScan() {
@@ -1455,6 +1538,15 @@
}
}
+ public void dump(@NonNull PrintWriter pw, @NonNull String prefix) {
+ pw.println(prefix + "UserHandler");
+
+ String indent = prefix + " ";
+ pw.println(indent + "mRunning=" + mRunning);
+
+ mWatcher.dump(pw, prefix);
+ }
+
private void onProviderStateChangedOnHandler(@NonNull MediaRoute2Provider provider) {
MediaRoute2ProviderInfo currentInfo = provider.getProviderInfo();
@@ -2340,5 +2432,14 @@
mOldSession = oldSession;
mRoute = route;
}
+
+ public void dump(@NonNull PrintWriter pw, @NonNull String prefix) {
+ pw.println(prefix + "SessionCreationRequest");
+
+ String indent = prefix + " ";
+
+ pw.println(indent + "mUniqueRequestId=" + mUniqueRequestId);
+ pw.println(indent + "mManagerRequestId=" + mManagerRequestId);
+ }
}
}
diff --git a/services/core/java/com/android/server/media/MediaRouterService.java b/services/core/java/com/android/server/media/MediaRouterService.java
index 4806b52..4f0da795 100644
--- a/services/core/java/com/android/server/media/MediaRouterService.java
+++ b/services/core/java/com/android/server/media/MediaRouterService.java
@@ -387,6 +387,9 @@
userRecord.dump(pw, "");
}
}
+
+ pw.println();
+ mService2.dump(pw, "");
}
// Binder call
diff --git a/services/core/java/com/android/server/resources/ResourcesManagerService.java b/services/core/java/com/android/server/resources/ResourcesManagerService.java
index cc27546..eec3a02 100644
--- a/services/core/java/com/android/server/resources/ResourcesManagerService.java
+++ b/services/core/java/com/android/server/resources/ResourcesManagerService.java
@@ -20,6 +20,7 @@
import android.annotation.Nullable;
import android.content.Context;
import android.content.res.IResourcesManager;
+import android.content.res.ResourceTimer;
import android.os.Binder;
import android.os.IBinder;
import android.os.ParcelFileDescriptor;
@@ -55,7 +56,7 @@
@Override
public void onStart() {
- // Intentionally left empty.
+ ResourceTimer.start();
}
private final IBinder mService = new IResourcesManager.Stub() {
diff --git a/services/core/jni/com_android_server_display_DisplayControl.cpp b/services/core/jni/com_android_server_display_DisplayControl.cpp
index 61f2b14..02e5061 100644
--- a/services/core/jni/com_android_server_display_DisplayControl.cpp
+++ b/services/core/jni/com_android_server_display_DisplayControl.cpp
@@ -17,6 +17,7 @@
#include <android_util_Binder.h>
#include <gui/SurfaceComposerClient.h>
#include <jni.h>
+#include <nativehelper/ScopedPrimitiveArray.h>
#include <nativehelper/ScopedUtfChars.h>
namespace android {
@@ -33,6 +34,27 @@
SurfaceComposerClient::destroyDisplay(token);
}
+static void nativeOverrideHdrTypes(JNIEnv* env, jclass clazz, jobject tokenObject,
+ jintArray jHdrTypes) {
+ sp<IBinder> token(ibinderForJavaObject(env, tokenObject));
+ if (token == nullptr || jHdrTypes == nullptr) return;
+
+ ScopedIntArrayRO hdrTypes(env, jHdrTypes);
+ size_t numHdrTypes = hdrTypes.size();
+
+ std::vector<ui::Hdr> hdrTypesVector;
+ hdrTypesVector.reserve(numHdrTypes);
+ for (int i = 0; i < numHdrTypes; i++) {
+ hdrTypesVector.push_back(static_cast<ui::Hdr>(hdrTypes[i]));
+ }
+
+ status_t error = SurfaceComposerClient::overrideHdrTypes(token, hdrTypesVector);
+ if (error != NO_ERROR) {
+ jniThrowExceptionFmt(env, "java/lang/SecurityException",
+ "ACCESS_SURFACE_FLINGER is missing");
+ }
+}
+
// ----------------------------------------------------------------------------
static const JNINativeMethod sDisplayMethods[] = {
@@ -41,6 +63,8 @@
(void*)nativeCreateDisplay },
{"nativeDestroyDisplay", "(Landroid/os/IBinder;)V",
(void*)nativeDestroyDisplay },
+ {"nativeOverrideHdrTypes", "(Landroid/os/IBinder;[I)V",
+ (void*)nativeOverrideHdrTypes },
// clang-format on
};
diff --git a/services/tests/mockingservicestests/src/com/android/server/display/DisplayPowerController2Test.java b/services/tests/mockingservicestests/src/com/android/server/display/DisplayPowerController2Test.java
new file mode 100644
index 0000000..1a5d496
--- /dev/null
+++ b/services/tests/mockingservicestests/src/com/android/server/display/DisplayPowerController2Test.java
@@ -0,0 +1,235 @@
+/*
+ * Copyright (C) 2022 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.server.display;
+
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.verify;
+
+import static org.junit.Assert.assertNotNull;
+import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.ArgumentMatchers.isA;
+import static org.mockito.Mockito.spy;
+import static org.mockito.Mockito.when;
+
+import android.content.Context;
+import android.content.ContextWrapper;
+import android.content.res.Resources;
+import android.hardware.Sensor;
+import android.hardware.SensorEventListener;
+import android.hardware.SensorManager;
+import android.hardware.display.DisplayManagerInternal.DisplayPowerCallbacks;
+import android.hardware.display.DisplayManagerInternal.DisplayPowerRequest;
+import android.os.Handler;
+import android.os.PowerManager;
+import android.os.test.TestLooper;
+import android.util.FloatProperty;
+import android.view.Display;
+import android.view.DisplayInfo;
+
+import androidx.test.core.app.ApplicationProvider;
+import androidx.test.filters.SmallTest;
+import androidx.test.runner.AndroidJUnit4;
+
+import com.android.server.LocalServices;
+import com.android.server.display.RampAnimator.DualRampAnimator;
+import com.android.server.policy.WindowManagerPolicy;
+import com.android.server.testutils.OffsettableClock;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.ArgumentCaptor;
+import org.mockito.Captor;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+
+import java.util.List;
+
+
+@SmallTest
+@RunWith(AndroidJUnit4.class)
+public final class DisplayPowerController2Test {
+ private static final String UNIQUE_DISPLAY_ID = "unique_id_test123";
+ private static final int DISPLAY_ID = 42;
+
+ private OffsettableClock mClock;
+ private TestLooper mTestLooper;
+ private Handler mHandler;
+ private DisplayPowerController2.Injector mInjector;
+ private Context mContextSpy;
+
+ @Mock
+ private DisplayPowerCallbacks mDisplayPowerCallbacksMock;
+ @Mock
+ private SensorManager mSensorManagerMock;
+ @Mock
+ private DisplayBlanker mDisplayBlankerMock;
+ @Mock
+ private LogicalDisplay mLogicalDisplayMock;
+ @Mock
+ private DisplayDevice mDisplayDeviceMock;
+ @Mock
+ private BrightnessTracker mBrightnessTrackerMock;
+ @Mock
+ private BrightnessSetting mBrightnessSettingMock;
+ @Mock
+ private WindowManagerPolicy mWindowManagerPolicyMock;
+ @Mock
+ private PowerManager mPowerManagerMock;
+ @Mock
+ private Resources mResourcesMock;
+ @Mock
+ private DisplayDeviceConfig mDisplayDeviceConfigMock;
+ @Mock
+ private DisplayPowerState mDisplayPowerStateMock;
+ @Mock
+ private DualRampAnimator<DisplayPowerState> mDualRampAnimatorMock;
+
+ @Captor
+ private ArgumentCaptor<SensorEventListener> mSensorEventListenerCaptor;
+
+ @Before
+ public void setUp() throws Exception {
+ MockitoAnnotations.initMocks(this);
+ mContextSpy = spy(new ContextWrapper(ApplicationProvider.getApplicationContext()));
+ mClock = new OffsettableClock.Stopped();
+ mTestLooper = new TestLooper(mClock::now);
+ mHandler = new Handler(mTestLooper.getLooper());
+ mInjector = new DisplayPowerController2.Injector() {
+ @Override
+ DisplayPowerController2.Clock getClock() {
+ return mClock::now;
+ }
+
+ @Override
+ DisplayPowerState getDisplayPowerState(DisplayBlanker blanker, ColorFade colorFade,
+ int displayId, int displayState) {
+ return mDisplayPowerStateMock;
+ }
+
+ @Override
+ DualRampAnimator<DisplayPowerState> getDualRampAnimator(DisplayPowerState dps,
+ FloatProperty<DisplayPowerState> firstProperty,
+ FloatProperty<DisplayPowerState> secondProperty) {
+ return mDualRampAnimatorMock;
+ }
+ };
+
+ addLocalServiceMock(WindowManagerPolicy.class, mWindowManagerPolicyMock);
+
+ when(mContextSpy.getSystemService(eq(PowerManager.class))).thenReturn(mPowerManagerMock);
+ when(mContextSpy.getResources()).thenReturn(mResourcesMock);
+ }
+
+ @After
+ public void tearDown() {
+ LocalServices.removeServiceForTest(WindowManagerPolicy.class);
+ }
+
+ @Test
+ public void testReleaseProxSuspendBlockersOnExit() throws Exception {
+ setUpDisplay(DISPLAY_ID, UNIQUE_DISPLAY_ID);
+
+ Sensor proxSensor = setUpProxSensor();
+
+ DisplayPowerController2 dpc = new DisplayPowerController2(
+ mContextSpy, mInjector, mDisplayPowerCallbacksMock, mHandler,
+ mSensorManagerMock, mDisplayBlankerMock, mLogicalDisplayMock,
+ mBrightnessTrackerMock, mBrightnessSettingMock, () -> {
+ });
+
+ when(mDisplayPowerStateMock.getScreenState()).thenReturn(Display.STATE_ON);
+ // send a display power request
+ DisplayPowerRequest dpr = new DisplayPowerRequest();
+ dpr.policy = DisplayPowerRequest.POLICY_BRIGHT;
+ dpr.useProximitySensor = true;
+ dpc.requestPowerState(dpr, false /* waitForNegativeProximity */);
+
+ // Run updatePowerState to start listener for the prox sensor
+ advanceTime(1);
+
+ SensorEventListener listener = getSensorEventListener(proxSensor);
+ assertNotNull(listener);
+
+ listener.onSensorChanged(TestUtils.createSensorEvent(proxSensor, 5 /* lux */));
+ advanceTime(1);
+
+ // two times, one for unfinished business and one for proximity
+ verify(mDisplayPowerCallbacksMock).acquireSuspendBlocker(
+ dpc.getSuspendBlockerUnfinishedBusinessId(DISPLAY_ID));
+ verify(mDisplayPowerCallbacksMock).acquireSuspendBlocker(
+ dpc.getSuspendBlockerProxDebounceId(DISPLAY_ID));
+
+ dpc.stop();
+ advanceTime(1);
+
+ // two times, one for unfinished business and one for proximity
+ verify(mDisplayPowerCallbacksMock).releaseSuspendBlocker(
+ dpc.getSuspendBlockerUnfinishedBusinessId(DISPLAY_ID));
+ verify(mDisplayPowerCallbacksMock).releaseSuspendBlocker(
+ dpc.getSuspendBlockerProxDebounceId(DISPLAY_ID));
+ }
+
+ /**
+ * Creates a mock and registers it to {@link LocalServices}.
+ */
+ private static <T> void addLocalServiceMock(Class<T> clazz, T mock) {
+ LocalServices.removeServiceForTest(clazz);
+ LocalServices.addService(clazz, mock);
+ }
+
+ private void advanceTime(long timeMs) {
+ mClock.fastForward(timeMs);
+ mTestLooper.dispatchAll();
+ }
+
+ private Sensor setUpProxSensor() throws Exception {
+ Sensor proxSensor = TestUtils.createSensor(
+ Sensor.TYPE_PROXIMITY, Sensor.STRING_TYPE_PROXIMITY);
+ when(mSensorManagerMock.getSensorList(eq(Sensor.TYPE_ALL)))
+ .thenReturn(List.of(proxSensor));
+ return proxSensor;
+ }
+
+ private SensorEventListener getSensorEventListener(Sensor sensor) {
+ verify(mSensorManagerMock).registerListener(mSensorEventListenerCaptor.capture(),
+ eq(sensor), eq(SensorManager.SENSOR_DELAY_NORMAL), isA(Handler.class));
+ return mSensorEventListenerCaptor.getValue();
+ }
+
+ private void setUpDisplay(int displayId, String uniqueId) {
+ DisplayInfo info = new DisplayInfo();
+ DisplayDeviceInfo deviceInfo = new DisplayDeviceInfo();
+
+ when(mLogicalDisplayMock.getDisplayIdLocked()).thenReturn(displayId);
+ when(mLogicalDisplayMock.getPrimaryDisplayDeviceLocked()).thenReturn(mDisplayDeviceMock);
+ when(mLogicalDisplayMock.getDisplayInfoLocked()).thenReturn(info);
+ when(mLogicalDisplayMock.isEnabled()).thenReturn(true);
+ when(mLogicalDisplayMock.getPhase()).thenReturn(LogicalDisplay.DISPLAY_PHASE_ENABLED);
+ when(mDisplayDeviceMock.getDisplayDeviceInfoLocked()).thenReturn(deviceInfo);
+ when(mDisplayDeviceMock.getUniqueId()).thenReturn(uniqueId);
+ when(mDisplayDeviceMock.getDisplayDeviceConfig()).thenReturn(mDisplayDeviceConfigMock);
+ when(mDisplayDeviceConfigMock.getProximitySensor()).thenReturn(
+ new DisplayDeviceConfig.SensorData() {
+ {
+ type = Sensor.STRING_TYPE_PROXIMITY;
+ name = null;
+ }
+ });
+ when(mDisplayDeviceConfigMock.getNits()).thenReturn(new float[]{2, 500});
+ }
+}
diff --git a/services/tests/mockingservicestests/src/com/android/server/location/injector/FakePackageResetHelper.java b/services/tests/mockingservicestests/src/com/android/server/location/injector/FakePackageResetHelper.java
new file mode 100644
index 0000000..c2768d51
--- /dev/null
+++ b/services/tests/mockingservicestests/src/com/android/server/location/injector/FakePackageResetHelper.java
@@ -0,0 +1,38 @@
+/*
+ * Copyright (C) 2022 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.server.location.injector;
+
+/** Version of PackageResetHelper for testing. */
+public class FakePackageResetHelper extends PackageResetHelper {
+
+ public FakePackageResetHelper() {}
+
+ @Override
+ protected void onRegister() {}
+
+ @Override
+ protected void onUnregister() {}
+
+ public boolean isResetableForPackage(String packageName) {
+ return queryResetableForPackage(packageName);
+ }
+
+ public void reset(String packageName) {
+ notifyPackageReset(packageName);
+ }
+}
+
diff --git a/services/tests/mockingservicestests/src/com/android/server/location/injector/TestInjector.java b/services/tests/mockingservicestests/src/com/android/server/location/injector/TestInjector.java
index 02cacb7..ca73091 100644
--- a/services/tests/mockingservicestests/src/com/android/server/location/injector/TestInjector.java
+++ b/services/tests/mockingservicestests/src/com/android/server/location/injector/TestInjector.java
@@ -35,6 +35,7 @@
private final FakeDeviceIdleHelper mDeviceIdleHelper;
private final FakeEmergencyHelper mEmergencyHelper;
private final LocationUsageLogger mLocationUsageLogger;
+ private final FakePackageResetHelper mPackageResetHelper;
public TestInjector(Context context) {
mUserInfoHelper = new FakeUserInfoHelper();
@@ -50,6 +51,7 @@
mDeviceIdleHelper = new FakeDeviceIdleHelper();
mEmergencyHelper = new FakeEmergencyHelper();
mLocationUsageLogger = new LocationUsageLogger();
+ mPackageResetHelper = new FakePackageResetHelper();
}
@Override
@@ -116,4 +118,9 @@
public LocationUsageLogger getLocationUsageLogger() {
return mLocationUsageLogger;
}
+
+ @Override
+ public FakePackageResetHelper getPackageResetHelper() {
+ return mPackageResetHelper;
+ }
}
diff --git a/services/tests/mockingservicestests/src/com/android/server/location/provider/LocationProviderManagerTest.java b/services/tests/mockingservicestests/src/com/android/server/location/provider/LocationProviderManagerTest.java
index 0ac1443..20e4e80 100644
--- a/services/tests/mockingservicestests/src/com/android/server/location/provider/LocationProviderManagerTest.java
+++ b/services/tests/mockingservicestests/src/com/android/server/location/provider/LocationProviderManagerTest.java
@@ -1218,6 +1218,44 @@
assertThat(mProvider.getRequest().isActive()).isFalse();
}
+ @Test
+ public void testQueryPackageReset() {
+ assertThat(mInjector.getPackageResetHelper().isResetableForPackage("mypackage")).isFalse();
+
+ ILocationListener listener1 = createMockLocationListener();
+ mManager.registerLocationRequest(new LocationRequest.Builder(0).setWorkSource(
+ WORK_SOURCE).build(), IDENTITY, PERMISSION_FINE, listener1);
+ assertThat(mInjector.getPackageResetHelper().isResetableForPackage("mypackage")).isTrue();
+
+ ILocationListener listener2 = createMockLocationListener();
+ mManager.registerLocationRequest(new LocationRequest.Builder(0).setWorkSource(
+ WORK_SOURCE).build(), IDENTITY, PERMISSION_FINE, listener2);
+ assertThat(mInjector.getPackageResetHelper().isResetableForPackage("mypackage")).isTrue();
+
+ mManager.unregisterLocationRequest(listener1);
+ assertThat(mInjector.getPackageResetHelper().isResetableForPackage("mypackage")).isTrue();
+
+ mManager.unregisterLocationRequest(listener2);
+ assertThat(mInjector.getPackageResetHelper().isResetableForPackage("mypackage")).isFalse();
+ }
+
+ @Test
+ public void testPackageReset() {
+ ILocationListener listener1 = createMockLocationListener();
+ mManager.registerLocationRequest(new LocationRequest.Builder(0).setWorkSource(
+ WORK_SOURCE).build(), IDENTITY, PERMISSION_FINE, listener1);
+ ILocationListener listener2 = createMockLocationListener();
+ mManager.registerLocationRequest(new LocationRequest.Builder(0).setWorkSource(
+ WORK_SOURCE).build(), IDENTITY, PERMISSION_FINE, listener2);
+
+ assertThat(mProvider.getRequest().isActive()).isTrue();
+ assertThat(mInjector.getPackageResetHelper().isResetableForPackage("mypackage")).isTrue();
+
+ mInjector.getPackageResetHelper().reset("mypackage");
+ assertThat(mProvider.getRequest().isActive()).isFalse();
+ assertThat(mInjector.getPackageResetHelper().isResetableForPackage("mypackage")).isFalse();
+ }
+
private ILocationListener createMockLocationListener() {
return spy(new ILocationListener.Stub() {
@Override
diff --git a/tests/FlickerTests/AndroidManifest.xml b/tests/FlickerTests/AndroidManifest.xml
index e173eba0..487a0c3 100644
--- a/tests/FlickerTests/AndroidManifest.xml
+++ b/tests/FlickerTests/AndroidManifest.xml
@@ -40,6 +40,8 @@
<uses-permission android:name="android.permission.READ_LOGS"/>
<!-- ATM.removeRootTasksWithActivityTypes() -->
<uses-permission android:name="android.permission.MANAGE_ACTIVITY_TASKS" />
+ <!-- ActivityOptions.makeCustomTaskAnimation() -->
+ <uses-permission android:name="android.permission.START_TASKS_FROM_RECENTS" />
<!-- Allow the test to write directly to /sdcard/ -->
<application android:requestLegacyExternalStorage="true">
<uses-library android:name="android.test.runner"/>
diff --git a/tests/FlickerTests/res/anim/show_2000ms.xml b/tests/FlickerTests/res/anim/show_2000ms.xml
new file mode 100644
index 0000000..76e375f
--- /dev/null
+++ b/tests/FlickerTests/res/anim/show_2000ms.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ ~ Copyright (C) 2022 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.
+ -->
+
+<translate xmlns:android="http://schemas.android.com/apk/res/android"
+ android:duration="2000"
+ android:fromXDelta="0"
+ android:toXDelta="0" />
\ No newline at end of file
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OverrideTaskTransitionTest.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OverrideTaskTransitionTest.kt
new file mode 100644
index 0000000..4f21412
--- /dev/null
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OverrideTaskTransitionTest.kt
@@ -0,0 +1,112 @@
+/*
+ * Copyright (C) 2022 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.server.wm.flicker.launch
+
+import android.app.Instrumentation
+import android.os.Bundle
+import android.os.Handler
+import android.platform.test.annotations.Presubmit
+import androidx.test.filters.RequiresDevice
+import androidx.test.platform.app.InstrumentationRegistry
+import com.android.server.wm.flicker.FlickerBuilderProvider
+import com.android.server.wm.flicker.FlickerParametersRunnerFactory
+import com.android.server.wm.flicker.FlickerTestParameter
+import com.android.server.wm.flicker.FlickerTestParameterFactory
+import com.android.server.wm.flicker.R
+import com.android.server.wm.flicker.annotation.Group4
+import com.android.server.wm.flicker.dsl.FlickerBuilder
+import com.android.server.wm.flicker.helpers.SimpleAppHelper
+import com.android.server.wm.flicker.helpers.StandardAppHelper
+import com.android.server.wm.flicker.helpers.setRotation
+import com.android.server.wm.flicker.helpers.wakeUpAndGoToHomeScreen
+import com.android.server.wm.flicker.rules.RemoveAllTasksButHomeRule
+import com.android.server.wm.traces.common.ComponentNameMatcher
+import com.android.server.wm.traces.common.WindowManagerConditionsFactory
+import org.junit.FixMethodOrder
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.junit.runners.MethodSorters
+import org.junit.runners.Parameterized
+
+/**
+ * Test the [android.app.ActivityOptions.makeCustomTaskAnimation].
+ *
+ * To run this test: `atest FlickerTests:OverrideTaskTransitionTest`
+ *
+ * Actions:
+ * Launches SimpleActivity with alpha_2000ms animation
+ */
+@RequiresDevice
+@RunWith(Parameterized::class)
+@Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class)
+@FixMethodOrder(MethodSorters.NAME_ASCENDING)
+@Group4
+class OverrideTaskTransitionTest(val testSpec: FlickerTestParameter) {
+
+ private val instrumentation: Instrumentation = InstrumentationRegistry.getInstrumentation()
+ private val testApp: StandardAppHelper = SimpleAppHelper(instrumentation)
+
+ @FlickerBuilderProvider
+ fun buildFlicker(): FlickerBuilder {
+ return FlickerBuilder(instrumentation).apply {
+ setup {
+ device.wakeUpAndGoToHomeScreen()
+ RemoveAllTasksButHomeRule.removeAllTasksButHome()
+ setRotation(testSpec.startRotation)
+ }
+ transitions {
+ instrumentation.context.startActivity(
+ testApp.openAppIntent, createCustomTaskAnimation())
+ wmHelper.StateSyncBuilder()
+ .add(WindowManagerConditionsFactory.isWMStateComplete())
+ .withAppTransitionIdle()
+ .withWindowSurfaceAppeared(testApp)
+ .waitForAndVerify()
+ }
+ teardown {
+ testApp.exit()
+ }
+ }
+ }
+
+ @Presubmit
+ @Test
+ fun testSimpleActivityIsShownDirectly() {
+ testSpec.assertLayers {
+ isVisible(ComponentNameMatcher.LAUNCHER)
+ .isInvisible(ComponentNameMatcher.SPLASH_SCREEN)
+ .isInvisible(testApp)
+ .then()
+ // The custom animation should block the entire launcher from the very beginning
+ .isInvisible(ComponentNameMatcher.LAUNCHER)
+ }
+ }
+
+ private fun createCustomTaskAnimation(): Bundle {
+ return android.app.ActivityOptions.makeCustomTaskAnimation(instrumentation.context,
+ R.anim.show_2000ms, 0, Handler.getMain(), null, null).toBundle()
+ }
+
+ companion object {
+ @Parameterized.Parameters(name = "{0}")
+ @JvmStatic
+ fun getParams(): Collection<FlickerTestParameter> {
+ return FlickerTestParameterFactory.getInstance()
+ .getConfigNonRotationTests()
+ }
+ }
+}
diff --git a/tests/vcn/java/com/android/server/VcnManagementServiceTest.java b/tests/vcn/java/com/android/server/VcnManagementServiceTest.java
index 54b3c40..f924b2e 100644
--- a/tests/vcn/java/com/android/server/VcnManagementServiceTest.java
+++ b/tests/vcn/java/com/android/server/VcnManagementServiceTest.java
@@ -23,6 +23,7 @@
import static android.net.NetworkCapabilities.TRANSPORT_WIFI;
import static android.net.vcn.VcnManager.VCN_STATUS_CODE_ACTIVE;
import static android.net.vcn.VcnManager.VCN_STATUS_CODE_SAFE_MODE;
+import static android.telephony.SubscriptionManager.INVALID_SUBSCRIPTION_ID;
import static android.telephony.TelephonyManager.CARRIER_PRIVILEGE_STATUS_HAS_ACCESS;
import static android.telephony.TelephonyManager.CARRIER_PRIVILEGE_STATUS_NO_ACCESS;
@@ -276,7 +277,6 @@
@Test
public void testSystemReady() throws Exception {
mVcnMgmtSvc.systemReady();
- mTestLooper.dispatchAll();
verify(mConnMgr).registerNetworkProvider(any(VcnNetworkProvider.class));
verify(mSubscriptionTracker).register();
@@ -494,8 +494,10 @@
mVcnMgmtSvc.addVcnUnderlyingNetworkPolicyListener(mMockPolicyListener);
triggerSubscriptionTrackerCbAndGetSnapshot(null, Collections.emptySet());
- mTestLooper.dispatchAll();
+ // Verify teardown after delay
+ mTestLooper.moveTimeForward(VcnManagementService.CARRIER_PRIVILEGES_LOST_TEARDOWN_DELAY_MS);
+ mTestLooper.dispatchAll();
verify(vcn).teardownAsynchronously();
verify(mMockPolicyListener).onPolicyChanged();
}
@@ -521,6 +523,92 @@
assertEquals(0, mVcnMgmtSvc.getAllVcns().size());
}
+ /**
+ * Tests an intermediate state where carrier privileges are marked as lost before active data
+ * subId changes during a SIM ejection.
+ *
+ * <p>The expected outcome is that the VCN is torn down after a delay, as opposed to
+ * immediately.
+ */
+ @Test
+ public void testTelephonyNetworkTrackerCallbackLostCarrierPrivilegesBeforeActiveDataSubChanges()
+ throws Exception {
+ setupActiveSubscription(TEST_UUID_2);
+
+ final TelephonySubscriptionTrackerCallback cb = getTelephonySubscriptionTrackerCallback();
+ final Vcn vcn = startAndGetVcnInstance(TEST_UUID_2);
+
+ // Simulate privileges lost
+ triggerSubscriptionTrackerCbAndGetSnapshot(
+ TEST_SUBSCRIPTION_ID,
+ TEST_UUID_2,
+ Collections.emptySet(),
+ Collections.emptyMap(),
+ false /* hasCarrierPrivileges */);
+
+ // Verify teardown after delay
+ mTestLooper.moveTimeForward(VcnManagementService.CARRIER_PRIVILEGES_LOST_TEARDOWN_DELAY_MS);
+ mTestLooper.dispatchAll();
+ verify(vcn).teardownAsynchronously();
+ }
+
+ @Test
+ public void testTelephonyNetworkTrackerCallbackSimSwitchesDoNotKillVcnInstances()
+ throws Exception {
+ setupActiveSubscription(TEST_UUID_2);
+
+ final TelephonySubscriptionTrackerCallback cb = getTelephonySubscriptionTrackerCallback();
+ final Vcn vcn = startAndGetVcnInstance(TEST_UUID_2);
+
+ // Simulate SIM unloaded
+ triggerSubscriptionTrackerCbAndGetSnapshot(
+ INVALID_SUBSCRIPTION_ID,
+ null /* activeDataSubscriptionGroup */,
+ Collections.emptySet(),
+ Collections.emptyMap(),
+ false /* hasCarrierPrivileges */);
+
+ // Simulate new SIM loaded right during teardown delay.
+ mTestLooper.moveTimeForward(
+ VcnManagementService.CARRIER_PRIVILEGES_LOST_TEARDOWN_DELAY_MS / 2);
+ mTestLooper.dispatchAll();
+ triggerSubscriptionTrackerCbAndGetSnapshot(TEST_UUID_2, Collections.singleton(TEST_UUID_2));
+
+ // Verify that even after the full timeout duration, the VCN instance is not torn down
+ mTestLooper.moveTimeForward(VcnManagementService.CARRIER_PRIVILEGES_LOST_TEARDOWN_DELAY_MS);
+ mTestLooper.dispatchAll();
+ verify(vcn, never()).teardownAsynchronously();
+ }
+
+ @Test
+ public void testTelephonyNetworkTrackerCallbackDoesNotKillNewVcnInstances() throws Exception {
+ setupActiveSubscription(TEST_UUID_2);
+
+ final TelephonySubscriptionTrackerCallback cb = getTelephonySubscriptionTrackerCallback();
+ final Vcn oldInstance = startAndGetVcnInstance(TEST_UUID_2);
+
+ // Simulate SIM unloaded
+ triggerSubscriptionTrackerCbAndGetSnapshot(null, Collections.emptySet());
+
+ // Config cleared, SIM reloaded & config re-added right before teardown delay, staring new
+ // vcnInstance.
+ mTestLooper.moveTimeForward(
+ VcnManagementService.CARRIER_PRIVILEGES_LOST_TEARDOWN_DELAY_MS / 2);
+ mTestLooper.dispatchAll();
+ mVcnMgmtSvc.clearVcnConfig(TEST_UUID_2, TEST_PACKAGE_NAME);
+ triggerSubscriptionTrackerCbAndGetSnapshot(TEST_UUID_2, Collections.singleton(TEST_UUID_2));
+ final Vcn newInstance = startAndGetVcnInstance(TEST_UUID_2);
+
+ // Verify that new instance was different, and the old one was torn down
+ assertTrue(oldInstance != newInstance);
+ verify(oldInstance).teardownAsynchronously();
+
+ // Verify that even after the full timeout duration, the new VCN instance is not torn down
+ mTestLooper.moveTimeForward(VcnManagementService.CARRIER_PRIVILEGES_LOST_TEARDOWN_DELAY_MS);
+ mTestLooper.dispatchAll();
+ verify(newInstance, never()).teardownAsynchronously();
+ }
+
@Test
public void testPackageChangeListenerRegistered() throws Exception {
verify(mMockContext).registerReceiver(any(BroadcastReceiver.class), argThat(filter -> {
@@ -910,8 +998,6 @@
private void setupSubscriptionAndStartVcn(
int subId, ParcelUuid subGrp, boolean isVcnActive, boolean hasCarrierPrivileges) {
mVcnMgmtSvc.systemReady();
- mTestLooper.dispatchAll();
-
triggerSubscriptionTrackerCbAndGetSnapshot(
subGrp,
Collections.singleton(subGrp),
@@ -1007,7 +1093,6 @@
private void setupTrackedCarrierWifiNetwork(NetworkCapabilities caps) {
mVcnMgmtSvc.systemReady();
- mTestLooper.dispatchAll();
final ArgumentCaptor<NetworkCallback> captor =
ArgumentCaptor.forClass(NetworkCallback.class);
@@ -1252,14 +1337,15 @@
true /* isActive */,
true /* hasCarrierPrivileges */);
- // VCN is currently active. Lose carrier privileges for TEST_PACKAGE so the VCN goes
- // inactive.
+ // VCN is currently active. Lose carrier privileges for TEST_PACKAGE and hit teardown
+ // timeout so the VCN goes inactive.
final TelephonySubscriptionSnapshot snapshot =
triggerSubscriptionTrackerCbAndGetSnapshot(
TEST_UUID_1,
Collections.singleton(TEST_UUID_1),
Collections.singletonMap(TEST_SUBSCRIPTION_ID, TEST_UUID_1),
false /* hasCarrierPrivileges */);
+ mTestLooper.moveTimeForward(VcnManagementService.CARRIER_PRIVILEGES_LOST_TEARDOWN_DELAY_MS);
mTestLooper.dispatchAll();
// Giving TEST_PACKAGE privileges again will restart the VCN (which will indicate ACTIVE