diff options
9 files changed, 121 insertions, 42 deletions
diff --git a/core/java/android/os/PowerManager.java b/core/java/android/os/PowerManager.java index 3aa0bcb6abee..4dae7c7c96d5 100644 --- a/core/java/android/os/PowerManager.java +++ b/core/java/android/os/PowerManager.java @@ -548,6 +548,7 @@ public final class PowerManager { WAKE_REASON_HDMI, WAKE_REASON_DISPLAY_GROUP_ADDED, WAKE_REASON_DISPLAY_GROUP_TURNED_ON, + WAKE_REASON_UNFOLD_DEVICE }) @Retention(RetentionPolicy.SOURCE) public @interface WakeReason{} @@ -647,6 +648,12 @@ public final class PowerManager { public static final int WAKE_REASON_DISPLAY_GROUP_TURNED_ON = 11; /** + * Wake up reason code: Waking the device due to unfolding of a foldable device. + * @hide + */ + public static final int WAKE_REASON_UNFOLD_DEVICE = 12; + + /** * Convert the wake reason to a string for debugging purposes. * @hide */ @@ -664,6 +671,7 @@ public final class PowerManager { case WAKE_REASON_LID: return "WAKE_REASON_LID"; case WAKE_REASON_DISPLAY_GROUP_ADDED: return "WAKE_REASON_DISPLAY_GROUP_ADDED"; case WAKE_REASON_DISPLAY_GROUP_TURNED_ON: return "WAKE_REASON_DISPLAY_GROUP_TURNED_ON"; + case WAKE_REASON_UNFOLD_DEVICE: return "WAKE_REASON_UNFOLD_DEVICE"; default: return Integer.toString(wakeReason); } } diff --git a/core/res/res/values/config.xml b/core/res/res/values/config.xml index ed80013ff412..cbe0eef6408b 100644 --- a/core/res/res/values/config.xml +++ b/core/res/res/values/config.xml @@ -654,6 +654,11 @@ --> </integer-array> + <!-- When entering this device state (defined in device_state_configuration.xml), + we should wake the device. -1 to disable the feature (do not wake on any device-state + transition). --> + <integer name="config_deviceStateOnWhichToWakeUp">-1</integer> + <!-- Indicate the display area rect for foldable devices in folded state. --> <string name="config_foldedArea"></string> diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml index 0c0db2c4e549..fbc629e2dc81 100644 --- a/core/res/res/values/symbols.xml +++ b/core/res/res/values/symbols.xml @@ -3846,6 +3846,7 @@ <!-- For Foldables --> <java-symbol type="array" name="config_foldedDeviceStates" /> + <java-symbol type="integer" name="config_deviceStateOnWhichToWakeUp" /> <java-symbol type="string" name="config_foldedArea" /> <java-symbol type="bool" name="config_supportsConcurrentInternalDisplays" /> <java-symbol type="bool" name="config_unfoldTransitionEnabled" /> diff --git a/packages/SystemUI/src/com/android/keyguard/LockIconView.java b/packages/SystemUI/src/com/android/keyguard/LockIconView.java index 371564a98aad..ef4353b93179 100644 --- a/packages/SystemUI/src/com/android/keyguard/LockIconView.java +++ b/packages/SystemUI/src/com/android/keyguard/LockIconView.java @@ -48,6 +48,7 @@ public class LockIconView extends FrameLayout implements Dumpable { private ImageView mBgView; private int mLockIconColor; + private boolean mUseBackground = false; public LockIconView(Context context, AttributeSet attrs) { super(context, attrs); @@ -61,8 +62,8 @@ public class LockIconView extends FrameLayout implements Dumpable { mBgView = findViewById(R.id.lock_icon_bg); } - void updateColorAndBackgroundVisibility(boolean useBackground) { - if (useBackground && mLockIcon.getDrawable() != null) { + void updateColorAndBackgroundVisibility() { + if (mUseBackground && mLockIcon.getDrawable() != null) { mLockIconColor = Utils.getColorAttrDefaultColor(getContext(), android.R.attr.textColorPrimary); mBgView.setBackground(getContext().getDrawable(R.drawable.fingerprint_bg)); @@ -78,6 +79,9 @@ public class LockIconView extends FrameLayout implements Dumpable { void setImageDrawable(Drawable drawable) { mLockIcon.setImageDrawable(drawable); + + if (!mUseBackground) return; + if (drawable == null) { mBgView.setVisibility(View.INVISIBLE); } else { @@ -86,6 +90,14 @@ public class LockIconView extends FrameLayout implements Dumpable { } /** + * Whether or not to render the lock icon background. Mainly used for UDPFS. + */ + public void setUseBackground(boolean useBackground) { + mUseBackground = useBackground; + updateColorAndBackgroundVisibility(); + } + + /** * Set the location of the lock icon. */ @VisibleForTesting diff --git a/packages/SystemUI/src/com/android/keyguard/LockIconViewController.java b/packages/SystemUI/src/com/android/keyguard/LockIconViewController.java index 28e19acce506..8cfd225fc4df 100644 --- a/packages/SystemUI/src/com/android/keyguard/LockIconViewController.java +++ b/packages/SystemUI/src/com/android/keyguard/LockIconViewController.java @@ -346,7 +346,7 @@ public class LockIconViewController extends ViewController<LockIconView> impleme } private void updateColors() { - mView.updateColorAndBackgroundVisibility(mUdfpsSupported); + mView.updateColorAndBackgroundVisibility(); } private void updateConfiguration() { @@ -427,6 +427,8 @@ public class LockIconViewController extends ViewController<LockIconView> impleme boolean wasUdfpsEnrolled = mUdfpsEnrolled; mUdfpsSupported = mAuthController.getUdfpsSensorLocation() != null; + mView.setUseBackground(mUdfpsSupported); + mUdfpsEnrolled = mKeyguardUpdateMonitor.isUdfpsEnrolled(); if (wasUdfpsSupported != mUdfpsSupported || wasUdfpsEnrolled != mUdfpsEnrolled) { updateVisibility(); diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/LockIconViewControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/keyguard/LockIconViewControllerTest.java index af6dee76139e..c464cad3e0b2 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/LockIconViewControllerTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/LockIconViewControllerTest.java @@ -32,6 +32,7 @@ import android.os.Vibrator; import android.testing.AndroidTestingRunner; import android.testing.TestableLooper; import android.util.DisplayMetrics; +import android.util.Pair; import android.view.View; import android.view.accessibility.AccessibilityManager; @@ -128,20 +129,7 @@ public class LockIconViewControllerTest extends SysuiTestCase { @Test public void testUpdateFingerprintLocationOnInit() { // GIVEN fp sensor location is available pre-attached - final PointF udfpsLocation = new PointF(50, 75); - final int radius = 33; - final FingerprintSensorPropertiesInternal fpProps = - new FingerprintSensorPropertiesInternal( - /* sensorId */ 0, - /* strength */ 0, - /* max enrollments per user */ 5, - /* component info */ new ArrayList<>(), - /* sensorType */ 3, - /* resetLockoutRequiresHwToken */ false, - List.of(new SensorLocationInternal("" /* displayId */, - (int) udfpsLocation.x, (int) udfpsLocation.y, radius))); - when(mAuthController.getUdfpsSensorLocation()).thenReturn(udfpsLocation); - when(mAuthController.getUdfpsProps()).thenReturn(List.of(fpProps)); + Pair<Integer, PointF> udfps = setupUdfps(); // WHEN lock icon view controller is initialized and attached mLockIconViewController.init(); @@ -149,8 +137,8 @@ public class LockIconViewControllerTest extends SysuiTestCase { mAttachListener.onViewAttachedToWindow(mLockIconView); // THEN lock icon view location is updated with the same coordinates as fpProps - verify(mLockIconView).setCenterLocation(mPointCaptor.capture(), eq(radius)); - assertEquals(udfpsLocation, mPointCaptor.getValue()); + verify(mLockIconView).setCenterLocation(mPointCaptor.capture(), eq(udfps.first)); + assertEquals(udfps.second, mPointCaptor.getValue()); } @Test @@ -164,6 +152,47 @@ public class LockIconViewControllerTest extends SysuiTestCase { // GIVEN fp sensor location is available post-atttached captureAuthControllerCallback(); + Pair<Integer, PointF> udfps = setupUdfps(); + + // WHEN all authenticators are registered + mAuthControllerCallback.onAllAuthenticatorsRegistered(); + + // THEN lock icon view location is updated with the same coordinates as fpProps + verify(mLockIconView).setCenterLocation(mPointCaptor.capture(), eq(udfps.first)); + assertEquals(udfps.second, mPointCaptor.getValue()); + } + + @Test + public void testLockIconViewBackgroundEnabledWhenUdfpsIsAvailable() { + // GIVEN Udpfs sensor location is available + setupUdfps(); + + mLockIconViewController.init(); + captureAttachListener(); + + // WHEN the view is attached + mAttachListener.onViewAttachedToWindow(mLockIconView); + + // THEN the lock icon view background should be enabled + verify(mLockIconView).setUseBackground(true); + } + + @Test + public void testLockIconViewBackgroundDisabledWhenUdfpsIsUnavailable() { + // GIVEN Udfps sensor location is not available + when(mAuthController.getUdfpsSensorLocation()).thenReturn(null); + + mLockIconViewController.init(); + captureAttachListener(); + + // WHEN the view is attached + mAttachListener.onViewAttachedToWindow(mLockIconView); + + // THEN the lock icon view background should be disabled + verify(mLockIconView).setUseBackground(false); + } + + private Pair<Integer, PointF> setupUdfps() { final PointF udfpsLocation = new PointF(50, 75); final int radius = 33; final FingerprintSensorPropertiesInternal fpProps = @@ -179,12 +208,7 @@ public class LockIconViewControllerTest extends SysuiTestCase { when(mAuthController.getUdfpsSensorLocation()).thenReturn(udfpsLocation); when(mAuthController.getUdfpsProps()).thenReturn(List.of(fpProps)); - // WHEN all authenticators are registered - mAuthControllerCallback.onAllAuthenticatorsRegistered(); - - // THEN lock icon view location is updated with the same coordinates as fpProps - verify(mLockIconView).setCenterLocation(mPointCaptor.capture(), eq(radius)); - assertEquals(udfpsLocation, mPointCaptor.getValue()); + return new Pair(radius, udfpsLocation); } private void captureAuthControllerCallback() { diff --git a/services/core/java/com/android/server/display/DisplayPowerController.java b/services/core/java/com/android/server/display/DisplayPowerController.java index abbe13ac260f..1063481d6788 100644 --- a/services/core/java/com/android/server/display/DisplayPowerController.java +++ b/services/core/java/com/android/server/display/DisplayPowerController.java @@ -721,13 +721,13 @@ final class DisplayPowerController implements AutomaticBrightnessController.Call final IBinder token = device.getDisplayTokenLocked(); final DisplayDeviceInfo info = device.getDisplayDeviceInfoLocked(); mHandler.post(() -> { - if (mDisplayDevice == device) { - return; + if (mDisplayDevice != device) { + mDisplayDevice = device; + mUniqueDisplayId = uniqueId; + mDisplayDeviceConfig = config; + loadFromDisplayDeviceConfig(token, info); + updatePowerState(); } - mDisplayDevice = device; - mUniqueDisplayId = uniqueId; - mDisplayDeviceConfig = config; - loadFromDisplayDeviceConfig(token, info); }); } diff --git a/services/core/java/com/android/server/display/LogicalDisplayMapper.java b/services/core/java/com/android/server/display/LogicalDisplayMapper.java index 973dcc4c79e5..0fbc3e8fb89a 100644 --- a/services/core/java/com/android/server/display/LogicalDisplayMapper.java +++ b/services/core/java/com/android/server/display/LogicalDisplayMapper.java @@ -22,6 +22,8 @@ import android.hardware.devicestate.DeviceStateManager; import android.os.Handler; import android.os.Looper; import android.os.Message; +import android.os.PowerManager; +import android.os.SystemClock; import android.os.SystemProperties; import android.text.TextUtils; import android.util.ArraySet; @@ -69,7 +71,7 @@ class LogicalDisplayMapper implements DisplayDeviceRepository.Listener { public static final int DISPLAY_GROUP_EVENT_CHANGED = 2; public static final int DISPLAY_GROUP_EVENT_REMOVED = 3; - private static final int TIMEOUT_STATE_TRANSITION_MILLIS = 500; + private static final int TIMEOUT_STATE_TRANSITION_MILLIS = 300; private static final int MSG_TRANSITION_TO_PENDING_DEVICE_STATE = 1; @@ -98,6 +100,11 @@ class LogicalDisplayMapper implements DisplayDeviceRepository.Listener { private final boolean mSupportsConcurrentInternalDisplays; /** + * Wake the device when transitioning into this device state. + */ + private final int mDeviceStateOnWhichToWakeUp; + + /** * Map of all logical displays indexed by logical display id. * Any modification to mLogicalDisplays must invalidate the DisplayManagerGlobal cache. * TODO: multi-display - Move the aforementioned comment? @@ -113,6 +120,7 @@ class LogicalDisplayMapper implements DisplayDeviceRepository.Listener { private final Listener mListener; private final DisplayManagerService.SyncRoot mSyncRoot; private final LogicalDisplayMapperHandler mHandler; + private final PowerManager mPowerManager; /** * Has an entry for every logical display that the rest of the system has been notified about. @@ -150,12 +158,15 @@ class LogicalDisplayMapper implements DisplayDeviceRepository.Listener { @NonNull Listener listener, @NonNull DisplayManagerService.SyncRoot syncRoot, @NonNull Handler handler) { mSyncRoot = syncRoot; + mPowerManager = context.getSystemService(PowerManager.class); mHandler = new LogicalDisplayMapperHandler(handler.getLooper()); mDisplayDeviceRepo = repo; mListener = listener; mSingleDisplayDemoMode = SystemProperties.getBoolean("persist.demo.singledisplay", false); mSupportsConcurrentInternalDisplays = context.getResources().getBoolean( com.android.internal.R.bool.config_supportsConcurrentInternalDisplays); + mDeviceStateOnWhichToWakeUp = context.getResources().getInteger( + com.android.internal.R.integer.config_deviceStateOnWhichToWakeUp); mDisplayDeviceRepo.addListener(this); mDeviceStateToLayoutMap = new DeviceStateToLayoutMap(); } @@ -318,6 +329,7 @@ class LogicalDisplayMapper implements DisplayDeviceRepository.Listener { ipw.println("mSingleDisplayDemoMode=" + mSingleDisplayDemoMode); ipw.println("mCurrentLayout=" + mCurrentLayout); + ipw.println("mDeviceStateOnWhichToWakeUp=" + mDeviceStateOnWhichToWakeUp); final int logicalDisplayCount = mLogicalDisplays.size(); ipw.println(); @@ -335,7 +347,9 @@ class LogicalDisplayMapper implements DisplayDeviceRepository.Listener { } void setDeviceStateLocked(int state) { - Slog.i(TAG, "Requesting Transition to state: " + state); + final boolean isInteractive = mPowerManager.isInteractive(); + Slog.i(TAG, "Requesting Transition to state: " + state + ", from state=" + mDeviceState + + ", interactive=" + isInteractive); // As part of a state transition, we may need to turn off some displays temporarily so that // the transition is smooth. Plus, on some devices, only one internal displays can be // on at a time. We use DISPLAY_PHASE_LAYOUT_TRANSITION to mark a display that needs to be @@ -344,8 +358,13 @@ class LogicalDisplayMapper implements DisplayDeviceRepository.Listener { resetLayoutLocked(mDeviceState, state, LogicalDisplay.DISPLAY_PHASE_LAYOUT_TRANSITION); } mPendingDeviceState = state; - if (areAllTransitioningDisplaysOffLocked()) { - // Nothing to wait on, we're good to go + final boolean wakeDevice = mPendingDeviceState == mDeviceStateOnWhichToWakeUp + && !isInteractive; + + // If all displays are off already, we can just transition here, unless the device is asleep + // and we plan on waking it up. In that case, fall through to the call to wakeUp, and defer + // the final transition until later once the device is awake. + if (areAllTransitioningDisplaysOffLocked() && !wakeDevice) { transitionToPendingStateLocked(); return; } @@ -356,6 +375,14 @@ class LogicalDisplayMapper implements DisplayDeviceRepository.Listener { // Send the transitioning phase updates to DisplayManager so that the displays can // start turning OFF in preparation for the new layout. updateLogicalDisplaysLocked(); + + if (wakeDevice) { + // We already told the displays to turn off, now we need to wake the device as + // we transition to this new state. We do it here so that the waking happens between the + // transition from one layout to another. + mPowerManager.wakeUp(SystemClock.uptimeMillis(), PowerManager.WAKE_REASON_UNFOLD_DEVICE, + "server.display:unfold"); + } mHandler.sendEmptyMessageDelayed(MSG_TRANSITION_TO_PENDING_DEVICE_STATE, TIMEOUT_STATE_TRANSITION_MILLIS); } @@ -482,6 +509,7 @@ class LogicalDisplayMapper implements DisplayDeviceRepository.Listener { assignDisplayGroupLocked(display); mLogicalDisplaysToUpdate.put(displayId, LOGICAL_DISPLAY_EVENT_CHANGED); + // The display is involved in a display layout transition } else if (updateState == UPDATE_STATE_TRANSITION) { mLogicalDisplaysToUpdate.put(displayId, LOGICAL_DISPLAY_EVENT_DEVICE_STATE_TRANSITION); @@ -672,14 +700,14 @@ class LogicalDisplayMapper implements DisplayDeviceRepository.Listener { // We consider a display-device as changing/transition if // 1) It's already marked as transitioning - // 2) It's going from enabled to disabled + // 2) It's going from enabled to disabled, or vice versa // 3) It's enabled, but it's mapped to a new logical display ID. To the user this // would look like apps moving from one screen to another since task-stacks stay // with the logical display [ID]. final boolean isTransitioning = (logicalDisplay.getPhase() == LogicalDisplay.DISPLAY_PHASE_LAYOUT_TRANSITION) - || (wasEnabled && !willBeEnabled) - || (wasEnabled && deviceHasNewLogicalDisplayId); + || (wasEnabled != willBeEnabled) + || deviceHasNewLogicalDisplayId; if (isTransitioning) { setDisplayPhase(logicalDisplay, phase); diff --git a/services/core/java/com/android/server/wm/EnsureActivitiesVisibleHelper.java b/services/core/java/com/android/server/wm/EnsureActivitiesVisibleHelper.java index 852a6583b680..6560d15ca744 100644 --- a/services/core/java/com/android/server/wm/EnsureActivitiesVisibleHelper.java +++ b/services/core/java/com/android/server/wm/EnsureActivitiesVisibleHelper.java @@ -103,13 +103,12 @@ class EnsureActivitiesVisibleHelper { ArrayList<TaskFragment> adjacentTaskFragments = null; for (int i = mTaskFragment.mChildren.size() - 1; i >= 0; --i) { final WindowContainer child = mTaskFragment.mChildren.get(i); - if (child.asTaskFragment() != null) { - final TaskFragment childTaskFragment = child.asTaskFragment(); + final TaskFragment childTaskFragment = child.asTaskFragment(); + if (childTaskFragment != null && childTaskFragment.topRunningActivity() != null) { childTaskFragment.updateActivityVisibilities(starting, configChanges, preserveWindows, notifyClients); mBehindFullyOccludedContainer |= - childTaskFragment.topRunningActivity() != null - && childTaskFragment.getBounds().equals(mTaskFragment.getBounds()); + childTaskFragment.getBounds().equals(mTaskFragment.getBounds()); if (mAboveTop && mTop.getTaskFragment() == childTaskFragment) { mAboveTop = false; } |