diff options
125 files changed, 2085 insertions, 1108 deletions
diff --git a/core/java/android/app/Notification.java b/core/java/android/app/Notification.java index 7aedd30d660e..96d785f0df9c 100644 --- a/core/java/android/app/Notification.java +++ b/core/java/android/app/Notification.java @@ -4067,8 +4067,9 @@ public class Notification implements Parcelable * notification if alerts for this notification's group should be handled by a different * notification. This is only applicable for notifications that belong to a * {@link #setGroup(String) group}. This must be called on all notifications you want to - * mute. For example, if you want only the summary of your group to make noise, all - * children in the group should have the group alert behavior {@link #GROUP_ALERT_SUMMARY}. + * mute. For example, if you want only the summary of your group to make noise and/or peek + * on screen, all children in the group should have the group alert behavior + * {@link #GROUP_ALERT_SUMMARY}. * * <p> The default value is {@link #GROUP_ALERT_ALL}.</p> */ diff --git a/core/java/android/app/admin/DevicePolicyManager.java b/core/java/android/app/admin/DevicePolicyManager.java index bad6c77a17f3..7dabe60e3ba8 100644 --- a/core/java/android/app/admin/DevicePolicyManager.java +++ b/core/java/android/app/admin/DevicePolicyManager.java @@ -6413,7 +6413,7 @@ public class DevicePolicyManager { public void lockNow(@LockNowFlag int flags) { if (mService != null) { try { - mService.lockNow(flags, mParentInstance); + mService.lockNow(flags, mContext.getPackageName(), mParentInstance); } catch (RemoteException e) { throw e.rethrowFromSystemServer(); } @@ -13646,8 +13646,8 @@ public class DevicePolicyManager { * privacy-sensitive events happening outside the managed profile would have been redacted * already. * - * @param admin Which device admin this request is associated with. Null if the caller is not - * a device admin + * @param admin Which device admin this request is associated with, or {@code null} + * if called by a delegated app. * @param enabled whether security logging should be enabled or not. * @throws SecurityException if the caller is not permitted to control security logging. * @see #setAffiliationIds @@ -13699,8 +13699,8 @@ public class DevicePolicyManager { * it must be affiliated with the device. Otherwise a {@link SecurityException} will be thrown. * See {@link #isAffiliatedUser}. * - * @param admin Which device admin this request is associated with. Null if the caller is not - * a device admin. + * @param admin Which device admin this request is associated with, or {@code null} + * if called by a delegated app. * @return the new batch of security logs which is a list of {@link SecurityEvent}, * or {@code null} if rate limitation is exceeded or if logging is currently disabled. * @throws SecurityException if the caller is not allowed to access security logging, @@ -13857,8 +13857,8 @@ public class DevicePolicyManager { * it must be affiliated with the device. Otherwise a {@link SecurityException} will be thrown. * See {@link #isAffiliatedUser}. * - * @param admin Which device admin this request is associated with. Null if the caller is not - * a device admin. + * @param admin Which device admin this request is associated with, or {@code null} + * if called by a delegated app. * @return Device logs from before the latest reboot of the system, or {@code null} if this API * is not supported on the device. * @throws SecurityException if the caller is not allowed to access security logging, or diff --git a/core/java/android/app/admin/DevicePolicyResources.java b/core/java/android/app/admin/DevicePolicyResources.java index a49891356ccd..593f73635617 100644 --- a/core/java/android/app/admin/DevicePolicyResources.java +++ b/core/java/android/app/admin/DevicePolicyResources.java @@ -1857,6 +1857,17 @@ public final class DevicePolicyResources { public static final String WORK_PROFILE_TELEPHONY_PAUSED_TURN_ON_BUTTON = PREFIX + "TURN_ON_WORK_PROFILE_BUTTON_TEXT"; + public static final String MINIRESOLVER_OPEN_IN_WORK = + PREFIX + "MINIRESOLVER_OPEN_IN_WORK"; + + public static final String MINIRESOLVER_OPEN_IN_PERSONAL = + PREFIX + "MINIRESOLVER_OPEN_IN_PERSONAL"; + + public static final String MINIRESOLVER_USE_WORK_BROWSER = + PREFIX + "MINIRESOLVER_OPEN_IN_PERSONAL"; + + public static final String MINIRESOLVER_USE_PERSONAL_BROWSER = + PREFIX + "MINIRESOLVER_OPEN_IN_PERSONAL"; } /** diff --git a/core/java/android/app/admin/IDevicePolicyManager.aidl b/core/java/android/app/admin/IDevicePolicyManager.aidl index 8d508c0fb79d..9b0b18ac74ec 100644 --- a/core/java/android/app/admin/IDevicePolicyManager.aidl +++ b/core/java/android/app/admin/IDevicePolicyManager.aidl @@ -119,7 +119,7 @@ interface IDevicePolicyManager { void setRequiredStrongAuthTimeout(in ComponentName who, String callerPackageName, long timeMs, boolean parent); long getRequiredStrongAuthTimeout(in ComponentName who, int userId, boolean parent); - void lockNow(int flags, boolean parent); + void lockNow(int flags, String callerPackageName, boolean parent); /** * @param factoryReset only applicable when `targetSdk >= U`, either tries to factoryReset/fail or removeUser/fail otherwise diff --git a/core/java/android/companion/virtual/VirtualDeviceManager.java b/core/java/android/companion/virtual/VirtualDeviceManager.java index da6784be4404..2ca2b79bcc08 100644 --- a/core/java/android/companion/virtual/VirtualDeviceManager.java +++ b/core/java/android/companion/virtual/VirtualDeviceManager.java @@ -64,6 +64,7 @@ import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; import java.util.ArrayList; import java.util.List; +import java.util.Objects; import java.util.concurrent.Executor; import java.util.function.IntConsumer; @@ -171,6 +172,7 @@ public final class VirtualDeviceManager { public VirtualDevice createVirtualDevice( int associationId, @NonNull VirtualDeviceParams params) { + Objects.requireNonNull(params, "params must not be null"); try { return new VirtualDevice(mService, mContext, associationId, params); } catch (RemoteException e) { @@ -409,6 +411,9 @@ public final class VirtualDeviceManager { @NonNull PendingIntent pendingIntent, @NonNull Executor executor, @NonNull IntConsumer listener) { + Objects.requireNonNull(pendingIntent, "pendingIntent must not be null"); + Objects.requireNonNull(executor, "executor must not be null"); + Objects.requireNonNull(listener, "listener must not be null"); mVirtualDeviceInternal.launchPendingIntent( displayId, pendingIntent, executor, listener); } @@ -483,6 +488,7 @@ public final class VirtualDeviceManager { @NonNull VirtualDisplayConfig config, @Nullable @CallbackExecutor Executor executor, @Nullable VirtualDisplay.Callback callback) { + Objects.requireNonNull(config, "config must not be null"); return mVirtualDeviceInternal.createVirtualDisplay(config, executor, callback); } @@ -503,6 +509,7 @@ public final class VirtualDeviceManager { @RequiresPermission(android.Manifest.permission.CREATE_VIRTUAL_DEVICE) @NonNull public VirtualDpad createVirtualDpad(@NonNull VirtualDpadConfig config) { + Objects.requireNonNull(config, "config must not be null"); return mVirtualDeviceInternal.createVirtualDpad(config); } @@ -514,6 +521,7 @@ public final class VirtualDeviceManager { @RequiresPermission(android.Manifest.permission.CREATE_VIRTUAL_DEVICE) @NonNull public VirtualKeyboard createVirtualKeyboard(@NonNull VirtualKeyboardConfig config) { + Objects.requireNonNull(config, "config must not be null"); return mVirtualDeviceInternal.createVirtualKeyboard(config); } @@ -550,6 +558,7 @@ public final class VirtualDeviceManager { @RequiresPermission(android.Manifest.permission.CREATE_VIRTUAL_DEVICE) @NonNull public VirtualMouse createVirtualMouse(@NonNull VirtualMouseConfig config) { + Objects.requireNonNull(config, "config must not be null"); return mVirtualDeviceInternal.createVirtualMouse(config); } @@ -587,6 +596,7 @@ public final class VirtualDeviceManager { @NonNull public VirtualTouchscreen createVirtualTouchscreen( @NonNull VirtualTouchscreenConfig config) { + Objects.requireNonNull(config, "config must not be null"); return mVirtualDeviceInternal.createVirtualTouchscreen(config); } @@ -659,6 +669,7 @@ public final class VirtualDeviceManager { @NonNull VirtualDisplay display, @Nullable Executor executor, @Nullable AudioConfigurationChangeCallback callback) { + Objects.requireNonNull(display, "display must not be null"); return mVirtualDeviceInternal.createVirtualAudioDevice(display, executor, callback); } diff --git a/core/java/android/credentials/CredentialManager.java b/core/java/android/credentials/CredentialManager.java index 00ce17adfda6..9140d02e223d 100644 --- a/core/java/android/credentials/CredentialManager.java +++ b/core/java/android/credentials/CredentialManager.java @@ -460,9 +460,17 @@ public final class CredentialManager { return false; } + /** + * Returns whether the service is enabled. + * + * @hide + */ private boolean isServiceEnabled() { - return DeviceConfig.getBoolean( - DeviceConfig.NAMESPACE_CREDENTIAL, DEVICE_CONFIG_ENABLE_CREDENTIAL_MANAGER, true); + try { + return mService.isServiceEnabled(); + } catch (RemoteException e) { + return false; + } } /** diff --git a/core/java/android/credentials/ICredentialManager.aidl b/core/java/android/credentials/ICredentialManager.aidl index 5fde96b0b9ff..b779c56035d3 100644 --- a/core/java/android/credentials/ICredentialManager.aidl +++ b/core/java/android/credentials/ICredentialManager.aidl @@ -58,5 +58,7 @@ interface ICredentialManager { List<CredentialProviderInfo> getCredentialProviderServices(in int userId, in int providerFilter); List<CredentialProviderInfo> getCredentialProviderServicesForTesting(in int providerFilter); + + boolean isServiceEnabled(); } diff --git a/core/java/android/hardware/display/DisplayManager.java b/core/java/android/hardware/display/DisplayManager.java index b5281a5025b8..2aead3c22deb 100644 --- a/core/java/android/hardware/display/DisplayManager.java +++ b/core/java/android/hardware/display/DisplayManager.java @@ -1758,10 +1758,12 @@ public final class DisplayManager { /** * Key for the brightness throttling data as a String formatted: * <displayId>,<no of throttling levels>,[<severity as string>,<brightness cap>] - * Where the latter part is repeated for each throttling level, and the entirety is repeated - * for each display, separated by a semicolon. + * [,<throttlingId>]? + * Where [<severity as string>,<brightness cap>] is repeated for each throttling level. + * The entirety is repeated for each display and throttling id, separated by a semicolon. * For example: * 123,1,critical,0.8;456,2,moderate,0.9,critical,0.7 + * 123,1,critical,0.8,default;123,1,moderate,0.6,id_2;456,2,moderate,0.9,critical,0.7 */ String KEY_BRIGHTNESS_THROTTLING_DATA = "brightness_throttling_data"; } diff --git a/core/java/android/service/notification/ZenModeConfig.java b/core/java/android/service/notification/ZenModeConfig.java index 5c2b38963005..402da28b3c5c 100644 --- a/core/java/android/service/notification/ZenModeConfig.java +++ b/core/java/android/service/notification/ZenModeConfig.java @@ -1008,9 +1008,8 @@ public class ZenModeConfig implements Parcelable { .allowAlarms(allowAlarms) .allowMedia(allowMedia) .allowSystem(allowSystem) - .allowConversations(allowConversations - ? ZenModeConfig.getZenPolicySenders(allowConversationsFrom) - : ZenPolicy.PEOPLE_TYPE_NONE); + .allowConversations(allowConversations ? allowConversationsFrom + : ZenPolicy.CONVERSATION_SENDERS_NONE); if (suppressedVisualEffects == 0) { builder.showAllVisualEffects(); } else { diff --git a/core/java/android/view/ViewRootImpl.java b/core/java/android/view/ViewRootImpl.java index c30b5953adce..2f5cd5434b89 100644 --- a/core/java/android/view/ViewRootImpl.java +++ b/core/java/android/view/ViewRootImpl.java @@ -265,6 +265,7 @@ public final class ViewRootImpl implements ViewParent, private static final boolean DEBUG_KEEP_SCREEN_ON = false || LOCAL_LOGV; private static final boolean DEBUG_CONTENT_CAPTURE = false || LOCAL_LOGV; private static final boolean DEBUG_SCROLL_CAPTURE = false || LOCAL_LOGV; + private static final boolean DEBUG_TOUCH_NAVIGATION = false || LOCAL_LOGV; private static final boolean DEBUG_BLAST = false || LOCAL_LOGV; private static final int LOGTAG_INPUT_FOCUS = 62001; @@ -7122,7 +7123,8 @@ public final class ViewRootImpl implements ViewParent, mJoystick.cancel(); } else if ((source & InputDevice.SOURCE_TOUCH_NAVIGATION) == InputDevice.SOURCE_TOUCH_NAVIGATION) { - mTouchNavigation.cancel(event); + // Touch navigation events cannot be cancelled since they are dispatched + // immediately. } } } @@ -7641,392 +7643,109 @@ public final class ViewRootImpl implements ViewParent, } /** - * Creates dpad events from unhandled touch navigation movements. + * Creates DPAD events from unhandled touch navigation movements. */ final class SyntheticTouchNavigationHandler extends Handler { private static final String LOCAL_TAG = "SyntheticTouchNavigationHandler"; - private static final boolean LOCAL_DEBUG = false; - - // Assumed nominal width and height in millimeters of a touch navigation pad, - // if no resolution information is available from the input system. - private static final float DEFAULT_WIDTH_MILLIMETERS = 48; - private static final float DEFAULT_HEIGHT_MILLIMETERS = 48; - - /* TODO: These constants should eventually be moved to ViewConfiguration. */ - - // The nominal distance traveled to move by one unit. - private static final int TICK_DISTANCE_MILLIMETERS = 12; - - // Minimum and maximum fling velocity in ticks per second. - // The minimum velocity should be set such that we perform enough ticks per - // second that the fling appears to be fluid. For example, if we set the minimum - // to 2 ticks per second, then there may be up to half a second delay between the next - // to last and last ticks which is noticeably discrete and jerky. This value should - // probably not be set to anything less than about 4. - // If fling accuracy is a problem then consider tuning the tick distance instead. - private static final float MIN_FLING_VELOCITY_TICKS_PER_SECOND = 6f; - private static final float MAX_FLING_VELOCITY_TICKS_PER_SECOND = 20f; - - // Fling velocity decay factor applied after each new key is emitted. - // This parameter controls the deceleration and overall duration of the fling. - // The fling stops automatically when its velocity drops below the minimum - // fling velocity defined above. - private static final float FLING_TICK_DECAY = 0.8f; - - /* The input device that we are tracking. */ + // The id of the input device that is being tracked. private int mCurrentDeviceId = -1; private int mCurrentSource; - private boolean mCurrentDeviceSupported; - - /* Configuration for the current input device. */ - // The scaled tick distance. A movement of this amount should generally translate - // into a single dpad event in a given direction. - private float mConfigTickDistance; - - // The minimum and maximum scaled fling velocity. - private float mConfigMinFlingVelocity; - private float mConfigMaxFlingVelocity; - - /* Tracking state. */ - - // The velocity tracker for detecting flings. - private VelocityTracker mVelocityTracker; + private int mPendingKeyMetaState; - // The active pointer id, or -1 if none. - private int mActivePointerId = -1; + private final GestureDetector mGestureDetector = new GestureDetector(mContext, + new GestureDetector.OnGestureListener() { + @Override + public boolean onDown(@NonNull MotionEvent e) { + // This can be ignored since it's not clear what KeyEvent this will + // belong to. + return true; + } - // Location where tracking started. - private float mStartX; - private float mStartY; + @Override + public void onShowPress(@NonNull MotionEvent e) { - // Most recently observed position. - private float mLastX; - private float mLastY; + } - // Accumulated movement delta since the last direction key was sent. - private float mAccumulatedX; - private float mAccumulatedY; + @Override + public boolean onSingleTapUp(@NonNull MotionEvent e) { + dispatchTap(e.getEventTime()); + return true; + } - // Set to true if any movement was delivered to the app. - // Implies that tap slop was exceeded. - private boolean mConsumedMovement; + @Override + public boolean onScroll(@Nullable MotionEvent e1, @NonNull MotionEvent e2, + float distanceX, float distanceY) { + // Scroll doesn't translate to DPAD events so should be ignored. + return true; + } - // The most recently sent key down event. - // The keycode remains set until the direction changes or a fling ends - // so that repeated key events may be generated as required. - private long mPendingKeyDownTime; - private int mPendingKeyCode = KeyEvent.KEYCODE_UNKNOWN; - private int mPendingKeyRepeatCount; - private int mPendingKeyMetaState; + @Override + public void onLongPress(@NonNull MotionEvent e) { + // Long presses don't translate to DPAD events so should be ignored. + } - // The current fling velocity while a fling is in progress. - private boolean mFlinging; - private float mFlingVelocity; + @Override + public boolean onFling(@Nullable MotionEvent e1, @NonNull MotionEvent e2, + float velocityX, float velocityY) { + dispatchFling(velocityX, velocityY, e2.getEventTime()); + return true; + } + }); - public SyntheticTouchNavigationHandler() { + SyntheticTouchNavigationHandler() { super(true); } public void process(MotionEvent event) { + if (event.getDevice() == null) { + // The current device is not supported. + if (DEBUG_TOUCH_NAVIGATION) { + Log.d(LOCAL_TAG, + "Current device not supported so motion event is not processed"); + } + return; + } + mPendingKeyMetaState = event.getMetaState(); // Update the current device information. - final long time = event.getEventTime(); final int deviceId = event.getDeviceId(); final int source = event.getSource(); if (mCurrentDeviceId != deviceId || mCurrentSource != source) { - finishKeys(time); - finishTracking(time); mCurrentDeviceId = deviceId; mCurrentSource = source; - mCurrentDeviceSupported = false; - InputDevice device = event.getDevice(); - if (device != null) { - // In order to support an input device, we must know certain - // characteristics about it, such as its size and resolution. - InputDevice.MotionRange xRange = device.getMotionRange(MotionEvent.AXIS_X); - InputDevice.MotionRange yRange = device.getMotionRange(MotionEvent.AXIS_Y); - if (xRange != null && yRange != null) { - mCurrentDeviceSupported = true; - - // Infer the resolution if it not actually known. - float xRes = xRange.getResolution(); - if (xRes <= 0) { - xRes = xRange.getRange() / DEFAULT_WIDTH_MILLIMETERS; - } - float yRes = yRange.getResolution(); - if (yRes <= 0) { - yRes = yRange.getRange() / DEFAULT_HEIGHT_MILLIMETERS; - } - float nominalRes = (xRes + yRes) * 0.5f; - - // Precompute all of the configuration thresholds we will need. - mConfigTickDistance = TICK_DISTANCE_MILLIMETERS * nominalRes; - mConfigMinFlingVelocity = - MIN_FLING_VELOCITY_TICKS_PER_SECOND * mConfigTickDistance; - mConfigMaxFlingVelocity = - MAX_FLING_VELOCITY_TICKS_PER_SECOND * mConfigTickDistance; - - if (LOCAL_DEBUG) { - Log.d(LOCAL_TAG, "Configured device " + mCurrentDeviceId - + " (" + Integer.toHexString(mCurrentSource) + "): " - + ", mConfigTickDistance=" + mConfigTickDistance - + ", mConfigMinFlingVelocity=" + mConfigMinFlingVelocity - + ", mConfigMaxFlingVelocity=" + mConfigMaxFlingVelocity); - } - } - } - } - if (!mCurrentDeviceSupported) { - return; - } - - // Handle the event. - final int action = event.getActionMasked(); - switch (action) { - case MotionEvent.ACTION_DOWN: { - boolean caughtFling = mFlinging; - finishKeys(time); - finishTracking(time); - mActivePointerId = event.getPointerId(0); - mVelocityTracker = VelocityTracker.obtain(); - mVelocityTracker.addMovement(event); - mStartX = event.getX(); - mStartY = event.getY(); - mLastX = mStartX; - mLastY = mStartY; - mAccumulatedX = 0; - mAccumulatedY = 0; - - // If we caught a fling, then pretend that the tap slop has already - // been exceeded to suppress taps whose only purpose is to stop the fling. - mConsumedMovement = caughtFling; - break; - } - - case MotionEvent.ACTION_MOVE: - case MotionEvent.ACTION_UP: { - if (mActivePointerId < 0) { - break; - } - final int index = event.findPointerIndex(mActivePointerId); - if (index < 0) { - finishKeys(time); - finishTracking(time); - break; - } - - mVelocityTracker.addMovement(event); - final float x = event.getX(index); - final float y = event.getY(index); - mAccumulatedX += x - mLastX; - mAccumulatedY += y - mLastY; - mLastX = x; - mLastY = y; - - // Consume any accumulated movement so far. - final int metaState = event.getMetaState(); - consumeAccumulatedMovement(time, metaState); - - // Detect taps and flings. - if (action == MotionEvent.ACTION_UP) { - if (mConsumedMovement && mPendingKeyCode != KeyEvent.KEYCODE_UNKNOWN) { - // It might be a fling. - mVelocityTracker.computeCurrentVelocity(1000, mConfigMaxFlingVelocity); - final float vx = mVelocityTracker.getXVelocity(mActivePointerId); - final float vy = mVelocityTracker.getYVelocity(mActivePointerId); - if (!startFling(time, vx, vy)) { - finishKeys(time); - } - } - finishTracking(time); - } - break; - } - - case MotionEvent.ACTION_CANCEL: { - finishKeys(time); - finishTracking(time); - break; - } - } - } - - public void cancel(MotionEvent event) { - if (mCurrentDeviceId == event.getDeviceId() - && mCurrentSource == event.getSource()) { - final long time = event.getEventTime(); - finishKeys(time); - finishTracking(time); } - } - - private void finishKeys(long time) { - cancelFling(); - sendKeyUp(time); - } - - private void finishTracking(long time) { - if (mActivePointerId >= 0) { - mActivePointerId = -1; - mVelocityTracker.recycle(); - mVelocityTracker = null; - } - } - private void consumeAccumulatedMovement(long time, int metaState) { - final float absX = Math.abs(mAccumulatedX); - final float absY = Math.abs(mAccumulatedY); - if (absX >= absY) { - if (absX >= mConfigTickDistance) { - mAccumulatedX = consumeAccumulatedMovement(time, metaState, mAccumulatedX, - KeyEvent.KEYCODE_DPAD_LEFT, KeyEvent.KEYCODE_DPAD_RIGHT); - mAccumulatedY = 0; - mConsumedMovement = true; - } - } else { - if (absY >= mConfigTickDistance) { - mAccumulatedY = consumeAccumulatedMovement(time, metaState, mAccumulatedY, - KeyEvent.KEYCODE_DPAD_UP, KeyEvent.KEYCODE_DPAD_DOWN); - mAccumulatedX = 0; - mConsumedMovement = true; - } - } + // Interpret the event. + mGestureDetector.onTouchEvent(event); } - private float consumeAccumulatedMovement(long time, int metaState, - float accumulator, int negativeKeyCode, int positiveKeyCode) { - while (accumulator <= -mConfigTickDistance) { - sendKeyDownOrRepeat(time, negativeKeyCode, metaState); - accumulator += mConfigTickDistance; - } - while (accumulator >= mConfigTickDistance) { - sendKeyDownOrRepeat(time, positiveKeyCode, metaState); - accumulator -= mConfigTickDistance; - } - return accumulator; + private void dispatchTap(long time) { + dispatchEvent(time, KeyEvent.KEYCODE_DPAD_CENTER); } - private void sendKeyDownOrRepeat(long time, int keyCode, int metaState) { - if (mPendingKeyCode != keyCode) { - sendKeyUp(time); - mPendingKeyDownTime = time; - mPendingKeyCode = keyCode; - mPendingKeyRepeatCount = 0; + private void dispatchFling(float x, float y, long time) { + if (Math.abs(x) > Math.abs(y)) { + dispatchEvent(time, + x > 0 ? KeyEvent.KEYCODE_DPAD_RIGHT : KeyEvent.KEYCODE_DPAD_LEFT); } else { - mPendingKeyRepeatCount += 1; - } - mPendingKeyMetaState = metaState; - - // Note: Normally we would pass FLAG_LONG_PRESS when the repeat count is 1 - // but it doesn't quite make sense when simulating the events in this way. - if (LOCAL_DEBUG) { - Log.d(LOCAL_TAG, "Sending key down: keyCode=" + mPendingKeyCode - + ", repeatCount=" + mPendingKeyRepeatCount - + ", metaState=" + Integer.toHexString(mPendingKeyMetaState)); - } - enqueueInputEvent(new KeyEvent(mPendingKeyDownTime, time, - KeyEvent.ACTION_DOWN, mPendingKeyCode, mPendingKeyRepeatCount, - mPendingKeyMetaState, mCurrentDeviceId, - KeyEvent.FLAG_FALLBACK, mCurrentSource)); - } - - private void sendKeyUp(long time) { - if (mPendingKeyCode != KeyEvent.KEYCODE_UNKNOWN) { - if (LOCAL_DEBUG) { - Log.d(LOCAL_TAG, "Sending key up: keyCode=" + mPendingKeyCode - + ", metaState=" + Integer.toHexString(mPendingKeyMetaState)); - } - enqueueInputEvent(new KeyEvent(mPendingKeyDownTime, time, - KeyEvent.ACTION_UP, mPendingKeyCode, 0, mPendingKeyMetaState, - mCurrentDeviceId, 0, KeyEvent.FLAG_FALLBACK, - mCurrentSource)); - mPendingKeyCode = KeyEvent.KEYCODE_UNKNOWN; - } - } - - private boolean startFling(long time, float vx, float vy) { - if (LOCAL_DEBUG) { - Log.d(LOCAL_TAG, "Considering fling: vx=" + vx + ", vy=" + vy - + ", min=" + mConfigMinFlingVelocity); - } - - // Flings must be oriented in the same direction as the preceding movements. - switch (mPendingKeyCode) { - case KeyEvent.KEYCODE_DPAD_LEFT: - if (-vx >= mConfigMinFlingVelocity - && Math.abs(vy) < mConfigMinFlingVelocity) { - mFlingVelocity = -vx; - break; - } - return false; - - case KeyEvent.KEYCODE_DPAD_RIGHT: - if (vx >= mConfigMinFlingVelocity - && Math.abs(vy) < mConfigMinFlingVelocity) { - mFlingVelocity = vx; - break; - } - return false; - - case KeyEvent.KEYCODE_DPAD_UP: - if (-vy >= mConfigMinFlingVelocity - && Math.abs(vx) < mConfigMinFlingVelocity) { - mFlingVelocity = -vy; - break; - } - return false; - - case KeyEvent.KEYCODE_DPAD_DOWN: - if (vy >= mConfigMinFlingVelocity - && Math.abs(vx) < mConfigMinFlingVelocity) { - mFlingVelocity = vy; - break; - } - return false; - } - - // Post the first fling event. - mFlinging = postFling(time); - return mFlinging; - } - - private boolean postFling(long time) { - // The idea here is to estimate the time when the pointer would have - // traveled one tick distance unit given the current fling velocity. - // This effect creates continuity of motion. - if (mFlingVelocity >= mConfigMinFlingVelocity) { - long delay = (long)(mConfigTickDistance / mFlingVelocity * 1000); - postAtTime(mFlingRunnable, time + delay); - if (LOCAL_DEBUG) { - Log.d(LOCAL_TAG, "Posted fling: velocity=" - + mFlingVelocity + ", delay=" + delay - + ", keyCode=" + mPendingKeyCode); - } - return true; + dispatchEvent(time, y > 0 ? KeyEvent.KEYCODE_DPAD_DOWN : KeyEvent.KEYCODE_DPAD_UP); } - return false; } - private void cancelFling() { - if (mFlinging) { - removeCallbacks(mFlingRunnable); - mFlinging = false; + private void dispatchEvent(long time, int keyCode) { + if (DEBUG_TOUCH_NAVIGATION) { + Log.d(LOCAL_TAG, "Dispatching DPAD events DOWN and UP with keycode " + keyCode); } + enqueueInputEvent(new KeyEvent(time, time, + KeyEvent.ACTION_DOWN, keyCode, /* repeat= */ 0, mPendingKeyMetaState, + mCurrentDeviceId, /* scancode= */ 0, KeyEvent.FLAG_FALLBACK, + mCurrentSource)); + enqueueInputEvent(new KeyEvent(time, time, + KeyEvent.ACTION_UP, keyCode, /* repeat= */ 0, mPendingKeyMetaState, + mCurrentDeviceId, /* scancode= */ 0, KeyEvent.FLAG_FALLBACK, + mCurrentSource)); } - - private final Runnable mFlingRunnable = new Runnable() { - @Override - public void run() { - final long time = SystemClock.uptimeMillis(); - sendKeyDownOrRepeat(time, mPendingKeyCode, mPendingKeyMetaState); - mFlingVelocity *= FLING_TICK_DECAY; - if (!postFling(time)) { - mFlinging = false; - finishKeys(time); - } - } - }; } final class SyntheticKeyboardHandler { diff --git a/core/java/android/view/WindowManager.java b/core/java/android/view/WindowManager.java index 48686fc25916..02b34786e9f2 100644 --- a/core/java/android/view/WindowManager.java +++ b/core/java/android/view/WindowManager.java @@ -869,6 +869,42 @@ public interface WindowManager extends ViewManager { "android.window.PROPERTY_COMPAT_IGNORE_REQUESTED_ORIENTATION"; /** + * Application level {@link android.content.pm.PackageManager.Property PackageManager.Property} + * for an app to inform the system that the app can be opted-out from the compatibility + * treatment that avoids {@link android.app.Activity#setRequestedOrientation} loops. The loop + * can be trigerred by ignoreRequestedOrientation display setting enabled on the device or + * by the landscape natural orientation of the device. + * + * <p>The system could ignore {@link android.app.Activity#setRequestedOrientation} + * call from an app if both of the following conditions are true: + * <ul> + * <li>Activity has requested orientation more than 2 times within 1-second timer + * <li>Activity is not letterboxed for fixed orientation + * </ul> + * + * <p>Setting this property to {@code false} informs the system that the app must be + * opted-out from the compatibility treatment even if the device manufacturer has opted the app + * into the treatment. + * + * <p>Not setting this property at all, or setting this property to {@code true} has no effect. + * + * <p><b>Syntax:</b> + * <pre> + * <application> + * <property + * android:name= + * "android.window.PROPERTY_COMPAT_IGNORE_ORIENTATION_REQUEST_WHEN_LOOP_DETECTED" + * android:value="false"/> + * </application> + * </pre> + * + * @hide + */ + // TODO(b/274924641): Make this public API. + String PROPERTY_COMPAT_IGNORE_ORIENTATION_REQUEST_WHEN_LOOP_DETECTED = + "android.window.PROPERTY_COMPAT_IGNORE_ORIENTATION_REQUEST_WHEN_LOOP_DETECTED"; + + /** * Application level {@link android.content.pm.PackageManager.Property PackageManager * .Property} for an app to inform the system that it needs to be opted-out from the * compatibility treatment that sandboxes {@link android.view.View} API. diff --git a/core/java/android/window/ScreenCapture.java b/core/java/android/window/ScreenCapture.java index d8e64d48f456..95451a966055 100644 --- a/core/java/android/window/ScreenCapture.java +++ b/core/java/android/window/ScreenCapture.java @@ -309,7 +309,7 @@ public class ScreenCapture { /** Release any layers if set using {@link Builder#setExcludeLayers(SurfaceControl[])}. */ public void release() { - if (mExcludeLayers.length == 0) { + if (mExcludeLayers == null || mExcludeLayers.length == 0) { return; } diff --git a/core/java/com/android/internal/app/IntentForwarderActivity.java b/core/java/com/android/internal/app/IntentForwarderActivity.java index 75e797b4ec9d..44d517ad0c32 100644 --- a/core/java/com/android/internal/app/IntentForwarderActivity.java +++ b/core/java/com/android/internal/app/IntentForwarderActivity.java @@ -19,6 +19,7 @@ package com.android.internal.app; import static android.Manifest.permission.INTERACT_ACROSS_USERS; import static android.app.admin.DevicePolicyResources.Strings.Core.FORWARD_INTENT_TO_PERSONAL; import static android.app.admin.DevicePolicyResources.Strings.Core.FORWARD_INTENT_TO_WORK; +import static android.app.admin.DevicePolicyResources.Strings.Core.MINIRESOLVER_OPEN_IN_WORK; import static android.content.pm.PackageManager.MATCH_DEFAULT_ONLY; import static android.content.pm.PackageManager.PERMISSION_GRANTED; @@ -212,9 +213,7 @@ public class IntentForwarderActivity extends Activity { buttonContainer.setPadding(0, 0, 0, buttonContainer.getPaddingBottom()); ((TextView) findViewById(R.id.open_cross_profile)).setText( - getResources().getString( - R.string.miniresolver_open_in_work, - target.loadLabel(packageManagerForTargetUser))); + getOpenInWorkMessage(target.loadLabel(packageManagerForTargetUser))); // The mini-resolver's negative button is reused in this flow to cancel the intent ((Button) findViewById(R.id.use_same_profile_browser)).setText(R.string.cancel); @@ -226,6 +225,13 @@ public class IntentForwarderActivity extends Activity { }); } + private String getOpenInWorkMessage(CharSequence targetLabel) { + return getSystemService(DevicePolicyManager.class).getResources().getString( + MINIRESOLVER_OPEN_IN_WORK, + () -> getString(R.string.miniresolver_open_in_work, targetLabel), + targetLabel); + } + private String getForwardToPersonalMessage() { return getSystemService(DevicePolicyManager.class).getResources().getString( FORWARD_INTENT_TO_PERSONAL, diff --git a/core/java/com/android/internal/app/ResolverActivity.java b/core/java/com/android/internal/app/ResolverActivity.java index 73c5207e6238..499d38c31b59 100644 --- a/core/java/com/android/internal/app/ResolverActivity.java +++ b/core/java/com/android/internal/app/ResolverActivity.java @@ -19,6 +19,10 @@ package com.android.internal.app; import static android.Manifest.permission.INTERACT_ACROSS_PROFILES; import static android.app.admin.DevicePolicyResources.Strings.Core.FORWARD_INTENT_TO_PERSONAL; import static android.app.admin.DevicePolicyResources.Strings.Core.FORWARD_INTENT_TO_WORK; +import static android.app.admin.DevicePolicyResources.Strings.Core.MINIRESOLVER_OPEN_IN_PERSONAL; +import static android.app.admin.DevicePolicyResources.Strings.Core.MINIRESOLVER_OPEN_IN_WORK; +import static android.app.admin.DevicePolicyResources.Strings.Core.MINIRESOLVER_USE_PERSONAL_BROWSER; +import static android.app.admin.DevicePolicyResources.Strings.Core.MINIRESOLVER_USE_WORK_BROWSER; import static android.app.admin.DevicePolicyResources.Strings.Core.RESOLVER_CANT_ACCESS_PERSONAL; import static android.app.admin.DevicePolicyResources.Strings.Core.RESOLVER_CANT_ACCESS_WORK; import static android.app.admin.DevicePolicyResources.Strings.Core.RESOLVER_CROSS_PROFILE_BLOCKED_TITLE; @@ -46,6 +50,7 @@ import android.app.VoiceInteractor.PickOptionRequest.Option; import android.app.VoiceInteractor.Prompt; import android.app.admin.DevicePolicyEventLogger; import android.app.admin.DevicePolicyManager; +import android.app.admin.DevicePolicyResourcesManager; import android.compat.annotation.UnsupportedAppUsage; import android.content.BroadcastReceiver; import android.content.ComponentName; @@ -1713,14 +1718,29 @@ public class ResolverActivity extends Activity implements } }.execute(); - ((TextView) findViewById(R.id.open_cross_profile)).setText( - getResources().getString( - inWorkProfile ? R.string.miniresolver_open_in_personal - : R.string.miniresolver_open_in_work, - otherProfileResolveInfo.getDisplayLabel())); - ((Button) findViewById(R.id.use_same_profile_browser)).setText( - inWorkProfile ? R.string.miniresolver_use_work_browser - : R.string.miniresolver_use_personal_browser); + CharSequence targetDisplayLabel = otherProfileResolveInfo.getDisplayLabel(); + + DevicePolicyResourcesManager devicePolicyResourcesManager = getSystemService( + DevicePolicyManager.class).getResources(); + + if (inWorkProfile) { + ((TextView) findViewById(R.id.open_cross_profile)).setText( + devicePolicyResourcesManager.getString(MINIRESOLVER_OPEN_IN_WORK, + () -> getString(R.string.miniresolver_open_in_work, targetDisplayLabel), + targetDisplayLabel)); + ((Button) findViewById(R.id.use_same_profile_browser)).setText( + devicePolicyResourcesManager.getString(MINIRESOLVER_USE_WORK_BROWSER, + () -> getString(R.string.miniresolver_use_work_browser))); + } else { + ((TextView) findViewById(R.id.open_cross_profile)).setText( + devicePolicyResourcesManager.getString(MINIRESOLVER_OPEN_IN_PERSONAL, + () -> getString(R.string.miniresolver_open_in_personal, + targetDisplayLabel), + targetDisplayLabel)); + ((Button) findViewById(R.id.use_same_profile_browser)).setText( + devicePolicyResourcesManager.getString(MINIRESOLVER_USE_PERSONAL_BROWSER, + () -> getString(R.string.miniresolver_use_personal_browser))); + } findViewById(R.id.use_same_profile_browser).setOnClickListener( v -> { diff --git a/core/java/com/android/internal/widget/MessagingImageMessage.java b/core/java/com/android/internal/widget/MessagingImageMessage.java index 8e7fe18b222b..098bce14e619 100644 --- a/core/java/com/android/internal/widget/MessagingImageMessage.java +++ b/core/java/com/android/internal/widget/MessagingImageMessage.java @@ -198,6 +198,11 @@ public class MessagingImageMessage extends ImageView implements MessagingMessage @Override public int getMeasuredType() { + if (mDrawable == null) { + Log.e(TAG, "getMeasuredType() after recycle()!"); + return MEASURED_NORMAL; + } + int measuredHeight = getMeasuredHeight(); int minImageHeight; if (mIsIsolated) { diff --git a/core/res/res/drawable-hdpi/pointer_copy.png b/core/res/res/drawable-hdpi/pointer_copy.png Binary files differindex c5eda2e5b5c4..5d06a8eaa551 100644 --- a/core/res/res/drawable-hdpi/pointer_copy.png +++ b/core/res/res/drawable-hdpi/pointer_copy.png diff --git a/core/res/res/drawable-hdpi/pointer_grab.png b/core/res/res/drawable-hdpi/pointer_grab.png Binary files differindex 26da04de1d12..b76ec16e243b 100644 --- a/core/res/res/drawable-hdpi/pointer_grab.png +++ b/core/res/res/drawable-hdpi/pointer_grab.png diff --git a/core/res/res/drawable-hdpi/pointer_grabbing.png b/core/res/res/drawable-hdpi/pointer_grabbing.png Binary files differindex f4031a9227c7..10013e9a1821 100644 --- a/core/res/res/drawable-hdpi/pointer_grabbing.png +++ b/core/res/res/drawable-hdpi/pointer_grabbing.png diff --git a/core/res/res/drawable-hdpi/pointer_hand.png b/core/res/res/drawable-hdpi/pointer_hand.png Binary files differindex a7ae55fc4b5f..8a7277477ab0 100644 --- a/core/res/res/drawable-hdpi/pointer_hand.png +++ b/core/res/res/drawable-hdpi/pointer_hand.png diff --git a/core/res/res/drawable-hdpi/pointer_nodrop.png b/core/res/res/drawable-hdpi/pointer_nodrop.png Binary files differindex 7043323701dd..9df140c5816b 100644 --- a/core/res/res/drawable-hdpi/pointer_nodrop.png +++ b/core/res/res/drawable-hdpi/pointer_nodrop.png diff --git a/core/res/res/drawable-mdpi/pointer_copy.png b/core/res/res/drawable-mdpi/pointer_copy.png Binary files differindex e731108370d3..7189dada1bf2 100644 --- a/core/res/res/drawable-mdpi/pointer_copy.png +++ b/core/res/res/drawable-mdpi/pointer_copy.png diff --git a/core/res/res/drawable-mdpi/pointer_copy_large.png b/core/res/res/drawable-mdpi/pointer_copy_large.png Binary files differindex 15ccb04b8f76..a3d487df9e5e 100644 --- a/core/res/res/drawable-mdpi/pointer_copy_large.png +++ b/core/res/res/drawable-mdpi/pointer_copy_large.png diff --git a/core/res/res/drawable-mdpi/pointer_grab.png b/core/res/res/drawable-mdpi/pointer_grab.png Binary files differindex d625b55f7066..977b36cd875a 100644 --- a/core/res/res/drawable-mdpi/pointer_grab.png +++ b/core/res/res/drawable-mdpi/pointer_grab.png diff --git a/core/res/res/drawable-mdpi/pointer_grab_large.png b/core/res/res/drawable-mdpi/pointer_grab_large.png Binary files differindex 9d36df0d6d94..80587ceb8aba 100644 --- a/core/res/res/drawable-mdpi/pointer_grab_large.png +++ b/core/res/res/drawable-mdpi/pointer_grab_large.png diff --git a/core/res/res/drawable-mdpi/pointer_grabbing.png b/core/res/res/drawable-mdpi/pointer_grabbing.png Binary files differindex 71bb17ba5592..2bdcbdc7af75 100644 --- a/core/res/res/drawable-mdpi/pointer_grabbing.png +++ b/core/res/res/drawable-mdpi/pointer_grabbing.png diff --git a/core/res/res/drawable-mdpi/pointer_grabbing_large.png b/core/res/res/drawable-mdpi/pointer_grabbing_large.png Binary files differindex 5574b07faf44..a8a599c5ebee 100644 --- a/core/res/res/drawable-mdpi/pointer_grabbing_large.png +++ b/core/res/res/drawable-mdpi/pointer_grabbing_large.png diff --git a/core/res/res/drawable-mdpi/pointer_hand.png b/core/res/res/drawable-mdpi/pointer_hand.png Binary files differindex d7f7beda111c..e94b927cada6 100644 --- a/core/res/res/drawable-mdpi/pointer_hand.png +++ b/core/res/res/drawable-mdpi/pointer_hand.png diff --git a/core/res/res/drawable-mdpi/pointer_hand_large.png b/core/res/res/drawable-mdpi/pointer_hand_large.png Binary files differindex f775464ced18..7d89067b98bd 100644 --- a/core/res/res/drawable-mdpi/pointer_hand_large.png +++ b/core/res/res/drawable-mdpi/pointer_hand_large.png diff --git a/core/res/res/drawable-mdpi/pointer_nodrop.png b/core/res/res/drawable-mdpi/pointer_nodrop.png Binary files differindex 931b74094d79..15764fa4e51b 100644 --- a/core/res/res/drawable-mdpi/pointer_nodrop.png +++ b/core/res/res/drawable-mdpi/pointer_nodrop.png diff --git a/core/res/res/drawable-mdpi/pointer_nodrop_large.png b/core/res/res/drawable-mdpi/pointer_nodrop_large.png Binary files differindex 88f77d300a15..46ff5f779cef 100644 --- a/core/res/res/drawable-mdpi/pointer_nodrop_large.png +++ b/core/res/res/drawable-mdpi/pointer_nodrop_large.png diff --git a/core/res/res/drawable-xhdpi/pointer_copy.png b/core/res/res/drawable-xhdpi/pointer_copy.png Binary files differindex 5b6cc5bc454f..8d889e1ad131 100644 --- a/core/res/res/drawable-xhdpi/pointer_copy.png +++ b/core/res/res/drawable-xhdpi/pointer_copy.png diff --git a/core/res/res/drawable-xhdpi/pointer_copy_large.png b/core/res/res/drawable-xhdpi/pointer_copy_large.png Binary files differindex d78a410a2887..860c85499617 100644 --- a/core/res/res/drawable-xhdpi/pointer_copy_large.png +++ b/core/res/res/drawable-xhdpi/pointer_copy_large.png diff --git a/core/res/res/drawable-xhdpi/pointer_grab.png b/core/res/res/drawable-xhdpi/pointer_grab.png Binary files differindex 46dd3eeb95c6..dd3c6de46be5 100644 --- a/core/res/res/drawable-xhdpi/pointer_grab.png +++ b/core/res/res/drawable-xhdpi/pointer_grab.png diff --git a/core/res/res/drawable-xhdpi/pointer_grab_large.png b/core/res/res/drawable-xhdpi/pointer_grab_large.png Binary files differindex 1c7e63e2e527..bcae2c943d12 100644 --- a/core/res/res/drawable-xhdpi/pointer_grab_large.png +++ b/core/res/res/drawable-xhdpi/pointer_grab_large.png diff --git a/core/res/res/drawable-xhdpi/pointer_grabbing.png b/core/res/res/drawable-xhdpi/pointer_grabbing.png Binary files differindex 2fb8a9c4442f..500fa73f58e4 100644 --- a/core/res/res/drawable-xhdpi/pointer_grabbing.png +++ b/core/res/res/drawable-xhdpi/pointer_grabbing.png diff --git a/core/res/res/drawable-xhdpi/pointer_grabbing_large.png b/core/res/res/drawable-xhdpi/pointer_grabbing_large.png Binary files differindex 3467a03e2a56..c9c6f187e2a8 100644 --- a/core/res/res/drawable-xhdpi/pointer_grabbing_large.png +++ b/core/res/res/drawable-xhdpi/pointer_grabbing_large.png diff --git a/core/res/res/drawable-xhdpi/pointer_hand.png b/core/res/res/drawable-xhdpi/pointer_hand.png Binary files differindex 926310cce74d..e931afe4eec8 100644 --- a/core/res/res/drawable-xhdpi/pointer_hand.png +++ b/core/res/res/drawable-xhdpi/pointer_hand.png diff --git a/core/res/res/drawable-xhdpi/pointer_hand_large.png b/core/res/res/drawable-xhdpi/pointer_hand_large.png Binary files differindex 546b222164b5..39ae1b7438d0 100644 --- a/core/res/res/drawable-xhdpi/pointer_hand_large.png +++ b/core/res/res/drawable-xhdpi/pointer_hand_large.png diff --git a/core/res/res/drawable-xhdpi/pointer_nodrop.png b/core/res/res/drawable-xhdpi/pointer_nodrop.png Binary files differindex fdfc2671255c..4c9fe63136a8 100644 --- a/core/res/res/drawable-xhdpi/pointer_nodrop.png +++ b/core/res/res/drawable-xhdpi/pointer_nodrop.png diff --git a/core/res/res/drawable-xhdpi/pointer_nodrop_large.png b/core/res/res/drawable-xhdpi/pointer_nodrop_large.png Binary files differindex 2b5e8a4b0f59..ae60e77bbc5e 100644 --- a/core/res/res/drawable-xhdpi/pointer_nodrop_large.png +++ b/core/res/res/drawable-xhdpi/pointer_nodrop_large.png diff --git a/core/res/res/drawable-xxhdpi/pointer_copy.png b/core/res/res/drawable-xxhdpi/pointer_copy.png Binary files differindex bef4fb4e8c17..24661a653f42 100644 --- a/core/res/res/drawable-xxhdpi/pointer_copy.png +++ b/core/res/res/drawable-xxhdpi/pointer_copy.png diff --git a/core/res/res/drawable-xxhdpi/pointer_grab.png b/core/res/res/drawable-xxhdpi/pointer_grab.png Binary files differindex 6caa1ba77eb9..1917722fa4fa 100644 --- a/core/res/res/drawable-xxhdpi/pointer_grab.png +++ b/core/res/res/drawable-xxhdpi/pointer_grab.png diff --git a/core/res/res/drawable-xxhdpi/pointer_grabbing.png b/core/res/res/drawable-xxhdpi/pointer_grabbing.png Binary files differindex b52f75174a16..a4cd9e56ba2f 100644 --- a/core/res/res/drawable-xxhdpi/pointer_grabbing.png +++ b/core/res/res/drawable-xxhdpi/pointer_grabbing.png diff --git a/core/res/res/drawable-xxhdpi/pointer_hand.png b/core/res/res/drawable-xxhdpi/pointer_hand.png Binary files differindex f3ee077b5cc9..917529b9aa2a 100644 --- a/core/res/res/drawable-xxhdpi/pointer_hand.png +++ b/core/res/res/drawable-xxhdpi/pointer_hand.png diff --git a/core/res/res/drawable-xxhdpi/pointer_nodrop.png b/core/res/res/drawable-xxhdpi/pointer_nodrop.png Binary files differindex ef54301e4eac..35b4d13bd0be 100644 --- a/core/res/res/drawable-xxhdpi/pointer_nodrop.png +++ b/core/res/res/drawable-xxhdpi/pointer_nodrop.png diff --git a/core/res/res/drawable/pointer_copy_icon.xml b/core/res/res/drawable/pointer_copy_icon.xml index da32939b5b0f..7e7c0caf6d78 100644 --- a/core/res/res/drawable/pointer_copy_icon.xml +++ b/core/res/res/drawable/pointer_copy_icon.xml @@ -1,5 +1,5 @@ <?xml version="1.0" encoding="utf-8"?> <pointer-icon xmlns:android="http://schemas.android.com/apk/res/android" android:bitmap="@drawable/pointer_copy" - android:hotSpotX="7.5dp" + android:hotSpotX="8.5dp" android:hotSpotY="7.5dp" /> diff --git a/core/res/res/drawable/pointer_copy_large_icon.xml b/core/res/res/drawable/pointer_copy_large_icon.xml index 55d47b4f4d63..54e95238f501 100644 --- a/core/res/res/drawable/pointer_copy_large_icon.xml +++ b/core/res/res/drawable/pointer_copy_large_icon.xml @@ -1,5 +1,5 @@ <?xml version="1.0" encoding="utf-8"?> <pointer-icon xmlns:android="http://schemas.android.com/apk/res/android" android:bitmap="@drawable/pointer_copy_large" - android:hotSpotX="22.5dp" - android:hotSpotY="22.5dp" /> + android:hotSpotX="21.25dp" + android:hotSpotY="18.75dp" /> diff --git a/core/res/res/drawable/pointer_grab_icon.xml b/core/res/res/drawable/pointer_grab_icon.xml index b3d4e78828cd..dd1216a2995b 100644 --- a/core/res/res/drawable/pointer_grab_icon.xml +++ b/core/res/res/drawable/pointer_grab_icon.xml @@ -1,5 +1,5 @@ <?xml version="1.0" encoding="utf-8"?> <pointer-icon xmlns:android="http://schemas.android.com/apk/res/android" android:bitmap="@drawable/pointer_grab" - android:hotSpotX="13.5dp" - android:hotSpotY="13.5dp" /> + android:hotSpotX="9.5dp" + android:hotSpotY="4.5dp" /> diff --git a/core/res/res/drawable/pointer_grab_large_icon.xml b/core/res/res/drawable/pointer_grab_large_icon.xml index 343c7d27c749..b5dbd11fb286 100644 --- a/core/res/res/drawable/pointer_grab_large_icon.xml +++ b/core/res/res/drawable/pointer_grab_large_icon.xml @@ -1,5 +1,5 @@ <?xml version="1.0" encoding="utf-8"?> <pointer-icon xmlns:android="http://schemas.android.com/apk/res/android" android:bitmap="@drawable/pointer_grab_large" - android:hotSpotX="33.75dp" - android:hotSpotY="33.75dp" /> + android:hotSpotX="23.75dp" + android:hotSpotY="11.25dp" /> diff --git a/core/res/res/drawable/pointer_grabbing_large_icon.xml b/core/res/res/drawable/pointer_grabbing_large_icon.xml index ac1626530778..b041c8397771 100644 --- a/core/res/res/drawable/pointer_grabbing_large_icon.xml +++ b/core/res/res/drawable/pointer_grabbing_large_icon.xml @@ -1,5 +1,5 @@ <?xml version="1.0" encoding="utf-8"?> <pointer-icon xmlns:android="http://schemas.android.com/apk/res/android" android:bitmap="@drawable/pointer_grabbing_large" - android:hotSpotX="25.5dp" - android:hotSpotY="22.5dp" /> + android:hotSpotX="21.25dp" + android:hotSpotY="18.75dp" /> diff --git a/core/res/res/drawable/pointer_hand_icon.xml b/core/res/res/drawable/pointer_hand_icon.xml index 3f9d1a639ddb..0feccd296406 100644 --- a/core/res/res/drawable/pointer_hand_icon.xml +++ b/core/res/res/drawable/pointer_hand_icon.xml @@ -2,4 +2,4 @@ <pointer-icon xmlns:android="http://schemas.android.com/apk/res/android" android:bitmap="@drawable/pointer_hand" android:hotSpotX="9.5dp" - android:hotSpotY="1.5dp" /> + android:hotSpotY="2.5dp" /> diff --git a/core/res/res/drawable/pointer_hand_large_icon.xml b/core/res/res/drawable/pointer_hand_large_icon.xml index cd49762bd23f..6257b0ba2fb1 100644 --- a/core/res/res/drawable/pointer_hand_large_icon.xml +++ b/core/res/res/drawable/pointer_hand_large_icon.xml @@ -2,4 +2,4 @@ <pointer-icon xmlns:android="http://schemas.android.com/apk/res/android" android:bitmap="@drawable/pointer_hand_large" android:hotSpotX="23.75dp" - android:hotSpotY="3.75dp" /> + android:hotSpotY="6.25dp" /> diff --git a/core/res/res/drawable/pointer_nodrop_icon.xml b/core/res/res/drawable/pointer_nodrop_icon.xml index 4dffd23638da..fb78d7469120 100644 --- a/core/res/res/drawable/pointer_nodrop_icon.xml +++ b/core/res/res/drawable/pointer_nodrop_icon.xml @@ -1,5 +1,5 @@ <?xml version="1.0" encoding="utf-8"?> <pointer-icon xmlns:android="http://schemas.android.com/apk/res/android" android:bitmap="@drawable/pointer_nodrop" - android:hotSpotX="7.5dp" + android:hotSpotX="8.5dp" android:hotSpotY="7.5dp" /> diff --git a/core/res/res/drawable/pointer_nodrop_large_icon.xml b/core/res/res/drawable/pointer_nodrop_large_icon.xml index 602c744401a9..2385e11109b0 100644 --- a/core/res/res/drawable/pointer_nodrop_large_icon.xml +++ b/core/res/res/drawable/pointer_nodrop_large_icon.xml @@ -1,5 +1,5 @@ <?xml version="1.0" encoding="utf-8"?> <pointer-icon xmlns:android="http://schemas.android.com/apk/res/android" android:bitmap="@drawable/pointer_nodrop_large" - android:hotSpotX="22.5dp" - android:hotSpotY="22.5dp" /> + android:hotSpotX="21.25dp" + android:hotSpotY="18.75dp" /> diff --git a/core/res/res/values/config.xml b/core/res/res/values/config.xml index 76bae8de304d..3ff63519572f 100644 --- a/core/res/res/values/config.xml +++ b/core/res/res/values/config.xml @@ -4461,7 +4461,8 @@ See android.credentials.CredentialManager --> - <string name="config_defaultCredentialProviderService" translatable="false"></string> + <string-array name="config_defaultCredentialProviderService" translatable="false"> + </string-array> <!-- The package name for the system's smartspace service. This service returns smartspace results. diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml index 9fc2ed16b0c1..7b582da836aa 100644 --- a/core/res/res/values/symbols.xml +++ b/core/res/res/values/symbols.xml @@ -3754,7 +3754,7 @@ <java-symbol type="string" name="config_defaultAppPredictionService" /> <java-symbol type="string" name="config_defaultContentSuggestionsService" /> <java-symbol type="string" name="config_defaultCredentialManagerHybridService" /> - <java-symbol type="string" name="config_defaultCredentialProviderService" /> + <java-symbol type="array" name="config_defaultCredentialProviderService" /> <java-symbol type="string" name="config_defaultSearchUiService" /> <java-symbol type="string" name="config_defaultSmartspaceService" /> <java-symbol type="string" name="config_defaultWallpaperEffectsGenerationService" /> diff --git a/core/tests/coretests/src/android/provider/DeviceConfigServiceManagerTest.java b/core/tests/coretests/src/android/provider/DeviceConfigServiceManagerTest.java new file mode 100644 index 000000000000..e20258a625dd --- /dev/null +++ b/core/tests/coretests/src/android/provider/DeviceConfigServiceManagerTest.java @@ -0,0 +1,73 @@ +/* + * Copyright (C) 2023 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 android.provider; + +import static com.google.common.truth.Truth.assertThat; + +import static org.junit.Assert.assertThrows; +import static org.junit.Assume.assumeTrue; + +import android.platform.test.annotations.Presubmit; + +import androidx.test.filters.SmallTest; +import androidx.test.runner.AndroidJUnit4; + +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; + +/** + * Class that tests the APIs of DeviceConfigServiceManager.ServiceRegisterer. + */ +@Presubmit +@RunWith(AndroidJUnit4.class) +@SmallTest +public class DeviceConfigServiceManagerTest { + + private static final String SERVICE_NAME = "device_config_updatable"; + private DeviceConfigServiceManager.ServiceRegisterer mRegisterer; + + @Before + public void setUp() { + mRegisterer = new DeviceConfigServiceManager.ServiceRegisterer(SERVICE_NAME); + } + + @Test + public void testGetOrThrow() throws DeviceConfigServiceManager.ServiceNotFoundException { + if (UpdatableDeviceConfigServiceReadiness.shouldStartUpdatableService()) { + assertThat(mRegisterer.getOrThrow()).isNotNull(); + } else { + assertThrows(DeviceConfigServiceManager.ServiceNotFoundException.class, + mRegisterer::getOrThrow); + } + } + + @Test + public void testGet() { + assumeTrue(UpdatableDeviceConfigServiceReadiness.shouldStartUpdatableService()); + assertThat(mRegisterer.get()).isNotNull(); + } + + @Test + public void testTryGet() { + if (UpdatableDeviceConfigServiceReadiness.shouldStartUpdatableService()) { + assertThat(mRegisterer.tryGet()).isNotNull(); + } else { + assertThat(mRegisterer.tryGet()).isNull(); + } + } +} diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/kidsmode/KidsModeTaskOrganizer.java b/libs/WindowManager/Shell/src/com/android/wm/shell/kidsmode/KidsModeTaskOrganizer.java index 318a49a8de31..6d46a9c3d0e7 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/kidsmode/KidsModeTaskOrganizer.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/kidsmode/KidsModeTaskOrganizer.java @@ -23,6 +23,7 @@ import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN; import static android.app.WindowConfiguration.WINDOWING_MODE_UNDEFINED; import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE; import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_REVERSE_LANDSCAPE; +import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_SENSOR_LANDSCAPE; import static android.view.Display.DEFAULT_DISPLAY; import android.app.ActivityManager; @@ -310,8 +311,11 @@ public class KidsModeTaskOrganizer extends ShellTaskOrganizer { // TODO(229961548): Remove ignoreOrientationRequest exception for Kids Mode once possible. if (mReverseDefaultRotationEnabled) { setOrientationRequestPolicy(/* isIgnoreOrientationRequestDisabled */ true, - /* fromOrientations */ new int[]{SCREEN_ORIENTATION_REVERSE_LANDSCAPE}, - /* toOrientations */ new int[]{SCREEN_ORIENTATION_LANDSCAPE}); + /* fromOrientations */ + new int[]{SCREEN_ORIENTATION_LANDSCAPE, SCREEN_ORIENTATION_REVERSE_LANDSCAPE}, + /* toOrientations */ + new int[]{SCREEN_ORIENTATION_SENSOR_LANDSCAPE, + SCREEN_ORIENTATION_SENSOR_LANDSCAPE}); } else { setOrientationRequestPolicy(/* isIgnoreOrientationRequestDisabled */ true, /* fromOrientations */ null, /* toOrientations */ null); diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipBoundsAlgorithm.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipBoundsAlgorithm.java index 867162be4c6d..24d0b996a3cb 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipBoundsAlgorithm.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipBoundsAlgorithm.java @@ -198,7 +198,7 @@ public class PipBoundsAlgorithm { /** * @return whether the given {@param aspectRatio} is valid. */ - private boolean isValidPictureInPictureAspectRatio(float aspectRatio) { + public boolean isValidPictureInPictureAspectRatio(float aspectRatio) { return Float.compare(mMinAspectRatio, aspectRatio) <= 0 && Float.compare(aspectRatio, mMaxAspectRatio) <= 0; } diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTaskOrganizer.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTaskOrganizer.java index 09e050e91c64..a0bd064149d2 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTaskOrganizer.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTaskOrganizer.java @@ -1259,7 +1259,16 @@ public class PipTaskOrganizer implements ShellTaskOrganizer.TaskListener, protected void applyNewPictureInPictureParams(@NonNull PictureInPictureParams params) { if (mDeferredTaskInfo != null || PipUtils.aspectRatioChanged(params.getAspectRatioFloat(), mPictureInPictureParams.getAspectRatioFloat())) { - mPipParamsChangedForwarder.notifyAspectRatioChanged(params.getAspectRatioFloat()); + if (mPipBoundsAlgorithm.isValidPictureInPictureAspectRatio( + params.getAspectRatioFloat())) { + mPipParamsChangedForwarder.notifyAspectRatioChanged(params.getAspectRatioFloat()); + } else { + ProtoLog.d(ShellProtoLogGroup.WM_SHELL_PICTURE_IN_PICTURE, + "%s: New aspect ratio is not valid." + + " hasAspectRatio=%b" + + " aspectRatio=%f", + TAG, params.hasSetAspectRatio(), params.getAspectRatioFloat()); + } } if (mDeferredTaskInfo != null || PipUtils.remoteActionsChanged(params.getActions(), diff --git a/libs/hwui/Android.bp b/libs/hwui/Android.bp index 5d79104200d9..70c36a5803ee 100644 --- a/libs/hwui/Android.bp +++ b/libs/hwui/Android.bp @@ -55,6 +55,10 @@ cc_defaults { // GCC false-positives on this warning, and since we -Werror that's // a problem "-Wno-free-nonheap-object", + + // Do not de-optimise cold code paths in AFDO. + // Some code paths might be infrequently executed but critical to latency. + "-fno-profile-sample-accurate", ], include_dirs: [ diff --git a/libs/hwui/pipeline/skia/SkiaPipeline.cpp b/libs/hwui/pipeline/skia/SkiaPipeline.cpp index 8ea71f11e2f0..1f929685b62c 100644 --- a/libs/hwui/pipeline/skia/SkiaPipeline.cpp +++ b/libs/hwui/pipeline/skia/SkiaPipeline.cpp @@ -28,6 +28,7 @@ #include <SkMultiPictureDocument.h> #include <SkOverdrawCanvas.h> #include <SkOverdrawColorFilter.h> +#include <SkPaintFilterCanvas.h> #include <SkPicture.h> #include <SkPictureRecorder.h> #include <SkRect.h> @@ -36,15 +37,15 @@ #include <SkStream.h> #include <SkString.h> #include <SkTypeface.h> -#include "include/gpu/GpuTypes.h" // from Skia #include <android-base/properties.h> +#include <gui/TraceUtils.h> #include <unistd.h> #include <sstream> -#include <gui/TraceUtils.h> #include "LightingInfo.h" #include "VectorDrawable.h" +#include "include/gpu/GpuTypes.h" // from Skia #include "thread/CommonPool.h" #include "tools/SkSharingProc.h" #include "utils/Color.h" @@ -449,6 +450,23 @@ void SkiaPipeline::endCapture(SkSurface* surface) { } } +class ForceDitherCanvas : public SkPaintFilterCanvas { +public: + ForceDitherCanvas(SkCanvas* canvas) : SkPaintFilterCanvas(canvas) {} + +protected: + bool onFilter(SkPaint& paint) const override { + paint.setDither(true); + return true; + } + + void onDrawDrawable(SkDrawable* drawable, const SkMatrix* matrix) override { + // We unroll the drawable using "this" canvas, so that draw calls contained inside will + // get dithering applied + drawable->draw(this, matrix); + } +}; + void SkiaPipeline::renderFrame(const LayerUpdateQueue& layers, const SkRect& clip, const std::vector<sp<RenderNode>>& nodes, bool opaque, const Rect& contentDrawBounds, sk_sp<SkSurface> surface, @@ -503,6 +521,12 @@ void SkiaPipeline::renderFrameImpl(const SkRect& clip, canvas->clear(SK_ColorTRANSPARENT); } + std::optional<ForceDitherCanvas> forceDitherCanvas; + if (shouldForceDither()) { + forceDitherCanvas.emplace(canvas); + canvas = &forceDitherCanvas.value(); + } + if (1 == nodes.size()) { if (!nodes[0]->nothingToDraw()) { RenderNodeDrawable root(nodes[0].get(), canvas); diff --git a/libs/hwui/pipeline/skia/SkiaPipeline.h b/libs/hwui/pipeline/skia/SkiaPipeline.h index befee8989383..0763b06b53ef 100644 --- a/libs/hwui/pipeline/skia/SkiaPipeline.h +++ b/libs/hwui/pipeline/skia/SkiaPipeline.h @@ -98,6 +98,8 @@ protected: bool isCapturingSkp() const { return mCaptureMode != CaptureMode::None; } + virtual bool shouldForceDither() const { return mColorMode != ColorMode::Default; } + private: void renderFrameImpl(const SkRect& clip, const std::vector<sp<RenderNode>>& nodes, bool opaque, diff --git a/libs/hwui/pipeline/skia/SkiaVulkanPipeline.cpp b/libs/hwui/pipeline/skia/SkiaVulkanPipeline.cpp index c8f2e69ae0a4..6f1b99b95bbd 100644 --- a/libs/hwui/pipeline/skia/SkiaVulkanPipeline.cpp +++ b/libs/hwui/pipeline/skia/SkiaVulkanPipeline.cpp @@ -203,6 +203,11 @@ sk_sp<Bitmap> SkiaVulkanPipeline::allocateHardwareBitmap(renderthread::RenderThr return nullptr; } +bool SkiaVulkanPipeline::shouldForceDither() const { + if (mVkSurface && mVkSurface->isBeyond8Bit()) return false; + return SkiaPipeline::shouldForceDither(); +} + void SkiaVulkanPipeline::onContextDestroyed() { if (mVkSurface) { vulkanManager().destroySurface(mVkSurface); diff --git a/libs/hwui/pipeline/skia/SkiaVulkanPipeline.h b/libs/hwui/pipeline/skia/SkiaVulkanPipeline.h index d921ddb0d0fb..0713e93bccde 100644 --- a/libs/hwui/pipeline/skia/SkiaVulkanPipeline.h +++ b/libs/hwui/pipeline/skia/SkiaVulkanPipeline.h @@ -63,6 +63,8 @@ public: protected: void onContextDestroyed() override; + bool shouldForceDither() const override; + private: renderthread::VulkanManager& vulkanManager(); renderthread::VulkanSurface* mVkSurface = nullptr; diff --git a/libs/hwui/renderthread/VulkanSurface.cpp b/libs/hwui/renderthread/VulkanSurface.cpp index 21b6c44e997e..ae4f0572576e 100644 --- a/libs/hwui/renderthread/VulkanSurface.cpp +++ b/libs/hwui/renderthread/VulkanSurface.cpp @@ -530,6 +530,16 @@ void VulkanSurface::setColorSpace(sk_sp<SkColorSpace> colorSpace) { } } +bool VulkanSurface::isBeyond8Bit() const { + switch (mWindowInfo.bufferFormat) { + case AHARDWAREBUFFER_FORMAT_R10G10B10A2_UNORM: + case AHARDWAREBUFFER_FORMAT_R16G16B16A16_FLOAT: + return true; + default: + return false; + } +} + } /* namespace renderthread */ } /* namespace uirenderer */ } /* namespace android */ diff --git a/libs/hwui/renderthread/VulkanSurface.h b/libs/hwui/renderthread/VulkanSurface.h index e2ddc6b07768..3b69b73bcab3 100644 --- a/libs/hwui/renderthread/VulkanSurface.h +++ b/libs/hwui/renderthread/VulkanSurface.h @@ -48,6 +48,8 @@ public: void setColorSpace(sk_sp<SkColorSpace> colorSpace); + bool isBeyond8Bit() const; + private: /* * All structs/methods in this private section are specifically for use by the VulkanManager diff --git a/media/java/android/media/IAudioService.aidl b/media/java/android/media/IAudioService.aidl index f9d4efea57e2..fe5afc5a717e 100644 --- a/media/java/android/media/IAudioService.aidl +++ b/media/java/android/media/IAudioService.aidl @@ -268,7 +268,7 @@ interface IAudioService { boolean isVolumeControlUsingVolumeGroups(); @EnforcePermission("MODIFY_AUDIO_SETTINGS_PRIVILEGED") - oneway void registerStreamAliasingDispatcher(IStreamAliasingDispatcher isad, boolean register); + void registerStreamAliasingDispatcher(IStreamAliasingDispatcher isad, boolean register); @EnforcePermission("MODIFY_AUDIO_SETTINGS_PRIVILEGED") void setNotifAliasRingForTest(boolean alias); diff --git a/media/jni/android_media_MediaCodec.cpp b/media/jni/android_media_MediaCodec.cpp index 9a4aa3344374..dea7f03d369a 100644 --- a/media/jni/android_media_MediaCodec.cpp +++ b/media/jni/android_media_MediaCodec.cpp @@ -43,6 +43,8 @@ #include <android_runtime/android_hardware_HardwareBuffer.h> +#include <android-base/stringprintf.h> + #include <binder/MemoryDealer.h> #include <cutils/compiler.h> @@ -1276,7 +1278,8 @@ void JMediaCodec::handleCallback(const sp<AMessage> &msg) { ALOGE("Could not create MediaCodec.BufferInfo."); env->ExceptionClear(); } - jniThrowException(env, "java/lang/IllegalStateException", NULL); + jniThrowException(env, "java/lang/IllegalStateException", + "Fatal error: could not create MediaCodec.BufferInfo object"); return; } @@ -1309,7 +1312,8 @@ void JMediaCodec::handleCallback(const sp<AMessage> &msg) { ALOGE("Could not create CodecException object."); env->ExceptionClear(); } - jniThrowException(env, "java/lang/IllegalStateException", NULL); + jniThrowException(env, "java/lang/IllegalStateException", + "Fatal error: could not create CodecException object"); return; } @@ -1322,7 +1326,9 @@ void JMediaCodec::handleCallback(const sp<AMessage> &msg) { CHECK(msg->findMessage("format", &format)); if (OK != ConvertMessageToMap(env, format, &obj)) { - jniThrowException(env, "java/lang/IllegalStateException", NULL); + jniThrowException(env, "java/lang/IllegalStateException", + "Fatal error: failed to convert format " + "from native to Java object"); return; } @@ -1353,7 +1359,8 @@ void JMediaCodec::handleFirstTunnelFrameReadyNotification(const sp<AMessage> &ms status_t err = ConvertMessageToMap(env, data, &obj); if (err != OK) { - jniThrowException(env, "java/lang/IllegalStateException", NULL); + jniThrowException(env, "java/lang/IllegalStateException", + "Fatal error: failed to convert format from native to Java object"); return; } @@ -1374,7 +1381,8 @@ void JMediaCodec::handleFrameRenderedNotification(const sp<AMessage> &msg) { status_t err = ConvertMessageToMap(env, data, &obj); if (err != OK) { - jniThrowException(env, "java/lang/IllegalStateException", NULL); + jniThrowException(env, "java/lang/IllegalStateException", + "Fatal error: failed to convert format from native to Java object"); return; } @@ -1385,6 +1393,18 @@ void JMediaCodec::handleFrameRenderedNotification(const sp<AMessage> &msg) { env->DeleteLocalRef(obj); } +std::string JMediaCodec::getExceptionMessage(const char *msg = nullptr) const { + if (mCodec == nullptr) { + return msg ?: ""; + } + std::string prefix = ""; + if (msg && msg[0] != '\0') { + prefix.append(msg); + prefix.append("\n"); + } + return prefix + mCodec->getErrorLog().extract(); +} + void JMediaCodec::onMessageReceived(const sp<AMessage> &msg) { switch (msg->what()) { case kWhatCallbackNotify: @@ -1471,9 +1491,17 @@ static void throwCryptoException(JNIEnv *env, status_t err, const char *msg, env->Throw(exception); } +static std::string GetExceptionMessage(const sp<JMediaCodec> &codec, const char *msg) { + if (codec == NULL) { + return msg ?: "codec is released already"; + } + return codec->getExceptionMessage(msg); +} + static jint throwExceptionAsNecessary( JNIEnv *env, status_t err, int32_t actionCode = ACTION_CODE_FATAL, - const char *msg = NULL, const sp<ICrypto>& crypto = NULL) { + const char *msg = NULL, const sp<ICrypto>& crypto = NULL, + const sp<JMediaCodec> &codec = NULL) { switch (err) { case OK: return 0; @@ -1488,23 +1516,38 @@ static jint throwExceptionAsNecessary( return DEQUEUE_INFO_OUTPUT_BUFFERS_CHANGED; case INVALID_OPERATION: - jniThrowException(env, "java/lang/IllegalStateException", msg); + jniThrowException( + env, "java/lang/IllegalStateException", + GetExceptionMessage(codec, msg).c_str()); return 0; case BAD_VALUE: - jniThrowException(env, "java/lang/IllegalArgumentException", msg); + jniThrowException( + env, "java/lang/IllegalArgumentException", + GetExceptionMessage(codec, msg).c_str()); return 0; default: if (isCryptoError(err)) { - throwCryptoException(env, err, msg, crypto); + throwCryptoException( + env, err, + GetExceptionMessage(codec, msg).c_str(), + crypto); return 0; } - throwCodecException(env, err, actionCode, msg); + throwCodecException( + env, err, actionCode, + GetExceptionMessage(codec, msg).c_str()); return 0; } } +static jint throwExceptionAsNecessary( + JNIEnv *env, status_t err, const sp<JMediaCodec> &codec, + int32_t actionCode = ACTION_CODE_FATAL) { + return throwExceptionAsNecessary(env, err, actionCode, NULL, NULL, codec); +} + static void android_media_MediaCodec_native_enableOnFirstTunnelFrameReadyListener( JNIEnv *env, jobject thiz, @@ -1512,13 +1555,13 @@ static void android_media_MediaCodec_native_enableOnFirstTunnelFrameReadyListene sp<JMediaCodec> codec = getMediaCodec(env, thiz); if (codec == NULL || codec->initCheck() != OK) { - throwExceptionAsNecessary(env, INVALID_OPERATION); + throwExceptionAsNecessary(env, INVALID_OPERATION, codec); return; } status_t err = codec->enableOnFirstTunnelFrameReadyListener(enabled); - throwExceptionAsNecessary(env, err); + throwExceptionAsNecessary(env, err, codec); } static void android_media_MediaCodec_native_enableOnFrameRenderedListener( @@ -1528,13 +1571,13 @@ static void android_media_MediaCodec_native_enableOnFrameRenderedListener( sp<JMediaCodec> codec = getMediaCodec(env, thiz); if (codec == NULL || codec->initCheck() != OK) { - throwExceptionAsNecessary(env, INVALID_OPERATION); + throwExceptionAsNecessary(env, INVALID_OPERATION, codec); return; } status_t err = codec->enableOnFrameRenderedListener(enabled); - throwExceptionAsNecessary(env, err); + throwExceptionAsNecessary(env, err, codec); } static void android_media_MediaCodec_native_setCallback( @@ -1544,13 +1587,13 @@ static void android_media_MediaCodec_native_setCallback( sp<JMediaCodec> codec = getMediaCodec(env, thiz); if (codec == NULL || codec->initCheck() != OK) { - throwExceptionAsNecessary(env, INVALID_OPERATION); + throwExceptionAsNecessary(env, INVALID_OPERATION, codec); return; } status_t err = codec->setCallback(cb); - throwExceptionAsNecessary(env, err); + throwExceptionAsNecessary(env, err, codec); } static void android_media_MediaCodec_native_configure( @@ -1564,7 +1607,7 @@ static void android_media_MediaCodec_native_configure( sp<JMediaCodec> codec = getMediaCodec(env, thiz); if (codec == NULL || codec->initCheck() != OK) { - throwExceptionAsNecessary(env, INVALID_OPERATION); + throwExceptionAsNecessary(env, INVALID_OPERATION, codec); return; } @@ -1602,7 +1645,7 @@ static void android_media_MediaCodec_native_configure( err = codec->configure(format, bufferProducer, crypto, descrambler, flags); - throwExceptionAsNecessary(env, err); + throwExceptionAsNecessary(env, err, codec); } static void android_media_MediaCodec_native_setSurface( @@ -1612,7 +1655,7 @@ static void android_media_MediaCodec_native_setSurface( sp<JMediaCodec> codec = getMediaCodec(env, thiz); if (codec == NULL || codec->initCheck() != OK) { - throwExceptionAsNecessary(env, INVALID_OPERATION); + throwExceptionAsNecessary(env, INVALID_OPERATION, codec); return; } @@ -1631,7 +1674,7 @@ static void android_media_MediaCodec_native_setSurface( } status_t err = codec->setSurface(bufferProducer); - throwExceptionAsNecessary(env, err); + throwExceptionAsNecessary(env, err, codec); } sp<PersistentSurface> android_media_MediaCodec_getPersistentInputSurface( @@ -1735,7 +1778,7 @@ static void android_media_MediaCodec_setInputSurface( sp<JMediaCodec> codec = getMediaCodec(env, thiz); if (codec == NULL || codec->initCheck() != OK) { - throwExceptionAsNecessary(env, INVALID_OPERATION); + throwExceptionAsNecessary(env, INVALID_OPERATION, codec); return; } @@ -1749,7 +1792,7 @@ static void android_media_MediaCodec_setInputSurface( } status_t err = codec->setInputSurface(persistentSurface); if (err != NO_ERROR) { - throwExceptionAsNecessary(env, err); + throwExceptionAsNecessary(env, err, codec); } } @@ -1759,7 +1802,7 @@ static jobject android_media_MediaCodec_createInputSurface(JNIEnv* env, sp<JMediaCodec> codec = getMediaCodec(env, thiz); if (codec == NULL || codec->initCheck() != OK) { - throwExceptionAsNecessary(env, INVALID_OPERATION); + throwExceptionAsNecessary(env, INVALID_OPERATION, codec); return NULL; } @@ -1767,7 +1810,7 @@ static jobject android_media_MediaCodec_createInputSurface(JNIEnv* env, sp<IGraphicBufferProducer> bufferProducer; status_t err = codec->createInputSurface(&bufferProducer); if (err != NO_ERROR) { - throwExceptionAsNecessary(env, err); + throwExceptionAsNecessary(env, err, codec); return NULL; } @@ -1782,13 +1825,13 @@ static void android_media_MediaCodec_start(JNIEnv *env, jobject thiz) { sp<JMediaCodec> codec = getMediaCodec(env, thiz); if (codec == NULL || codec->initCheck() != OK) { - throwExceptionAsNecessary(env, INVALID_OPERATION); + throwExceptionAsNecessary(env, INVALID_OPERATION, codec); return; } status_t err = codec->start(); - throwExceptionAsNecessary(env, err, ACTION_CODE_FATAL, "start failed"); + throwExceptionAsNecessary(env, err, codec); } static void android_media_MediaCodec_stop(JNIEnv *env, jobject thiz) { @@ -1797,13 +1840,13 @@ static void android_media_MediaCodec_stop(JNIEnv *env, jobject thiz) { sp<JMediaCodec> codec = getMediaCodec(env, thiz); if (codec == NULL || codec->initCheck() != OK) { - throwExceptionAsNecessary(env, INVALID_OPERATION); + throwExceptionAsNecessary(env, INVALID_OPERATION, codec); return; } status_t err = codec->stop(); - throwExceptionAsNecessary(env, err); + throwExceptionAsNecessary(env, err, codec); } static void android_media_MediaCodec_reset(JNIEnv *env, jobject thiz) { @@ -1812,7 +1855,7 @@ static void android_media_MediaCodec_reset(JNIEnv *env, jobject thiz) { sp<JMediaCodec> codec = getMediaCodec(env, thiz); if (codec == NULL || codec->initCheck() != OK) { - throwExceptionAsNecessary(env, INVALID_OPERATION); + throwExceptionAsNecessary(env, INVALID_OPERATION, codec); return; } @@ -1825,7 +1868,7 @@ static void android_media_MediaCodec_reset(JNIEnv *env, jobject thiz) { // trigger an IllegalStateException. err = UNKNOWN_ERROR; } - throwExceptionAsNecessary(env, err); + throwExceptionAsNecessary(env, err, codec); } static void android_media_MediaCodec_flush(JNIEnv *env, jobject thiz) { @@ -1834,13 +1877,13 @@ static void android_media_MediaCodec_flush(JNIEnv *env, jobject thiz) { sp<JMediaCodec> codec = getMediaCodec(env, thiz); if (codec == NULL || codec->initCheck() != OK) { - throwExceptionAsNecessary(env, INVALID_OPERATION); + throwExceptionAsNecessary(env, INVALID_OPERATION, codec); return; } status_t err = codec->flush(); - throwExceptionAsNecessary(env, err); + throwExceptionAsNecessary(env, err, codec); } static void android_media_MediaCodec_queueInputBuffer( @@ -1856,7 +1899,7 @@ static void android_media_MediaCodec_queueInputBuffer( sp<JMediaCodec> codec = getMediaCodec(env, thiz); if (codec == NULL || codec->initCheck() != OK) { - throwExceptionAsNecessary(env, INVALID_OPERATION); + throwExceptionAsNecessary(env, INVALID_OPERATION, codec); return; } @@ -1866,7 +1909,8 @@ static void android_media_MediaCodec_queueInputBuffer( index, offset, size, timestampUs, flags, &errorDetailMsg); throwExceptionAsNecessary( - env, err, ACTION_CODE_FATAL, errorDetailMsg.empty() ? NULL : errorDetailMsg.c_str()); + env, err, ACTION_CODE_FATAL, + codec->getExceptionMessage(errorDetailMsg.c_str()).c_str()); } struct NativeCryptoInfo { @@ -1890,7 +1934,9 @@ struct NativeCryptoInfo { } else if (jmode == gCryptoModes.AesCbc) { mMode = CryptoPlugin::kMode_AES_CBC; } else { - throwExceptionAsNecessary(env, INVALID_OPERATION); + throwExceptionAsNecessary( + env, INVALID_OPERATION, ACTION_CODE_FATAL, + base::StringPrintf("unrecognized crypto mode: %d", jmode).c_str()); return; } @@ -2026,7 +2072,7 @@ static void android_media_MediaCodec_queueSecureInputBuffer( sp<JMediaCodec> codec = getMediaCodec(env, thiz); if (codec == NULL || codec->initCheck() != OK) { - throwExceptionAsNecessary(env, INVALID_OPERATION); + throwExceptionAsNecessary(env, INVALID_OPERATION, codec); return; } @@ -2056,7 +2102,9 @@ static void android_media_MediaCodec_queueSecureInputBuffer( } else if (jmode == gCryptoModes.AesCbc) { mode = CryptoPlugin::kMode_AES_CBC; } else { - throwExceptionAsNecessary(env, INVALID_OPERATION); + throwExceptionAsNecessary( + env, INVALID_OPERATION, ACTION_CODE_FATAL, + base::StringPrintf("Unrecognized crypto mode: %d", jmode).c_str()); return; } @@ -2175,8 +2223,8 @@ static void android_media_MediaCodec_queueSecureInputBuffer( subSamples = NULL; throwExceptionAsNecessary( - env, err, ACTION_CODE_FATAL, errorDetailMsg.empty() ? NULL : errorDetailMsg.c_str(), - codec->getCrypto()); + env, err, ACTION_CODE_FATAL, + codec->getExceptionMessage(errorDetailMsg.c_str()).c_str(), codec->getCrypto()); } static jobject android_media_MediaCodec_mapHardwareBuffer(JNIEnv *env, jclass, jobject bufferObj) { @@ -2518,14 +2566,16 @@ static void android_media_MediaCodec_native_queueLinearBlock( sp<JMediaCodec> codec = getMediaCodec(env, thiz); if (codec == nullptr || codec->initCheck() != OK) { - throwExceptionAsNecessary(env, INVALID_OPERATION); + throwExceptionAsNecessary(env, INVALID_OPERATION, codec); return; } sp<AMessage> tunings; status_t err = ConvertKeyValueListsToAMessage(env, keys, values, &tunings); if (err != OK) { - throwExceptionAsNecessary(env, err); + throwExceptionAsNecessary( + env, err, ACTION_CODE_FATAL, + "error occurred while converting tunings from Java to native"); return; } @@ -2545,15 +2595,23 @@ static void android_media_MediaCodec_native_queueLinearBlock( } env->MonitorExit(lock.get()); } else { - throwExceptionAsNecessary(env, INVALID_OPERATION); + throwExceptionAsNecessary( + env, INVALID_OPERATION, ACTION_CODE_FATAL, + "Failed to grab lock for a LinearBlock object"); return; } AString errorDetailMsg; if (codec->hasCryptoOrDescrambler()) { if (!memory) { + // It means there was an unexpected failure in extractMemoryFromContext above ALOGI("queueLinearBlock: no ashmem memory for encrypted content"); - throwExceptionAsNecessary(env, BAD_VALUE); + throwExceptionAsNecessary( + env, BAD_VALUE, ACTION_CODE_FATAL, + "Unexpected error: the input buffer is not compatible with " + "the secure codec, and a fallback logic failed.\n" + "Suggestion: please try including the secure codec when calling " + "MediaCodec.LinearBlock#obtain method to obtain a compatible buffer."); return; } auto cryptoInfo = @@ -2577,14 +2635,22 @@ static void android_media_MediaCodec_native_queueLinearBlock( ALOGI_IF(err != OK, "queueEncryptedLinearBlock returned err = %d", err); } else { if (!buffer) { + // It means there was an unexpected failure in extractBufferFromContext above ALOGI("queueLinearBlock: no C2Buffer found"); - throwExceptionAsNecessary(env, BAD_VALUE); + throwExceptionAsNecessary( + env, BAD_VALUE, ACTION_CODE_FATAL, + "Unexpected error: the input buffer is not compatible with " + "the non-secure codec, and a fallback logic failed.\n" + "Suggestion: please do not include the secure codec when calling " + "MediaCodec.LinearBlock#obtain method to obtain a compatible buffer."); return; } err = codec->queueBuffer( index, buffer, presentationTimeUs, flags, tunings, &errorDetailMsg); } - throwExceptionAsNecessary(env, err, ACTION_CODE_FATAL, errorDetailMsg.c_str()); + throwExceptionAsNecessary( + env, err, ACTION_CODE_FATAL, + codec->getExceptionMessage(errorDetailMsg.c_str()).c_str()); } static void android_media_MediaCodec_native_queueHardwareBuffer( @@ -2595,14 +2661,16 @@ static void android_media_MediaCodec_native_queueHardwareBuffer( sp<JMediaCodec> codec = getMediaCodec(env, thiz); if (codec == NULL || codec->initCheck() != OK) { - throwExceptionAsNecessary(env, INVALID_OPERATION); + throwExceptionAsNecessary(env, INVALID_OPERATION, codec); return; } sp<AMessage> tunings; status_t err = ConvertKeyValueListsToAMessage(env, keys, values, &tunings); if (err != OK) { - throwExceptionAsNecessary(env, err); + throwExceptionAsNecessary( + env, err, ACTION_CODE_FATAL, + "error occurred while converting tunings from Java to native"); return; } @@ -2627,7 +2695,9 @@ static void android_media_MediaCodec_native_queueHardwareBuffer( ALOGW("Failed to wrap AHardwareBuffer into C2GraphicAllocation"); native_handle_close(handle); native_handle_delete(handle); - throwExceptionAsNecessary(env, BAD_VALUE); + throwExceptionAsNecessary( + env, BAD_VALUE, ACTION_CODE_FATAL, + "HardwareBuffer not recognized"); return; } std::shared_ptr<C2GraphicBlock> block = _C2BlockFactory::CreateGraphicBlock(alloc); @@ -2636,7 +2706,9 @@ static void android_media_MediaCodec_native_queueHardwareBuffer( AString errorDetailMsg; err = codec->queueBuffer( index, buffer, presentationTimeUs, flags, tunings, &errorDetailMsg); - throwExceptionAsNecessary(env, err, ACTION_CODE_FATAL, errorDetailMsg.c_str()); + throwExceptionAsNecessary( + env, err, ACTION_CODE_FATAL, + codec->getExceptionMessage(errorDetailMsg.c_str()).c_str()); } static void android_media_MediaCodec_native_getOutputFrame( @@ -2646,13 +2718,13 @@ static void android_media_MediaCodec_native_getOutputFrame( sp<JMediaCodec> codec = getMediaCodec(env, thiz); if (codec == NULL || codec->initCheck() != OK) { - throwExceptionAsNecessary(env, INVALID_OPERATION); + throwExceptionAsNecessary(env, INVALID_OPERATION, codec); return; } status_t err = codec->getOutputFrame(env, frame, index); if (err != OK) { - throwExceptionAsNecessary(env, err); + throwExceptionAsNecessary(env, err, codec); } } @@ -2663,7 +2735,7 @@ static jint android_media_MediaCodec_dequeueInputBuffer( sp<JMediaCodec> codec = getMediaCodec(env, thiz); if (codec == NULL || codec->initCheck() != OK) { - throwExceptionAsNecessary(env, INVALID_OPERATION); + throwExceptionAsNecessary(env, INVALID_OPERATION, codec); return -1; } @@ -2674,7 +2746,7 @@ static jint android_media_MediaCodec_dequeueInputBuffer( return (jint) index; } - return throwExceptionAsNecessary(env, err); + return throwExceptionAsNecessary(env, err, codec); } static jint android_media_MediaCodec_dequeueOutputBuffer( @@ -2684,7 +2756,7 @@ static jint android_media_MediaCodec_dequeueOutputBuffer( sp<JMediaCodec> codec = getMediaCodec(env, thiz); if (codec == NULL || codec->initCheck() != OK) { - throwExceptionAsNecessary(env, INVALID_OPERATION); + throwExceptionAsNecessary(env, INVALID_OPERATION, codec); return 0; } @@ -2696,7 +2768,7 @@ static jint android_media_MediaCodec_dequeueOutputBuffer( return (jint) index; } - return throwExceptionAsNecessary(env, err); + return throwExceptionAsNecessary(env, err, codec); } static void android_media_MediaCodec_releaseOutputBuffer( @@ -2707,13 +2779,13 @@ static void android_media_MediaCodec_releaseOutputBuffer( sp<JMediaCodec> codec = getMediaCodec(env, thiz); if (codec == NULL || codec->initCheck() != OK) { - throwExceptionAsNecessary(env, INVALID_OPERATION); + throwExceptionAsNecessary(env, INVALID_OPERATION, codec); return; } status_t err = codec->releaseOutputBuffer(index, render, updatePTS, timestampNs); - throwExceptionAsNecessary(env, err); + throwExceptionAsNecessary(env, err, codec); } static void android_media_MediaCodec_signalEndOfInputStream(JNIEnv* env, @@ -2722,13 +2794,13 @@ static void android_media_MediaCodec_signalEndOfInputStream(JNIEnv* env, sp<JMediaCodec> codec = getMediaCodec(env, thiz); if (codec == NULL || codec->initCheck() != OK) { - throwExceptionAsNecessary(env, INVALID_OPERATION); + throwExceptionAsNecessary(env, INVALID_OPERATION, codec); return; } status_t err = codec->signalEndOfInputStream(); - throwExceptionAsNecessary(env, err); + throwExceptionAsNecessary(env, err, codec); } static jobject android_media_MediaCodec_getFormatNative( @@ -2738,7 +2810,7 @@ static jobject android_media_MediaCodec_getFormatNative( sp<JMediaCodec> codec = getMediaCodec(env, thiz); if (codec == NULL || codec->initCheck() != OK) { - throwExceptionAsNecessary(env, INVALID_OPERATION); + throwExceptionAsNecessary(env, INVALID_OPERATION, codec); return NULL; } @@ -2749,7 +2821,7 @@ static jobject android_media_MediaCodec_getFormatNative( return format; } - throwExceptionAsNecessary(env, err); + throwExceptionAsNecessary(env, err, codec); return NULL; } @@ -2761,7 +2833,7 @@ static jobject android_media_MediaCodec_getOutputFormatForIndexNative( sp<JMediaCodec> codec = getMediaCodec(env, thiz); if (codec == NULL || codec->initCheck() != OK) { - throwExceptionAsNecessary(env, INVALID_OPERATION); + throwExceptionAsNecessary(env, INVALID_OPERATION, codec); return NULL; } @@ -2772,7 +2844,7 @@ static jobject android_media_MediaCodec_getOutputFormatForIndexNative( return format; } - throwExceptionAsNecessary(env, err); + throwExceptionAsNecessary(env, err, codec); return NULL; } @@ -2784,7 +2856,7 @@ static jobjectArray android_media_MediaCodec_getBuffers( sp<JMediaCodec> codec = getMediaCodec(env, thiz); if (codec == NULL || codec->initCheck() != OK) { - throwExceptionAsNecessary(env, INVALID_OPERATION); + throwExceptionAsNecessary(env, INVALID_OPERATION, codec); return NULL; } @@ -2797,7 +2869,7 @@ static jobjectArray android_media_MediaCodec_getBuffers( // if we're out of memory, an exception was already thrown if (err != NO_MEMORY) { - throwExceptionAsNecessary(env, err); + throwExceptionAsNecessary(env, err, codec); } return NULL; @@ -2810,7 +2882,7 @@ static jobject android_media_MediaCodec_getBuffer( sp<JMediaCodec> codec = getMediaCodec(env, thiz); if (codec == NULL || codec->initCheck() != OK) { - throwExceptionAsNecessary(env, INVALID_OPERATION); + throwExceptionAsNecessary(env, INVALID_OPERATION, codec); return NULL; } @@ -2823,7 +2895,7 @@ static jobject android_media_MediaCodec_getBuffer( // if we're out of memory, an exception was already thrown if (err != NO_MEMORY) { - throwExceptionAsNecessary(env, err); + throwExceptionAsNecessary(env, err, codec); } return NULL; @@ -2836,7 +2908,7 @@ static jobject android_media_MediaCodec_getImage( sp<JMediaCodec> codec = getMediaCodec(env, thiz); if (codec == NULL || codec->initCheck() != OK) { - throwExceptionAsNecessary(env, INVALID_OPERATION); + throwExceptionAsNecessary(env, INVALID_OPERATION, codec); return NULL; } @@ -2849,7 +2921,7 @@ static jobject android_media_MediaCodec_getImage( // if we're out of memory, an exception was already thrown if (err != NO_MEMORY) { - throwExceptionAsNecessary(env, err); + throwExceptionAsNecessary(env, err, codec); } return NULL; @@ -2862,7 +2934,7 @@ static jobject android_media_MediaCodec_getName( sp<JMediaCodec> codec = getMediaCodec(env, thiz); if (codec == NULL || codec->initCheck() != OK) { - throwExceptionAsNecessary(env, INVALID_OPERATION); + throwExceptionAsNecessary(env, INVALID_OPERATION, codec); return NULL; } @@ -2873,7 +2945,7 @@ static jobject android_media_MediaCodec_getName( return name; } - throwExceptionAsNecessary(env, err); + throwExceptionAsNecessary(env, err, codec); return NULL; } @@ -2885,7 +2957,7 @@ static jobject android_media_MediaCodec_getOwnCodecInfo( sp<JMediaCodec> codec = getMediaCodec(env, thiz); if (codec == NULL || codec->initCheck() != OK) { - throwExceptionAsNecessary(env, INVALID_OPERATION); + throwExceptionAsNecessary(env, INVALID_OPERATION, codec); return NULL; } @@ -2896,7 +2968,7 @@ static jobject android_media_MediaCodec_getOwnCodecInfo( return codecInfoObj; } - throwExceptionAsNecessary(env, err); + throwExceptionAsNecessary(env, err, codec); return NULL; } @@ -2908,7 +2980,8 @@ android_media_MediaCodec_native_getMetrics(JNIEnv *env, jobject thiz) sp<JMediaCodec> codec = getMediaCodec(env, thiz); if (codec == NULL || codec->initCheck() != OK) { - jniThrowException(env, "java/lang/IllegalStateException", NULL); + jniThrowException(env, "java/lang/IllegalStateException", + GetExceptionMessage(codec, NULL).c_str()); return 0; } @@ -2937,7 +3010,7 @@ static void android_media_MediaCodec_setParameters( sp<JMediaCodec> codec = getMediaCodec(env, thiz); if (codec == NULL || codec->initCheck() != OK) { - throwExceptionAsNecessary(env, INVALID_OPERATION); + throwExceptionAsNecessary(env, INVALID_OPERATION, codec); return; } @@ -2948,7 +3021,7 @@ static void android_media_MediaCodec_setParameters( err = codec->setParameters(params); } - throwExceptionAsNecessary(env, err); + throwExceptionAsNecessary(env, err, codec); } static void android_media_MediaCodec_setVideoScalingMode( @@ -2956,13 +3029,14 @@ static void android_media_MediaCodec_setVideoScalingMode( sp<JMediaCodec> codec = getMediaCodec(env, thiz); if (codec == NULL || codec->initCheck() != OK) { - throwExceptionAsNecessary(env, INVALID_OPERATION); + throwExceptionAsNecessary(env, INVALID_OPERATION, codec); return; } if (mode != NATIVE_WINDOW_SCALING_MODE_SCALE_TO_WINDOW && mode != NATIVE_WINDOW_SCALING_MODE_SCALE_CROP) { - jniThrowException(env, "java/lang/IllegalArgumentException", NULL); + jniThrowException(env, "java/lang/IllegalArgumentException", + String8::format("Unrecognized mode: %d", mode)); return; } @@ -2974,7 +3048,7 @@ static void android_media_MediaCodec_setAudioPresentation( sp<JMediaCodec> codec = getMediaCodec(env, thiz); if (codec == NULL || codec->initCheck() != OK) { - throwExceptionAsNecessary(env, INVALID_OPERATION); + throwExceptionAsNecessary(env, INVALID_OPERATION, codec); return; } @@ -2986,14 +3060,14 @@ static jobject android_media_MediaCodec_getSupportedVendorParameters( sp<JMediaCodec> codec = getMediaCodec(env, thiz); if (codec == NULL || codec->initCheck() != OK) { - throwExceptionAsNecessary(env, INVALID_OPERATION); + throwExceptionAsNecessary(env, INVALID_OPERATION, codec); return NULL; } jobject ret = NULL; status_t status = codec->querySupportedVendorParameters(env, &ret); if (status != OK) { - throwExceptionAsNecessary(env, status); + throwExceptionAsNecessary(env, status, codec); } return ret; @@ -3004,7 +3078,7 @@ static jobject android_media_MediaCodec_getParameterDescriptor( sp<JMediaCodec> codec = getMediaCodec(env, thiz); if (codec == NULL || codec->initCheck() != OK) { - throwExceptionAsNecessary(env, INVALID_OPERATION); + throwExceptionAsNecessary(env, INVALID_OPERATION, codec); return NULL; } @@ -3021,13 +3095,13 @@ static void android_media_MediaCodec_subscribeToVendorParameters( sp<JMediaCodec> codec = getMediaCodec(env, thiz); if (codec == NULL || codec->initCheck() != OK) { - throwExceptionAsNecessary(env, INVALID_OPERATION); + throwExceptionAsNecessary(env, INVALID_OPERATION, codec); return; } status_t status = codec->subscribeToVendorParameters(env, names); if (status != OK) { - throwExceptionAsNecessary(env, status); + throwExceptionAsNecessary(env, status, codec); } return; } @@ -3037,13 +3111,13 @@ static void android_media_MediaCodec_unsubscribeFromVendorParameters( sp<JMediaCodec> codec = getMediaCodec(env, thiz); if (codec == NULL || codec->initCheck() != OK) { - throwExceptionAsNecessary(env, INVALID_OPERATION); + throwExceptionAsNecessary(env, INVALID_OPERATION, codec); return; } status_t status = codec->unsubscribeFromVendorParameters(env, names); if (status != OK) { - throwExceptionAsNecessary(env, status); + throwExceptionAsNecessary(env, status, codec); } return; } @@ -3440,11 +3514,15 @@ static jobject android_media_MediaCodec_LinearBlock_native_map( if (!context->mReadonlyMapping) { const C2BufferData data = buffer->data(); if (data.type() != C2BufferData::LINEAR) { - throwExceptionAsNecessary(env, INVALID_OPERATION); + throwExceptionAsNecessary( + env, INVALID_OPERATION, ACTION_CODE_FATAL, + "Underlying buffer is not a linear buffer"); return nullptr; } if (data.linearBlocks().size() != 1u) { - throwExceptionAsNecessary(env, INVALID_OPERATION); + throwExceptionAsNecessary( + env, INVALID_OPERATION, ACTION_CODE_FATAL, + "Underlying buffer contains more than one block"); return nullptr; } C2ConstLinearBlock block = data.linearBlocks().front(); @@ -3492,7 +3570,9 @@ static jobject android_media_MediaCodec_LinearBlock_native_map( false, // readOnly true /* clearBuffer */); } - throwExceptionAsNecessary(env, INVALID_OPERATION); + throwExceptionAsNecessary( + env, INVALID_OPERATION, ACTION_CODE_FATAL, + "Underlying buffer is empty"); return nullptr; } @@ -3515,7 +3595,9 @@ static void PopulateNamesVector( } const char *cstr = env->GetStringUTFChars(jstr, nullptr); if (cstr == nullptr) { - throwExceptionAsNecessary(env, BAD_VALUE); + throwExceptionAsNecessary( + env, BAD_VALUE, ACTION_CODE_FATAL, + "Error converting Java string to native"); return; } names->emplace_back(cstr); @@ -3567,6 +3649,7 @@ static jboolean android_media_MediaCodec_LinearBlock_checkCompatible( } status_t err = MediaCodec::CanFetchLinearBlock(names, &isCompatible); if (err != OK) { + // TODO: CodecErrorLog throwExceptionAsNecessary(env, err); } return isCompatible; diff --git a/media/jni/android_media_MediaCodec.h b/media/jni/android_media_MediaCodec.h index 616c31b29157..fbaf64fda572 100644 --- a/media/jni/android_media_MediaCodec.h +++ b/media/jni/android_media_MediaCodec.h @@ -176,6 +176,8 @@ struct JMediaCodec : public AHandler { const sp<ICrypto> &getCrypto() { return mCrypto; } + std::string getExceptionMessage(const char *msg) const; + protected: virtual ~JMediaCodec(); diff --git a/packages/CredentialManager/src/com/android/credentialmanager/CredentialSelectorActivity.kt b/packages/CredentialManager/src/com/android/credentialmanager/CredentialSelectorActivity.kt index 6f5015d6c79b..24f92c00c772 100644 --- a/packages/CredentialManager/src/com/android/credentialmanager/CredentialSelectorActivity.kt +++ b/packages/CredentialManager/src/com/android/credentialmanager/CredentialSelectorActivity.kt @@ -5,7 +5,7 @@ * 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 + * http://www.apache.org/licenses/LICENSE-2.0N * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, @@ -74,7 +74,6 @@ class CredentialSelectorActivity : ComponentActivity() { override fun onNewIntent(intent: Intent) { super.onNewIntent(intent) setIntent(intent) - Log.d(Constants.LOG_TAG, "Existing activity received new intent") try { val viewModel: CredentialSelectorViewModel by viewModels() val (isCancellationRequest, shouldShowCancellationUi, appDisplayName) = diff --git a/packages/CredentialManager/src/com/android/credentialmanager/common/ui/BottomSheet.kt b/packages/CredentialManager/src/com/android/credentialmanager/common/ui/BottomSheet.kt index edc902e41e9a..53731f06816a 100644 --- a/packages/CredentialManager/src/com/android/credentialmanager/common/ui/BottomSheet.kt +++ b/packages/CredentialManager/src/com/android/credentialmanager/common/ui/BottomSheet.kt @@ -22,6 +22,7 @@ import androidx.compose.runtime.Composable import androidx.compose.runtime.LaunchedEffect import androidx.compose.ui.Modifier import androidx.compose.ui.graphics.Color +import com.android.compose.rememberSystemUiController import com.android.credentialmanager.common.material.ModalBottomSheetLayout import com.android.credentialmanager.common.material.ModalBottomSheetValue import com.android.credentialmanager.common.material.rememberModalBottomSheetState @@ -38,6 +39,12 @@ fun ModalBottomSheet( initialValue = ModalBottomSheetValue.Expanded, skipHalfExpanded = true ) + val sysUiController = rememberSystemUiController() + if (state.targetValue == ModalBottomSheetValue.Hidden) { + setTransparentSystemBarsColor(sysUiController) + } else { + setBottomSheetSystemBarsColor(sysUiController) + } ModalBottomSheetLayout( sheetBackgroundColor = LocalAndroidColorScheme.current.colorSurfaceBright, modifier = Modifier.background(Color.Transparent), diff --git a/packages/CredentialManager/src/com/android/credentialmanager/common/ui/SnackBar.kt b/packages/CredentialManager/src/com/android/credentialmanager/common/ui/SnackBar.kt index dfff3d694877..244b604a87f9 100644 --- a/packages/CredentialManager/src/com/android/credentialmanager/common/ui/SnackBar.kt +++ b/packages/CredentialManager/src/com/android/credentialmanager/common/ui/SnackBar.kt @@ -37,6 +37,7 @@ import androidx.compose.ui.graphics.Color import androidx.compose.ui.platform.LocalAccessibilityManager import androidx.compose.ui.res.stringResource import androidx.compose.ui.unit.dp +import com.android.compose.rememberSystemUiController import com.android.credentialmanager.R import com.android.credentialmanager.common.material.Scrim import com.android.credentialmanager.ui.theme.Shapes @@ -49,6 +50,8 @@ fun Snackbar( onDismiss: () -> Unit, dismissOnTimeout: Boolean = false, ) { + val sysUiController = rememberSystemUiController() + setTransparentSystemBarsColor(sysUiController) BoxWithConstraints { Box(Modifier.fillMaxSize()) { Scrim( diff --git a/packages/CredentialManager/src/com/android/credentialmanager/createflow/CreateCredentialComponents.kt b/packages/CredentialManager/src/com/android/credentialmanager/createflow/CreateCredentialComponents.kt index ed4cc959543b..648d83268541 100644 --- a/packages/CredentialManager/src/com/android/credentialmanager/createflow/CreateCredentialComponents.kt +++ b/packages/CredentialManager/src/com/android/credentialmanager/createflow/CreateCredentialComponents.kt @@ -46,7 +46,6 @@ import androidx.compose.ui.res.painterResource import androidx.compose.ui.res.stringResource import androidx.compose.ui.unit.dp import androidx.core.graphics.drawable.toBitmap -import com.android.compose.rememberSystemUiController import com.android.credentialmanager.CredentialSelectorViewModel import com.android.credentialmanager.R import com.android.credentialmanager.common.BaseEntry @@ -67,7 +66,6 @@ import com.android.credentialmanager.common.ui.MoreOptionTopAppBar import com.android.credentialmanager.common.ui.SheetContainerCard import com.android.credentialmanager.common.ui.PasskeyBenefitRow import com.android.credentialmanager.common.ui.HeadlineText -import com.android.credentialmanager.common.ui.setBottomSheetSystemBarsColor import com.android.credentialmanager.logging.CreateCredentialEvent import com.android.internal.logging.UiEventLogger.UiEventEnum @@ -77,8 +75,6 @@ fun CreateCredentialScreen( createCredentialUiState: CreateCredentialUiState, providerActivityLauncher: ManagedActivityResultLauncher<IntentSenderRequest, ActivityResult> ) { - val sysUiController = rememberSystemUiController() - setBottomSheetSystemBarsColor(sysUiController) ModalBottomSheet( sheetContent = { // Hide the sheet content as opposed to the whole bottom sheet to maintain the scrim diff --git a/packages/CredentialManager/src/com/android/credentialmanager/getflow/GetCredentialComponents.kt b/packages/CredentialManager/src/com/android/credentialmanager/getflow/GetCredentialComponents.kt index c27ac943bca7..6d7ecd70eb0b 100644 --- a/packages/CredentialManager/src/com/android/credentialmanager/getflow/GetCredentialComponents.kt +++ b/packages/CredentialManager/src/com/android/credentialmanager/getflow/GetCredentialComponents.kt @@ -41,7 +41,6 @@ import androidx.compose.ui.res.painterResource import androidx.compose.ui.res.stringResource import androidx.compose.ui.unit.dp import androidx.core.graphics.drawable.toBitmap -import com.android.compose.rememberSystemUiController import com.android.credentialmanager.CredentialSelectorViewModel import com.android.credentialmanager.R import com.android.credentialmanager.common.BaseEntry @@ -62,8 +61,6 @@ import com.android.credentialmanager.common.ui.CredentialListSectionHeader import com.android.credentialmanager.common.ui.HeadlineIcon import com.android.credentialmanager.common.ui.LargeLabelTextOnSurfaceVariant import com.android.credentialmanager.common.ui.Snackbar -import com.android.credentialmanager.common.ui.setTransparentSystemBarsColor -import com.android.credentialmanager.common.ui.setBottomSheetSystemBarsColor import com.android.credentialmanager.logging.GetCredentialEvent import com.android.internal.logging.UiEventLogger.UiEventEnum @@ -73,9 +70,7 @@ fun GetCredentialScreen( getCredentialUiState: GetCredentialUiState, providerActivityLauncher: ManagedActivityResultLauncher<IntentSenderRequest, ActivityResult> ) { - val sysUiController = rememberSystemUiController() if (getCredentialUiState.currentScreenState == GetScreenState.REMOTE_ONLY) { - setTransparentSystemBarsColor(sysUiController) RemoteCredentialSnackBarScreen( onClick = viewModel::getFlowOnMoreOptionOnSnackBarSelected, onCancel = viewModel::onUserCancel, @@ -84,7 +79,6 @@ fun GetCredentialScreen( viewModel.uiMetrics.log(GetCredentialEvent.CREDMAN_GET_CRED_SCREEN_REMOTE_ONLY) } else if (getCredentialUiState.currentScreenState == GetScreenState.UNLOCKED_AUTH_ENTRIES_ONLY) { - setTransparentSystemBarsColor(sysUiController) EmptyAuthEntrySnackBarScreen( authenticationEntryList = getCredentialUiState.providerDisplayInfo.authenticationEntryList, @@ -95,7 +89,6 @@ fun GetCredentialScreen( viewModel.uiMetrics.log(GetCredentialEvent .CREDMAN_GET_CRED_SCREEN_UNLOCKED_AUTH_ENTRIES_ONLY) } else { - setBottomSheetSystemBarsColor(sysUiController) ModalBottomSheet( sheetContent = { // Hide the sheet content as opposed to the whole bottom sheet to maintain the scrim diff --git a/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java b/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java index 7a97b78a4517..b0a19270018a 100644 --- a/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java +++ b/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java @@ -3748,7 +3748,7 @@ public class SettingsProvider extends ContentProvider { } private final class UpgradeController { - private static final int SETTINGS_VERSION = 216; + private static final int SETTINGS_VERSION = 217; private final int mUserId; @@ -5711,7 +5711,7 @@ public class SettingsProvider extends ContentProvider { .getSettingLocked(Settings.Secure.CREDENTIAL_SERVICE); if (currentSetting.isNull()) { final int resourceId = - com.android.internal.R.string.config_defaultCredentialProviderService; + com.android.internal.R.array.config_defaultCredentialProviderService; final Resources resources = getContext().getResources(); // If the config has not be defined we might get an exception. We also get // values from both the string array type and the single string in case the @@ -5771,6 +5771,39 @@ public class SettingsProvider extends ContentProvider { currentVersion = 216; } + if (currentVersion == 216) { + // Version 216: Set a default value for Credential Manager service. + // We are doing this migration again because of an incorrect setting. + + final SettingsState secureSettings = getSecureSettingsLocked(userId); + final Setting currentSetting = secureSettings + .getSettingLocked(Settings.Secure.CREDENTIAL_SERVICE); + if (currentSetting.isNull()) { + final int resourceId = + com.android.internal.R.array.config_defaultCredentialProviderService; + final Resources resources = getContext().getResources(); + // If the config has not be defined we might get an exception. + final List<String> providers = new ArrayList<>(); + try { + providers.addAll(Arrays.asList(resources.getStringArray(resourceId))); + } catch (Resources.NotFoundException e) { + Slog.w(LOG_TAG, + "Get default array Cred Provider not found: " + e.toString()); + } + + if (!providers.isEmpty()) { + final String defaultValue = String.join(":", providers); + Slog.d(LOG_TAG, "Setting [" + defaultValue + "] as CredMan Service " + + "for user " + userId); + secureSettings.insertSettingOverrideableByRestoreLocked( + Settings.Secure.CREDENTIAL_SERVICE, defaultValue, null, true, + SettingsState.SYSTEM_PACKAGE_NAME); + } + } + + currentVersion = 217; + } + // vXXX: Add new settings above this point. if (currentVersion != newVersion) { diff --git a/packages/SettingsProvider/src/com/android/providers/settings/WritableNamespacePrefixes.java b/packages/SettingsProvider/src/com/android/providers/settings/WritableNamespacePrefixes.java index df27f6a67cfa..bd99a8bbb09f 100644 --- a/packages/SettingsProvider/src/com/android/providers/settings/WritableNamespacePrefixes.java +++ b/packages/SettingsProvider/src/com/android/providers/settings/WritableNamespacePrefixes.java @@ -170,6 +170,7 @@ final class WritableNamespacePrefixes { "widget", "wifi", "window_manager", - "window_manager_native_boot" + "window_manager_native_boot", + "wrong" )); } diff --git a/packages/SystemUI/src/com/android/systemui/flags/Flags.kt b/packages/SystemUI/src/com/android/systemui/flags/Flags.kt index a7b45f4faee3..0bc8506f272d 100644 --- a/packages/SystemUI/src/com/android/systemui/flags/Flags.kt +++ b/packages/SystemUI/src/com/android/systemui/flags/Flags.kt @@ -285,7 +285,7 @@ object Flags { /** Enables Font Scaling Quick Settings tile */ // TODO(b/269341316): Tracking Bug @JvmField - val ENABLE_FONT_SCALING_TILE = unreleasedFlag(509, "enable_font_scaling_tile", teamfood = true) + val ENABLE_FONT_SCALING_TILE = releasedFlag(509, "enable_font_scaling_tile") /** Enables new QS Edit Mode visual refresh */ // TODO(b/269787742): Tracking Bug diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java index b1efdd733faa..c102c5b57375 100644 --- a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java +++ b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java @@ -2856,14 +2856,14 @@ public class KeyguardViewMediator implements CoreStartable, Dumpable, + " wasShowing=" + wasShowing); } + mKeyguardUnlockAnimationControllerLazy.get() + .notifyFinishedKeyguardExitAnimation(cancelled); finishSurfaceBehindRemoteAnimation(cancelled); // Dispatch the callback on animation finishes. mUpdateMonitor.dispatchKeyguardDismissAnimationFinished(); }); - mKeyguardUnlockAnimationControllerLazy.get().notifyFinishedKeyguardExitAnimation( - cancelled); } /** diff --git a/packages/SystemUI/src/com/android/systemui/media/controls/ui/KeyguardMediaController.kt b/packages/SystemUI/src/com/android/systemui/media/controls/ui/KeyguardMediaController.kt index 8f1c9048026f..30ee147e302a 100644 --- a/packages/SystemUI/src/com/android/systemui/media/controls/ui/KeyguardMediaController.kt +++ b/packages/SystemUI/src/com/android/systemui/media/controls/ui/KeyguardMediaController.kt @@ -63,6 +63,10 @@ constructor( override fun onStateChanged(newState: Int) { refreshMediaPosition() } + + override fun onDozingChanged(isDozing: Boolean) { + refreshMediaPosition() + } } ) configurationController.addCallback( @@ -198,7 +202,8 @@ constructor( mediaHost.visible && !bypassController.bypassEnabled && keyguardOrUserSwitcher && - allowMediaPlayerOnLockScreen + allowMediaPlayerOnLockScreen && + shouldBeVisibleForSplitShade() if (visible) { showMediaPlayer() } else { @@ -206,6 +211,19 @@ constructor( } } + private fun shouldBeVisibleForSplitShade(): Boolean { + if (!useSplitShade) { + return true + } + // We have to explicitly hide media for split shade when on AOD, as it is a child view of + // keyguard status view, and nothing hides keyguard status view on AOD. + // When using the double-line clock, it is not an issue, as media gets implicitly hidden + // by the clock. This is not the case for single-line clock though. + // For single shade, we don't need to do it, because media is a child of NSSL, which already + // gets hidden on AOD. + return !statusBarStateController.isDozing + } + private fun showMediaPlayer() { if (useSplitShade) { setVisibility(splitShadeContainer, View.VISIBLE) diff --git a/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputAdapter.java b/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputAdapter.java index 9928c4f79a96..f50a7a854169 100644 --- a/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputAdapter.java +++ b/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputAdapter.java @@ -205,7 +205,8 @@ public class MediaOutputAdapter extends MediaOutputBaseAdapter { && mController.isSubStatusSupported() && mController.isAdvancedLayoutSupported() && device.hasSubtext()) { boolean isActiveWithOngoingSession = - (device.hasOngoingSession() && currentlyConnected); + (device.hasOngoingSession() && (currentlyConnected || isDeviceIncluded( + mController.getSelectedMediaDevice(), device))); boolean isHost = device.isHostForOngoingSession() && isActiveWithOngoingSession; if (isHost) { @@ -224,10 +225,17 @@ public class MediaOutputAdapter extends MediaOutputBaseAdapter { if (isActiveWithOngoingSession) { //Selected device which has ongoing session, disable seekbar since we //only allow volume control on Host - initSeekbar(device, isCurrentSeekbarInvisible); mCurrentActivePosition = position; } - setUpDeviceIcon(device); + boolean showSeekbar = + (!device.hasOngoingSession() && currentlyConnected); + if (showSeekbar) { + updateTitleIcon(R.drawable.media_output_icon_volume, + mController.getColorItemContent()); + initSeekbar(device, isCurrentSeekbarInvisible); + } else { + setUpDeviceIcon(device); + } mSubTitleText.setText(device.getSubtextString()); Drawable deviceStatusIcon = device.hasOngoingSession() ? mContext.getDrawable( @@ -241,8 +249,8 @@ public class MediaOutputAdapter extends MediaOutputBaseAdapter { updateTwoLineLayoutContentAlpha( updateClickActionBasedOnSelectionBehavior(device) ? DEVICE_CONNECTED_ALPHA : DEVICE_DISCONNECTED_ALPHA); - setTwoLineLayout(device, isActiveWithOngoingSession /* bFocused */, - isActiveWithOngoingSession /* showSeekBar */, + setTwoLineLayout(device, currentlyConnected /* bFocused */, + showSeekbar /* showSeekBar */, false /* showProgressBar */, true /* showSubtitle */, deviceStatusIcon != null /* showStatus */, isActiveWithOngoingSession /* isFakeActive */); diff --git a/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputBaseAdapter.java b/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputBaseAdapter.java index 731bb2f4db7c..73ab52722a79 100644 --- a/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputBaseAdapter.java +++ b/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputBaseAdapter.java @@ -270,10 +270,10 @@ public abstract class MediaOutputBaseAdapter extends final Drawable backgroundDrawable; if (mController.isAdvancedLayoutSupported() && mController.isSubStatusSupported()) { backgroundDrawable = mContext.getDrawable( - showSeekBar ? R.drawable.media_output_item_background_active + showSeekBar || isFakeActive ? R.drawable.media_output_item_background_active : R.drawable.media_output_item_background).mutate(); backgroundDrawable.setTint( - showSeekBar ? mController.getColorConnectedItemBackground() + showSeekBar || isFakeActive ? mController.getColorConnectedItemBackground() : mController.getColorItemBackground()); mIconAreaLayout.setBackgroundTintList( ColorStateList.valueOf(showProgressBar || isFakeActive diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/ui/viewmodel/MobileIconViewModel.kt b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/ui/viewmodel/MobileIconViewModel.kt index 0fd007cf40ef..62bc27f9dd4e 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/ui/viewmodel/MobileIconViewModel.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/ui/viewmodel/MobileIconViewModel.kt @@ -170,7 +170,10 @@ constructor( if (networkTypeIconGroup.dataContentDescription != 0) ContentDescription.Resource(networkTypeIconGroup.dataContentDescription) else null - val icon = Icon.Resource(networkTypeIconGroup.dataType, desc) + val icon = + if (networkTypeIconGroup.dataType != 0) + Icon.Resource(networkTypeIconGroup.dataType, desc) + else null return@combine when { !shouldShow -> null else -> icon diff --git a/packages/SystemUI/tests/src/com/android/systemui/controls/management/ControlsFavoritingActivityTest.kt b/packages/SystemUI/tests/src/com/android/systemui/controls/management/ControlsFavoritingActivityTest.kt index 68846168d17b..f4cc8bcb09a5 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/controls/management/ControlsFavoritingActivityTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/controls/management/ControlsFavoritingActivityTest.kt @@ -60,6 +60,12 @@ class ControlsFavoritingActivityTest : SysuiTestCase() { } val TEST_STRUCTURE: CharSequence = "TestStructure" val TEST_APP: CharSequence = "TestApp" + + private fun View.waitForPost() { + val latch = CountDownLatch(1) + post(latch::countDown) + latch.await() + } } @Main private val executor: Executor = MoreExecutors.directExecutor() @@ -140,7 +146,10 @@ class ControlsFavoritingActivityTest : SysuiTestCase() { val rearrangeButton = requireViewById<Button>(R.id.rearrange) assertThat(rearrangeButton.visibility).isEqualTo(View.VISIBLE) assertThat(rearrangeButton.isEnabled).isFalse() - assertThat(requireViewById<Button>(R.id.other_apps).visibility).isEqualTo(View.GONE) + + val otherAppsButton = requireViewById<Button>(R.id.other_apps) + otherAppsButton.waitForPost() + assertThat(otherAppsButton.visibility).isEqualTo(View.GONE) } } @@ -160,7 +169,10 @@ class ControlsFavoritingActivityTest : SysuiTestCase() { val rearrangeButton = requireViewById<Button>(R.id.rearrange) assertThat(rearrangeButton.visibility).isEqualTo(View.GONE) assertThat(rearrangeButton.isEnabled).isFalse() - assertThat(requireViewById<Button>(R.id.other_apps).visibility).isEqualTo(View.VISIBLE) + + val otherAppsButton = requireViewById<Button>(R.id.other_apps) + otherAppsButton.waitForPost() + assertThat(otherAppsButton.visibility).isEqualTo(View.VISIBLE) } } diff --git a/packages/SystemUI/tests/src/com/android/systemui/media/controls/ui/KeyguardMediaControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/media/controls/ui/KeyguardMediaControllerTest.kt index 20260069c943..b40ebc9bb156 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/media/controls/ui/KeyguardMediaControllerTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/media/controls/ui/KeyguardMediaControllerTest.kt @@ -24,12 +24,14 @@ import android.view.View.GONE import android.view.View.VISIBLE import android.widget.FrameLayout import com.android.systemui.SysuiTestCase +import com.android.systemui.plugins.statusbar.StatusBarStateController import com.android.systemui.statusbar.StatusBarState import com.android.systemui.statusbar.SysuiStatusBarStateController import com.android.systemui.statusbar.notification.stack.MediaContainerView import com.android.systemui.statusbar.phone.KeyguardBypassController import com.android.systemui.statusbar.policy.ConfigurationController import com.android.systemui.util.animation.UniqueObjectHostView +import com.android.systemui.util.mockito.whenever import com.android.systemui.util.settings.FakeSettings import com.android.systemui.utils.os.FakeHandler import com.google.common.truth.Truth.assertThat @@ -39,8 +41,9 @@ import org.junit.Rule import org.junit.Test import org.junit.runner.RunWith import org.mockito.Mock +import org.mockito.Mockito.any +import org.mockito.Mockito.doAnswer import org.mockito.Mockito.verify -import org.mockito.Mockito.`when` as whenever import org.mockito.junit.MockitoJUnit @SmallTest @@ -61,9 +64,16 @@ class KeyguardMediaControllerTest : SysuiTestCase() { private lateinit var keyguardMediaController: KeyguardMediaController private lateinit var testableLooper: TestableLooper private lateinit var fakeHandler: FakeHandler + private lateinit var statusBarStateListener: StatusBarStateController.StateListener @Before fun setup() { + doAnswer { + statusBarStateListener = it.arguments[0] as StatusBarStateController.StateListener + return@doAnswer Unit + } + .whenever(statusBarStateController) + .addCallback(any(StatusBarStateController.StateListener::class.java)) // default state is positive, media should show up whenever(mediaHost.visible).thenReturn(true) whenever(statusBarStateController.state).thenReturn(StatusBarState.KEYGUARD) @@ -170,4 +180,31 @@ class KeyguardMediaControllerTest : SysuiTestCase() { fun testMediaHost_expandedPlayer() { verify(mediaHost).expansion = MediaHostState.EXPANDED } + + @Test + fun dozing_inSplitShade_mediaIsHidden() { + val splitShadeContainer = FrameLayout(context) + keyguardMediaController.attachSplitShadeContainer(splitShadeContainer) + keyguardMediaController.useSplitShade = true + + setDozing() + + assertThat(splitShadeContainer.visibility).isEqualTo(GONE) + } + + @Test + fun dozing_inSingleShade_mediaIsVisible() { + val splitShadeContainer = FrameLayout(context) + keyguardMediaController.attachSplitShadeContainer(splitShadeContainer) + keyguardMediaController.useSplitShade = false + + setDozing() + + assertThat(mediaContainerView.visibility).isEqualTo(VISIBLE) + } + + private fun setDozing() { + whenever(statusBarStateController.isDozing).thenReturn(true) + statusBarStateListener.onDozingChanged(true) + } } diff --git a/packages/SystemUI/tests/src/com/android/systemui/media/dialog/MediaOutputAdapterTest.java b/packages/SystemUI/tests/src/com/android/systemui/media/dialog/MediaOutputAdapterTest.java index 17d8799b4f84..7f7952feb10b 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/media/dialog/MediaOutputAdapterTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/media/dialog/MediaOutputAdapterTest.java @@ -532,7 +532,7 @@ public class MediaOutputAdapterTest extends SysuiTestCase { mMediaOutputAdapter.onBindViewHolder(mViewHolder, 0); assertThat(mViewHolder.mTitleText.getVisibility()).isEqualTo(View.GONE); - assertThat(mViewHolder.mSeekBar.getVisibility()).isEqualTo(View.VISIBLE); + assertThat(mViewHolder.mSeekBar.getVisibility()).isEqualTo(View.GONE); assertThat(mViewHolder.mProgressBar.getVisibility()).isEqualTo(View.GONE); assertThat(mViewHolder.mCheckBox.getVisibility()).isEqualTo(View.GONE); assertThat(mViewHolder.mStatusIcon.getVisibility()).isEqualTo(View.VISIBLE); diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/ui/viewmodel/MobileIconViewModelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/ui/viewmodel/MobileIconViewModelTest.kt index 8ea8f87e6aff..1593e5c735a5 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/ui/viewmodel/MobileIconViewModelTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/ui/viewmodel/MobileIconViewModelTest.kt @@ -20,6 +20,7 @@ import androidx.test.filters.SmallTest import com.android.settingslib.AccessibilityContentDescriptions.PHONE_SIGNAL_STRENGTH import com.android.settingslib.AccessibilityContentDescriptions.PHONE_SIGNAL_STRENGTH_NONE import com.android.settingslib.mobile.TelephonyIcons.THREE_G +import com.android.settingslib.mobile.TelephonyIcons.UNKNOWN import com.android.systemui.SysuiTestCase import com.android.systemui.common.shared.model.ContentDescription import com.android.systemui.common.shared.model.Icon @@ -343,7 +344,7 @@ class MobileIconViewModelTest : SysuiTestCase() { fun networkType_alwaysShow_shownEvenWhenDisabled() = testScope.runTest { interactor.setIconGroup(THREE_G) - interactor.setIsDataEnabled(true) + interactor.setIsDataEnabled(false) interactor.alwaysShowDataRatIcon.value = true var latest: Icon? = null @@ -400,6 +401,21 @@ class MobileIconViewModelTest : SysuiTestCase() { } @Test + fun networkType_alwaysShow_notShownWhenInvalidDataTypeIcon() = + testScope.runTest { + // The UNKNOWN icon group doesn't have a valid data type icon ID + interactor.setIconGroup(UNKNOWN) + interactor.alwaysShowDataRatIcon.value = true + + var latest: Icon? = null + val job = underTest.networkTypeIcon.onEach { latest = it }.launchIn(this) + + assertThat(latest).isNull() + + job.cancel() + } + + @Test fun `network type - alwaysShow - shown when not default`() = testScope.runTest { interactor.setIconGroup(THREE_G) diff --git a/services/companion/java/com/android/server/companion/virtual/VirtualDeviceImpl.java b/services/companion/java/com/android/server/companion/virtual/VirtualDeviceImpl.java index 1363ef31c68d..ae88f24ab409 100644 --- a/services/companion/java/com/android/server/companion/virtual/VirtualDeviceImpl.java +++ b/services/companion/java/com/android/server/companion/virtual/VirtualDeviceImpl.java @@ -126,7 +126,7 @@ final class VirtualDeviceImpl extends IVirtualDevice.Stub private final VirtualDeviceManagerService mService; private final PendingTrampolineCallback mPendingTrampolineCallback; private final int mOwnerUid; - private final int mDeviceId; + private int mDeviceId; // Thou shall not hold the mVirtualDeviceLock over the mInputController calls. // Holding the lock can lead to lock inversion with GlobalWindowManagerLock. // 1. After display is created the window manager calls into VDM during construction @@ -348,6 +348,7 @@ final class VirtualDeviceImpl extends IVirtualDevice.Stub @Override // Binder call public void launchPendingIntent(int displayId, PendingIntent pendingIntent, ResultReceiver resultReceiver) { + Objects.requireNonNull(pendingIntent); synchronized (mVirtualDeviceLock) { if (!mVirtualDisplays.contains(displayId)) { throw new SecurityException("Display ID " + displayId @@ -404,6 +405,7 @@ final class VirtualDeviceImpl extends IVirtualDevice.Stub super.close_enforcePermission(); // Remove about-to-be-closed virtual device from the service before butchering it. mService.removeVirtualDevice(mDeviceId); + mDeviceId = Context.DEVICE_ID_INVALID; VirtualDisplayWrapper[] virtualDisplaysToBeReleased; synchronized (mVirtualDeviceLock) { @@ -497,6 +499,7 @@ final class VirtualDeviceImpl extends IVirtualDevice.Stub @EnforcePermission(android.Manifest.permission.CREATE_VIRTUAL_DEVICE) public void createVirtualDpad(VirtualDpadConfig config, @NonNull IBinder deviceToken) { super.createVirtualDpad_enforcePermission(); + Objects.requireNonNull(config); synchronized (mVirtualDeviceLock) { if (!mVirtualDisplays.contains(config.getAssociatedDisplayId())) { throw new SecurityException( @@ -517,6 +520,7 @@ final class VirtualDeviceImpl extends IVirtualDevice.Stub @EnforcePermission(android.Manifest.permission.CREATE_VIRTUAL_DEVICE) public void createVirtualKeyboard(VirtualKeyboardConfig config, @NonNull IBinder deviceToken) { super.createVirtualKeyboard_enforcePermission(); + Objects.requireNonNull(config); synchronized (mVirtualDeviceLock) { if (!mVirtualDisplays.contains(config.getAssociatedDisplayId())) { throw new SecurityException( @@ -539,6 +543,7 @@ final class VirtualDeviceImpl extends IVirtualDevice.Stub @EnforcePermission(android.Manifest.permission.CREATE_VIRTUAL_DEVICE) public void createVirtualMouse(VirtualMouseConfig config, @NonNull IBinder deviceToken) { super.createVirtualMouse_enforcePermission(); + Objects.requireNonNull(config); synchronized (mVirtualDeviceLock) { if (!mVirtualDisplays.contains(config.getAssociatedDisplayId())) { throw new SecurityException( @@ -560,6 +565,7 @@ final class VirtualDeviceImpl extends IVirtualDevice.Stub public void createVirtualTouchscreen(VirtualTouchscreenConfig config, @NonNull IBinder deviceToken) { super.createVirtualTouchscreen_enforcePermission(); + Objects.requireNonNull(config); synchronized (mVirtualDeviceLock) { if (!mVirtualDisplays.contains(config.getAssociatedDisplayId())) { throw new SecurityException( @@ -590,6 +596,7 @@ final class VirtualDeviceImpl extends IVirtualDevice.Stub public void createVirtualNavigationTouchpad(VirtualNavigationTouchpadConfig config, @NonNull IBinder deviceToken) { super.createVirtualNavigationTouchpad_enforcePermission(); + Objects.requireNonNull(config); synchronized (mVirtualDeviceLock) { if (!mVirtualDisplays.contains(config.getAssociatedDisplayId())) { throw new SecurityException( diff --git a/services/companion/java/com/android/server/companion/virtual/VirtualDeviceManagerService.java b/services/companion/java/com/android/server/companion/virtual/VirtualDeviceManagerService.java index 3b1983f55fb6..291c05877c17 100644 --- a/services/companion/java/com/android/server/companion/virtual/VirtualDeviceManagerService.java +++ b/services/companion/java/com/android/server/companion/virtual/VirtualDeviceManagerService.java @@ -66,7 +66,10 @@ import com.android.server.wm.ActivityTaskManagerInternal; import java.io.FileDescriptor; import java.io.PrintWriter; import java.util.ArrayList; +import java.util.HashSet; import java.util.List; +import java.util.Objects; +import java.util.Set; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.atomic.AtomicInteger; import java.util.function.Consumer; @@ -86,6 +89,14 @@ public class VirtualDeviceManagerService extends SystemService { private static AtomicInteger sNextUniqueIndex = new AtomicInteger( Context.DEVICE_ID_DEFAULT + 1); + private final CompanionDeviceManager.OnAssociationsChangedListener mCdmAssociationListener = + new CompanionDeviceManager.OnAssociationsChangedListener() { + @Override + public void onAssociationsChanged(@NonNull List<AssociationInfo> associations) { + syncVirtualDevicesToCdmAssociations(associations); + } + }; + /** * Mapping from device IDs to virtual devices. */ @@ -204,11 +215,56 @@ public class VirtualDeviceManagerService extends SystemService { final long identity = Binder.clearCallingIdentity(); try { getContext().sendBroadcastAsUser(i, UserHandle.ALL); + + synchronized (mVirtualDeviceManagerLock) { + if (mVirtualDevices.size() == 0) { + unregisterCdmAssociationListener(); + } + } } finally { Binder.restoreCallingIdentity(identity); } } + private void syncVirtualDevicesToCdmAssociations(List<AssociationInfo> associations) { + Set<VirtualDeviceImpl> virtualDevicesToRemove = new HashSet<>(); + synchronized (mVirtualDeviceManagerLock) { + if (mVirtualDevices.size() == 0) { + return; + } + + Set<Integer> activeAssociationIds = new HashSet<>(associations.size()); + for (AssociationInfo association : associations) { + activeAssociationIds.add(association.getId()); + } + + for (int i = 0; i < mVirtualDevices.size(); i++) { + VirtualDeviceImpl virtualDevice = mVirtualDevices.valueAt(i); + if (!activeAssociationIds.contains(virtualDevice.getAssociationId())) { + virtualDevicesToRemove.add(virtualDevice); + } + } + } + + for (VirtualDeviceImpl virtualDevice : virtualDevicesToRemove) { + virtualDevice.close(); + } + + } + + private void registerCdmAssociationListener() { + final CompanionDeviceManager cdm = getContext().getSystemService( + CompanionDeviceManager.class); + cdm.addOnAssociationsChangedListener(getContext().getMainExecutor(), + mCdmAssociationListener); + } + + private void unregisterCdmAssociationListener() { + final CompanionDeviceManager cdm = getContext().getSystemService( + CompanionDeviceManager.class); + cdm.removeOnAssociationsChangedListener(mCdmAssociationListener); + } + class VirtualDeviceManagerImpl extends IVirtualDeviceManager.Stub { private final VirtualDeviceImpl.PendingTrampolineCallback mPendingTrampolineCallback = @@ -254,7 +310,20 @@ public class VirtualDeviceManagerService extends SystemService { if (associationInfo == null) { throw new IllegalArgumentException("No association with ID " + associationId); } + Objects.requireNonNull(params); + Objects.requireNonNull(activityListener); + Objects.requireNonNull(soundEffectListener); + synchronized (mVirtualDeviceManagerLock) { + if (mVirtualDevices.size() == 0) { + final long callindId = Binder.clearCallingIdentity(); + try { + registerCdmAssociationListener(); + } finally { + Binder.restoreCallingIdentity(callindId); + } + } + final UserHandle userHandle = getCallingUserHandle(); final CameraAccessController cameraAccessController = getCameraAccessController(userHandle); @@ -275,6 +344,7 @@ public class VirtualDeviceManagerService extends SystemService { public int createVirtualDisplay(VirtualDisplayConfig virtualDisplayConfig, IVirtualDisplayCallback callback, IVirtualDevice virtualDevice, String packageName) throws RemoteException { + Objects.requireNonNull(virtualDisplayConfig); final int callingUid = getCallingUid(); if (!PermissionUtils.validateCallingPackageName(getContext(), packageName)) { throw new SecurityException( diff --git a/services/core/java/com/android/server/display/AutomaticBrightnessController.java b/services/core/java/com/android/server/display/AutomaticBrightnessController.java index af5609a68952..299f86550efd 100644 --- a/services/core/java/com/android/server/display/AutomaticBrightnessController.java +++ b/services/core/java/com/android/server/display/AutomaticBrightnessController.java @@ -75,10 +75,11 @@ public class AutomaticBrightnessController { private static final int MSG_UPDATE_AMBIENT_LUX = 1; private static final int MSG_BRIGHTNESS_ADJUSTMENT_SAMPLE = 2; - private static final int MSG_INVALIDATE_SHORT_TERM_MODEL = 3; + private static final int MSG_INVALIDATE_CURRENT_SHORT_TERM_MODEL = 3; private static final int MSG_UPDATE_FOREGROUND_APP = 4; private static final int MSG_UPDATE_FOREGROUND_APP_SYNC = 5; private static final int MSG_RUN_UPDATE = 6; + private static final int MSG_INVALIDATE_PAUSED_SHORT_TERM_MODEL = 7; // Callbacks for requesting updates to the display's power state private final Callbacks mCallbacks; @@ -216,12 +217,11 @@ public class AutomaticBrightnessController { private float mBrightnessAdjustmentSampleOldLux; private float mBrightnessAdjustmentSampleOldBrightness; - // When the short term model is invalidated, we don't necessarily reset it (i.e. clear the - // user's adjustment) immediately, but wait for a drastic enough change in the ambient light. - // The anchor determines what were the light levels when the user has set their preference, and - // we use a relative threshold to determine when to revert to the OEM curve. - private boolean mShortTermModelValid; - private float mShortTermModelAnchor; + // The short term models, current and previous. Eg, we might use the "paused" one to save out + // the interactive short term model when switching to idle screen brightness mode, and + // vice-versa. + private final ShortTermModel mShortTermModel; + private final ShortTermModel mPausedShortTermModel; // Controls High Brightness Mode. private HighBrightnessModeController mHbmController; @@ -309,8 +309,8 @@ public class AutomaticBrightnessController { mAmbientBrightnessThresholdsIdle = ambientBrightnessThresholdsIdle; mScreenBrightnessThresholds = screenBrightnessThresholds; mScreenBrightnessThresholdsIdle = screenBrightnessThresholdsIdle; - mShortTermModelValid = true; - mShortTermModelAnchor = -1; + mShortTermModel = new ShortTermModel(); + mPausedShortTermModel = new ShortTermModel(); mHandler = new AutomaticBrightnessHandler(looper); mAmbientLightRingBuffer = new AmbientLightRingBuffer(mNormalLightSensorRate, mAmbientLightHorizonLong, mClock); @@ -492,10 +492,10 @@ public class AutomaticBrightnessController { Slog.d(TAG, "Display policy transitioning from " + oldPolicy + " to " + policy); } if (!isInteractivePolicy(policy) && isInteractivePolicy(oldPolicy) && !isInIdleMode()) { - mHandler.sendEmptyMessageDelayed(MSG_INVALIDATE_SHORT_TERM_MODEL, + mHandler.sendEmptyMessageDelayed(MSG_INVALIDATE_CURRENT_SHORT_TERM_MODEL, mCurrentBrightnessMapper.getShortTermModelTimeout()); } else if (isInteractivePolicy(policy) && !isInteractivePolicy(oldPolicy)) { - mHandler.removeMessages(MSG_INVALIDATE_SHORT_TERM_MODEL); + mHandler.removeMessages(MSG_INVALIDATE_CURRENT_SHORT_TERM_MODEL); } return true; } @@ -516,25 +516,13 @@ public class AutomaticBrightnessController { private boolean setScreenBrightnessByUser(float lux, float brightness) { mCurrentBrightnessMapper.addUserDataPoint(lux, brightness); - mShortTermModelValid = true; - mShortTermModelAnchor = lux; - if (mLoggingEnabled) { - Slog.d(TAG, "ShortTermModel: anchor=" + mShortTermModelAnchor); - } + mShortTermModel.setUserBrightness(lux, brightness); return true; } public void resetShortTermModel() { mCurrentBrightnessMapper.clearUserDataPoints(); - mShortTermModelValid = true; - mShortTermModelAnchor = -1; - } - - private void invalidateShortTermModel() { - if (mLoggingEnabled) { - Slog.d(TAG, "ShortTermModel: invalidate user data"); - } - mShortTermModelValid = false; + mShortTermModel.reset(); } public boolean setBrightnessConfiguration(BrightnessConfiguration configuration, @@ -595,8 +583,12 @@ public class AutomaticBrightnessController { pw.println(" mShortTermModelTimeout(idle)=" + mIdleModeBrightnessMapper.getShortTermModelTimeout()); } - pw.println(" mShortTermModelAnchor=" + mShortTermModelAnchor); - pw.println(" mShortTermModelValid=" + mShortTermModelValid); + pw.println(" mShortTermModel="); + mShortTermModel.dump(pw); + pw.println(" mPausedShortTermModel="); + mPausedShortTermModel.dump(pw); + + pw.println(); pw.println(" mBrightnessAdjustmentSamplePending=" + mBrightnessAdjustmentSamplePending); pw.println(" mBrightnessAdjustmentSampleOldLux=" + mBrightnessAdjustmentSampleOldLux); pw.println(" mBrightnessAdjustmentSampleOldBrightness=" @@ -740,15 +732,9 @@ public class AutomaticBrightnessController { } mHbmController.onAmbientLuxChange(mAmbientLux); + // If the short term model was invalidated and the change is drastic enough, reset it. - if (!mShortTermModelValid && mShortTermModelAnchor != -1) { - if (mCurrentBrightnessMapper.shouldResetShortTermModel( - mAmbientLux, mShortTermModelAnchor)) { - resetShortTermModel(); - } else { - mShortTermModelValid = true; - } - } + mShortTermModel.maybeReset(mAmbientLux); } private float calculateAmbientLux(long now, long horizon) { @@ -1118,8 +1104,29 @@ public class AutomaticBrightnessController { return; } Slog.i(TAG, "Switching to Idle Screen Brightness Mode"); + // Stash short term model + ShortTermModel tempShortTermModel = new ShortTermModel(); + tempShortTermModel.set(mCurrentBrightnessMapper.getUserLux(), + mCurrentBrightnessMapper.getUserBrightness(), /* valid= */ true); + + // Send delayed timeout + mHandler.sendEmptyMessageAtTime(MSG_INVALIDATE_PAUSED_SHORT_TERM_MODEL, + mClock.uptimeMillis() + + mCurrentBrightnessMapper.getShortTermModelTimeout()); + + Slog.i(TAG, "mPreviousShortTermModel" + mPausedShortTermModel); + // new brightness mapper mCurrentBrightnessMapper = mIdleModeBrightnessMapper; - resetShortTermModel(); + + // if previous stm has been invalidated, and lux has drastically changed, just use + // the new, reset stm. + // if previous stm is still valid then revalidate it + if (mPausedShortTermModel != null && !mPausedShortTermModel.maybeReset(mAmbientLux)) { + setScreenBrightnessByUser(mPausedShortTermModel.mAnchor, + mPausedShortTermModel.mBrightness); + } + mPausedShortTermModel.copyFrom(tempShortTermModel); + update(); } @@ -1128,8 +1135,28 @@ public class AutomaticBrightnessController { return; } Slog.i(TAG, "Switching to Interactive Screen Brightness Mode"); + ShortTermModel tempShortTermModel = new ShortTermModel(); + tempShortTermModel.set(mCurrentBrightnessMapper.getUserLux(), + mCurrentBrightnessMapper.getUserBrightness(), /* valid= */ true); + mHandler.removeMessages(MSG_INVALIDATE_PAUSED_SHORT_TERM_MODEL); + // Send delayed timeout + mHandler.sendEmptyMessageAtTime(MSG_INVALIDATE_PAUSED_SHORT_TERM_MODEL, + mClock.uptimeMillis() + + mCurrentBrightnessMapper.getShortTermModelTimeout()); + Slog.i(TAG, "mPreviousShortTermModel" + mPausedShortTermModel.toString()); + + // restore interactive mapper. mCurrentBrightnessMapper = mInteractiveModeBrightnessMapper; - resetShortTermModel(); + + // if previous stm has been invalidated, and lux has drastically changed, just use + // the new, reset stm. + // if previous stm is still valid then revalidate it + if (!mPausedShortTermModel.maybeReset(mAmbientLux)) { + setScreenBrightnessByUser(mPausedShortTermModel.mAnchor, + mPausedShortTermModel.mBrightness); + } + mPausedShortTermModel.copyFrom(tempShortTermModel); + update(); } @@ -1164,6 +1191,77 @@ public class AutomaticBrightnessController { } } + private class ShortTermModel { + // When the short term model is invalidated, we don't necessarily reset it (i.e. clear the + // user's adjustment) immediately, but wait for a drastic enough change in the ambient + // light. + // The anchor determines what were the light levels when the user has set their preference, + // and we use a relative threshold to determine when to revert to the OEM curve. + private float mAnchor = -1f; + private float mBrightness; + private boolean mIsValid = true; + + private void reset() { + mAnchor = -1f; + mBrightness = -1f; + mIsValid = true; + } + + private void invalidate() { + mIsValid = false; + if (mLoggingEnabled) { + Slog.d(TAG, "ShortTermModel: invalidate user data"); + } + } + + private void setUserBrightness(float lux, float brightness) { + mAnchor = lux; + mBrightness = brightness; + mIsValid = true; + if (mLoggingEnabled) { + Slog.d(TAG, "ShortTermModel: anchor=" + mAnchor); + } + } + + private boolean maybeReset(float currentLux) { + // If the short term model was invalidated and the change is drastic enough, reset it. + // Otherwise, we revalidate it. + if (!mIsValid && mAnchor != -1) { + if (mCurrentBrightnessMapper != null + && mCurrentBrightnessMapper.shouldResetShortTermModel( + currentLux, mAnchor)) { + resetShortTermModel(); + } else { + mIsValid = true; + } + return mIsValid; + } + return false; + } + + private void set(float anchor, float brightness, boolean valid) { + mAnchor = anchor; + mBrightness = brightness; + mIsValid = valid; + } + private void copyFrom(ShortTermModel from) { + mAnchor = from.mAnchor; + mBrightness = from.mBrightness; + mIsValid = from.mIsValid; + } + + public String toString() { + return " mAnchor: " + mAnchor + + "\n mBrightness: " + mBrightness + + "\n mIsValid: " + mIsValid; + } + + void dump(PrintWriter pw) { + pw.println(this); + } + + } + private final class AutomaticBrightnessHandler extends Handler { public AutomaticBrightnessHandler(Looper looper) { super(looper, null, true /*async*/); @@ -1184,8 +1282,8 @@ public class AutomaticBrightnessController { collectBrightnessAdjustmentSample(); break; - case MSG_INVALIDATE_SHORT_TERM_MODEL: - invalidateShortTermModel(); + case MSG_INVALIDATE_CURRENT_SHORT_TERM_MODEL: + mShortTermModel.invalidate(); break; case MSG_UPDATE_FOREGROUND_APP: @@ -1195,6 +1293,10 @@ public class AutomaticBrightnessController { case MSG_UPDATE_FOREGROUND_APP_SYNC: updateForegroundAppSync(); break; + + case MSG_INVALIDATE_PAUSED_SHORT_TERM_MODEL: + mPausedShortTermModel.invalidate(); + break; } } } diff --git a/services/core/java/com/android/server/display/BrightnessMappingStrategy.java b/services/core/java/com/android/server/display/BrightnessMappingStrategy.java index d0471837d79b..3456e3e262de 100644 --- a/services/core/java/com/android/server/display/BrightnessMappingStrategy.java +++ b/services/core/java/com/android/server/display/BrightnessMappingStrategy.java @@ -363,13 +363,17 @@ public abstract class BrightnessMappingStrategy { public abstract void recalculateSplines(boolean applyAdjustment, float[] adjustment); /** - * Returns the timeout for the short term model + * Returns the timeout, in milliseconds for the short term model * * Timeout after which we remove the effects any user interactions might've had on the * brightness mapping. This timeout doesn't start until we transition to a non-interactive * display policy so that we don't reset while users are using their devices, but also so that * we don't erroneously keep the short-term model if the device is dozing but the * display is fully on. + * + * This timeout is also used when the device switches from interactive screen brightness mode + * to idle screen brightness mode, to preserve the user's preference when they resume usage of + * the device, within the specified timeframe. */ public abstract long getShortTermModelTimeout(); diff --git a/services/core/java/com/android/server/display/BrightnessThrottler.java b/services/core/java/com/android/server/display/BrightnessThrottler.java index eccee52f37aa..067a2d6d8b0d 100644 --- a/services/core/java/com/android/server/display/BrightnessThrottler.java +++ b/services/core/java/com/android/server/display/BrightnessThrottler.java @@ -16,7 +16,10 @@ package com.android.server.display; +import static com.android.server.display.DisplayDeviceConfig.DEFAULT_ID; + import android.annotation.NonNull; +import android.annotation.Nullable; import android.content.Context; import android.hardware.display.BrightnessInfo; import android.hardware.display.DisplayManager; @@ -63,8 +66,16 @@ class BrightnessThrottler { private final DeviceConfigInterface mDeviceConfig; private int mThrottlingStatus; + + // Maps the throttling ID to the data. Sourced from DisplayDeviceConfig. + @NonNull + private HashMap<String, BrightnessThrottlingData> mDdcThrottlingDataMap; + + // Current throttling data being used. + // Null if we do not support throttling. + @Nullable private BrightnessThrottlingData mThrottlingData; - private BrightnessThrottlingData mDdcThrottlingData; + private float mBrightnessCap = PowerManager.BRIGHTNESS_MAX; private @BrightnessInfo.BrightnessMaxReason int mBrightnessMaxReason = BrightnessInfo.BRIGHTNESS_MAX_REASON_NONE; @@ -73,35 +84,45 @@ class BrightnessThrottler { // The most recent string that has been set from DeviceConfig private String mBrightnessThrottlingDataString; + // The brightness throttling configuration that should be used. + private String mBrightnessThrottlingDataId; + // This is a collection of brightness throttling data that has been written as overrides from // the DeviceConfig. This will always take priority over the display device config data. - private HashMap<String, BrightnessThrottlingData> mBrightnessThrottlingDataOverride = - new HashMap<>(1); - - BrightnessThrottler(Handler handler, BrightnessThrottlingData throttlingData, - Runnable throttlingChangeCallback, String uniqueDisplayId) { - this(new Injector(), handler, handler, throttlingData, throttlingChangeCallback, - uniqueDisplayId); + // We need to store the data for every display device, so we do not need to update this each + // time the underlying display device changes. + // This map is indexed by uniqueDisplayId, to provide maps for throttlingId -> throttlingData. + // HashMap< uniqueDisplayId, HashMap< throttlingDataId, BrightnessThrottlingData >> + private final HashMap<String, HashMap<String, BrightnessThrottlingData>> + mBrightnessThrottlingDataOverride = new HashMap<>(1); + + BrightnessThrottler(Handler handler, Runnable throttlingChangeCallback, String uniqueDisplayId, + String throttlingDataId, + @NonNull HashMap<String, BrightnessThrottlingData> brightnessThrottlingDataMap) { + this(new Injector(), handler, handler, throttlingChangeCallback, + uniqueDisplayId, throttlingDataId, brightnessThrottlingDataMap); } @VisibleForTesting BrightnessThrottler(Injector injector, Handler handler, Handler deviceConfigHandler, - BrightnessThrottlingData throttlingData, Runnable throttlingChangeCallback, - String uniqueDisplayId) { + Runnable throttlingChangeCallback, String uniqueDisplayId, String throttlingDataId, + @NonNull HashMap<String, BrightnessThrottlingData> brightnessThrottlingDataMap) { mInjector = injector; mHandler = handler; mDeviceConfigHandler = deviceConfigHandler; - mThrottlingData = throttlingData; - mDdcThrottlingData = throttlingData; + mDdcThrottlingDataMap = brightnessThrottlingDataMap; mThrottlingChangeCallback = throttlingChangeCallback; mSkinThermalStatusObserver = new SkinThermalStatusObserver(mInjector, mHandler); mUniqueDisplayId = uniqueDisplayId; mDeviceConfig = injector.getDeviceConfig(); mDeviceConfigListener = new DeviceConfigListener(); - - resetThrottlingData(mThrottlingData, mUniqueDisplayId); + mBrightnessThrottlingDataId = throttlingDataId; + mDdcThrottlingDataMap = brightnessThrottlingDataMap; + loadBrightnessThrottlingDataFromDeviceConfig(); + loadBrightnessThrottlingDataFromDisplayDeviceConfig(mDdcThrottlingDataMap, + mBrightnessThrottlingDataId, mUniqueDisplayId); } boolean deviceSupportsThrottling() { @@ -133,23 +154,14 @@ class BrightnessThrottler { mThrottlingStatus = THROTTLING_INVALID; } - private void resetThrottlingData() { - resetThrottlingData(mDdcThrottlingData, mUniqueDisplayId); - } - - void resetThrottlingData(BrightnessThrottlingData throttlingData, String displayId) { - stop(); - - mUniqueDisplayId = displayId; - mDdcThrottlingData = throttlingData; - mDeviceConfigListener.startListening(); - reloadBrightnessThrottlingDataOverride(); - mThrottlingData = mBrightnessThrottlingDataOverride.getOrDefault(mUniqueDisplayId, - throttlingData); - - if (deviceSupportsThrottling()) { - mSkinThermalStatusObserver.startObserving(); - } + void loadBrightnessThrottlingDataFromDisplayDeviceConfig( + HashMap<String, BrightnessThrottlingData> ddcThrottlingDataMap, + String brightnessThrottlingDataId, + String uniqueDisplayId) { + mDdcThrottlingDataMap = ddcThrottlingDataMap; + mBrightnessThrottlingDataId = brightnessThrottlingDataId; + mUniqueDisplayId = uniqueDisplayId; + resetThrottlingData(); } private float verifyAndConstrainBrightnessCap(float brightness) { @@ -183,7 +195,7 @@ class BrightnessThrottler { float brightnessCap = PowerManager.BRIGHTNESS_MAX; int brightnessMaxReason = BrightnessInfo.BRIGHTNESS_MAX_REASON_NONE; - if (mThrottlingStatus != THROTTLING_INVALID) { + if (mThrottlingStatus != THROTTLING_INVALID && mThrottlingData != null) { // Throttling levels are sorted by increasing severity for (ThrottlingLevel level : mThrottlingData.throttlingLevels) { if (level.thermalStatus <= mThrottlingStatus) { @@ -218,13 +230,14 @@ class BrightnessThrottler { private void dumpLocal(PrintWriter pw) { pw.println("BrightnessThrottler:"); + pw.println(" mBrightnessThrottlingDataId=" + mBrightnessThrottlingDataId); pw.println(" mThrottlingData=" + mThrottlingData); - pw.println(" mDdcThrottlingData=" + mDdcThrottlingData); pw.println(" mUniqueDisplayId=" + mUniqueDisplayId); pw.println(" mThrottlingStatus=" + mThrottlingStatus); pw.println(" mBrightnessCap=" + mBrightnessCap); pw.println(" mBrightnessMaxReason=" + BrightnessInfo.briMaxReasonToString(mBrightnessMaxReason)); + pw.println(" mDdcThrottlingDataMap=" + mDdcThrottlingDataMap); pw.println(" mBrightnessThrottlingDataOverride=" + mBrightnessThrottlingDataOverride); pw.println(" mBrightnessThrottlingDataString=" + mBrightnessThrottlingDataString); @@ -237,8 +250,18 @@ class BrightnessThrottler { /* defaultValue= */ null); } - private boolean parseAndSaveData(@NonNull String strArray, - @NonNull HashMap<String, BrightnessThrottlingData> tempBrightnessThrottlingData) { + // The brightness throttling data id may or may not be specified in the string that is passed + // in, if there is none specified, we assume it is for the default case. Each string passed in + // here must be for one display and one throttling id. + // 123,1,critical,0.8 + // 456,2,moderate,0.9,critical,0.7 + // 456,2,moderate,0.9,critical,0.7,default + // 456,2,moderate,0.9,critical,0.7,id_2 + // displayId, number, <state, val> * number + // displayId, <number, <state, val> * number>, throttlingId + private boolean parseAndAddData(@NonNull String strArray, + @NonNull HashMap<String, HashMap<String, BrightnessThrottlingData>> + displayIdToThrottlingIdToBtd) { boolean validConfig = true; String[] items = strArray.split(","); int i = 0; @@ -254,29 +277,42 @@ class BrightnessThrottler { for (int j = 0; j < noOfThrottlingPoints; j++) { String severity = items[i++]; int status = parseThermalStatus(severity); - float brightnessPoint = parseBrightness(items[i++]); - throttlingLevels.add(new ThrottlingLevel(status, brightnessPoint)); } - BrightnessThrottlingData toSave = + + String throttlingDataId = (i < items.length) ? items[i++] : DEFAULT_ID; + BrightnessThrottlingData throttlingLevelsData = DisplayDeviceConfig.BrightnessThrottlingData.create(throttlingLevels); - tempBrightnessThrottlingData.put(uniqueDisplayId, toSave); + + // Add throttlingLevelsData to inner map where necessary. + HashMap<String, BrightnessThrottlingData> throttlingMapForDisplay = + displayIdToThrottlingIdToBtd.get(uniqueDisplayId); + if (throttlingMapForDisplay == null) { + throttlingMapForDisplay = new HashMap<>(); + throttlingMapForDisplay.put(throttlingDataId, throttlingLevelsData); + displayIdToThrottlingIdToBtd.put(uniqueDisplayId, throttlingMapForDisplay); + } else if (throttlingMapForDisplay.containsKey(throttlingDataId)) { + Slog.e(TAG, "Throttling data for display " + uniqueDisplayId + + "contains duplicate throttling ids: '" + throttlingDataId + "'"); + return false; + } else { + throttlingMapForDisplay.put(throttlingDataId, throttlingLevelsData); + } } catch (NumberFormatException | IndexOutOfBoundsException | UnknownThermalStatusException e) { - validConfig = false; Slog.e(TAG, "Throttling data is invalid array: '" + strArray + "'", e); + validConfig = false; } if (i != items.length) { validConfig = false; } - return validConfig; } - public void reloadBrightnessThrottlingDataOverride() { - HashMap<String, BrightnessThrottlingData> tempBrightnessThrottlingData = + private void loadBrightnessThrottlingDataFromDeviceConfig() { + HashMap<String, HashMap<String, BrightnessThrottlingData>> tempThrottlingData = new HashMap<>(1); mBrightnessThrottlingDataString = getBrightnessThrottlingDataString(); boolean validConfig = true; @@ -284,15 +320,15 @@ class BrightnessThrottler { if (mBrightnessThrottlingDataString != null) { String[] throttlingDataSplits = mBrightnessThrottlingDataString.split(";"); for (String s : throttlingDataSplits) { - if (!parseAndSaveData(s, tempBrightnessThrottlingData)) { + if (!parseAndAddData(s, tempThrottlingData)) { validConfig = false; break; } } if (validConfig) { - mBrightnessThrottlingDataOverride.putAll(tempBrightnessThrottlingData); - tempBrightnessThrottlingData.clear(); + mBrightnessThrottlingDataOverride.putAll(tempThrottlingData); + tempThrottlingData.clear(); } } else { @@ -300,15 +336,50 @@ class BrightnessThrottler { } } + private void resetThrottlingData() { + stop(); + + mDeviceConfigListener.startListening(); + + // Get throttling data for this id, if it exists + mThrottlingData = getConfigFromId(mBrightnessThrottlingDataId); + + // Fallback to default id otherwise. + if (!DEFAULT_ID.equals(mBrightnessThrottlingDataId) && mThrottlingData == null) { + mThrottlingData = getConfigFromId(DEFAULT_ID); + Slog.d(TAG, "Falling back to default throttling Id"); + } + + if (deviceSupportsThrottling()) { + mSkinThermalStatusObserver.startObserving(); + } + } + + private BrightnessThrottlingData getConfigFromId(String id) { + BrightnessThrottlingData returnValue; + + // Fallback pattern for fetching correct throttling data for this display and id. + // 1) throttling data from device config for this throttling data id + returnValue = mBrightnessThrottlingDataOverride.get(mUniqueDisplayId) == null + ? null + : mBrightnessThrottlingDataOverride.get(mUniqueDisplayId).get(id); + // 2) throttling data from ddc for this throttling data id + returnValue = returnValue == null + ? mDdcThrottlingDataMap.get(id) + : returnValue; + + return returnValue; + } + /** * Listens to config data change and updates the brightness throttling data using * DisplayManager#KEY_BRIGHTNESS_THROTTLING_DATA. * The format should be a string similar to: "local:4619827677550801152,2,moderate,0.5,severe, * 0.379518072;local:4619827677550801151,1,moderate,0.75" * In this order: - * <displayId>,<no of throttling levels>,[<severity as string>,<brightness cap>] - * Where the latter part is repeated for each throttling level, and the entirety is repeated - * for each display, separated by a semicolon. + * <displayId>,<no of throttling levels>,[<severity as string>,<brightness cap>][,throttlingId]? + * Where [<severity as string>,<brightness cap>] is repeated for each throttling level, and the + * entirety is repeated for each display & throttling data id, separated by a semicolon. */ public class DeviceConfigListener implements DeviceConfig.OnPropertiesChangedListener { public Executor mExecutor = new HandlerExecutor(mDeviceConfigHandler); @@ -320,7 +391,7 @@ class BrightnessThrottler { @Override public void onPropertiesChanged(DeviceConfig.Properties properties) { - reloadBrightnessThrottlingDataOverride(); + loadBrightnessThrottlingDataFromDeviceConfig(); resetThrottlingData(); } } diff --git a/services/core/java/com/android/server/display/DisplayDeviceConfig.java b/services/core/java/com/android/server/display/DisplayDeviceConfig.java index f4b3f1ab7b11..da021158eb65 100644 --- a/services/core/java/com/android/server/display/DisplayDeviceConfig.java +++ b/services/core/java/com/android/server/display/DisplayDeviceConfig.java @@ -143,17 +143,17 @@ import javax.xml.datatype.DatatypeConfigurationException; * <brightness>0.01</brightness> * </brightnessThrottlingPoint> * </brightnessThrottlingMap> - * <concurrentDisplaysBrightnessThrottlingMap> - * <brightnessThrottlingPoint> - * <thermalStatus>severe</thermalStatus> - * <brightness>0.07</brightness> - * </brightnessThrottlingPoint> - * <brightnessThrottlingPoint> - * <thermalStatus>critical</thermalStatus> - * <brightness>0.005</brightness> - * </brightnessThrottlingPoint> - * </concurrentDisplaysBrightnessThrottlingMap> - * <refreshRateThrottlingMap> + * <brightnessThrottlingMap id="id_2"> // optional attribute, leave blank for default + * <brightnessThrottlingPoint> + * <thermalStatus>moderate</thermalStatus> + * <brightness>0.2</brightness> + * </brightnessThrottlingPoint> + * <brightnessThrottlingPoint> + * <thermalStatus>severe</thermalStatus> + * <brightness>0.1</brightness> + * </brightnessThrottlingPoint> + * </brightnessThrottlingMap> + <refreshRateThrottlingMap> * <refreshRateThrottlingPoint> * <thermalStatus>critical</thermalStatus> * <refreshRateRange> @@ -687,8 +687,8 @@ public class DisplayDeviceConfig { private int[] mHighDisplayBrightnessThresholds = DEFAULT_BRIGHTNESS_THRESHOLDS; private int[] mHighAmbientBrightnessThresholds = DEFAULT_BRIGHTNESS_THRESHOLDS; - private final Map<String, BrightnessThrottlingData> mBrightnessThrottlingDataMap = - new HashMap<>(); + private final HashMap<String, BrightnessThrottlingData> + mBrightnessThrottlingDataMapByThrottlingId = new HashMap<>(); private final Map<String, SparseArray<SurfaceControl.RefreshRateRange>> mRefreshRateThrottlingMap = new HashMap<>(); @@ -1346,11 +1346,11 @@ public class DisplayDeviceConfig { } /** - * @param id The ID of the throttling data - * @return brightness throttling configuration data for the display. + * @return brightness throttling configuration data for this display, for each throttling id. */ - public BrightnessThrottlingData getBrightnessThrottlingData(String id) { - return BrightnessThrottlingData.create(mBrightnessThrottlingDataMap.get(id)); + public HashMap<String, BrightnessThrottlingData> + getBrightnessThrottlingDataMapByThrottlingId() { + return mBrightnessThrottlingDataMapByThrottlingId; } /** @@ -1525,7 +1525,8 @@ public class DisplayDeviceConfig { + ", isHbmEnabled=" + mIsHighBrightnessModeEnabled + ", mHbmData=" + mHbmData + ", mSdrToHdrRatioSpline=" + mSdrToHdrRatioSpline - + ", mBrightnessThrottlingData=" + mBrightnessThrottlingDataMap + + ", mBrightnessThrottlingDataMapByThrottlingId=" + + mBrightnessThrottlingDataMapByThrottlingId + "\n" + ", mBrightnessRampFastDecrease=" + mBrightnessRampFastDecrease + ", mBrightnessRampFastIncrease=" + mBrightnessRampFastIncrease @@ -1918,11 +1919,11 @@ public class DisplayDeviceConfig { if (!badConfig) { String id = map.getId() == null ? DEFAULT_ID : map.getId(); - if (mBrightnessThrottlingDataMap.containsKey(id)) { + if (mBrightnessThrottlingDataMapByThrottlingId.containsKey(id)) { throw new RuntimeException("Brightness throttling data with ID " + id + " already exists"); } - mBrightnessThrottlingDataMap.put(id, + mBrightnessThrottlingDataMapByThrottlingId.put(id, BrightnessThrottlingData.create(throttlingLevels)); } } @@ -1971,8 +1972,8 @@ public class DisplayDeviceConfig { )); } if (refreshRates.size() == 0) { - Slog.w(TAG, "RefreshRateThrottling: no valid throttling points fond for map, mapId=" - + id); + Slog.w(TAG, "RefreshRateThrottling: no valid throttling points found for map, " + + "mapId=" + id); continue; } mRefreshRateThrottlingMap.put(id, refreshRates); @@ -3077,7 +3078,7 @@ public class DisplayDeviceConfig { /** - * Creates multiple teperature based throttling levels of brightness + * Creates multiple temperature based throttling levels of brightness */ public static BrightnessThrottlingData create(List<ThrottlingLevel> throttlingLevels) { if (throttlingLevels == null || throttlingLevels.size() == 0) { @@ -3120,15 +3121,6 @@ public class DisplayDeviceConfig { return new BrightnessThrottlingData(throttlingLevels); } - static public BrightnessThrottlingData create(BrightnessThrottlingData other) { - if (other == null) { - return null; - } - - return BrightnessThrottlingData.create(other.throttlingLevels); - } - - @Override public String toString() { return "BrightnessThrottlingData{" diff --git a/services/core/java/com/android/server/display/DisplayPowerController.java b/services/core/java/com/android/server/display/DisplayPowerController.java index f5859eed34f1..2322e03d6d37 100644 --- a/services/core/java/com/android/server/display/DisplayPowerController.java +++ b/services/core/java/com/android/server/display/DisplayPowerController.java @@ -505,7 +505,7 @@ final class DisplayPowerController implements AutomaticBrightnessController.Call private DisplayDeviceConfig mDisplayDeviceConfig; - // Identifiers for suspend blocker acuisition requests + // Identifiers for suspend blocker acquisition requests private final String mSuspendBlockerIdUnfinishedBusiness; private final String mSuspendBlockerIdOnStateChanged; private final String mSuspendBlockerIdProxPositive; @@ -515,6 +515,7 @@ final class DisplayPowerController implements AutomaticBrightnessController.Call private boolean mIsEnabled; private boolean mIsInTransition; + // The id of the brightness throttling policy that should be used. private String mBrightnessThrottlingDataId; // DPCs following the brightness of this DPC. This is used in concurrent displays mode - there @@ -905,14 +906,15 @@ final class DisplayPowerController implements AutomaticBrightnessController.Call loadNitBasedBrightnessSetting(); /// Since the underlying display-device changed, we really don't know the - // last command that was sent to change it's state. Lets assume it is unknown so + // last command that was sent to change it's state. Let's assume it is unknown so // that we trigger a change immediately. mPowerState.resetScreenState(); } else if (!mBrightnessThrottlingDataId.equals(brightnessThrottlingDataId)) { changed = true; mBrightnessThrottlingDataId = brightnessThrottlingDataId; - mBrightnessThrottler.resetThrottlingData( - config.getBrightnessThrottlingData(mBrightnessThrottlingDataId), + mBrightnessThrottler.loadBrightnessThrottlingDataFromDisplayDeviceConfig( + config.getBrightnessThrottlingDataMapByThrottlingId(), + mBrightnessThrottlingDataId, mUniqueDisplayId); } if (mIsEnabled != isEnabled || mIsInTransition != isInTransition) { @@ -981,9 +983,9 @@ final class DisplayPowerController implements AutomaticBrightnessController.Call sdrBrightness, maxDesiredHdrSdrRatio); } }); - mBrightnessThrottler.resetThrottlingData( - mDisplayDeviceConfig.getBrightnessThrottlingData(mBrightnessThrottlingDataId), - mUniqueDisplayId); + mBrightnessThrottler.loadBrightnessThrottlingDataFromDisplayDeviceConfig( + mDisplayDeviceConfig.getBrightnessThrottlingDataMapByThrottlingId(), + mBrightnessThrottlingDataId, mUniqueDisplayId); } private void sendUpdatePowerState() { @@ -2116,11 +2118,11 @@ final class DisplayPowerController implements AutomaticBrightnessController.Call final DisplayDevice device = mLogicalDisplay.getPrimaryDisplayDeviceLocked(); final DisplayDeviceConfig ddConfig = device.getDisplayDeviceConfig(); return new BrightnessThrottler(mHandler, - ddConfig.getBrightnessThrottlingData(mBrightnessThrottlingDataId), () -> { sendUpdatePowerState(); postBrightnessChangeRunnable(); - }, mUniqueDisplayId); + }, mUniqueDisplayId, mLogicalDisplay.getBrightnessThrottlingDataIdLocked(), + ddConfig.getBrightnessThrottlingDataMapByThrottlingId()); } private void blockScreenOn() { diff --git a/services/core/java/com/android/server/display/DisplayPowerController2.java b/services/core/java/com/android/server/display/DisplayPowerController2.java index 8ce4b66eba28..d2af88b1c6c9 100644 --- a/services/core/java/com/android/server/display/DisplayPowerController2.java +++ b/services/core/java/com/android/server/display/DisplayPowerController2.java @@ -400,6 +400,7 @@ final class DisplayPowerController2 implements AutomaticBrightnessController.Cal private boolean mIsEnabled; private boolean mIsInTransition; + // The id of the brightness throttling policy that should be used. private String mBrightnessThrottlingDataId; // DPCs following the brightness of this DPC. This is used in concurrent displays mode - there @@ -722,14 +723,15 @@ final class DisplayPowerController2 implements AutomaticBrightnessController.Cal mDisplayPowerProximityStateController.notifyDisplayDeviceChanged(config); // Since the underlying display-device changed, we really don't know the - // last command that was sent to change it's state. Lets assume it is unknown so + // last command that was sent to change it's state. Let's assume it is unknown so // that we trigger a change immediately. mPowerState.resetScreenState(); } else if (!mBrightnessThrottlingDataId.equals(brightnessThrottlingDataId)) { changed = true; mBrightnessThrottlingDataId = brightnessThrottlingDataId; - mBrightnessThrottler.resetThrottlingData( - config.getBrightnessThrottlingData(mBrightnessThrottlingDataId), + mBrightnessThrottler.loadBrightnessThrottlingDataFromDisplayDeviceConfig( + config.getBrightnessThrottlingDataMapByThrottlingId(), + mBrightnessThrottlingDataId, mUniqueDisplayId); } if (mIsEnabled != isEnabled || mIsInTransition != isInTransition) { @@ -795,9 +797,9 @@ final class DisplayPowerController2 implements AutomaticBrightnessController.Cal sdrBrightness, maxDesiredHdrSdrRatio); } }); - mBrightnessThrottler.resetThrottlingData( - mDisplayDeviceConfig.getBrightnessThrottlingData(mBrightnessThrottlingDataId), - mUniqueDisplayId); + mBrightnessThrottler.loadBrightnessThrottlingDataFromDisplayDeviceConfig( + mDisplayDeviceConfig.getBrightnessThrottlingDataMapByThrottlingId(), + mBrightnessThrottlingDataId, mUniqueDisplayId); } private void sendUpdatePowerState() { @@ -1757,11 +1759,11 @@ final class DisplayPowerController2 implements AutomaticBrightnessController.Cal final DisplayDevice device = mLogicalDisplay.getPrimaryDisplayDeviceLocked(); final DisplayDeviceConfig ddConfig = device.getDisplayDeviceConfig(); return new BrightnessThrottler(mHandler, - ddConfig.getBrightnessThrottlingData(mBrightnessThrottlingDataId), () -> { sendUpdatePowerState(); postBrightnessChangeRunnable(); - }, mUniqueDisplayId); + }, mUniqueDisplayId, mLogicalDisplay.getBrightnessThrottlingDataIdLocked(), + ddConfig.getBrightnessThrottlingDataMapByThrottlingId()); } private void blockScreenOn() { diff --git a/services/core/java/com/android/server/display/layout/Layout.java b/services/core/java/com/android/server/display/layout/Layout.java index f86ee249408c..634f31d6268c 100644 --- a/services/core/java/com/android/server/display/layout/Layout.java +++ b/services/core/java/com/android/server/display/layout/Layout.java @@ -338,7 +338,9 @@ public class Layout { } /** - * @return The ID of the brightness throttling map that this display should use. + * Gets the id of the brightness throttling map that should be used. + * @return The ID of the brightness throttling map that this display should use, null if + * unspecified, will fall back to default. */ public String getBrightnessThrottlingMapId() { return mBrightnessThrottlingMapId; diff --git a/services/core/java/com/android/server/notification/NotificationManagerService.java b/services/core/java/com/android/server/notification/NotificationManagerService.java index 81cca50a4d29..87070595f172 100755 --- a/services/core/java/com/android/server/notification/NotificationManagerService.java +++ b/services/core/java/com/android/server/notification/NotificationManagerService.java @@ -6625,20 +6625,15 @@ public class NotificationManagerService extends SystemService { || r.getImportance() == IMPORTANCE_NONE)) { // Increase the importance of foreground service notifications unless the user had // an opinion otherwise (and the channel hasn't yet shown a fg service). - if (TextUtils.isEmpty(channelId) - || NotificationChannel.DEFAULT_CHANNEL_ID.equals(channelId)) { - r.setSystemImportance(IMPORTANCE_LOW); - } else { - channel.setImportance(IMPORTANCE_LOW); - r.setSystemImportance(IMPORTANCE_LOW); - if (!fgServiceShown) { - channel.unlockFields(NotificationChannel.USER_LOCKED_IMPORTANCE); - channel.setFgServiceShown(true); - } - mPreferencesHelper.updateNotificationChannel( - pkg, notificationUid, channel, false); - r.updateNotificationChannel(channel); - } + channel.setImportance(IMPORTANCE_LOW); + r.setSystemImportance(IMPORTANCE_LOW); + if (!fgServiceShown) { + channel.unlockFields(NotificationChannel.USER_LOCKED_IMPORTANCE); + channel.setFgServiceShown(true); + } + mPreferencesHelper.updateNotificationChannel( + pkg, notificationUid, channel, false); + r.updateNotificationChannel(channel); } else if (!fgServiceShown && !TextUtils.isEmpty(channelId) && !NotificationChannel.DEFAULT_CHANNEL_ID.equals(channelId)) { channel.setFgServiceShown(true); diff --git a/services/core/java/com/android/server/wm/ActivityRecord.java b/services/core/java/com/android/server/wm/ActivityRecord.java index 5f56923e2f53..8346e7c83190 100644 --- a/services/core/java/com/android/server/wm/ActivityRecord.java +++ b/services/core/java/com/android/server/wm/ActivityRecord.java @@ -4068,7 +4068,7 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A } void finishRelaunching() { - mLetterboxUiController.setRelauchingAfterRequestedOrientationChanged(false); + mLetterboxUiController.setRelaunchingAfterRequestedOrientationChanged(false); mTaskSupervisor.getActivityMetricsLogger().notifyActivityRelaunched(this); if (mPendingRelaunchCount > 0) { @@ -9500,7 +9500,7 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A mRelaunchReason = RELAUNCH_REASON_NONE; } if (isRequestedOrientationChanged) { - mLetterboxUiController.setRelauchingAfterRequestedOrientationChanged(true); + mLetterboxUiController.setRelaunchingAfterRequestedOrientationChanged(true); } if (mState == PAUSING) { // A little annoying: we are waiting for this activity to finish pausing. Let's not diff --git a/services/core/java/com/android/server/wm/BackgroundActivityStartController.java b/services/core/java/com/android/server/wm/BackgroundActivityStartController.java index 6773bcd6fac8..e447049a7362 100644 --- a/services/core/java/com/android/server/wm/BackgroundActivityStartController.java +++ b/services/core/java/com/android/server/wm/BackgroundActivityStartController.java @@ -487,7 +487,7 @@ public class BackgroundActivityStartController { // The verdict changed from allow (resultIfPiSenderAllowsBal) to block, PI sender // default change is on (otherwise we would have fallen into if above) and we'd // allow if it were off - Slog.wtf(TAG, "Without BAL hardening this activity start would NOT be allowed!" + Slog.wtf(TAG, "Without BAL hardening this activity start would be allowed!" + stateDumpLog); } } diff --git a/services/core/java/com/android/server/wm/DisplayArea.java b/services/core/java/com/android/server/wm/DisplayArea.java index c2ddb4158846..00299c286b08 100644 --- a/services/core/java/com/android/server/wm/DisplayArea.java +++ b/services/core/java/com/android/server/wm/DisplayArea.java @@ -148,7 +148,7 @@ public class DisplayArea<T extends WindowContainer> extends WindowContainer<T> { @ScreenOrientation int getOrientation(int candidate) { final int orientation = super.getOrientation(candidate); - if (getIgnoreOrientationRequest(orientation)) { + if (shouldIgnoreOrientationRequest(orientation)) { // In all the other case, mLastOrientationSource will be reassigned to a new value mLastOrientationSource = null; return SCREEN_ORIENTATION_UNSET; @@ -158,7 +158,7 @@ public class DisplayArea<T extends WindowContainer> extends WindowContainer<T> { @Override boolean handlesOrientationChangeFromDescendant(@ScreenOrientation int orientation) { - return !getIgnoreOrientationRequest(orientation) + return !shouldIgnoreOrientationRequest(orientation) && super.handlesOrientationChangeFromDescendant(orientation); } @@ -169,7 +169,7 @@ public class DisplayArea<T extends WindowContainer> extends WindowContainer<T> { final int orientation = requestingContainer != null ? requestingContainer.getOverrideOrientation() : SCREEN_ORIENTATION_UNSET; - return !getIgnoreOrientationRequest(orientation) + return !shouldIgnoreOrientationRequest(orientation) && super.onDescendantOrientationChanged(requestingContainer); } @@ -236,8 +236,7 @@ public class DisplayArea<T extends WindowContainer> extends WindowContainer<T> { /** * @return {@value true} if we need to ignore the orientation in input. */ - // TODO(b/262366204): Rename getIgnoreOrientationRequest to shouldIgnoreOrientationRequest - boolean getIgnoreOrientationRequest(@ScreenOrientation int orientation) { + boolean shouldIgnoreOrientationRequest(@ScreenOrientation int orientation) { // We always respect orientation request for ActivityInfo.SCREEN_ORIENTATION_LOCKED // ActivityInfo.SCREEN_ORIENTATION_NOSENSOR. // Main use case why this is important is Camera apps that rely on those diff --git a/services/core/java/com/android/server/wm/DisplayContent.java b/services/core/java/com/android/server/wm/DisplayContent.java index ab109fcaa673..bec58b848478 100644 --- a/services/core/java/com/android/server/wm/DisplayContent.java +++ b/services/core/java/com/android/server/wm/DisplayContent.java @@ -1648,7 +1648,7 @@ class DisplayContent extends RootDisplayArea implements WindowManagerPolicy.Disp @Override boolean handlesOrientationChangeFromDescendant(@ScreenOrientation int orientation) { - return !getIgnoreOrientationRequest(orientation) + return !shouldIgnoreOrientationRequest(orientation) && !getDisplayRotation().isFixedToUserRotation(); } @@ -1752,7 +1752,7 @@ class DisplayContent extends RootDisplayArea implements WindowManagerPolicy.Disp } final int activityOrientation = r.getOverrideOrientation(); if (!WindowManagerService.ENABLE_FIXED_ROTATION_TRANSFORM - || getIgnoreOrientationRequest(activityOrientation)) { + || shouldIgnoreOrientationRequest(activityOrientation)) { return ROTATION_UNDEFINED; } if (activityOrientation == ActivityInfo.SCREEN_ORIENTATION_BEHIND) { @@ -5149,7 +5149,7 @@ class DisplayContent extends RootDisplayArea implements WindowManagerPolicy.Disp @ScreenOrientation int getOrientation(@ScreenOrientation int candidate) { // IME does not participate in orientation. - return getIgnoreOrientationRequest(candidate) ? SCREEN_ORIENTATION_UNSET : candidate; + return shouldIgnoreOrientationRequest(candidate) ? SCREEN_ORIENTATION_UNSET : candidate; } @Override diff --git a/services/core/java/com/android/server/wm/EmbeddedWindowController.java b/services/core/java/com/android/server/wm/EmbeddedWindowController.java index d65f464590c1..44d67687e260 100644 --- a/services/core/java/com/android/server/wm/EmbeddedWindowController.java +++ b/services/core/java/com/android/server/wm/EmbeddedWindowController.java @@ -292,7 +292,9 @@ class EmbeddedWindowController { private void handleTap(boolean grantFocus) { if (mInputChannel != null) { if (mHostWindowState != null) { - mWmService.grantEmbeddedWindowFocus(mSession, mHostWindowState.mClient, + // Use null session since this is being granted by system server and doesn't + // require the host session to be passed in + mWmService.grantEmbeddedWindowFocus(null, mHostWindowState.mClient, mFocusGrantToken, grantFocus); if (grantFocus) { // If granting focus to the embedded when tapped, we need to ensure the host diff --git a/services/core/java/com/android/server/wm/LetterboxUiController.java b/services/core/java/com/android/server/wm/LetterboxUiController.java index b0c384dcd7ac..93233dd4bda8 100644 --- a/services/core/java/com/android/server/wm/LetterboxUiController.java +++ b/services/core/java/com/android/server/wm/LetterboxUiController.java @@ -50,6 +50,7 @@ import static android.view.WindowManager.PROPERTY_CAMERA_COMPAT_ENABLE_REFRESH_V import static android.view.WindowManager.PROPERTY_COMPAT_ALLOW_DISPLAY_ORIENTATION_OVERRIDE; import static android.view.WindowManager.PROPERTY_COMPAT_ALLOW_ORIENTATION_OVERRIDE; import static android.view.WindowManager.PROPERTY_COMPAT_ENABLE_FAKE_FOCUS; +import static android.view.WindowManager.PROPERTY_COMPAT_IGNORE_ORIENTATION_REQUEST_WHEN_LOOP_DETECTED; import static android.view.WindowManager.PROPERTY_COMPAT_IGNORE_REQUESTED_ORIENTATION; import static com.android.internal.util.FrameworkStatsLog.APP_COMPAT_STATE_CHANGED__LETTERBOX_POSITION__BOTTOM; @@ -235,9 +236,14 @@ final class LetterboxUiController { private final Boolean mBooleanPropertyIgnoreRequestedOrientation; @Nullable + private final Boolean mBooleanPropertyIgnoreOrientationRequestWhenLoopDetected; + + @Nullable private final Boolean mBooleanPropertyFakeFocus; - private boolean mIsRelauchingAfterRequestedOrientationChanged; + private boolean mIsRelaunchingAfterRequestedOrientationChanged; + + private boolean mLastShouldShowLetterboxUi; private boolean mDoubleTapEvent; @@ -253,6 +259,10 @@ final class LetterboxUiController { readComponentProperty(packageManager, mActivityRecord.packageName, mLetterboxConfiguration::isPolicyForIgnoringRequestedOrientationEnabled, PROPERTY_COMPAT_IGNORE_REQUESTED_ORIENTATION); + mBooleanPropertyIgnoreOrientationRequestWhenLoopDetected = + readComponentProperty(packageManager, mActivityRecord.packageName, + mLetterboxConfiguration::isPolicyForIgnoringRequestedOrientationEnabled, + PROPERTY_COMPAT_IGNORE_ORIENTATION_REQUEST_WHEN_LOOP_DETECTED); mBooleanPropertyFakeFocus = readComponentProperty(packageManager, mActivityRecord.packageName, mLetterboxConfiguration::isCompatFakeFocusEnabled, @@ -387,7 +397,7 @@ final class LetterboxUiController { ::isPolicyForIgnoringRequestedOrientationEnabled, mIsOverrideEnableCompatIgnoreRequestedOrientationEnabled, mBooleanPropertyIgnoreRequestedOrientation)) { - if (mIsRelauchingAfterRequestedOrientationChanged) { + if (mIsRelaunchingAfterRequestedOrientationChanged) { Slog.w(TAG, "Ignoring orientation update to " + screenOrientationToString(requestedOrientation) + " due to relaunching after setRequestedOrientation for " @@ -422,6 +432,8 @@ final class LetterboxUiController { * * <p>This treatment is enabled when the following conditions are met: * <ul> + * <li>Flag gating the treatment is enabled + * <li>Opt-out component property isn't enabled * <li>Per-app override is enabled * <li>App has requested orientation more than 2 times within 1-second * timer and activity is not letterboxed for fixed orientation @@ -429,7 +441,11 @@ final class LetterboxUiController { */ @VisibleForTesting boolean shouldIgnoreOrientationRequestLoop() { - if (!mIsOverrideEnableCompatIgnoreOrientationRequestWhenLoopDetectedEnabled) { + if (!shouldEnableWithOptInOverrideAndOptOutProperty( + /* gatingCondition */ mLetterboxConfiguration + ::isPolicyForIgnoringRequestedOrientationEnabled, + mIsOverrideEnableCompatIgnoreOrientationRequestWhenLoopDetectedEnabled, + mBooleanPropertyIgnoreOrientationRequestWhenLoopDetected)) { return false; } @@ -476,8 +492,8 @@ final class LetterboxUiController { * Sets whether an activity is relaunching after the app has called {@link * android.app.Activity#setRequestedOrientation}. */ - void setRelauchingAfterRequestedOrientationChanged(boolean isRelaunching) { - mIsRelauchingAfterRequestedOrientationChanged = isRelaunching; + void setRelaunchingAfterRequestedOrientationChanged(boolean isRelaunching) { + mIsRelaunchingAfterRequestedOrientationChanged = isRelaunching; } /** @@ -1154,12 +1170,28 @@ final class LetterboxUiController { @VisibleForTesting boolean shouldShowLetterboxUi(WindowState mainWindow) { - return (mActivityRecord.isInLetterboxAnimation() || isSurfaceVisible(mainWindow)) + if (mIsRelaunchingAfterRequestedOrientationChanged || !isSurfaceReadyToShow(mainWindow)) { + return mLastShouldShowLetterboxUi; + } + + final boolean shouldShowLetterboxUi = + (mActivityRecord.isInLetterboxAnimation() || isSurfaceVisible(mainWindow)) && mainWindow.areAppWindowBoundsLetterboxed() // Check for FLAG_SHOW_WALLPAPER explicitly instead of using // WindowContainer#showWallpaper because the later will return true when this // activity is using blurred wallpaper for letterbox background. && (mainWindow.getAttrs().flags & FLAG_SHOW_WALLPAPER) == 0; + + mLastShouldShowLetterboxUi = shouldShowLetterboxUi; + + return shouldShowLetterboxUi; + } + + @VisibleForTesting + boolean isSurfaceReadyToShow(WindowState mainWindow) { + return mainWindow.isDrawn() // Regular case + // Waiting for relayoutWindow to call preserveSurface + || mainWindow.isDragResizeChanged(); } @VisibleForTesting @@ -1297,6 +1329,10 @@ final class LetterboxUiController { return null; } + boolean getIsRelaunchingAfterRequestedOrientationChanged() { + return mIsRelaunchingAfterRequestedOrientationChanged; + } + private void adjustBoundsForTaskbar(final WindowState mainWindow, final Rect bounds) { // Rounded corners should be displayed above the taskbar. When taskbar is hidden, // an insets frame is equal to a navigation bar which shouldn't affect position of diff --git a/services/core/java/com/android/server/wm/TaskDisplayArea.java b/services/core/java/com/android/server/wm/TaskDisplayArea.java index 76759ba53f5a..b0a879e96dcf 100644 --- a/services/core/java/com/android/server/wm/TaskDisplayArea.java +++ b/services/core/java/com/android/server/wm/TaskDisplayArea.java @@ -1883,7 +1883,7 @@ final class TaskDisplayArea extends DisplayArea<WindowContainer> { // Only allow to specify orientation if this TDA is the last focused one on this logical // display that can request orientation request. return mDisplayContent.getOrientationRequestingTaskDisplayArea() == this - && !getIgnoreOrientationRequest(orientation); + && !shouldIgnoreOrientationRequest(orientation); } void clearPreferredTopFocusableRootTask() { diff --git a/services/core/java/com/android/server/wm/Transition.java b/services/core/java/com/android/server/wm/Transition.java index 0fe1f923e4e5..1fb353476592 100644 --- a/services/core/java/com/android/server/wm/Transition.java +++ b/services/core/java/com/android/server/wm/Transition.java @@ -33,8 +33,6 @@ import static android.view.WindowManager.TRANSIT_CHANGE; import static android.view.WindowManager.TRANSIT_CLOSE; import static android.view.WindowManager.TRANSIT_FLAG_IS_RECENTS; import static android.view.WindowManager.TRANSIT_FLAG_KEYGUARD_LOCKED; -import static android.view.WindowManager.TRANSIT_KEYGUARD_OCCLUDE; -import static android.view.WindowManager.TRANSIT_KEYGUARD_UNOCCLUDE; import static android.view.WindowManager.TRANSIT_OPEN; import static android.view.WindowManager.TRANSIT_TO_BACK; import static android.view.WindowManager.TRANSIT_TO_FRONT; @@ -167,6 +165,9 @@ class Transition implements BLASTSyncEngine.TransactionReadyListener { private SurfaceControl.Transaction mStartTransaction = null; private SurfaceControl.Transaction mFinishTransaction = null; + /** Used for failsafe clean-up to prevent leaks due to misbehaving player impls. */ + private SurfaceControl.Transaction mCleanupTransaction = null; + /** * Contains change infos for both participants and all remote-animatable ancestors. The * ancestors can be the promotion candidates so their start-states need to be captured. @@ -787,6 +788,24 @@ class Transition implements BLASTSyncEngine.TransactionReadyListener { } /** + * Build a transaction that cleans-up transition-only surfaces (transition root and snapshots). + * This will ALWAYS be applied on transition finish just in-case + */ + private static void buildCleanupTransaction(SurfaceControl.Transaction t, TransitionInfo info) { + for (int i = info.getChanges().size() - 1; i >= 0; --i) { + final TransitionInfo.Change c = info.getChanges().get(i); + if (c.getSnapshot() != null) { + t.reparent(c.getSnapshot(), null); + } + } + for (int i = info.getRootCount() - 1; i >= 0; --i) { + final SurfaceControl leash = info.getRoot(i).getLeash(); + if (leash == null) continue; + t.reparent(leash, null); + } + } + + /** * Set whether this transition can start a pip-enter transition when finished. This is usually * true, but gets set to false when recents decides that it wants to finish its animation but * not actually finish its animation (yeah...). @@ -863,6 +882,10 @@ class Transition implements BLASTSyncEngine.TransactionReadyListener { if (mStartTransaction != null) mStartTransaction.close(); if (mFinishTransaction != null) mFinishTransaction.close(); mStartTransaction = mFinishTransaction = null; + if (mCleanupTransaction != null) { + mCleanupTransaction.apply(); + mCleanupTransaction = null; + } if (mState < STATE_PLAYING) { throw new IllegalStateException("Can't finish a non-playing transition " + mSyncId); } @@ -1286,6 +1309,8 @@ class Transition implements BLASTSyncEngine.TransactionReadyListener { } } buildFinishTransaction(mFinishTransaction, info); + mCleanupTransaction = mController.mAtm.mWindowManager.mTransactionFactory.get(); + buildCleanupTransaction(mCleanupTransaction, info); if (mController.getTransitionPlayer() != null && mIsPlayerEnabled) { mController.dispatchLegacyAppTransitionStarting(info, mStatusBarTransitionDelay); try { @@ -1385,6 +1410,10 @@ class Transition implements BLASTSyncEngine.TransactionReadyListener { ci.mSnapshot.release(); } } + if (mCleanupTransaction != null) { + mCleanupTransaction.apply(); + mCleanupTransaction = null; + } } /** The transition is ready to play. Make the start transaction show the surfaces. */ diff --git a/services/core/java/com/android/server/wm/WindowState.java b/services/core/java/com/android/server/wm/WindowState.java index 23934e00408a..736f489cf99d 100644 --- a/services/core/java/com/android/server/wm/WindowState.java +++ b/services/core/java/com/android/server/wm/WindowState.java @@ -5230,8 +5230,17 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP if (surfaceInsetsChanged) { mLastSurfaceInsets.set(mAttrs.surfaceInsets); } - if (surfaceSizeChanged && mWinAnimator.getShown() && !canPlayMoveAnimation() - && okToDisplay() && mSyncState == SYNC_STATE_NONE) { + final boolean surfaceResizedWithoutMoveAnimation = surfaceSizeChanged + && mWinAnimator.getShown() && !canPlayMoveAnimation() && okToDisplay() + && mSyncState == SYNC_STATE_NONE; + final ActivityRecord activityRecord = getActivityRecord(); + // If this window belongs to an activity that is relaunching due to an orientation + // change then delay the position update until it has redrawn to avoid any flickers. + final boolean isLetterboxedAndRelaunching = activityRecord != null + && activityRecord.areBoundsLetterboxed() + && activityRecord.mLetterboxUiController + .getIsRelaunchingAfterRequestedOrientationChanged(); + if (surfaceResizedWithoutMoveAnimation || isLetterboxedAndRelaunching) { applyWithNextDraw(mSetSurfacePositionConsumer); } else { mSetSurfacePositionConsumer.accept(t); diff --git a/services/credentials/java/com/android/server/credentials/ClearRequestSession.java b/services/credentials/java/com/android/server/credentials/ClearRequestSession.java index dce7b87c0328..bbebbf2383f5 100644 --- a/services/credentials/java/com/android/server/credentials/ClearRequestSession.java +++ b/services/credentials/java/com/android/server/credentials/ClearRequestSession.java @@ -40,11 +40,13 @@ public final class ClearRequestSession extends RequestSession<ClearCredentialSta implements ProviderSession.ProviderInternalCallback<Void> { private static final String TAG = "GetRequestSession"; - public ClearRequestSession(Context context, int userId, int callingUid, + public ClearRequestSession(Context context, RequestSession.SessionLifetime sessionCallback, + Object lock, int userId, int callingUid, IClearCredentialStateCallback callback, ClearCredentialStateRequest request, CallingAppInfo callingAppInfo, CancellationSignal cancellationSignal, long startedTimestamp) { - super(context, userId, callingUid, request, callback, RequestInfo.TYPE_UNDEFINED, + super(context, sessionCallback, lock, userId, callingUid, request, callback, + RequestInfo.TYPE_UNDEFINED, callingAppInfo, cancellationSignal, startedTimestamp); } diff --git a/services/credentials/java/com/android/server/credentials/CreateRequestSession.java b/services/credentials/java/com/android/server/credentials/CreateRequestSession.java index 98dc8ab8aa9c..4c456a88b00b 100644 --- a/services/credentials/java/com/android/server/credentials/CreateRequestSession.java +++ b/services/credentials/java/com/android/server/credentials/CreateRequestSession.java @@ -49,13 +49,15 @@ public final class CreateRequestSession extends RequestSession<CreateCredentialR implements ProviderSession.ProviderInternalCallback<CreateCredentialResponse> { private static final String TAG = "CreateRequestSession"; - CreateRequestSession(@NonNull Context context, int userId, int callingUid, + CreateRequestSession(@NonNull Context context, RequestSession.SessionLifetime sessionCallback, + Object lock, int userId, int callingUid, CreateCredentialRequest request, ICreateCredentialCallback callback, CallingAppInfo callingAppInfo, CancellationSignal cancellationSignal, long startedTimestamp) { - super(context, userId, callingUid, request, callback, RequestInfo.TYPE_CREATE, + super(context, sessionCallback, lock, userId, callingUid, request, callback, + RequestInfo.TYPE_CREATE, callingAppInfo, cancellationSignal, startedTimestamp); } @@ -83,6 +85,7 @@ public final class CreateRequestSession extends RequestSession<CreateCredentialR @Override protected void launchUiWithProviderData(ArrayList<ProviderData> providerDataList) { mRequestSessionMetric.collectUiCallStartTime(System.nanoTime()); + mCredentialManagerUi.setStatus(CredentialManagerUi.UiStatus.USER_INTERACTION); try { mClientCallback.onPendingIntent(mCredentialManagerUi.createPendingIntent( RequestInfo.newCreateRequestInfo( @@ -93,6 +96,7 @@ public final class CreateRequestSession extends RequestSession<CreateCredentialR providerDataList)); } catch (RemoteException e) { mRequestSessionMetric.collectUiReturnedFinalPhase(/*uiReturned=*/ false); + mCredentialManagerUi.setStatus(CredentialManagerUi.UiStatus.TERMINATED); respondToClientWithErrorAndFinish( CreateCredentialException.TYPE_UNKNOWN, "Unable to invoke selector"); diff --git a/services/credentials/java/com/android/server/credentials/CredentialManagerService.java b/services/credentials/java/com/android/server/credentials/CredentialManagerService.java index de06d440fa9d..ae6eaf09bec7 100644 --- a/services/credentials/java/com/android/server/credentials/CredentialManagerService.java +++ b/services/credentials/java/com/android/server/credentials/CredentialManagerService.java @@ -33,7 +33,6 @@ import android.content.pm.PackageManager; import android.credentials.ClearCredentialStateRequest; import android.credentials.CreateCredentialException; import android.credentials.CreateCredentialRequest; -import android.credentials.CredentialManager; import android.credentials.CredentialOption; import android.credentials.CredentialProviderInfo; import android.credentials.GetCredentialException; @@ -50,6 +49,7 @@ import android.credentials.UnregisterCredentialDescriptionRequest; import android.credentials.ui.IntentFactory; import android.os.Binder; import android.os.CancellationSignal; +import android.os.IBinder; import android.os.ICancellationSignal; import android.os.RemoteException; import android.os.UserHandle; @@ -70,9 +70,11 @@ import com.android.server.infra.AbstractMasterSystemService; import com.android.server.infra.SecureSettingsServiceNameResolver; import java.util.ArrayList; +import java.util.HashMap; import java.util.HashSet; import java.util.LinkedHashSet; import java.util.List; +import java.util.Map; import java.util.Set; import java.util.function.Consumer; import java.util.stream.Collectors; @@ -102,6 +104,13 @@ public final class CredentialManagerService private final SparseArray<List<CredentialManagerServiceImpl>> mSystemServicesCacheList = new SparseArray<>(); + /** Cache of all ongoing request sessions per user id. */ + @GuardedBy("mLock") + private final SparseArray<Map<IBinder, RequestSession>> mRequestSessions = + new SparseArray<>(); + + private final SessionManager mSessionManager = new SessionManager(); + public CredentialManagerService(@NonNull Context context) { super( context, @@ -331,7 +340,7 @@ public final class CredentialManagerService @NonNull private Set<Pair<CredentialOption, CredentialDescriptionRegistry.FilterResult>> - getFilteredResultFromRegistry(List<CredentialOption> options) { + getFilteredResultFromRegistry(List<CredentialOption> options) { // Session for active/provisioned credential descriptions; CredentialDescriptionRegistry registry = CredentialDescriptionRegistry.forUser(UserHandle.getCallingUserId()); @@ -389,14 +398,6 @@ public final class CredentialManagerService return providerSessions; } - private List<CredentialProviderInfo> getServicesForCredentialDescription(int userId) { - return CredentialProviderInfoFactory.getCredentialProviderServices( - mContext, - userId, - CredentialManager.PROVIDER_FILTER_ALL_PROVIDERS, - new HashSet<>()); - } - @Override @GuardedBy("CredentialDescriptionRegistry.sLock") public void onUserStopped(@NonNull TargetUser user) { @@ -448,6 +449,8 @@ public final class CredentialManagerService final GetRequestSession session = new GetRequestSession( getContext(), + mSessionManager, + mLock, userId, callingUid, callback, @@ -455,6 +458,7 @@ public final class CredentialManagerService constructCallingAppInfo(callingPackage, userId, request.getOrigin()), CancellationSignal.fromTransport(cancelTransport), timestampBegan); + addSessionLocked(userId, session); List<ProviderSession> providerSessions = prepareProviderSessions(request, session); @@ -499,6 +503,8 @@ public final class CredentialManagerService final PrepareGetRequestSession session = new PrepareGetRequestSession( getContext(), + mSessionManager, + mLock, userId, callingUid, getCredentialCallback, @@ -515,8 +521,8 @@ public final class CredentialManagerService // TODO: fix prepareGetCredentialCallback.onResponse( new PrepareGetCredentialResponseInternal( - false, null, - false, false, null)); + false, null, + false, false, null)); } catch (RemoteException e) { Log.i( TAG, @@ -540,10 +546,10 @@ public final class CredentialManagerService List<CredentialOption> optionsThatRequireActiveCredentials = request.getCredentialOptions().stream() .filter(credentialOption -> credentialOption - .getCredentialRetrievalData() - .getStringArrayList( - CredentialOption - .SUPPORTED_ELEMENT_KEYS) != null) + .getCredentialRetrievalData() + .getStringArrayList( + CredentialOption + .SUPPORTED_ELEMENT_KEYS) != null) .toList(); List<CredentialOption> optionsThatDoNotRequireActiveCredentials = @@ -614,6 +620,8 @@ public final class CredentialManagerService final CreateRequestSession session = new CreateRequestSession( getContext(), + mSessionManager, + mLock, userId, callingUid, request, @@ -621,6 +629,7 @@ public final class CredentialManagerService constructCallingAppInfo(callingPackage, userId, request.getOrigin()), CancellationSignal.fromTransport(cancelTransport), timestampBegan); + addSessionLocked(userId, session); processCreateCredential(request, callback, session); return cancelTransport; @@ -775,6 +784,19 @@ public final class CredentialManagerService mContext, userId, providerFilter, getEnabledProviders()); } + @Override + public boolean isServiceEnabled() { + final long origId = Binder.clearCallingIdentity(); + try { + return DeviceConfig.getBoolean( + DeviceConfig.NAMESPACE_CREDENTIAL, + CredentialManager.DEVICE_CONFIG_ENABLE_CREDENTIAL_MANAGER, + false); + } finally { + Binder.restoreCallingIdentity(origId); + } + } + @SuppressWarnings("GuardedBy") // ErrorProne requires service.mLock which is the same // this.mLock private Set<ComponentName> getEnabledProviders() { @@ -815,6 +837,8 @@ public final class CredentialManagerService final ClearRequestSession session = new ClearRequestSession( getContext(), + mSessionManager, + mLock, userId, callingUid, callback, @@ -822,6 +846,7 @@ public final class CredentialManagerService constructCallingAppInfo(callingPackage, userId, null), CancellationSignal.fromTransport(cancelTransport), timestampBegan); + addSessionLocked(userId, session); // Initiate all provider sessions // TODO: Determine if provider needs to have clear capability in their manifest @@ -905,6 +930,13 @@ public final class CredentialManagerService } } + private void addSessionLocked(@UserIdInt int userId, + RequestSession requestSession) { + synchronized (mLock) { + mSessionManager.addSession(userId, requestSession.mRequestId, requestSession); + } + } + private void enforceCallingPackage(String callingPackage, int callingUid) { int packageUid; PackageManager pm = mContext.createContextAsUser( @@ -919,4 +951,23 @@ public final class CredentialManagerService throw new SecurityException(callingPackage + " does not belong to uid " + callingUid); } } + + private class SessionManager implements RequestSession.SessionLifetime { + @Override + @GuardedBy("mLock") + public void onFinishRequestSession(@UserIdInt int userId, IBinder token) { + Log.i(TAG, "In onFinishRequestSession"); + if (mRequestSessions.get(userId) != null) { + mRequestSessions.get(userId).remove(token); + } + } + + @GuardedBy("mLock") + public void addSession(int userId, IBinder token, RequestSession requestSession) { + if (mRequestSessions.get(userId) == null) { + mRequestSessions.put(userId, new HashMap<>()); + } + mRequestSessions.get(userId).put(token, requestSession); + } + } } diff --git a/services/credentials/java/com/android/server/credentials/CredentialManagerUi.java b/services/credentials/java/com/android/server/credentials/CredentialManagerUi.java index 546c37ff95af..e16d48e0a680 100644 --- a/services/credentials/java/com/android/server/credentials/CredentialManagerUi.java +++ b/services/credentials/java/com/android/server/credentials/CredentialManagerUi.java @@ -30,6 +30,7 @@ import android.credentials.ui.RequestInfo; import android.credentials.ui.UserSelectionDialogResult; import android.os.Bundle; import android.os.Handler; +import android.os.IBinder; import android.os.Looper; import android.os.ResultReceiver; import android.service.credentials.CredentialProviderInfoFactory; @@ -50,6 +51,20 @@ public class CredentialManagerUi { @NonNull private final Context mContext; // TODO : Use for starting the activity for this user private final int mUserId; + + private UiStatus mStatus; + + /** Creates intent that is ot be invoked to cancel an in-progress UI session. */ + public Intent createCancelIntent(IBinder requestId, String packageName) { + return IntentFactory.createCancelUiIntent(requestId, /*shouldShowCancellationUi=*/ true, + packageName); + } + + enum UiStatus { + IN_PROGRESS, + USER_INTERACTION, + NOT_STARTED, TERMINATED + } @NonNull private final ResultReceiver mResultReceiver = new ResultReceiver( new Handler(Looper.getMainLooper())) { @Override @@ -61,6 +76,7 @@ public class CredentialManagerUi { private void handleUiResult(int resultCode, Bundle resultData) { switch (resultCode) { case UserSelectionDialogResult.RESULT_CODE_DIALOG_COMPLETE_WITH_SELECTION: + mStatus = UiStatus.IN_PROGRESS; UserSelectionDialogResult selection = UserSelectionDialogResult .fromResultData(resultData); if (selection != null) { @@ -70,16 +86,20 @@ public class CredentialManagerUi { } break; case UserSelectionDialogResult.RESULT_CODE_DIALOG_USER_CANCELED: + mStatus = UiStatus.TERMINATED; mCallbacks.onUiCancellation(/* isUserCancellation= */ true); break; case UserSelectionDialogResult.RESULT_CODE_CANCELED_AND_LAUNCHED_SETTINGS: + mStatus = UiStatus.TERMINATED; mCallbacks.onUiCancellation(/* isUserCancellation= */ false); break; case UserSelectionDialogResult.RESULT_CODE_DATA_PARSING_FAILURE: + mStatus = UiStatus.TERMINATED; mCallbacks.onUiSelectorInvocationFailure(); break; default: Slog.i(TAG, "Unknown error code returned from the UI"); + mStatus = UiStatus.IN_PROGRESS; mCallbacks.onUiSelectorInvocationFailure(); break; } @@ -103,6 +123,17 @@ public class CredentialManagerUi { mContext = context; mUserId = userId; mCallbacks = callbacks; + mStatus = UiStatus.IN_PROGRESS; + } + + /** Set status for credential manager UI */ + public void setStatus(UiStatus status) { + mStatus = status; + } + + /** Returns status for credential manager UI */ + public UiStatus getStatus() { + return mStatus; } /** diff --git a/services/credentials/java/com/android/server/credentials/GetRequestSession.java b/services/credentials/java/com/android/server/credentials/GetRequestSession.java index c0c7be9d80e2..2548bd888b9d 100644 --- a/services/credentials/java/com/android/server/credentials/GetRequestSession.java +++ b/services/credentials/java/com/android/server/credentials/GetRequestSession.java @@ -45,12 +45,13 @@ public class GetRequestSession extends RequestSession<GetCredentialRequest, IGetCredentialCallback, GetCredentialResponse> implements ProviderSession.ProviderInternalCallback<GetCredentialResponse> { private static final String TAG = "GetRequestSession"; - public GetRequestSession(Context context, int userId, int callingUid, + public GetRequestSession(Context context, RequestSession.SessionLifetime sessionCallback, + Object lock, int userId, int callingUid, IGetCredentialCallback callback, GetCredentialRequest request, CallingAppInfo callingAppInfo, CancellationSignal cancellationSignal, long startedTimestamp) { - super(context, userId, callingUid, request, callback, RequestInfo.TYPE_GET, - callingAppInfo, cancellationSignal, startedTimestamp); + super(context, sessionCallback, lock, userId, callingUid, request, callback, + RequestInfo.TYPE_GET, callingAppInfo, cancellationSignal, startedTimestamp); int numTypes = (request.getCredentialOptions().stream() .map(CredentialOption::getType).collect( Collectors.toSet())).size(); // Dedupe type strings @@ -81,6 +82,7 @@ public class GetRequestSession extends RequestSession<GetCredentialRequest, @Override protected void launchUiWithProviderData(ArrayList<ProviderData> providerDataList) { mRequestSessionMetric.collectUiCallStartTime(System.nanoTime()); + mCredentialManagerUi.setStatus(CredentialManagerUi.UiStatus.USER_INTERACTION); try { mClientCallback.onPendingIntent(mCredentialManagerUi.createPendingIntent( RequestInfo.newGetRequestInfo( @@ -88,6 +90,7 @@ public class GetRequestSession extends RequestSession<GetCredentialRequest, providerDataList)); } catch (RemoteException e) { mRequestSessionMetric.collectUiReturnedFinalPhase(/*uiReturned=*/ false); + mCredentialManagerUi.setStatus(CredentialManagerUi.UiStatus.TERMINATED); respondToClientWithErrorAndFinish( GetCredentialException.TYPE_UNKNOWN, "Unable to instantiate selector"); } diff --git a/services/credentials/java/com/android/server/credentials/PrepareGetRequestSession.java b/services/credentials/java/com/android/server/credentials/PrepareGetRequestSession.java index c4e480a8e609..88f3e6c92947 100644 --- a/services/credentials/java/com/android/server/credentials/PrepareGetRequestSession.java +++ b/services/credentials/java/com/android/server/credentials/PrepareGetRequestSession.java @@ -49,14 +49,13 @@ public class PrepareGetRequestSession extends GetRequestSession { private final IPrepareGetCredentialCallback mPrepareGetCredentialCallback; - public PrepareGetRequestSession(Context context, int userId, int callingUid, - IGetCredentialCallback callback, - GetCredentialRequest request, - CallingAppInfo callingAppInfo, - CancellationSignal cancellationSignal, long startedTimestamp, - IPrepareGetCredentialCallback prepareGetCredentialCallback) { - super(context, userId, callingUid, callback, request, callingAppInfo, cancellationSignal, - startedTimestamp); + public PrepareGetRequestSession(Context context, + RequestSession.SessionLifetime sessionCallback, Object lock, int userId, + int callingUid, IGetCredentialCallback getCredCallback, GetCredentialRequest request, + CallingAppInfo callingAppInfo, CancellationSignal cancellationSignal, + long startedTimestamp, IPrepareGetCredentialCallback prepareGetCredentialCallback) { + super(context, sessionCallback, lock, userId, callingUid, getCredCallback, request, + callingAppInfo, cancellationSignal, startedTimestamp); int numTypes = (request.getCredentialOptions().stream() .map(CredentialOption::getType).collect( Collectors.toSet())).size(); // Dedupe type strings diff --git a/services/credentials/java/com/android/server/credentials/ProviderClearSession.java b/services/credentials/java/com/android/server/credentials/ProviderClearSession.java index 1b736e01c842..eaf58f13e2fa 100644 --- a/services/credentials/java/com/android/server/credentials/ProviderClearSession.java +++ b/services/credentials/java/com/android/server/credentials/ProviderClearSession.java @@ -126,7 +126,8 @@ public final class ProviderClearSession extends ProviderSession<ClearCredentialS protected void invokeSession() { if (mRemoteCredentialService != null) { startCandidateMetrics(); - mRemoteCredentialService.onClearCredentialState(mProviderRequest, this); + mProviderCancellationSignal = + mRemoteCredentialService.onClearCredentialState(mProviderRequest, this); } } } diff --git a/services/credentials/java/com/android/server/credentials/ProviderCreateSession.java b/services/credentials/java/com/android/server/credentials/ProviderCreateSession.java index bef045f8f890..c657e3b1b389 100644 --- a/services/credentials/java/com/android/server/credentials/ProviderCreateSession.java +++ b/services/credentials/java/com/android/server/credentials/ProviderCreateSession.java @@ -236,7 +236,8 @@ public final class ProviderCreateSession extends ProviderSession< protected void invokeSession() { if (mRemoteCredentialService != null) { startCandidateMetrics(); - mRemoteCredentialService.onCreateCredential(mProviderRequest, this); + mProviderCancellationSignal = + mRemoteCredentialService.onCreateCredential(mProviderRequest, this); } } diff --git a/services/credentials/java/com/android/server/credentials/ProviderGetSession.java b/services/credentials/java/com/android/server/credentials/ProviderGetSession.java index 427a8945c573..9c9c0c212d0c 100644 --- a/services/credentials/java/com/android/server/credentials/ProviderGetSession.java +++ b/services/credentials/java/com/android/server/credentials/ProviderGetSession.java @@ -302,7 +302,9 @@ public final class ProviderGetSession extends ProviderSession<BeginGetCredential protected void invokeSession() { if (mRemoteCredentialService != null) { startCandidateMetrics(); - mRemoteCredentialService.onBeginGetCredential(mProviderRequest, this); + mProviderCancellationSignal = + mRemoteCredentialService.onBeginGetCredential(mProviderRequest, this); + boolean foundSig = mProviderCancellationSignal == null; } } diff --git a/services/credentials/java/com/android/server/credentials/ProviderSession.java b/services/credentials/java/com/android/server/credentials/ProviderSession.java index 8c0e1c1511e6..d165756b3811 100644 --- a/services/credentials/java/com/android/server/credentials/ProviderSession.java +++ b/services/credentials/java/com/android/server/credentials/ProviderSession.java @@ -30,6 +30,7 @@ import android.credentials.ui.ProviderPendingIntentResponse; import android.os.ICancellationSignal; import android.os.RemoteException; import android.util.Log; +import android.util.Slog; import com.android.server.credentials.metrics.ProviderSessionMetric; @@ -189,7 +190,7 @@ public abstract class ProviderSession<T, R> } setStatus(Status.CANCELED); } catch (RemoteException e) { - Log.i(TAG, "Issue while cancelling provider session: " + e.getMessage()); + Slog.e(TAG, "Issue while cancelling provider session: ", e); } } diff --git a/services/credentials/java/com/android/server/credentials/RequestSession.java b/services/credentials/java/com/android/server/credentials/RequestSession.java index 04c4bc4c7c4f..ed175ed9601f 100644 --- a/services/credentials/java/com/android/server/credentials/RequestSession.java +++ b/services/credentials/java/com/android/server/credentials/RequestSession.java @@ -20,6 +20,7 @@ import android.annotation.NonNull; import android.annotation.UserIdInt; import android.content.ComponentName; import android.content.Context; +import android.content.Intent; import android.credentials.CredentialProviderInfo; import android.credentials.ui.ProviderData; import android.credentials.ui.UserSelectionDialogResult; @@ -29,8 +30,10 @@ import android.os.Handler; import android.os.IBinder; import android.os.Looper; import android.os.RemoteException; +import android.os.UserHandle; import android.service.credentials.CallingAppInfo; import android.util.Log; +import android.util.Slog; import com.android.internal.R; import com.android.server.credentials.metrics.ApiName; @@ -39,8 +42,8 @@ import com.android.server.credentials.metrics.ProviderStatusForMetrics; import com.android.server.credentials.metrics.RequestSessionMetric; import java.util.ArrayList; -import java.util.HashMap; import java.util.Map; +import java.util.concurrent.ConcurrentHashMap; /** * Base class of a request session, that listens to UI events. This class must be extended @@ -49,6 +52,11 @@ import java.util.Map; abstract class RequestSession<T, U, V> implements CredentialManagerUi.CredentialManagerUiCallback { private static final String TAG = "RequestSession"; + public interface SessionLifetime { + /** Called when the user makes a selection. */ + void onFinishRequestSession(@UserIdInt int userId, IBinder token); + } + // TODO: Revise access levels of attributes @NonNull protected final T mClientRequest; @@ -72,10 +80,14 @@ abstract class RequestSession<T, U, V> implements CredentialManagerUi.Credential @NonNull protected final CancellationSignal mCancellationSignal; - protected final Map<String, ProviderSession> mProviders = new HashMap<>(); + protected final Map<String, ProviderSession> mProviders = new ConcurrentHashMap<>(); protected final RequestSessionMetric mRequestSessionMetric = new RequestSessionMetric(); protected final String mHybridService; + protected final Object mLock; + + protected final SessionLifetime mSessionCallback; + @NonNull protected RequestSessionStatus mRequestSessionStatus = RequestSessionStatus.IN_PROGRESS; @@ -91,11 +103,15 @@ abstract class RequestSession<T, U, V> implements CredentialManagerUi.Credential } protected RequestSession(@NonNull Context context, - @UserIdInt int userId, int callingUid, @NonNull T clientRequest, U clientCallback, + RequestSession.SessionLifetime sessionCallback, + Object lock, @UserIdInt int userId, int callingUid, + @NonNull T clientRequest, U clientCallback, @NonNull String requestType, CallingAppInfo callingAppInfo, CancellationSignal cancellationSignal, long timestampStarted) { mContext = context; + mLock = lock; + mSessionCallback = sessionCallback; mUserId = userId; mCallingUid = callingUid; mClientRequest = clientRequest; @@ -111,6 +127,32 @@ abstract class RequestSession<T, U, V> implements CredentialManagerUi.Credential R.string.config_defaultCredentialManagerHybridService); mRequestSessionMetric.collectInitialPhaseMetricInfo(timestampStarted, mRequestId, mCallingUid, ApiName.getMetricCodeFromRequestInfo(mRequestType)); + setCancellationListener(); + } + + private void setCancellationListener() { + mCancellationSignal.setOnCancelListener( + () -> { + boolean isUiActive = maybeCancelUi(); + finishSession(!isUiActive); + } + ); + } + + private boolean maybeCancelUi() { + if (mCredentialManagerUi.getStatus() + == CredentialManagerUi.UiStatus.USER_INTERACTION) { + final long originalCallingUidToken = Binder.clearCallingIdentity(); + try { + mContext.startActivityAsUser(mCredentialManagerUi.createCancelIntent( + mRequestId, mClientAppInfo.getPackageName()) + .addFlags(Intent.FLAG_ACTIVITY_NEW_TASK), UserHandle.of(mUserId)); + return true; + } finally { + Binder.restoreCallingIdentity(originalCallingUidToken); + } + } + return false; } public abstract ProviderSession initiateProviderSession(CredentialProviderInfo providerInfo, @@ -154,12 +196,19 @@ abstract class RequestSession<T, U, V> implements CredentialManagerUi.Credential } protected void finishSession(boolean propagateCancellation) { - Log.i(TAG, "finishing session"); + Slog.d(TAG, "finishing session with propagateCancellation " + propagateCancellation); if (propagateCancellation) { mProviders.values().forEach(ProviderSession::cancelProviderRemoteSession); } mRequestSessionStatus = RequestSessionStatus.COMPLETE; mProviders.clear(); + clearRequestSessionLocked(); + } + + private void clearRequestSessionLocked() { + synchronized (mLock) { + mSessionCallback.onFinishRequestSession(mUserId, mRequestId); + } } protected boolean isAnyProviderPending() { diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java index a109dee73fe8..b2156414b122 100644 --- a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java +++ b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java @@ -92,6 +92,8 @@ import static android.app.AppOpsManager.OPSTR_SYSTEM_EXEMPT_FROM_HIBERNATION; import static android.app.AppOpsManager.OPSTR_SYSTEM_EXEMPT_FROM_POWER_RESTRICTIONS; import static android.app.AppOpsManager.OPSTR_SYSTEM_EXEMPT_FROM_SUSPENSION; import static android.app.admin.DeviceAdminInfo.HEADLESS_DEVICE_OWNER_MODE_AFFILIATED; +import static android.app.admin.DeviceAdminInfo.USES_POLICY_FORCE_LOCK; +import static android.app.admin.DeviceAdminInfo.USES_POLICY_WIPE_DATA; import static android.app.admin.DeviceAdminReceiver.ACTION_COMPLIANCE_ACKNOWLEDGEMENT_REQUIRED; import static android.app.admin.DeviceAdminReceiver.EXTRA_TRANSFER_OWNERSHIP_ADMIN_EXTRAS_BUNDLE; import static android.app.admin.DevicePolicyIdentifiers.AUTO_TIMEZONE_POLICY; @@ -439,6 +441,7 @@ import android.util.AtomicFile; import android.util.DebugUtils; import android.util.IndentingPrintWriter; import android.util.IntArray; +import android.util.Log; import android.util.Pair; import android.util.Slog; import android.util.SparseArray; @@ -773,6 +776,14 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub { @EnabledSince(targetSdkVersion = Build.VERSION_CODES.UPSIDE_DOWN_CAKE) public static final long EXPLICIT_WIPE_BEHAVIOUR = 242193913L; + /** + * Apps targetting U+ should now expect that attempts to grant sensor permissions without + * authorisation will result in a security exception. + */ + @ChangeId + @EnabledSince(targetSdkVersion = Build.VERSION_CODES.UPSIDE_DOWN_CAKE) + public static final long THROW_SECURITY_EXCEPTION_FOR_SENSOR_PERMISSIONS = 277035314L; + // Only add to the end of the list. Do not change or rearrange these values, that will break // historical data. Do not use negative numbers or zero, logger only handles positive // integers. @@ -2819,6 +2830,16 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub { return doAdmin; } + ActiveAdmin getDefaultDeviceOwnerLocked(@UserIdInt int userId) { + ensureLocked(); + ComponentName doComponent = mOwners.getDeviceOwnerComponent(); + if (mOwners.getDeviceOwnerType(doComponent.getPackageName()) == DEFAULT_DEVICE_OWNER) { + ActiveAdmin doAdmin = getUserData(userId).mAdminMap.get(doComponent); + return doAdmin; + } + return null; + } + ActiveAdmin getProfileOwnerLocked(@UserIdInt int userId) { ensureLocked(); final ComponentName poAdminComponent = mOwners.getProfileOwnerComponent(userId); @@ -2848,6 +2869,18 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub { return getDeviceOwnerLocked(userId); } + ActiveAdmin getProfileOwnerOrDefaultDeviceOwnerLocked(@UserIdInt int userId) { + ensureLocked(); + // Try to find an admin which can use reqPolicy + final ComponentName poAdminComponent = mOwners.getProfileOwnerComponent(userId); + + if (poAdminComponent != null) { + return getProfileOwnerLocked(userId); + } + + return getDefaultDeviceOwnerLocked(userId); + } + @NonNull ActiveAdmin getParentOfAdminIfRequired(ActiveAdmin admin, boolean parent) { Objects.requireNonNull(admin); return parent ? admin.getParentActiveAdmin() : admin; @@ -5337,9 +5370,12 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub { saveSettingsLocked(caller.getUserId()); }); + + //TODO(b/276855301): caller.getPackageName() will be null when the coexistence flags are + // turned off. Change back to caller.getPackageName once this API is unflagged. DevicePolicyEventLogger .createEvent(DevicePolicyEnums.SET_PASSWORD_COMPLEXITY) - .setAdmin(caller.getPackageName()) + .setAdmin(admin.info.getPackageName()) .setInt(passwordComplexity) .setBoolean(calledOnParent) .write(); @@ -5974,8 +6010,13 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub { } @Override - public void lockNow(int flags, boolean parent) { - final CallerIdentity caller = getCallerIdentity(); + public void lockNow(int flags, String callerPackageName, boolean parent) { + CallerIdentity caller; + if (isPermissionCheckFlagEnabled()) { + caller = getCallerIdentity(callerPackageName); + } else { + caller = getCallerIdentity(); + } final int callingUserId = caller.getUserId(); ComponentName adminComponent = null; @@ -5984,11 +6025,13 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub { // Make sure the caller has any active admin with the right policy or // the required permission. if (isPermissionCheckFlagEnabled()) { - admin = getActiveAdminOrCheckPermissionsForCallerLocked( - null, - DeviceAdminInfo.USES_POLICY_FORCE_LOCK, - parent, - Set.of(MANAGE_DEVICE_POLICY_LOCK, LOCK_DEVICE)); + admin = enforcePermissionAndGetEnforcingAdmin( + /* admin= */ null, + /* permission= */ MANAGE_DEVICE_POLICY_LOCK, + USES_POLICY_FORCE_LOCK, + caller.getPackageName(), + getAffectedUser(parent) + ).getActiveAdmin(); } else { admin = getActiveAdminOrCheckPermissionForCallerLocked( null, @@ -7481,7 +7524,8 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub { if (isPolicyEngineForFinanceFlagEnabled()) { EnforcingAdmin enforcingAdmin = enforcePermissionAndGetEnforcingAdmin( /*admin=*/ null, - MANAGE_DEVICE_POLICY_WIPE_DATA, + /*permission= */ MANAGE_DEVICE_POLICY_WIPE_DATA, + USES_POLICY_WIPE_DATA, caller.getPackageName(), factoryReset ? UserHandle.USER_ALL : getAffectedUser(calledOnParentInstance)); admin = enforcingAdmin.getActiveAdmin(); @@ -8511,7 +8555,7 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub { isProfileOwnerOfOrganizationOwnedDevice(caller)); } else { Preconditions.checkCallAuthorization(isProfileOwner(caller) - || isDeviceOwner(caller)); + || isDefaultDeviceOwner(caller)); } } @@ -8525,7 +8569,7 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub { targetUserId).getActiveAdmin(); } else { ap = getParentOfAdminIfRequired( - getProfileOwnerOrDeviceOwnerLocked(caller.getUserId()), parent); + getProfileOwnerOrDefaultDeviceOwnerLocked(caller.getUserId()), parent); } if (ap.disableScreenCapture != disabled) { @@ -8557,15 +8601,21 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub { } if (admin != null && admin.disableScreenCapture) { setScreenCaptureDisabled(UserHandle.USER_ALL); - } else { - // Otherwise, update screen capture only for the calling user. - admin = getProfileOwnerAdminLocked(adminUserId); - if (admin != null && admin.disableScreenCapture) { - setScreenCaptureDisabled(adminUserId); - } else { - setScreenCaptureDisabled(UserHandle.USER_NULL); - } + return; + } + // Otherwise, update screen capture only for the calling user. + admin = getProfileOwnerAdminLocked(adminUserId); + if (admin != null && admin.disableScreenCapture) { + setScreenCaptureDisabled(adminUserId); + return; } + // If the admin is permission based, update only for the calling user. + admin = getUserData(adminUserId).createOrGetPermissionBasedAdmin(adminUserId); + if (admin != null && admin.disableScreenCapture) { + setScreenCaptureDisabled(adminUserId); + return; + } + setScreenCaptureDisabled(UserHandle.USER_NULL); } // Set the latest screen capture policy, overriding any existing ones. @@ -11424,7 +11474,7 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub { RoleManager.ROLE_DIALER, packageName, 0, UserHandle.of(callerUserId), AsyncTask.THREAD_POOL_EXECUTOR, callback); try { - future.get(5, TimeUnit.SECONDS); + future.get(20, TimeUnit.SECONDS); } catch (TimeoutException e) { throw new IllegalArgumentException("Timeout when setting the app as the dialer", e); } catch (ExecutionException e) { @@ -16466,6 +16516,25 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub { MANAGE_DEVICE_POLICY_RUNTIME_PERMISSIONS, callerPackage, caller.getUserId()); + if (SENSOR_PERMISSIONS.contains(permission) + && grantState == PERMISSION_GRANT_STATE_GRANTED + && (!canAdminGrantSensorsPermissions() || isCallerDelegate(caller))) { + if (mInjector.isChangeEnabled(THROW_SECURITY_EXCEPTION_FOR_SENSOR_PERMISSIONS, + caller.getPackageName(), caller.getUserId())) { + throw new SecurityException( + "Caller not permitted to grant sensor permissions."); + } else { + // This is to match the legacy behaviour. + callback.sendResult(Bundle.EMPTY); + return; + } + } + // Check all the states where Exceptions aren't thrown but the permission + // isn't granted either. + if (!canGrantPermission(caller, permission, packageName)) { + callback.sendResult(null); + return; + } // TODO(b/266924257): decide how to handle the internal state if the package doesn't // exist, or the permission isn't requested by the app, because we could end up with // inconsistent state between the policy engine and package manager. Also a package @@ -16541,6 +16610,41 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub { } } + private static final List<String> SENSOR_PERMISSIONS = new ArrayList<>(); + { + SENSOR_PERMISSIONS.add(Manifest.permission.ACCESS_FINE_LOCATION); + SENSOR_PERMISSIONS.add(Manifest.permission.ACCESS_BACKGROUND_LOCATION); + SENSOR_PERMISSIONS.add(Manifest.permission.ACCESS_COARSE_LOCATION); + SENSOR_PERMISSIONS.add(Manifest.permission.CAMERA); + SENSOR_PERMISSIONS.add(Manifest.permission.RECORD_AUDIO); + SENSOR_PERMISSIONS.add(Manifest.permission.ACTIVITY_RECOGNITION); + SENSOR_PERMISSIONS.add(Manifest.permission.BODY_SENSORS); + SENSOR_PERMISSIONS.add(Manifest.permission.BACKGROUND_CAMERA); + SENSOR_PERMISSIONS.add(Manifest.permission.RECORD_BACKGROUND_AUDIO); + SENSOR_PERMISSIONS.add(Manifest.permission.BODY_SENSORS_BACKGROUND); + SENSOR_PERMISSIONS.add( + Manifest.permission.BODY_SENSORS_WRIST_TEMPERATURE); + SENSOR_PERMISSIONS.add( + Manifest.permission.BODY_SENSORS_WRIST_TEMPERATURE_BACKGROUND); + } + + private boolean canGrantPermission(CallerIdentity caller, String permission, + String targetPackageName) { + boolean isPostQAdmin = getTargetSdk(caller.getPackageName(), caller.getUserId()) + >= android.os.Build.VERSION_CODES.Q; + if (!isPostQAdmin) { + // Legacy admins assume that they cannot control pre-M apps + if (getTargetSdk(targetPackageName, caller.getUserId()) + < android.os.Build.VERSION_CODES.M) { + return false; + } + } + if (!isRuntimePermission(permission)) { + return false; + } + return true; + } + private void enforcePermissionGrantStateOnFinancedDevice( String packageName, String permission) { if (!Manifest.permission.READ_PHONE_STATE.equals(permission)) { @@ -17637,7 +17741,6 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub { synchronized (getLockObject()) { if (isPermissionCheckFlagEnabled()) { - // TODO: add support for DELEGATION_SECURITY_LOGGING enforcePermission(MANAGE_DEVICE_POLICY_SECURITY_LOGGING, caller.getPackageName(), UserHandle.USER_ALL); } else { @@ -17718,7 +17821,8 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub { final CallerIdentity caller = getCallerIdentity(admin, packageName); if (isPermissionCheckFlagEnabled()) { - // TODO: Restore the "affiliated users" check + Preconditions.checkCallAuthorization(isOrganizationOwnedDeviceWithManagedProfile() + || areAllUsersAffiliatedWithDeviceLocked()); enforcePermission(MANAGE_DEVICE_POLICY_SECURITY_LOGGING, caller.getPackageName(), UserHandle.USER_ALL); } else { @@ -17770,7 +17874,9 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub { final CallerIdentity caller = getCallerIdentity(admin, packageName); if (isPermissionCheckFlagEnabled()) { - // TODO: Restore the "affiliated users" check + Preconditions.checkCallAuthorization(isOrganizationOwnedDeviceWithManagedProfile() + || areAllUsersAffiliatedWithDeviceLocked()); + enforcePermission(MANAGE_DEVICE_POLICY_SECURITY_LOGGING, caller.getPackageName(), UserHandle.USER_ALL); } else { @@ -22321,244 +22427,208 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub { // Permissions of existing DPC types. private static final List<String> DEFAULT_DEVICE_OWNER_PERMISSIONS = List.of( - MANAGE_DEVICE_POLICY_ACROSS_USERS_FULL, + MANAGE_DEVICE_POLICY_ACCOUNT_MANAGEMENT, MANAGE_DEVICE_POLICY_ACROSS_USERS, + MANAGE_DEVICE_POLICY_ACROSS_USERS_FULL, MANAGE_DEVICE_POLICY_ACROSS_USERS_SECURITY_CRITICAL, - SET_TIME, - SET_TIME_ZONE, - MANAGE_DEVICE_POLICY_ORGANIZATION_IDENTITY, - MANAGE_DEVICE_POLICY_RUNTIME_PERMISSIONS, - MANAGE_DEVICE_POLICY_WIFI, - MANAGE_DEVICE_POLICY_WIPE_DATA, - MANAGE_DEVICE_POLICY_SCREEN_CAPTURE, - MANAGE_DEVICE_POLICY_SYSTEM_UPDATES, - MANAGE_DEVICE_POLICY_SECURITY_LOGGING, - MANAGE_DEVICE_POLICY_USB_DATA_SIGNALLING, - MANAGE_DEVICE_POLICY_MTE, - MANAGE_DEVICE_POLICY_LOCK_CREDENTIALS, - MANAGE_DEVICE_POLICY_PACKAGE_STATE, - MANAGE_DEVICE_POLICY_LOCK, - MANAGE_DEVICE_POLICY_FACTORY_RESET, - MANAGE_DEVICE_POLICY_KEYGUARD, - MANAGE_DEVICE_POLICY_CERTIFICATES, - MANAGE_DEVICE_POLICY_KEYGUARD, - MANAGE_DEVICE_POLICY_RUNTIME_PERMISSIONS, - MANAGE_DEVICE_POLICY_SUPPORT_MESSAGE, - MANAGE_DEVICE_POLICY_APPS_CONTROL, - MANAGE_DEVICE_POLICY_LOCK_TASK, - MANAGE_DEVICE_POLICY_ACCOUNT_MANAGEMENT, - MANAGE_DEVICE_POLICY_CAMERA, - MANAGE_DEVICE_POLICY_COMMON_CRITERIA_MODE, - MANAGE_DEVICE_POLICY_DEFAULT_SMS, - MANAGE_DEVICE_POLICY_PACKAGE_STATE, - MANAGE_DEVICE_POLICY_PROFILE_INTERACTION, - MANAGE_DEVICE_POLICY_RESET_PASSWORD, - MANAGE_DEVICE_POLICY_STATUS_BAR, - MANAGE_DEVICE_POLICY_LOCK_TASK, - MANAGE_DEVICE_POLICY_ACCOUNT_MANAGEMENT, MANAGE_DEVICE_POLICY_AIRPLANE_MODE, + MANAGE_DEVICE_POLICY_APPS_CONTROL, + MANAGE_DEVICE_POLICY_APP_RESTRICTIONS, MANAGE_DEVICE_POLICY_AUDIO_OUTPUT, MANAGE_DEVICE_POLICY_AUTOFILL, MANAGE_DEVICE_POLICY_BLUETOOTH, MANAGE_DEVICE_POLICY_CALLS, MANAGE_DEVICE_POLICY_CAMERA, + MANAGE_DEVICE_POLICY_CERTIFICATES, + MANAGE_DEVICE_POLICY_COMMON_CRITERIA_MODE, MANAGE_DEVICE_POLICY_DEBUGGING_FEATURES, + MANAGE_DEVICE_POLICY_DEFAULT_SMS, MANAGE_DEVICE_POLICY_DISPLAY, MANAGE_DEVICE_POLICY_FACTORY_RESET, MANAGE_DEVICE_POLICY_FUN, MANAGE_DEVICE_POLICY_INSTALL_UNKNOWN_SOURCES, + MANAGE_DEVICE_POLICY_KEYGUARD, MANAGE_DEVICE_POLICY_LOCALE, MANAGE_DEVICE_POLICY_LOCATION, + MANAGE_DEVICE_POLICY_LOCK, MANAGE_DEVICE_POLICY_LOCK_CREDENTIALS, + MANAGE_DEVICE_POLICY_LOCK_TASK, MANAGE_DEVICE_POLICY_MICROPHONE, MANAGE_DEVICE_POLICY_MOBILE_NETWORK, + MANAGE_DEVICE_POLICY_MTE, MANAGE_DEVICE_POLICY_NEARBY_COMMUNICATION, + MANAGE_DEVICE_POLICY_ORGANIZATION_IDENTITY, + MANAGE_DEVICE_POLICY_PACKAGE_STATE, MANAGE_DEVICE_POLICY_PHYSICAL_MEDIA, MANAGE_DEVICE_POLICY_PRINTING, - MANAGE_DEVICE_POLICY_RESTRICT_PRIVATE_DNS, MANAGE_DEVICE_POLICY_PROFILES, MANAGE_DEVICE_POLICY_PROFILE_INTERACTION, + MANAGE_DEVICE_POLICY_RESET_PASSWORD, + MANAGE_DEVICE_POLICY_RESTRICT_PRIVATE_DNS, + MANAGE_DEVICE_POLICY_RUNTIME_PERMISSIONS, MANAGE_DEVICE_POLICY_SAFE_BOOT, + MANAGE_DEVICE_POLICY_SCREEN_CAPTURE, MANAGE_DEVICE_POLICY_SCREEN_CONTENT, + MANAGE_DEVICE_POLICY_SECURITY_LOGGING, MANAGE_DEVICE_POLICY_SMS, + MANAGE_DEVICE_POLICY_STATUS_BAR, + MANAGE_DEVICE_POLICY_SUPPORT_MESSAGE, MANAGE_DEVICE_POLICY_SYSTEM_DIALOGS, + MANAGE_DEVICE_POLICY_SYSTEM_UPDATES, MANAGE_DEVICE_POLICY_TIME, + MANAGE_DEVICE_POLICY_USB_DATA_SIGNALLING, MANAGE_DEVICE_POLICY_USB_FILE_TRANSFER, MANAGE_DEVICE_POLICY_USERS, MANAGE_DEVICE_POLICY_VPN, MANAGE_DEVICE_POLICY_WALLPAPER, MANAGE_DEVICE_POLICY_WIFI, MANAGE_DEVICE_POLICY_WINDOWS, - MANAGE_DEVICE_POLICY_APP_RESTRICTIONS + MANAGE_DEVICE_POLICY_WIPE_DATA, + SET_TIME, + SET_TIME_ZONE ); private static final List<String> FINANCED_DEVICE_OWNER_PERMISSIONS = List.of( - MANAGE_DEVICE_POLICY_ACROSS_USERS_FULL, MANAGE_DEVICE_POLICY_ACROSS_USERS, + MANAGE_DEVICE_POLICY_ACROSS_USERS_FULL, MANAGE_DEVICE_POLICY_ACROSS_USERS_SECURITY_CRITICAL, - MANAGE_DEVICE_POLICY_ORGANIZATION_IDENTITY, - MANAGE_DEVICE_POLICY_RUNTIME_PERMISSIONS, - MANAGE_DEVICE_POLICY_ORGANIZATION_IDENTITY, - MANAGE_DEVICE_POLICY_FACTORY_RESET, - MANAGE_DEVICE_POLICY_KEYGUARD, - MANAGE_DEVICE_POLICY_RUNTIME_PERMISSIONS, - MANAGE_DEVICE_POLICY_SUPPORT_MESSAGE, MANAGE_DEVICE_POLICY_APPS_CONTROL, - MANAGE_DEVICE_POLICY_LOCK_TASK, MANAGE_DEVICE_POLICY_CALLS, MANAGE_DEVICE_POLICY_DEBUGGING_FEATURES, + MANAGE_DEVICE_POLICY_FACTORY_RESET, MANAGE_DEVICE_POLICY_INSTALL_UNKNOWN_SOURCES, - MANAGE_DEVICE_POLICY_USERS, + MANAGE_DEVICE_POLICY_KEYGUARD, + MANAGE_DEVICE_POLICY_LOCK_TASK, + MANAGE_DEVICE_POLICY_ORGANIZATION_IDENTITY, + MANAGE_DEVICE_POLICY_RUNTIME_PERMISSIONS, MANAGE_DEVICE_POLICY_SAFE_BOOT, + MANAGE_DEVICE_POLICY_SUPPORT_MESSAGE, MANAGE_DEVICE_POLICY_TIME, - MANAGE_DEVICE_POLICY_LOCK_CREDENTIALS); - private static final List<String> PROFILE_OWNER_OF_ORGANIZATION_OWNED_DEVICE_PERMISSIONS = + MANAGE_DEVICE_POLICY_USERS, + MANAGE_DEVICE_POLICY_LOCK_CREDENTIALS + ); + + /** + * All the permisisons granted to a profile owner. + */ + private static final List<String> PROFILE_OWNER_PERMISSIONS = List.of( - MANAGE_DEVICE_POLICY_ACROSS_USERS, + MANAGE_DEVICE_POLICY_ACCOUNT_MANAGEMENT, MANAGE_DEVICE_POLICY_ACROSS_USERS_SECURITY_CRITICAL, - SET_TIME, - SET_TIME_ZONE, - MANAGE_DEVICE_POLICY_ORGANIZATION_IDENTITY, - MANAGE_DEVICE_POLICY_RUNTIME_PERMISSIONS, - MANAGE_DEVICE_POLICY_SUPPORT_MESSAGE, MANAGE_DEVICE_POLICY_APPS_CONTROL, - MANAGE_DEVICE_POLICY_WIFI, - MANAGE_DEVICE_POLICY_WIPE_DATA, - MANAGE_DEVICE_POLICY_SCREEN_CAPTURE, - MANAGE_DEVICE_POLICY_SYSTEM_UPDATES, - MANAGE_DEVICE_POLICY_SECURITY_LOGGING, - MANAGE_DEVICE_POLICY_USB_DATA_SIGNALLING, - MANAGE_DEVICE_POLICY_MTE, - MANAGE_DEVICE_POLICY_LOCK_CREDENTIALS, - MANAGE_DEVICE_POLICY_PACKAGE_STATE, - MANAGE_DEVICE_POLICY_LOCK, - MANAGE_DEVICE_POLICY_FACTORY_RESET, - MANAGE_DEVICE_POLICY_KEYGUARD, - MANAGE_DEVICE_POLICY_AIRPLANE_MODE, - MANAGE_DEVICE_POLICY_ACCOUNT_MANAGEMENT, + MANAGE_DEVICE_POLICY_APP_RESTRICTIONS, MANAGE_DEVICE_POLICY_AUDIO_OUTPUT, MANAGE_DEVICE_POLICY_AUTOFILL, - MANAGE_DEVICE_POLICY_BLUETOOTH, MANAGE_DEVICE_POLICY_CALLS, - MANAGE_DEVICE_POLICY_CAMERA, MANAGE_DEVICE_POLICY_DEBUGGING_FEATURES, MANAGE_DEVICE_POLICY_DISPLAY, MANAGE_DEVICE_POLICY_FACTORY_RESET, MANAGE_DEVICE_POLICY_INSTALL_UNKNOWN_SOURCES, + MANAGE_DEVICE_POLICY_KEYGUARD, MANAGE_DEVICE_POLICY_LOCALE, MANAGE_DEVICE_POLICY_LOCATION, + MANAGE_DEVICE_POLICY_LOCK, MANAGE_DEVICE_POLICY_LOCK_CREDENTIALS, - MANAGE_DEVICE_POLICY_MICROPHONE, - MANAGE_DEVICE_POLICY_MOBILE_NETWORK, MANAGE_DEVICE_POLICY_NEARBY_COMMUNICATION, - MANAGE_DEVICE_POLICY_PHYSICAL_MEDIA, + MANAGE_DEVICE_POLICY_ORGANIZATION_IDENTITY, + MANAGE_DEVICE_POLICY_PACKAGE_STATE, MANAGE_DEVICE_POLICY_PRINTING, MANAGE_DEVICE_POLICY_PROFILES, MANAGE_DEVICE_POLICY_PROFILE_INTERACTION, - MANAGE_DEVICE_POLICY_RESTRICT_PRIVATE_DNS, - MANAGE_DEVICE_POLICY_SAFE_BOOT, + MANAGE_DEVICE_POLICY_RESET_PASSWORD, + MANAGE_DEVICE_POLICY_RUNTIME_PERMISSIONS, + MANAGE_DEVICE_POLICY_SCREEN_CAPTURE, MANAGE_DEVICE_POLICY_SCREEN_CONTENT, - MANAGE_DEVICE_POLICY_SMS, + MANAGE_DEVICE_POLICY_SUPPORT_MESSAGE, MANAGE_DEVICE_POLICY_SYSTEM_DIALOGS, MANAGE_DEVICE_POLICY_TIME, MANAGE_DEVICE_POLICY_VPN, - MANAGE_DEVICE_POLICY_USB_FILE_TRANSFER, - MANAGE_DEVICE_POLICY_COMMON_CRITERIA_MODE, - MANAGE_DEVICE_POLICY_DEFAULT_SMS, - MANAGE_DEVICE_POLICY_PACKAGE_STATE, - MANAGE_DEVICE_POLICY_RESET_PASSWORD, - MANAGE_DEVICE_POLICY_APP_RESTRICTIONS, - MANAGE_DEVICE_POLICY_USB_FILE_TRANSFER, - MANAGE_DEVICE_POLICY_KEYGUARD, - MANAGE_DEVICE_POLICY_WIFI, - MANAGE_DEVICE_POLICY_WIPE_DATA, - MANAGE_DEVICE_POLICY_SCREEN_CAPTURE, - MANAGE_DEVICE_POLICY_SYSTEM_UPDATES, - MANAGE_DEVICE_POLICY_SECURITY_LOGGING, - MANAGE_DEVICE_POLICY_USB_DATA_SIGNALLING, - MANAGE_DEVICE_POLICY_MTE, - MANAGE_DEVICE_POLICY_PACKAGE_STATE, - MANAGE_DEVICE_POLICY_LOCK, - MANAGE_DEVICE_POLICY_FACTORY_RESET, - MANAGE_DEVICE_POLICY_KEYGUARD, - MANAGE_DEVICE_POLICY_CERTIFICATES); - private static final List<String> PROFILE_OWNER_ON_USER_0_PERMISSIONS = List.of( - SET_TIME, - SET_TIME_ZONE, - MANAGE_DEVICE_POLICY_RUNTIME_PERMISSIONS, - MANAGE_DEVICE_POLICY_SUPPORT_MESSAGE, - MANAGE_DEVICE_POLICY_APPS_CONTROL, - MANAGE_DEVICE_POLICY_LOCK_TASK, - MANAGE_DEVICE_POLICY_RUNTIME_PERMISSIONS, - MANAGE_DEVICE_POLICY_WIPE_DATA, - MANAGE_DEVICE_POLICY_SCREEN_CAPTURE, - MANAGE_DEVICE_POLICY_LOCK_CREDENTIALS, - MANAGE_DEVICE_POLICY_PACKAGE_STATE, - MANAGE_DEVICE_POLICY_LOCK, - MANAGE_DEVICE_POLICY_KEYGUARD, - MANAGE_DEVICE_POLICY_LOCK_TASK, - MANAGE_DEVICE_POLICY_AIRPLANE_MODE, - MANAGE_DEVICE_POLICY_BLUETOOTH, - MANAGE_DEVICE_POLICY_DEBUGGING_FEATURES, - MANAGE_DEVICE_POLICY_FACTORY_RESET, - MANAGE_DEVICE_POLICY_FUN, - MANAGE_DEVICE_POLICY_INSTALL_UNKNOWN_SOURCES, - MANAGE_DEVICE_POLICY_MOBILE_NETWORK, - MANAGE_DEVICE_POLICY_USERS, - MANAGE_DEVICE_POLICY_PHYSICAL_MEDIA, - MANAGE_DEVICE_POLICY_SAFE_BOOT, - MANAGE_DEVICE_POLICY_SMS, - MANAGE_DEVICE_POLICY_TIME, - MANAGE_DEVICE_POLICY_USB_FILE_TRANSFER, - MANAGE_DEVICE_POLICY_WINDOWS, - MANAGE_DEVICE_POLICY_LOCK_TASK, - MANAGE_DEVICE_POLICY_ACCOUNT_MANAGEMENT, - MANAGE_DEVICE_POLICY_CAMERA, - MANAGE_DEVICE_POLICY_PACKAGE_STATE, - MANAGE_DEVICE_POLICY_RESET_PASSWORD, - MANAGE_DEVICE_POLICY_STATUS_BAR, - MANAGE_DEVICE_POLICY_APP_RESTRICTIONS, - MANAGE_DEVICE_POLICY_SYSTEM_DIALOGS); - private static final List<String> PROFILE_OWNER_PERMISSIONS = List.of( - MANAGE_DEVICE_POLICY_ACROSS_USERS_SECURITY_CRITICAL, - MANAGE_DEVICE_POLICY_ORGANIZATION_IDENTITY, - MANAGE_DEVICE_POLICY_RUNTIME_PERMISSIONS, - MANAGE_DEVICE_POLICY_SUPPORT_MESSAGE, - MANAGE_DEVICE_POLICY_APPS_CONTROL, - MANAGE_DEVICE_POLICY_RUNTIME_PERMISSIONS, - MANAGE_DEVICE_POLICY_WIPE_DATA, - MANAGE_DEVICE_POLICY_SCREEN_CAPTURE, - MANAGE_DEVICE_POLICY_LOCK_CREDENTIALS, - MANAGE_DEVICE_POLICY_PACKAGE_STATE, - MANAGE_DEVICE_POLICY_LOCK, - MANAGE_DEVICE_POLICY_KEYGUARD, - MANAGE_DEVICE_POLICY_APPS_CONTROL, - MANAGE_DEVICE_POLICY_ACCOUNT_MANAGEMENT, - MANAGE_DEVICE_POLICY_AUDIO_OUTPUT, - MANAGE_DEVICE_POLICY_AUTOFILL, - MANAGE_DEVICE_POLICY_CALLS, - MANAGE_DEVICE_POLICY_DEBUGGING_FEATURES, - MANAGE_DEVICE_POLICY_DISPLAY, - MANAGE_DEVICE_POLICY_FACTORY_RESET, - MANAGE_DEVICE_POLICY_INSTALL_UNKNOWN_SOURCES, - MANAGE_DEVICE_POLICY_LOCALE, - MANAGE_DEVICE_POLICY_LOCATION, - MANAGE_DEVICE_POLICY_LOCK_CREDENTIALS, - MANAGE_DEVICE_POLICY_NEARBY_COMMUNICATION, - MANAGE_DEVICE_POLICY_PRINTING, - MANAGE_DEVICE_POLICY_PROFILES, - MANAGE_DEVICE_POLICY_PROFILE_INTERACTION, - MANAGE_DEVICE_POLICY_SCREEN_CONTENT, - MANAGE_DEVICE_POLICY_SYSTEM_DIALOGS, - MANAGE_DEVICE_POLICY_TIME, - MANAGE_DEVICE_POLICY_VPN, - MANAGE_DEVICE_POLICY_PACKAGE_STATE, - MANAGE_DEVICE_POLICY_PROFILE_INTERACTION, - MANAGE_DEVICE_POLICY_RESET_PASSWORD, - MANAGE_DEVICE_POLICY_APP_RESTRICTIONS + MANAGE_DEVICE_POLICY_WIPE_DATA ); + /** + * All the additional permissions granted to an organisation owned profile owner. + */ + private static final List<String> + ADDITIONAL_PROFILE_OWNER_OF_ORGANIZATION_OWNED_DEVICE_PERMISSIONS = + List.of( + MANAGE_DEVICE_POLICY_ACROSS_USERS, + MANAGE_DEVICE_POLICY_AIRPLANE_MODE, + MANAGE_DEVICE_POLICY_APPS_CONTROL, + MANAGE_DEVICE_POLICY_BLUETOOTH, + MANAGE_DEVICE_POLICY_CAMERA, + MANAGE_DEVICE_POLICY_CERTIFICATES, + MANAGE_DEVICE_POLICY_COMMON_CRITERIA_MODE, + MANAGE_DEVICE_POLICY_DEFAULT_SMS, + MANAGE_DEVICE_POLICY_LOCALE, + MANAGE_DEVICE_POLICY_MICROPHONE, + MANAGE_DEVICE_POLICY_MOBILE_NETWORK, + MANAGE_DEVICE_POLICY_MTE, + MANAGE_DEVICE_POLICY_NEARBY_COMMUNICATION, + MANAGE_DEVICE_POLICY_PHYSICAL_MEDIA, + MANAGE_DEVICE_POLICY_RESTRICT_PRIVATE_DNS, + MANAGE_DEVICE_POLICY_SAFE_BOOT, + MANAGE_DEVICE_POLICY_SECURITY_LOGGING, + MANAGE_DEVICE_POLICY_SMS, + MANAGE_DEVICE_POLICY_SYSTEM_UPDATES, + MANAGE_DEVICE_POLICY_USB_DATA_SIGNALLING, + MANAGE_DEVICE_POLICY_USB_FILE_TRANSFER, + MANAGE_DEVICE_POLICY_WIFI, + SET_TIME, + SET_TIME_ZONE + ); + + + private static final List<String> ADDITIONAL_PROFILE_OWNER_ON_USER_0_PERMISSIONS = + List.of( + MANAGE_DEVICE_POLICY_AIRPLANE_MODE, + MANAGE_DEVICE_POLICY_BLUETOOTH, + MANAGE_DEVICE_POLICY_CAMERA, + MANAGE_DEVICE_POLICY_DISPLAY, + MANAGE_DEVICE_POLICY_FUN, + MANAGE_DEVICE_POLICY_LOCK_TASK, + MANAGE_DEVICE_POLICY_MOBILE_NETWORK, + MANAGE_DEVICE_POLICY_PHYSICAL_MEDIA, + MANAGE_DEVICE_POLICY_PRINTING, + MANAGE_DEVICE_POLICY_PROFILES, + MANAGE_DEVICE_POLICY_PROFILE_INTERACTION, + MANAGE_DEVICE_POLICY_SAFE_BOOT, + MANAGE_DEVICE_POLICY_SMS, + MANAGE_DEVICE_POLICY_STATUS_BAR, + MANAGE_DEVICE_POLICY_SYSTEM_DIALOGS, + MANAGE_DEVICE_POLICY_USB_FILE_TRANSFER, + MANAGE_DEVICE_POLICY_USERS, + MANAGE_DEVICE_POLICY_WINDOWS, + SET_TIME, + SET_TIME_ZONE + ); + + /** + * Combination of {@link PROFILE_OWNER_PERMISSIONS} and + * {@link ADDITIONAL_PROFILE_OWNER_OF_ORGANIZATION_OWNED_DEVICE_PERMISSIONS}. + */ + private static final List<String> PROFILE_OWNER_OF_ORGANIZATION_OWNED_DEVICE_PERMISSIONS = + new ArrayList(); + + /** + * Combination of {@link PROFILE_OWNER_PERMISSIONS} and + * {@link ADDITIONAL_PROFILE_OWNER_ON_USER_0_PERMISSIONS}. + */ + private static final List<String> PROFILE_OWNER_ON_USER_0_PERMISSIONS = + new ArrayList(); + + private static final HashMap<Integer, List<String>> DPC_PERMISSIONS = new HashMap<>(); { + // Organisation owned profile owners have all the permission of a profile owner plus + // some extra permissions. + PROFILE_OWNER_OF_ORGANIZATION_OWNED_DEVICE_PERMISSIONS.addAll(PROFILE_OWNER_PERMISSIONS); + PROFILE_OWNER_OF_ORGANIZATION_OWNED_DEVICE_PERMISSIONS.addAll( + ADDITIONAL_PROFILE_OWNER_OF_ORGANIZATION_OWNED_DEVICE_PERMISSIONS); + // Profile owners on user 0 have all the permission of a profile owner plus + // some extra permissions. + PROFILE_OWNER_ON_USER_0_PERMISSIONS.addAll(PROFILE_OWNER_PERMISSIONS); + PROFILE_OWNER_ON_USER_0_PERMISSIONS.addAll(ADDITIONAL_PROFILE_OWNER_ON_USER_0_PERMISSIONS); + DPC_PERMISSIONS.put(DEFAULT_DEVICE_OWNER, DEFAULT_DEVICE_OWNER_PERMISSIONS); DPC_PERMISSIONS.put(FINANCED_DEVICE_OWNER, FINANCED_DEVICE_OWNER_PERMISSIONS); DPC_PERMISSIONS.put(PROFILE_OWNER_OF_ORGANIZATION_OWNED_DEVICE, @@ -22566,14 +22636,6 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub { DPC_PERMISSIONS.put(PROFILE_OWNER_ON_USER_0, PROFILE_OWNER_ON_USER_0_PERMISSIONS); DPC_PERMISSIONS.put(PROFILE_OWNER, PROFILE_OWNER_PERMISSIONS); } - - // Map of permission Active admin DEVICE_POLICY. - //TODO(b/254253251) Fill this map in as new permissions are added for policies. - private static final HashMap<String, Integer> ACTIVE_ADMIN_POLICIES = new HashMap<>(); - { - //Any ActiveAdmin is able to call the support message APIs without certain policies. - ACTIVE_ADMIN_POLICIES.put(MANAGE_DEVICE_POLICY_SUPPORT_MESSAGE, null); - } //Map of Permission to Delegate Scope. private static final HashMap<String, String> DELEGATE_SCOPES = new HashMap<>(); { @@ -22587,123 +22649,119 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub { private static final HashMap<String, String> CROSS_USER_PERMISSIONS = new HashMap<>(); { - // Time and Timezone is intrinsically global so there is no cross-user permission. - CROSS_USER_PERMISSIONS.put(SET_TIME, null); - CROSS_USER_PERMISSIONS.put(SET_TIME_ZONE, null); - // system updates are intrinsically global so there is no cross-user permission - CROSS_USER_PERMISSIONS.put(MANAGE_DEVICE_POLICY_SYSTEM_UPDATES, null); - // security logs are intrinsically global so there is no cross-user permission - CROSS_USER_PERMISSIONS.put(MANAGE_DEVICE_POLICY_SECURITY_LOGGING, null); - // usb signalling is intrinsically global so there is no cross-user permission - CROSS_USER_PERMISSIONS.put(MANAGE_DEVICE_POLICY_USB_DATA_SIGNALLING, null); - // mte is intrinsically global so there is no cross-user permission - CROSS_USER_PERMISSIONS.put(MANAGE_DEVICE_POLICY_MTE, null); + // The permissions are all intrinsically global and therefore have no cross-user permission. CROSS_USER_PERMISSIONS.put(MANAGE_DEVICE_POLICY_FACTORY_RESET, null); + CROSS_USER_PERMISSIONS.put(MANAGE_DEVICE_POLICY_MTE, null); + CROSS_USER_PERMISSIONS.put(MANAGE_DEVICE_POLICY_SECURITY_LOGGING, null); CROSS_USER_PERMISSIONS.put(MANAGE_DEVICE_POLICY_STATUS_BAR, null); - // Organisation identity policy will involve data of other organisations on the device and - // therefore the FULL cross-user permission is required. - CROSS_USER_PERMISSIONS.put(MANAGE_DEVICE_POLICY_ORGANIZATION_IDENTITY, - MANAGE_DEVICE_POLICY_ACROSS_USERS_FULL); - CROSS_USER_PERMISSIONS.put(MANAGE_DEVICE_POLICY_WIFI, - MANAGE_DEVICE_POLICY_ACROSS_USERS); - CROSS_USER_PERMISSIONS.put(MANAGE_DEVICE_POLICY_WIPE_DATA, - MANAGE_DEVICE_POLICY_ACROSS_USERS); - CROSS_USER_PERMISSIONS.put(MANAGE_DEVICE_POLICY_SCREEN_CAPTURE, - MANAGE_DEVICE_POLICY_ACROSS_USERS); + CROSS_USER_PERMISSIONS.put(MANAGE_DEVICE_POLICY_SYSTEM_UPDATES, null); + CROSS_USER_PERMISSIONS.put(MANAGE_DEVICE_POLICY_USB_DATA_SIGNALLING, null); + CROSS_USER_PERMISSIONS.put(SET_TIME, null); + CROSS_USER_PERMISSIONS.put(SET_TIME_ZONE, null); + + // The permissions are all critical for securing data within the current user and + // therefore are protected with MANAGE_DEVICE_POLICY_ACROSS_USERS_SECURITY_CRITICAL for + // cross-user calls. + CROSS_USER_PERMISSIONS.put(MANAGE_DEVICE_POLICY_INSTALL_UNKNOWN_SOURCES, + MANAGE_DEVICE_POLICY_ACROSS_USERS_SECURITY_CRITICAL); + CROSS_USER_PERMISSIONS.put(MANAGE_DEVICE_POLICY_KEYGUARD, + MANAGE_DEVICE_POLICY_ACROSS_USERS_SECURITY_CRITICAL); CROSS_USER_PERMISSIONS.put(MANAGE_DEVICE_POLICY_LOCK_CREDENTIALS, MANAGE_DEVICE_POLICY_ACROSS_USERS_SECURITY_CRITICAL); - CROSS_USER_PERMISSIONS.put(MANAGE_DEVICE_POLICY_PACKAGE_STATE, - MANAGE_DEVICE_POLICY_ACROSS_USERS); - CROSS_USER_PERMISSIONS.put( - MANAGE_DEVICE_POLICY_LOCK, MANAGE_DEVICE_POLICY_ACROSS_USERS_FULL); - CROSS_USER_PERMISSIONS.put( - MANAGE_DEVICE_POLICY_KEYGUARD, MANAGE_DEVICE_POLICY_ACROSS_USERS_SECURITY_CRITICAL); - // Granting runtime permissions can grant applications significant powers therefore the FULL - // cross-user permission is required. - CROSS_USER_PERMISSIONS.put(MANAGE_DEVICE_POLICY_RUNTIME_PERMISSIONS, - MANAGE_DEVICE_POLICY_ACROSS_USERS_FULL); - CROSS_USER_PERMISSIONS.put(MANAGE_DEVICE_POLICY_SUPPORT_MESSAGE, - MANAGE_DEVICE_POLICY_ACROSS_USERS_FULL); - CROSS_USER_PERMISSIONS.put(MANAGE_DEVICE_POLICY_APPS_CONTROL, - MANAGE_DEVICE_POLICY_ACROSS_USERS_FULL); - CROSS_USER_PERMISSIONS.put(MANAGE_DEVICE_POLICY_LOCK_TASK, - MANAGE_DEVICE_POLICY_ACROSS_USERS_FULL); + + // These permissions are required for securing device ownership without accessing user data + // and therefore are protected with MANAGE_DEVICE_POLICY_ACROSS_USERS for cross-user calls. CROSS_USER_PERMISSIONS.put(MANAGE_DEVICE_POLICY_ACCOUNT_MANAGEMENT, MANAGE_DEVICE_POLICY_ACROSS_USERS); CROSS_USER_PERMISSIONS.put(MANAGE_DEVICE_POLICY_AIRPLANE_MODE, MANAGE_DEVICE_POLICY_ACROSS_USERS); - CROSS_USER_PERMISSIONS.put(MANAGE_DEVICE_POLICY_AUDIO_OUTPUT, - MANAGE_DEVICE_POLICY_ACROSS_USERS_FULL); - CROSS_USER_PERMISSIONS.put(MANAGE_DEVICE_POLICY_AUTOFILL, - MANAGE_DEVICE_POLICY_ACROSS_USERS_FULL); CROSS_USER_PERMISSIONS.put(MANAGE_DEVICE_POLICY_BLUETOOTH, MANAGE_DEVICE_POLICY_ACROSS_USERS); CROSS_USER_PERMISSIONS.put(MANAGE_DEVICE_POLICY_CALLS, MANAGE_DEVICE_POLICY_ACROSS_USERS); CROSS_USER_PERMISSIONS.put(MANAGE_DEVICE_POLICY_CAMERA, MANAGE_DEVICE_POLICY_ACROSS_USERS); + CROSS_USER_PERMISSIONS.put(MANAGE_DEVICE_POLICY_DEFAULT_SMS, + MANAGE_DEVICE_POLICY_ACROSS_USERS); + CROSS_USER_PERMISSIONS.put(MANAGE_DEVICE_POLICY_MICROPHONE, + MANAGE_DEVICE_POLICY_ACROSS_USERS); + CROSS_USER_PERMISSIONS.put(MANAGE_DEVICE_POLICY_MOBILE_NETWORK, + MANAGE_DEVICE_POLICY_ACROSS_USERS); + CROSS_USER_PERMISSIONS.put(MANAGE_DEVICE_POLICY_NEARBY_COMMUNICATION, + MANAGE_DEVICE_POLICY_ACROSS_USERS); + CROSS_USER_PERMISSIONS.put(MANAGE_DEVICE_POLICY_PHYSICAL_MEDIA, + MANAGE_DEVICE_POLICY_ACROSS_USERS); + CROSS_USER_PERMISSIONS.put(MANAGE_DEVICE_POLICY_PACKAGE_STATE, + MANAGE_DEVICE_POLICY_ACROSS_USERS); + CROSS_USER_PERMISSIONS.put(MANAGE_DEVICE_POLICY_RESTRICT_PRIVATE_DNS, + MANAGE_DEVICE_POLICY_ACROSS_USERS); + CROSS_USER_PERMISSIONS.put(MANAGE_DEVICE_POLICY_SCREEN_CAPTURE, + MANAGE_DEVICE_POLICY_ACROSS_USERS); + CROSS_USER_PERMISSIONS.put(MANAGE_DEVICE_POLICY_SMS, + MANAGE_DEVICE_POLICY_ACROSS_USERS); + CROSS_USER_PERMISSIONS.put(MANAGE_DEVICE_POLICY_SAFE_BOOT, + MANAGE_DEVICE_POLICY_ACROSS_USERS); + CROSS_USER_PERMISSIONS.put(MANAGE_DEVICE_POLICY_TIME, + MANAGE_DEVICE_POLICY_ACROSS_USERS); + CROSS_USER_PERMISSIONS.put(MANAGE_DEVICE_POLICY_USB_FILE_TRANSFER, + MANAGE_DEVICE_POLICY_ACROSS_USERS); + CROSS_USER_PERMISSIONS.put(MANAGE_DEVICE_POLICY_WIFI, + MANAGE_DEVICE_POLICY_ACROSS_USERS); + CROSS_USER_PERMISSIONS.put(MANAGE_DEVICE_POLICY_WIPE_DATA, + MANAGE_DEVICE_POLICY_ACROSS_USERS); + + // These permissions may grant access to user data and therefore must be protected with + // MANAGE_DEVICE_POLICY_ACROSS_USERS_FULL for cross-user calls. + CROSS_USER_PERMISSIONS.put(MANAGE_DEVICE_POLICY_APPS_CONTROL, + MANAGE_DEVICE_POLICY_ACROSS_USERS_FULL); + CROSS_USER_PERMISSIONS.put(MANAGE_DEVICE_POLICY_APP_RESTRICTIONS, + MANAGE_DEVICE_POLICY_ACROSS_USERS_FULL); + CROSS_USER_PERMISSIONS.put(MANAGE_DEVICE_POLICY_AUDIO_OUTPUT, + MANAGE_DEVICE_POLICY_ACROSS_USERS_FULL); + CROSS_USER_PERMISSIONS.put(MANAGE_DEVICE_POLICY_AUTOFILL, + MANAGE_DEVICE_POLICY_ACROSS_USERS_FULL); + CROSS_USER_PERMISSIONS.put(MANAGE_DEVICE_POLICY_COMMON_CRITERIA_MODE, + MANAGE_DEVICE_POLICY_ACROSS_USERS_FULL); CROSS_USER_PERMISSIONS.put(MANAGE_DEVICE_POLICY_DEBUGGING_FEATURES, MANAGE_DEVICE_POLICY_ACROSS_USERS_FULL); CROSS_USER_PERMISSIONS.put(MANAGE_DEVICE_POLICY_DISPLAY, MANAGE_DEVICE_POLICY_ACROSS_USERS_FULL); - CROSS_USER_PERMISSIONS.put(MANAGE_DEVICE_POLICY_FACTORY_RESET, - MANAGE_DEVICE_POLICY_ACROSS_USERS_FULL); CROSS_USER_PERMISSIONS.put(MANAGE_DEVICE_POLICY_FUN, MANAGE_DEVICE_POLICY_ACROSS_USERS_FULL); - CROSS_USER_PERMISSIONS.put(MANAGE_DEVICE_POLICY_INSTALL_UNKNOWN_SOURCES, - MANAGE_DEVICE_POLICY_ACROSS_USERS_SECURITY_CRITICAL); CROSS_USER_PERMISSIONS.put(MANAGE_DEVICE_POLICY_LOCALE, MANAGE_DEVICE_POLICY_ACROSS_USERS_FULL); CROSS_USER_PERMISSIONS.put(MANAGE_DEVICE_POLICY_LOCATION, MANAGE_DEVICE_POLICY_ACROSS_USERS_FULL); - CROSS_USER_PERMISSIONS.put(MANAGE_DEVICE_POLICY_MICROPHONE, - MANAGE_DEVICE_POLICY_ACROSS_USERS); - CROSS_USER_PERMISSIONS.put(MANAGE_DEVICE_POLICY_MOBILE_NETWORK, - MANAGE_DEVICE_POLICY_ACROSS_USERS); - CROSS_USER_PERMISSIONS.put(MANAGE_DEVICE_POLICY_NEARBY_COMMUNICATION, - MANAGE_DEVICE_POLICY_ACROSS_USERS); - CROSS_USER_PERMISSIONS.put(MANAGE_DEVICE_POLICY_PHYSICAL_MEDIA, - MANAGE_DEVICE_POLICY_ACROSS_USERS); - CROSS_USER_PERMISSIONS.put(MANAGE_DEVICE_POLICY_PRINTING, + CROSS_USER_PERMISSIONS.put(MANAGE_DEVICE_POLICY_LOCK, + MANAGE_DEVICE_POLICY_ACROSS_USERS_FULL); + CROSS_USER_PERMISSIONS.put(MANAGE_DEVICE_POLICY_LOCK_TASK, + MANAGE_DEVICE_POLICY_ACROSS_USERS_FULL); + CROSS_USER_PERMISSIONS.put(MANAGE_DEVICE_POLICY_ORGANIZATION_IDENTITY, MANAGE_DEVICE_POLICY_ACROSS_USERS_FULL); - CROSS_USER_PERMISSIONS.put(MANAGE_DEVICE_POLICY_RESTRICT_PRIVATE_DNS, - MANAGE_DEVICE_POLICY_ACROSS_USERS); CROSS_USER_PERMISSIONS.put(MANAGE_DEVICE_POLICY_PROFILES, MANAGE_DEVICE_POLICY_ACROSS_USERS_FULL); CROSS_USER_PERMISSIONS.put(MANAGE_DEVICE_POLICY_PROFILE_INTERACTION, MANAGE_DEVICE_POLICY_ACROSS_USERS_FULL); - CROSS_USER_PERMISSIONS.put(MANAGE_DEVICE_POLICY_SAFE_BOOT, - MANAGE_DEVICE_POLICY_ACROSS_USERS); + CROSS_USER_PERMISSIONS.put(MANAGE_DEVICE_POLICY_PRINTING, + MANAGE_DEVICE_POLICY_ACROSS_USERS_FULL); + CROSS_USER_PERMISSIONS.put(MANAGE_DEVICE_POLICY_RESET_PASSWORD, + MANAGE_DEVICE_POLICY_ACROSS_USERS_FULL); + CROSS_USER_PERMISSIONS.put(MANAGE_DEVICE_POLICY_RUNTIME_PERMISSIONS, + MANAGE_DEVICE_POLICY_ACROSS_USERS_FULL); CROSS_USER_PERMISSIONS.put(MANAGE_DEVICE_POLICY_SCREEN_CONTENT, MANAGE_DEVICE_POLICY_ACROSS_USERS_FULL); - CROSS_USER_PERMISSIONS.put(MANAGE_DEVICE_POLICY_SMS, - MANAGE_DEVICE_POLICY_ACROSS_USERS); + CROSS_USER_PERMISSIONS.put(MANAGE_DEVICE_POLICY_SUPPORT_MESSAGE, + MANAGE_DEVICE_POLICY_ACROSS_USERS_FULL); CROSS_USER_PERMISSIONS.put(MANAGE_DEVICE_POLICY_SYSTEM_DIALOGS, MANAGE_DEVICE_POLICY_ACROSS_USERS_FULL); - CROSS_USER_PERMISSIONS.put(MANAGE_DEVICE_POLICY_TIME, - MANAGE_DEVICE_POLICY_ACROSS_USERS); - CROSS_USER_PERMISSIONS.put(MANAGE_DEVICE_POLICY_USB_FILE_TRANSFER, - MANAGE_DEVICE_POLICY_ACROSS_USERS); CROSS_USER_PERMISSIONS.put(MANAGE_DEVICE_POLICY_USERS, MANAGE_DEVICE_POLICY_ACROSS_USERS_FULL); CROSS_USER_PERMISSIONS.put(MANAGE_DEVICE_POLICY_VPN, MANAGE_DEVICE_POLICY_ACROSS_USERS_FULL); CROSS_USER_PERMISSIONS.put(MANAGE_DEVICE_POLICY_WALLPAPER, MANAGE_DEVICE_POLICY_ACROSS_USERS_FULL); - CROSS_USER_PERMISSIONS.put(MANAGE_DEVICE_POLICY_WIFI, - MANAGE_DEVICE_POLICY_ACROSS_USERS); CROSS_USER_PERMISSIONS.put(MANAGE_DEVICE_POLICY_WINDOWS, MANAGE_DEVICE_POLICY_ACROSS_USERS_FULL); - CROSS_USER_PERMISSIONS.put(MANAGE_DEVICE_POLICY_APP_RESTRICTIONS, - MANAGE_DEVICE_POLICY_ACROSS_USERS_FULL); - CROSS_USER_PERMISSIONS.put(MANAGE_DEVICE_POLICY_COMMON_CRITERIA_MODE, - MANAGE_DEVICE_POLICY_ACROSS_USERS_FULL); - CROSS_USER_PERMISSIONS.put(MANAGE_DEVICE_POLICY_DEFAULT_SMS, - MANAGE_DEVICE_POLICY_ACROSS_USERS); - CROSS_USER_PERMISSIONS.put(MANAGE_DEVICE_POLICY_PACKAGE_STATE, - MANAGE_DEVICE_POLICY_ACROSS_USERS); - CROSS_USER_PERMISSIONS.put(MANAGE_DEVICE_POLICY_RESET_PASSWORD, - MANAGE_DEVICE_POLICY_ACROSS_USERS_FULL); } /** @@ -22727,6 +22785,26 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub { } /** + * Checks if the calling process has been granted permission to apply a device policy on a + * specific user. + * The given permission will be checked along with its associated cross-user permission if it + * exists and the target user is different to the calling user. + * Returns an {@link EnforcingAdmin} for the caller. + * + * @param admin the component name of the admin. + * @param callerPackageName The package name of the calling application. + * @param permission The name of the permission being checked. + * @param deviceAdminPolicy The userId of the user which the caller needs permission to act on. + * @throws SecurityException if the caller has not been granted the given permission, + * the associated cross-user permission if the caller's user is different to the target user. + */ + private EnforcingAdmin enforcePermissionAndGetEnforcingAdmin(@Nullable ComponentName admin, + String permission, int deviceAdminPolicy, String callerPackageName, int targetUserId) { + enforcePermission(permission, deviceAdminPolicy, callerPackageName, targetUserId); + return getEnforcingAdminForCaller(admin, callerPackageName); + } + + /** * Checks whether the calling process has been granted permission to query a device policy on * a specific user. * The given permission will be checked along with its associated cross-user permission if it @@ -22748,6 +22826,9 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub { POLICY_IDENTIFIER_TO_PERMISSION.put(AUTO_TIMEZONE_POLICY, SET_TIME_ZONE); } + private static final HashMap<String, Integer> POLICY_IDENTIFIER_TO_ACTIVE_ADMIN_POLICY = + new HashMap<>(); + /** * Checks if the calling process has been granted permission to apply a device policy on a * specific user. @@ -22763,6 +22844,36 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub { private void enforcePermission(String permission, String callerPackageName, int targetUserId) throws SecurityException { if (!hasPermission(permission, callerPackageName, targetUserId)) { + // TODO(b/276920002): Split the error messages so that the cross-user permission + // is only mentioned when it is needed. + throw new SecurityException("Caller does not have the required permissions for " + + "this user. Permissions required: {" + + permission + + ", " + + CROSS_USER_PERMISSIONS.get(permission) + + "(if calling cross-user)" + + "}"); + } + } + + /** + * Checks if the calling process has been granted permission to apply a device policy on a + * specific user. + * The given permission will be checked along with its associated cross-user permission if it + * exists and the target user is different to the calling user. + * + * @param callerPackageName The package name of the calling application. + * @param permission The name of the permission being checked. + * @param targetUserId The userId of the user which the caller needs permission to act on. + * @throws SecurityException if the caller has not been granted the given permission, + * the associated cross-user permission if the caller's user is different to the target user. + */ + private void enforcePermission(String permission, int adminPolicy, + String callerPackageName, int targetUserId) + throws SecurityException { + if (!hasPermissionOrAdminPolicy(permission, callerPackageName, adminPolicy, targetUserId)) { + // TODO(b/276920002): Split the error messages so that the cross-user permission + // is only mentioned when it is needed. throw new SecurityException("Caller does not have the required permissions for " + "this user. Permissions required: {" + permission @@ -22804,16 +22915,26 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub { */ private boolean hasPermission(String permission, String callerPackageName, int targetUserId) { CallerIdentity caller = getCallerIdentity(callerPackageName); - boolean hasPermissionOnOwnUser = hasPermission(permission, callerPackageName); + boolean hasPermissionOnOwnUser = hasPermission(permission, caller.getPackageName()); boolean hasPermissionOnTargetUser = true; if (hasPermissionOnOwnUser & caller.getUserId() != targetUserId) { hasPermissionOnTargetUser = hasPermission(CROSS_USER_PERMISSIONS.get(permission), - callerPackageName); + caller.getPackageName()); } return hasPermissionOnOwnUser && hasPermissionOnTargetUser; } + private boolean hasPermissionOrAdminPolicy(String permission, String callerPackageName, + int adminPolicy, int targetUserId) { + CallerIdentity caller = getCallerIdentity(callerPackageName); + if (hasPermission(permission, caller.getPackageName(), targetUserId)) { + return true; + } + ActiveAdmin deviceAdmin = getActiveAdminForCaller(null, caller); + return deviceAdmin != null && deviceAdmin.info.usesPolicy(adminPolicy); + } + /** * Return whether the calling process has been granted the given permission. * @@ -22856,23 +22977,6 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub { if (DELEGATE_SCOPES.containsKey(permission)) { return isCallerDelegate(caller, DELEGATE_SCOPES.get(permission)); } - // Check if the caller is an active admin that uses a certain policy. - if (ACTIVE_ADMIN_POLICIES.containsKey(permission)) { - try { - if (ACTIVE_ADMIN_POLICIES.get(permission) != null) { - return getActiveAdminForCallerLocked( - null, ACTIVE_ADMIN_POLICIES.get(permission), false) != null; - } else { - // If the permission maps to no policy (null) this means that any active admin - // has permission. - return isCallerActiveAdminOrDelegate(caller, null); - } - } catch (SecurityException e) { - // A security exception means there is not an active admin with permission and - // therefore - return false; - } - } return false; } @@ -22925,9 +23029,7 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub { if (admin != null) { return EnforcingAdmin.createDeviceAdminEnforcingAdmin(who, userId, admin); } - if (admin == null) { - admin = getUserData(userId).createOrGetPermissionBasedAdmin(userId); - } + admin = getUserData(userId).createOrGetPermissionBasedAdmin(userId); return EnforcingAdmin.createEnforcingAdmin(caller.getPackageName(), userId, admin); } @@ -23476,15 +23578,13 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub { // We need to add a mapping of policyId to permission in POLICY_IDENTIFIER_TO_PERMISSION // for each migrated permission. private List<ActiveAdmin> getNonDPCActiveAdminsForPolicyLocked(String policyIdentifier) { - String permission = POLICY_IDENTIFIER_TO_PERMISSION.get(policyIdentifier); - if (permission == null) { - Slogf.e(LOG_TAG, "Can't find a permission for %s in POLICY_IDENTIFIER_TO_PERMISSION", + Integer activeAdminPolicy = POLICY_IDENTIFIER_TO_ACTIVE_ADMIN_POLICY.get(policyIdentifier); + if (activeAdminPolicy == null) { + Slogf.e(LOG_TAG, + "Can't find a active admin policy for %s in POLICY_IDENTIFIER_TO_PERMISSION", policyIdentifier); return new ArrayList<>(); } - if (!ACTIVE_ADMIN_POLICIES.containsKey(permission)) { - return new ArrayList<>(); - } List<ActiveAdmin> admins = new ArrayList<>(); for (UserInfo userInfo : mUserManager.getUsers()) { @@ -23495,7 +23595,7 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub { } DevicePolicyData policy = getUserDataUnchecked(userInfo.id); if (isActiveAdminWithPolicyForUserLocked( - policy.mAdminMap.get(admin), ACTIVE_ADMIN_POLICIES.get(permission), + policy.mAdminMap.get(admin), activeAdminPolicy, userInfo.id)) { admins.add(policy.mAdminMap.get(admin)); } diff --git a/services/tests/servicestests/src/com/android/server/display/AutomaticBrightnessControllerTest.java b/services/tests/servicestests/src/com/android/server/display/AutomaticBrightnessControllerTest.java index fc251526476e..3bef413a3e72 100644 --- a/services/tests/servicestests/src/com/android/server/display/AutomaticBrightnessControllerTest.java +++ b/services/tests/servicestests/src/com/android/server/display/AutomaticBrightnessControllerTest.java @@ -126,7 +126,7 @@ public class AutomaticBrightnessControllerTest { return mClock::now; } - }, // pass in test looper instead, pass in offsetable clock + }, // pass in test looper instead, pass in offsettable clock () -> { }, mTestLooper.getLooper(), mSensorManager, lightSensor, mBrightnessMappingStrategy, LIGHT_SENSOR_WARMUP_TIME, BRIGHTNESS_MIN_FLOAT, BRIGHTNESS_MAX_FLOAT, DOZE_SCALE_FACTOR, LIGHT_SENSOR_RATE, @@ -300,6 +300,179 @@ public class AutomaticBrightnessControllerTest { } @Test + public void testShortTermModelTimesOut() throws Exception { + ArgumentCaptor<SensorEventListener> listenerCaptor = + ArgumentCaptor.forClass(SensorEventListener.class); + verify(mSensorManager).registerListener(listenerCaptor.capture(), eq(mLightSensor), + eq(INITIAL_LIGHT_SENSOR_RATE * 1000), any(Handler.class)); + SensorEventListener listener = listenerCaptor.getValue(); + + // Sensor reads 123 lux, + listener.onSensorChanged(TestUtils.createSensorEvent(mLightSensor, 123)); + // User sets brightness to 100 + mController.configure(AUTO_BRIGHTNESS_ENABLED, /* configuration= */ null, + /* brightness= */ 0.5f, /* userChangedBrightness= */ true, /* adjustment= */ 0, + /* userChanged= */ false, DisplayPowerRequest.POLICY_BRIGHT, + /* shouldResetShortTermModel= */ true); + + when(mBrightnessMappingStrategy.getShortTermModelTimeout()).thenReturn(2000L); + + mController.switchToIdleMode(); + when(mIdleBrightnessMappingStrategy.isForIdleMode()).thenReturn(true); + when(mBrightnessMappingStrategy.shouldResetShortTermModel( + 123f, 0.5f)).thenReturn(true); + + // Sensor reads 1000 lux, + listener.onSensorChanged(TestUtils.createSensorEvent(mLightSensor, 1000)); + mTestLooper.moveTimeForward( + mBrightnessMappingStrategy.getShortTermModelTimeout() + 1000); + mTestLooper.dispatchAll(); + + mController.switchToInteractiveScreenBrightnessMode(); + mTestLooper.moveTimeForward(4000); + mTestLooper.dispatchAll(); + + // Verify only happens on the first configure. (i.e. not again when switching back) + // Intentionally using any() to ensure it's not called whatsoever. + verify(mBrightnessMappingStrategy, times(1)) + .addUserDataPoint(123.0f, 0.5f); + verify(mBrightnessMappingStrategy, times(1)) + .addUserDataPoint(anyFloat(), anyFloat()); + } + + @Test + public void testShortTermModelDoesntTimeOut() throws Exception { + ArgumentCaptor<SensorEventListener> listenerCaptor = + ArgumentCaptor.forClass(SensorEventListener.class); + verify(mSensorManager).registerListener(listenerCaptor.capture(), eq(mLightSensor), + eq(INITIAL_LIGHT_SENSOR_RATE * 1000), any(Handler.class)); + SensorEventListener listener = listenerCaptor.getValue(); + + // Sensor reads 123 lux, + listener.onSensorChanged(TestUtils.createSensorEvent(mLightSensor, 123)); + // User sets brightness to 100 + mController.configure(AUTO_BRIGHTNESS_ENABLED, null /* configuration= */, + 0.51f /* brightness= */, true /* userChangedBrightness= */, 0 /* adjustment= */, + false /* userChanged= */, DisplayPowerRequest.POLICY_BRIGHT, + /* shouldResetShortTermModel= */ true); + + when(mBrightnessMappingStrategy.shouldResetShortTermModel( + anyFloat(), anyFloat())).thenReturn(true); + when(mBrightnessMappingStrategy.getShortTermModelTimeout()).thenReturn(2000L); + when(mBrightnessMappingStrategy.getUserBrightness()).thenReturn(0.51f); + when(mBrightnessMappingStrategy.getUserLux()).thenReturn(123.0f); + + mController.switchToIdleMode(); + when(mIdleBrightnessMappingStrategy.isForIdleMode()).thenReturn(true); + + // Time does not move forward, since clock is doesn't increment naturally. + mTestLooper.dispatchAll(); + + // Sensor reads 100000 lux, + listener.onSensorChanged(TestUtils.createSensorEvent(mLightSensor, 678910)); + mController.switchToInteractiveScreenBrightnessMode(); + + // Verify short term model is not reset. + verify(mBrightnessMappingStrategy, never()).clearUserDataPoints(); + + // Verify that we add the data point once when the user sets it, and again when we return + // interactive mode. + verify(mBrightnessMappingStrategy, times(2)) + .addUserDataPoint(123.0f, 0.51f); + } + + @Test + public void testShortTermModelIsRestoredWhenSwitchingWithinTimeout() throws Exception { + ArgumentCaptor<SensorEventListener> listenerCaptor = + ArgumentCaptor.forClass(SensorEventListener.class); + verify(mSensorManager).registerListener(listenerCaptor.capture(), eq(mLightSensor), + eq(INITIAL_LIGHT_SENSOR_RATE * 1000), any(Handler.class)); + SensorEventListener listener = listenerCaptor.getValue(); + + // Sensor reads 123 lux, + listener.onSensorChanged(TestUtils.createSensorEvent(mLightSensor, 123)); + // User sets brightness to 100 + mController.configure(AUTO_BRIGHTNESS_ENABLED, /* configuration= */ null, + /* brightness= */ 0.5f, /* userChangedBrightness= */ true, /* adjustment= */ 0, + /* userChanged= */ false, DisplayPowerRequest.POLICY_BRIGHT, + /* shouldResetShortTermModel= */ true); + + when(mBrightnessMappingStrategy.getShortTermModelTimeout()).thenReturn(2000L); + when(mBrightnessMappingStrategy.getUserBrightness()).thenReturn(0.5f); + when(mBrightnessMappingStrategy.getUserLux()).thenReturn(123f); + + mController.switchToIdleMode(); + when(mIdleBrightnessMappingStrategy.isForIdleMode()).thenReturn(true); + when(mIdleBrightnessMappingStrategy.getUserBrightness()).thenReturn(-1f); + when(mIdleBrightnessMappingStrategy.getUserLux()).thenReturn(-1f); + when(mBrightnessMappingStrategy.shouldResetShortTermModel( + 123f, 0.5f)).thenReturn(true); + + // Sensor reads 1000 lux, + listener.onSensorChanged(TestUtils.createSensorEvent(mLightSensor, 1000)); + mTestLooper.moveTimeForward( + mBrightnessMappingStrategy.getShortTermModelTimeout() + 1000); + mTestLooper.dispatchAll(); + + mController.switchToInteractiveScreenBrightnessMode(); + mTestLooper.moveTimeForward(4000); + mTestLooper.dispatchAll(); + + // Verify only happens on the first configure. (i.e. not again when switching back) + // Intentionally using any() to ensure it's not called whatsoever. + verify(mBrightnessMappingStrategy, times(1)) + .addUserDataPoint(123.0f, 0.5f); + verify(mBrightnessMappingStrategy, times(1)) + .addUserDataPoint(anyFloat(), anyFloat()); + } + + @Test + public void testShortTermModelNotRestoredAfterTimeout() throws Exception { + ArgumentCaptor<SensorEventListener> listenerCaptor = + ArgumentCaptor.forClass(SensorEventListener.class); + verify(mSensorManager).registerListener(listenerCaptor.capture(), eq(mLightSensor), + eq(INITIAL_LIGHT_SENSOR_RATE * 1000), any(Handler.class)); + SensorEventListener listener = listenerCaptor.getValue(); + + // Sensor reads 123 lux, + listener.onSensorChanged(TestUtils.createSensorEvent(mLightSensor, 123)); + // User sets brightness to 100 + mController.configure(AUTO_BRIGHTNESS_ENABLED, /* configuration= */ null, + /* brightness= */ 0.5f, /* userChangedBrightness= */ true, /* adjustment= */ 0, + /* userChanged= */ false, DisplayPowerRequest.POLICY_BRIGHT, + /* shouldResetShortTermModel= */ true); + + when(mBrightnessMappingStrategy.getShortTermModelTimeout()).thenReturn(2000L); + + when(mBrightnessMappingStrategy.getUserBrightness()).thenReturn(0.5f); + when(mBrightnessMappingStrategy.getUserLux()).thenReturn(123f); + + mController.switchToIdleMode(); + when(mIdleBrightnessMappingStrategy.isForIdleMode()).thenReturn(true); + when(mIdleBrightnessMappingStrategy.getUserBrightness()).thenReturn(-1f); + when(mIdleBrightnessMappingStrategy.getUserLux()).thenReturn(-1f); + + when(mBrightnessMappingStrategy.shouldResetShortTermModel( + 123f, 0.5f)).thenReturn(true); + + // Sensor reads 1000 lux, + listener.onSensorChanged(TestUtils.createSensorEvent(mLightSensor, 1000)); + // Do not fast-forward time. + mTestLooper.dispatchAll(); + + mController.switchToInteractiveScreenBrightnessMode(); + // Do not fast-forward time + mTestLooper.dispatchAll(); + + // Verify this happens on the first configure and again when switching back + // Intentionally using any() to ensure it's not called any other times whatsoever. + verify(mBrightnessMappingStrategy, times(2)) + .addUserDataPoint(123.0f, 0.5f); + verify(mBrightnessMappingStrategy, times(2)) + .addUserDataPoint(anyFloat(), anyFloat()); + } + + @Test public void testSwitchToIdleMappingStrategy() throws Exception { ArgumentCaptor<SensorEventListener> listenerCaptor = ArgumentCaptor.forClass(SensorEventListener.class); @@ -326,6 +499,11 @@ public class AutomaticBrightnessControllerTest { // Called once for init, and once when switching, // setAmbientLux() is called twice and once in updateAutoBrightness() verify(mBrightnessMappingStrategy, times(5)).isForIdleMode(); + // Called when switching. + verify(mBrightnessMappingStrategy, times(1)).getShortTermModelTimeout(); + verify(mBrightnessMappingStrategy, times(1)).getUserBrightness(); + verify(mBrightnessMappingStrategy, times(1)).getUserLux(); + // Ensure, after switching, original BMS is not used anymore verifyNoMoreInteractions(mBrightnessMappingStrategy); diff --git a/services/tests/servicestests/src/com/android/server/display/BrightnessThrottlerTest.java b/services/tests/servicestests/src/com/android/server/display/BrightnessThrottlerTest.java index ffe2fec380a8..ce4b438470df 100644 --- a/services/tests/servicestests/src/com/android/server/display/BrightnessThrottlerTest.java +++ b/services/tests/servicestests/src/com/android/server/display/BrightnessThrottlerTest.java @@ -54,6 +54,7 @@ import org.mockito.Mock; import org.mockito.MockitoAnnotations; import java.util.ArrayList; +import java.util.HashMap; import java.util.List; @SmallTest @@ -122,8 +123,6 @@ public class BrightnessThrottlerTest { BrightnessThrottlingData data; data = BrightnessThrottlingData.create((List<ThrottlingLevel>)null); assertEquals(data, null); - data = BrightnessThrottlingData.create((BrightnessThrottlingData)null); - assertEquals(data, null); data = BrightnessThrottlingData.create(new ArrayList<ThrottlingLevel>()); assertEquals(data, null); data = BrightnessThrottlingData.create(unsortedThermalLevels); @@ -146,7 +145,7 @@ public class BrightnessThrottlerTest { } @Test - public void testThrottlingUnsupported() throws Exception { + public void testThrottlingUnsupported() { final BrightnessThrottler throttler = createThrottlerUnsupported(); assertFalse(throttler.deviceSupportsThrottling()); @@ -307,37 +306,18 @@ public class BrightnessThrottlerTest { verify(mThermalServiceMock).registerThermalEventListenerWithType( mThermalEventListenerCaptor.capture(), eq(Temperature.TYPE_SKIN)); final IThermalEventListener listener = mThermalEventListenerCaptor.getValue(); + testThrottling(throttler, listener, PowerManager.BRIGHTNESS_MAX, 0.4f); - // Set status too low to trigger throttling - listener.notifyThrottling(getSkinTemp(level.thermalStatus - 1)); - mTestLooper.dispatchAll(); - assertEquals(PowerManager.BRIGHTNESS_MAX, throttler.getBrightnessCap(), 0f); - assertFalse(throttler.isThrottled()); - - // Set status high enough to trigger throttling - listener.notifyThrottling(getSkinTemp(level.thermalStatus)); - mTestLooper.dispatchAll(); - assertEquals(0.4f, throttler.getBrightnessCap(), 0f); - assertTrue(throttler.isThrottled()); - - // Update thresholds - // This data is equivalent to the string "123,1,critical,0.8", passed below - final ThrottlingLevel newLevel = new ThrottlingLevel(PowerManager.THERMAL_STATUS_CRITICAL, - 0.8f); // Set new (valid) data from device config mDeviceConfigFake.setBrightnessThrottlingData("123,1,critical,0.8"); + testThrottling(throttler, listener, PowerManager.BRIGHTNESS_MAX, 0.8f); - // Set status too low to trigger throttling - listener.notifyThrottling(getSkinTemp(newLevel.thermalStatus - 1)); - mTestLooper.dispatchAll(); - assertEquals(PowerManager.BRIGHTNESS_MAX, throttler.getBrightnessCap(), 0f); - assertFalse(throttler.isThrottled()); - - // Set status high enough to trigger throttling - listener.notifyThrottling(getSkinTemp(newLevel.thermalStatus)); - mTestLooper.dispatchAll(); - assertEquals(newLevel.brightness, throttler.getBrightnessCap(), 0f); - assertTrue(throttler.isThrottled()); + mDeviceConfigFake.setBrightnessThrottlingData( + "123,1,critical,0.75;123,1,critical,0.99,id_2"); + testThrottling(throttler, listener, PowerManager.BRIGHTNESS_MAX, 0.75f); + mDeviceConfigFake.setBrightnessThrottlingData( + "123,1,critical,0.8,default;123,1,critical,0.99,id_2"); + testThrottling(throttler, listener, PowerManager.BRIGHTNESS_MAX, 0.8f); } @Test public void testInvalidThrottlingStrings() throws Exception { @@ -370,6 +350,18 @@ public class BrightnessThrottlerTest { testThrottling(throttler, listener, PowerManager.BRIGHTNESS_MAX, 0.25f); mDeviceConfigFake.setBrightnessThrottlingData(""); // Invalid format testThrottling(throttler, listener, PowerManager.BRIGHTNESS_MAX, 0.25f); + // Invalid string format + mDeviceConfigFake.setBrightnessThrottlingData( + "123,default,1,critical,0.75,1,critical,0.99"); + testThrottling(throttler, listener, PowerManager.BRIGHTNESS_MAX, 0.25f); + // Invalid level string and number string + mDeviceConfigFake.setBrightnessThrottlingData( + "123,1,1,critical,0.75,id_2,1,critical,0.99"); + testThrottling(throttler, listener, PowerManager.BRIGHTNESS_MAX, 0.25f); + // Invalid format - (two default ids for same display) + mDeviceConfigFake.setBrightnessThrottlingData( + "123,1,critical,0.75,default;123,1,critical,0.99"); + testThrottling(throttler, listener, PowerManager.BRIGHTNESS_MAX, 0.25f); } private void testThrottling(BrightnessThrottler throttler, IThermalEventListener listener, @@ -472,13 +464,17 @@ public class BrightnessThrottlerTest { } private BrightnessThrottler createThrottlerUnsupported() { - return new BrightnessThrottler(mInjectorMock, mHandler, mHandler, null, () -> {}, null); + return new BrightnessThrottler(mInjectorMock, mHandler, mHandler, + /* throttlingChangeCallback= */ () -> {}, /* uniqueDisplayId= */ null, + /* throttlingDataId= */ null, /* throttlingDataMap= */ new HashMap<>(1)); } private BrightnessThrottler createThrottlerSupported(BrightnessThrottlingData data) { assertNotNull(data); + HashMap<String, BrightnessThrottlingData> throttlingDataMap = new HashMap<>(1); + throttlingDataMap.put("default", data); return new BrightnessThrottler(mInjectorMock, mHandler, BackgroundThread.getHandler(), - data, () -> {}, "123"); + () -> {}, "123", "default", throttlingDataMap); } private Temperature getSkinTemp(@ThrottlingStatus int status) { diff --git a/services/tests/servicestests/src/com/android/server/display/DisplayDeviceConfigTest.java b/services/tests/servicestests/src/com/android/server/display/DisplayDeviceConfigTest.java index 9fd647bb0b90..ed0755949869 100644 --- a/services/tests/servicestests/src/com/android/server/display/DisplayDeviceConfigTest.java +++ b/services/tests/servicestests/src/com/android/server/display/DisplayDeviceConfigTest.java @@ -50,6 +50,7 @@ import java.nio.charset.StandardCharsets; import java.nio.file.Files; import java.nio.file.Path; import java.util.ArrayList; +import java.util.HashMap; import java.util.List; @SmallTest @@ -186,50 +187,72 @@ public final class DisplayDeviceConfigTest { assertArrayEquals(new int[]{-1, 10, 20, 30, 40}, mDisplayDeviceConfig.getScreenOffBrightnessSensorValueToLux()); - List<DisplayDeviceConfig.BrightnessThrottlingData.ThrottlingLevel> throttlingLevels = - new ArrayList(); - throttlingLevels.add(new DisplayDeviceConfig.BrightnessThrottlingData.ThrottlingLevel( + List<DisplayDeviceConfig.BrightnessThrottlingData.ThrottlingLevel> + defaultThrottlingLevels = new ArrayList<>(); + defaultThrottlingLevels.add( + new DisplayDeviceConfig.BrightnessThrottlingData.ThrottlingLevel( DisplayDeviceConfig.convertThermalStatus(ThermalStatus.light), 0.4f )); - throttlingLevels.add(new DisplayDeviceConfig.BrightnessThrottlingData.ThrottlingLevel( + defaultThrottlingLevels.add( + new DisplayDeviceConfig.BrightnessThrottlingData.ThrottlingLevel( DisplayDeviceConfig.convertThermalStatus(ThermalStatus.moderate), 0.3f )); - throttlingLevels.add(new DisplayDeviceConfig.BrightnessThrottlingData.ThrottlingLevel( + defaultThrottlingLevels.add( + new DisplayDeviceConfig.BrightnessThrottlingData.ThrottlingLevel( DisplayDeviceConfig.convertThermalStatus(ThermalStatus.severe), 0.2f )); - throttlingLevels.add(new DisplayDeviceConfig.BrightnessThrottlingData.ThrottlingLevel( + defaultThrottlingLevels.add( + new DisplayDeviceConfig.BrightnessThrottlingData.ThrottlingLevel( DisplayDeviceConfig.convertThermalStatus(ThermalStatus.critical), 0.1f )); - throttlingLevels.add(new DisplayDeviceConfig.BrightnessThrottlingData.ThrottlingLevel( + defaultThrottlingLevels.add( + new DisplayDeviceConfig.BrightnessThrottlingData.ThrottlingLevel( DisplayDeviceConfig.convertThermalStatus(ThermalStatus.emergency), 0.05f )); - throttlingLevels.add(new DisplayDeviceConfig.BrightnessThrottlingData.ThrottlingLevel( + defaultThrottlingLevels.add( + new DisplayDeviceConfig.BrightnessThrottlingData.ThrottlingLevel( DisplayDeviceConfig.convertThermalStatus(ThermalStatus.shutdown), 0.025f )); - assertEquals(new DisplayDeviceConfig.BrightnessThrottlingData(throttlingLevels), - mDisplayDeviceConfig.getBrightnessThrottlingData("default")); - throttlingLevels.clear(); - throttlingLevels.add(new DisplayDeviceConfig.BrightnessThrottlingData.ThrottlingLevel( + DisplayDeviceConfig.BrightnessThrottlingData defaultThrottlingData = + new DisplayDeviceConfig.BrightnessThrottlingData(defaultThrottlingLevels); + + List<DisplayDeviceConfig.BrightnessThrottlingData.ThrottlingLevel> + concurrentThrottlingLevels = new ArrayList<>(); + concurrentThrottlingLevels.add( + new DisplayDeviceConfig.BrightnessThrottlingData.ThrottlingLevel( DisplayDeviceConfig.convertThermalStatus(ThermalStatus.light), 0.2f )); - throttlingLevels.add(new DisplayDeviceConfig.BrightnessThrottlingData.ThrottlingLevel( + concurrentThrottlingLevels.add( + new DisplayDeviceConfig.BrightnessThrottlingData.ThrottlingLevel( DisplayDeviceConfig.convertThermalStatus(ThermalStatus.moderate), 0.15f )); - throttlingLevels.add(new DisplayDeviceConfig.BrightnessThrottlingData.ThrottlingLevel( + concurrentThrottlingLevels.add( + new DisplayDeviceConfig.BrightnessThrottlingData.ThrottlingLevel( DisplayDeviceConfig.convertThermalStatus(ThermalStatus.severe), 0.1f )); - throttlingLevels.add(new DisplayDeviceConfig.BrightnessThrottlingData.ThrottlingLevel( + concurrentThrottlingLevels.add( + new DisplayDeviceConfig.BrightnessThrottlingData.ThrottlingLevel( DisplayDeviceConfig.convertThermalStatus(ThermalStatus.critical), 0.05f )); - throttlingLevels.add(new DisplayDeviceConfig.BrightnessThrottlingData.ThrottlingLevel( + concurrentThrottlingLevels.add( + new DisplayDeviceConfig.BrightnessThrottlingData.ThrottlingLevel( DisplayDeviceConfig.convertThermalStatus(ThermalStatus.emergency), 0.025f )); - throttlingLevels.add(new DisplayDeviceConfig.BrightnessThrottlingData.ThrottlingLevel( + concurrentThrottlingLevels.add( + new DisplayDeviceConfig.BrightnessThrottlingData.ThrottlingLevel( DisplayDeviceConfig.convertThermalStatus(ThermalStatus.shutdown), 0.0125f )); - assertEquals(new DisplayDeviceConfig.BrightnessThrottlingData(throttlingLevels), - mDisplayDeviceConfig.getBrightnessThrottlingData("concurrent")); + DisplayDeviceConfig.BrightnessThrottlingData concurrentThrottlingData = + new DisplayDeviceConfig.BrightnessThrottlingData(concurrentThrottlingLevels); + + HashMap<String, DisplayDeviceConfig.BrightnessThrottlingData> throttlingDataMap = + new HashMap<>(2); + throttlingDataMap.put("default", defaultThrottlingData); + throttlingDataMap.put("concurrent", concurrentThrottlingData); + + assertEquals(throttlingDataMap, + mDisplayDeviceConfig.getBrightnessThrottlingDataMapByThrottlingId()); assertNotNull(mDisplayDeviceConfig.getHostUsiVersion()); assertEquals(mDisplayDeviceConfig.getHostUsiVersion().getMajorVersion(), 2); @@ -246,8 +269,7 @@ public final class DisplayDeviceConfigTest { mDisplayDeviceConfig.getHdrBrightnessFromSdr(0.62f, 1.25f), SMALL_DELTA); - - // Todo: Add asserts for BrightnessThrottlingData, DensityMapping, + // Todo: Add asserts for DensityMapping, // HighBrightnessModeData AmbientLightSensor, RefreshRateLimitations and ProximitySensor. } diff --git a/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java b/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java index dc954a2433c5..1ba6ad7c9f34 100755 --- a/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java +++ b/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java @@ -4734,7 +4734,7 @@ public class NotificationManagerServiceTest extends UiServiceTestCase { } @Test - public void testBumpFGImportance_noChannelChangePreOApp() throws Exception { + public void testBumpFGImportance_channelChangePreOApp() throws Exception { String preOPkg = PKG_N_MR1; final ApplicationInfo legacy = new ApplicationInfo(); legacy.targetSdkVersion = Build.VERSION_CODES.N_MR1; @@ -4752,7 +4752,7 @@ public class NotificationManagerServiceTest extends UiServiceTestCase { .setPriority(Notification.PRIORITY_MIN); StatusBarNotification sbn = new StatusBarNotification(preOPkg, preOPkg, 9, - "testBumpFGImportance_noChannelChangePreOApp", + "testBumpFGImportance_channelChangePreOApp", Binder.getCallingUid(), 0, nb.build(), UserHandle.getUserHandleForUid(Binder.getCallingUid()), null, 0); @@ -4772,11 +4772,11 @@ public class NotificationManagerServiceTest extends UiServiceTestCase { .setPriority(Notification.PRIORITY_MIN); sbn = new StatusBarNotification(preOPkg, preOPkg, 9, - "testBumpFGImportance_noChannelChangePreOApp", Binder.getCallingUid(), + "testBumpFGImportance_channelChangePreOApp", Binder.getCallingUid(), 0, nb.build(), UserHandle.getUserHandleForUid(Binder.getCallingUid()), null, 0); mBinderService.enqueueNotificationWithTag(preOPkg, preOPkg, - "testBumpFGImportance_noChannelChangePreOApp", + "testBumpFGImportance_channelChangePreOApp", sbn.getId(), sbn.getNotification(), sbn.getUserId()); waitForIdle(); assertEquals(IMPORTANCE_LOW, @@ -4784,7 +4784,7 @@ public class NotificationManagerServiceTest extends UiServiceTestCase { NotificationChannel defaultChannel = mBinderService.getNotificationChannel( preOPkg, mContext.getUserId(), preOPkg, NotificationChannel.DEFAULT_CHANNEL_ID); - assertEquals(IMPORTANCE_UNSPECIFIED, defaultChannel.getImportance()); + assertEquals(IMPORTANCE_LOW, defaultChannel.getImportance()); } @Test diff --git a/services/tests/uiservicestests/src/com/android/server/notification/ZenModeConfigTest.java b/services/tests/uiservicestests/src/com/android/server/notification/ZenModeConfigTest.java index 893f53825bbb..3ba94000d4a5 100644 --- a/services/tests/uiservicestests/src/com/android/server/notification/ZenModeConfigTest.java +++ b/services/tests/uiservicestests/src/com/android/server/notification/ZenModeConfigTest.java @@ -120,12 +120,20 @@ public class ZenModeConfigTest extends UiServiceTestCase { .showLights(false) .showBadges(false) .showInAmbientDisplay(false) + .allowCalls(ZenPolicy.PEOPLE_TYPE_CONTACTS) + .allowMessages(ZenPolicy.PEOPLE_TYPE_STARRED) + .allowConversations(ZenPolicy.CONVERSATION_SENDERS_NONE) .build(); ZenModeConfig config = getMutedAllConfig(); config.allowAlarms = true; config.allowReminders = true; config.allowEvents = true; + config.allowCalls = true; + config.allowCallsFrom = Policy.PRIORITY_SENDERS_CONTACTS; + config.allowMessages = true; + config.allowMessagesFrom = Policy.PRIORITY_SENDERS_STARRED; + config.allowConversations = false; config.suppressedVisualEffects |= Policy.SUPPRESSED_EFFECT_BADGE; config.suppressedVisualEffects |= Policy.SUPPRESSED_EFFECT_LIGHTS; config.suppressedVisualEffects |= Policy.SUPPRESSED_EFFECT_AMBIENT; @@ -138,6 +146,10 @@ public class ZenModeConfigTest extends UiServiceTestCase { assertEquals(expected.getPriorityCategoryEvents(), actual.getPriorityCategoryEvents()); assertEquals(expected.getVisualEffectLights(), actual.getVisualEffectLights()); assertEquals(expected.getVisualEffectAmbient(), actual.getVisualEffectAmbient()); + assertEquals(expected.getPriorityConversationSenders(), + actual.getPriorityConversationSenders()); + assertEquals(expected.getPriorityCallSenders(), actual.getPriorityCallSenders()); + assertEquals(expected.getPriorityMessageSenders(), actual.getPriorityMessageSenders()); } @Test diff --git a/services/tests/wmtests/src/com/android/server/wm/LetterboxUiControllerTest.java b/services/tests/wmtests/src/com/android/server/wm/LetterboxUiControllerTest.java index 12e4825e5f85..a15ee694d6a4 100644 --- a/services/tests/wmtests/src/com/android/server/wm/LetterboxUiControllerTest.java +++ b/services/tests/wmtests/src/com/android/server/wm/LetterboxUiControllerTest.java @@ -41,6 +41,7 @@ import static android.view.WindowManager.PROPERTY_CAMERA_COMPAT_ENABLE_REFRESH_V import static android.view.WindowManager.PROPERTY_COMPAT_ALLOW_DISPLAY_ORIENTATION_OVERRIDE; import static android.view.WindowManager.PROPERTY_COMPAT_ALLOW_ORIENTATION_OVERRIDE; import static android.view.WindowManager.PROPERTY_COMPAT_ENABLE_FAKE_FOCUS; +import static android.view.WindowManager.PROPERTY_COMPAT_IGNORE_ORIENTATION_REQUEST_WHEN_LOOP_DETECTED; import static android.view.WindowManager.PROPERTY_COMPAT_IGNORE_REQUESTED_ORIENTATION; import static com.android.dx.mockito.inline.extended.ExtendedMockito.doReturn; @@ -146,7 +147,7 @@ public class LetterboxUiControllerTest extends WindowTestsBase { mActivity = setUpActivityWithComponent(); mController = new LetterboxUiController(mWm, mActivity); prepareActivityThatShouldIgnoreRequestedOrientationDuringRelaunch(); - mController.setRelauchingAfterRequestedOrientationChanged(false); + mController.setRelaunchingAfterRequestedOrientationChanged(false); spyOn(mDisplayContent.mDisplayRotationCompatPolicy); doReturn(true).when(mDisplayContent.mDisplayRotationCompatPolicy) @@ -190,7 +191,28 @@ public class LetterboxUiControllerTest extends WindowTestsBase { @Test public void testShouldIgnoreOrientationRequestLoop_overrideDisabled_returnsFalse() { + doReturn(true).when(mLetterboxConfiguration) + .isPolicyForIgnoringRequestedOrientationEnabled(); + doReturn(false).when(mActivity).isLetterboxedForFixedOrientationAndAspectRatio(); + // Request 3 times to simulate orientation request loop + for (int i = 0; i <= MIN_COUNT_TO_IGNORE_REQUEST_IN_LOOP; i++) { + assertShouldIgnoreOrientationRequestLoop(/* shouldIgnore */ false, + /* expectedCount */ 0); + } + } + + @Test + @EnableCompatChanges({OVERRIDE_ENABLE_COMPAT_IGNORE_ORIENTATION_REQUEST_WHEN_LOOP_DETECTED}) + public void testShouldIgnoreOrientationRequestLoop_propertyIsFalseAndOverride_returnsFalse() + throws Exception { + doReturn(true).when(mLetterboxConfiguration) + .isPolicyForIgnoringRequestedOrientationEnabled(); + mockThatProperty(PROPERTY_COMPAT_IGNORE_ORIENTATION_REQUEST_WHEN_LOOP_DETECTED, + /* value */ false); doReturn(false).when(mActivity).isLetterboxedForFixedOrientationAndAspectRatio(); + + mController = new LetterboxUiController(mWm, mActivity); + // Request 3 times to simulate orientation request loop for (int i = 0; i <= MIN_COUNT_TO_IGNORE_REQUEST_IN_LOOP; i++) { assertShouldIgnoreOrientationRequestLoop(/* shouldIgnore */ false, @@ -201,7 +223,10 @@ public class LetterboxUiControllerTest extends WindowTestsBase { @Test @EnableCompatChanges({OVERRIDE_ENABLE_COMPAT_IGNORE_ORIENTATION_REQUEST_WHEN_LOOP_DETECTED}) public void testShouldIgnoreOrientationRequestLoop_isLetterboxed_returnsFalse() { + doReturn(true).when(mLetterboxConfiguration) + .isPolicyForIgnoringRequestedOrientationEnabled(); doReturn(true).when(mActivity).isLetterboxedForFixedOrientationAndAspectRatio(); + // Request 3 times to simulate orientation request loop for (int i = 0; i <= MIN_COUNT_TO_IGNORE_REQUEST_IN_LOOP; i++) { assertShouldIgnoreOrientationRequestLoop(/* shouldIgnore */ false, @@ -212,7 +237,10 @@ public class LetterboxUiControllerTest extends WindowTestsBase { @Test @EnableCompatChanges({OVERRIDE_ENABLE_COMPAT_IGNORE_ORIENTATION_REQUEST_WHEN_LOOP_DETECTED}) public void testShouldIgnoreOrientationRequestLoop_noLoop_returnsFalse() { + doReturn(true).when(mLetterboxConfiguration) + .isPolicyForIgnoringRequestedOrientationEnabled(); doReturn(false).when(mActivity).isLetterboxedForFixedOrientationAndAspectRatio(); + // No orientation request loop assertShouldIgnoreOrientationRequestLoop(/* shouldIgnore */ false, /* expectedCount */ 0); @@ -222,7 +250,10 @@ public class LetterboxUiControllerTest extends WindowTestsBase { @EnableCompatChanges({OVERRIDE_ENABLE_COMPAT_IGNORE_ORIENTATION_REQUEST_WHEN_LOOP_DETECTED}) public void testShouldIgnoreOrientationRequestLoop_timeout_returnsFalse() throws InterruptedException { + doReturn(true).when(mLetterboxConfiguration) + .isPolicyForIgnoringRequestedOrientationEnabled(); doReturn(false).when(mActivity).isLetterboxedForFixedOrientationAndAspectRatio(); + for (int i = MIN_COUNT_TO_IGNORE_REQUEST_IN_LOOP; i > 0; i--) { assertShouldIgnoreOrientationRequestLoop(/* shouldIgnore */ false, /* expectedCount */ 0); @@ -233,7 +264,10 @@ public class LetterboxUiControllerTest extends WindowTestsBase { @Test @EnableCompatChanges({OVERRIDE_ENABLE_COMPAT_IGNORE_ORIENTATION_REQUEST_WHEN_LOOP_DETECTED}) public void testShouldIgnoreOrientationRequestLoop_returnsTrue() { + doReturn(true).when(mLetterboxConfiguration) + .isPolicyForIgnoringRequestedOrientationEnabled(); doReturn(false).when(mActivity).isLetterboxedForFixedOrientationAndAspectRatio(); + for (int i = 0; i < MIN_COUNT_TO_IGNORE_REQUEST_IN_LOOP; i++) { assertShouldIgnoreOrientationRequestLoop(/* shouldIgnore */ false, /* expectedCount */ i); @@ -870,7 +904,7 @@ public class LetterboxUiControllerTest extends WindowTestsBase { private void prepareActivityThatShouldIgnoreRequestedOrientationDuringRelaunch() { doReturn(true).when(mLetterboxConfiguration) .isPolicyForIgnoringRequestedOrientationEnabled(); - mController.setRelauchingAfterRequestedOrientationChanged(true); + mController.setRelaunchingAfterRequestedOrientationChanged(true); } private ActivityRecord setUpActivityWithComponent() { diff --git a/services/tests/wmtests/src/com/android/server/wm/SizeCompatTests.java b/services/tests/wmtests/src/com/android/server/wm/SizeCompatTests.java index a646d01378aa..e96d1abf9ced 100644 --- a/services/tests/wmtests/src/com/android/server/wm/SizeCompatTests.java +++ b/services/tests/wmtests/src/com/android/server/wm/SizeCompatTests.java @@ -813,6 +813,8 @@ public class SizeCompatTests extends WindowTestsBase { spyOn(mActivity.mLetterboxUiController); doReturn(true).when(mActivity.mLetterboxUiController) + .isSurfaceReadyToShow(any()); + doReturn(true).when(mActivity.mLetterboxUiController) .isSurfaceVisible(any()); assertTrue(mActivity.mLetterboxUiController.shouldShowLetterboxUi( |