diff options
21 files changed, 419 insertions, 58 deletions
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainer.java b/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainer.java index 9ff338e8d5ab..42a4e7202c82 100644 --- a/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainer.java +++ b/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainer.java @@ -281,6 +281,8 @@ public class KeyguardSecurityContainer extends ConstraintLayout { public interface SwipeListener { void onSwipeUp(); + /** */ + void onSwipeDown(); } @VisibleForTesting @@ -543,6 +545,11 @@ public class KeyguardSecurityContainer extends ConstraintLayout { if (mSwipeListener != null) { mSwipeListener.onSwipeUp(); } + } else if (getTranslationY() > TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, + MIN_DRAG_SIZE, getResources().getDisplayMetrics())) { + if (mSwipeListener != null) { + mSwipeListener.onSwipeDown(); + } } } return true; diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainerController.java b/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainerController.java index e18050d77847..d3dbc4e8d6e1 100644 --- a/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainerController.java +++ b/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainerController.java @@ -309,6 +309,11 @@ public class KeyguardSecurityContainerController extends ViewController<Keyguard "swipeUpOnBouncer"); } } + + @Override + public void onSwipeDown() { + mViewMediatorCallback.onBouncerSwipeDown(); + } }; private final ConfigurationController.ConfigurationListener mConfigurationListener = new ConfigurationController.ConfigurationListener() { diff --git a/packages/SystemUI/src/com/android/keyguard/ViewMediatorCallback.java b/packages/SystemUI/src/com/android/keyguard/ViewMediatorCallback.java index 50f8f7e61230..14ec27ae6739 100644 --- a/packages/SystemUI/src/com/android/keyguard/ViewMediatorCallback.java +++ b/packages/SystemUI/src/com/android/keyguard/ViewMediatorCallback.java @@ -104,4 +104,9 @@ public interface ViewMediatorCallback { * Call when cancel button is pressed in bouncer. */ void onCancelClicked(); + + /** + * Determines if bouncer has swiped down. + */ + void onBouncerSwipeDown(); } diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java index cba717038871..df6007ddedfb 100644 --- a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java +++ b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java @@ -874,6 +874,11 @@ public class KeyguardViewMediator implements CoreStartable, Dumpable, } @Override + public void onBouncerSwipeDown() { + mKeyguardViewControllerLazy.get().reset(/* hideBouncerWhenShowing= */ true); + } + + @Override public void playTrustedSound() { KeyguardViewMediator.this.playTrustedSound(); } diff --git a/packages/SystemUI/tests/src/com/android/systemui/biometrics/AuthDialogPanelInteractionDetectorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/biometrics/AuthDialogPanelInteractionDetectorTest.kt index 9cabd35cb1e5..52e172620a52 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/biometrics/AuthDialogPanelInteractionDetectorTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/biometrics/AuthDialogPanelInteractionDetectorTest.kt @@ -22,6 +22,7 @@ import com.android.systemui.SysuiTestCase import com.android.systemui.shade.ShadeExpansionStateManager import org.junit.Assert import org.junit.Before +import org.junit.Ignore import org.junit.Rule import org.junit.Test import org.junit.runner.RunWith @@ -48,6 +49,7 @@ class AuthDialogPanelInteractionDetectorTest : SysuiTestCase() { AuthDialogPanelInteractionDetector(shadeExpansionStateManager, mContext.mainExecutor) } + @Ignore("b/316929376") @Test fun testEnableDetector_expandWithTrack_shouldPostRunnable() { detector.enable(action) diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/KeyguardViewMediatorTest.java b/packages/SystemUI/tests/src/com/android/systemui/keyguard/KeyguardViewMediatorTest.java index cfe3b0d2a803..eef5c1dc5309 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/KeyguardViewMediatorTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/KeyguardViewMediatorTest.java @@ -771,6 +771,11 @@ public class KeyguardViewMediatorTest extends SysuiTestCase { assertTrue(mViewMediator.isShowingAndNotOccluded()); } + @Test + public void testBouncerSwipeDown() { + mViewMediator.getViewMediatorCallback().onBouncerSwipeDown(); + verify(mStatusBarKeyguardViewManager).reset(true); + } private void createAndStartViewMediator() { createAndStartViewMediator(false); } diff --git a/services/accessibility/java/com/android/server/accessibility/AccessibilityInputFilter.java b/services/accessibility/java/com/android/server/accessibility/AccessibilityInputFilter.java index 8e7d27795c07..2519f4e7f3e3 100644 --- a/services/accessibility/java/com/android/server/accessibility/AccessibilityInputFilter.java +++ b/services/accessibility/java/com/android/server/accessibility/AccessibilityInputFilter.java @@ -388,11 +388,19 @@ class AccessibilityInputFilter extends InputFilter implements EventStreamTransfo @Override public void onMotionEvent(MotionEvent transformedEvent, MotionEvent rawEvent, int policyFlags) { + if (!mInstalled) { + Slog.w(TAG, "onMotionEvent called before input filter installed!"); + return; + } sendInputEvent(transformedEvent, policyFlags); } @Override public void onKeyEvent(KeyEvent event, int policyFlags) { + if (!mInstalled) { + Slog.w(TAG, "onKeyEvent called before input filter installed!"); + return; + } sendInputEvent(event, policyFlags); } diff --git a/services/core/java/com/android/server/notification/NotificationManagerService.java b/services/core/java/com/android/server/notification/NotificationManagerService.java index 647087de1c04..2e29941ee43b 100644 --- a/services/core/java/com/android/server/notification/NotificationManagerService.java +++ b/services/core/java/com/android/server/notification/NotificationManagerService.java @@ -9522,14 +9522,19 @@ public class NotificationManagerService extends SystemService { * Determine whether the userId applies to the notification in question, either because * they match exactly, or one of them is USER_ALL (which is treated as a wildcard). */ - private boolean notificationMatchesUserId(NotificationRecord r, int userId) { - return + private static boolean notificationMatchesUserId(NotificationRecord r, int userId, + boolean isAutogroupSummary) { + if (isAutogroupSummary) { + return r.getUserId() == userId; + } else { + return // looking for USER_ALL notifications? match everything - userId == UserHandle.USER_ALL - // a notification sent to USER_ALL matches any query - || r.getUserId() == UserHandle.USER_ALL - // an exact user match - || r.getUserId() == userId; + userId == UserHandle.USER_ALL + // a notification sent to USER_ALL matches any query + || r.getUserId() == UserHandle.USER_ALL + // an exact user match + || r.getUserId() == userId; + } } /** @@ -9538,7 +9543,7 @@ public class NotificationManagerService extends SystemService { * because it matches one of the users profiles. */ private boolean notificationMatchesCurrentProfiles(NotificationRecord r, int userId) { - return notificationMatchesUserId(r, userId) + return notificationMatchesUserId(r, userId, false) || mUserProfiles.isCurrentProfile(r.getUserId()); } @@ -9607,7 +9612,7 @@ public class NotificationManagerService extends SystemService { if (!notificationMatchesCurrentProfiles(r, userId)) { continue; } - } else if (!notificationMatchesUserId(r, userId)) { + } else if (!notificationMatchesUserId(r, userId, false)) { continue; } // Don't remove notifications to all, if there's no package name specified @@ -9845,7 +9850,7 @@ public class NotificationManagerService extends SystemService { final int len = list.size(); for (int i = 0; i < len; i++) { NotificationRecord r = list.get(i); - if (notificationMatchesUserId(r, userId) && r.getGroupKey().equals(groupKey) + if (notificationMatchesUserId(r, userId, false) && r.getGroupKey().equals(groupKey) && r.getSbn().getPackageName().equals(pkg)) { records.add(r); } @@ -9887,8 +9892,8 @@ public class NotificationManagerService extends SystemService { final int len = list.size(); for (int i = 0; i < len; i++) { NotificationRecord r = list.get(i); - if (notificationMatchesUserId(r, userId) && r.getSbn().getId() == id && - TextUtils.equals(r.getSbn().getTag(), tag) + if (notificationMatchesUserId(r, userId, (r.getFlags() & GroupHelper.BASE_FLAGS) != 0) + && r.getSbn().getId() == id && TextUtils.equals(r.getSbn().getTag(), tag) && r.getSbn().getPackageName().equals(pkg)) { return r; } @@ -9903,8 +9908,8 @@ public class NotificationManagerService extends SystemService { final int len = list.size(); for (int i = 0; i < len; i++) { NotificationRecord r = list.get(i); - if (notificationMatchesUserId(r, userId) && r.getSbn().getId() == id && - TextUtils.equals(r.getSbn().getTag(), tag) + if (notificationMatchesUserId(r, userId, false) && r.getSbn().getId() == id + && TextUtils.equals(r.getSbn().getTag(), tag) && r.getSbn().getPackageName().equals(pkg)) { matching.add(r); } @@ -9937,7 +9942,7 @@ public class NotificationManagerService extends SystemService { final int n = mEnqueuedNotifications.size(); for (int i = 0; i < n; i++) { NotificationRecord r = mEnqueuedNotifications.get(i); - if (notificationMatchesUserId(r, userId) + if (notificationMatchesUserId(r, userId, false) && r.getSbn().getId() == id && TextUtils.equals(r.getSbn().getTag(), tag) && r.getSbn().getPackageName().equals(pkg)) { diff --git a/services/core/java/com/android/server/pm/UserManagerService.java b/services/core/java/com/android/server/pm/UserManagerService.java index b03fb31fbb1e..80e1c7e9697b 100644 --- a/services/core/java/com/android/server/pm/UserManagerService.java +++ b/services/core/java/com/android/server/pm/UserManagerService.java @@ -2667,7 +2667,8 @@ public class UserManagerService extends IUserManager.Stub { } } - private void setUserRestrictionInner(int userId, @NonNull String key, boolean value) { + @VisibleForTesting + void setUserRestrictionInner(int userId, @NonNull String key, boolean value) { if (!UserRestrictionsUtils.isValidRestriction(key)) { Slog.e(LOG_TAG, "Setting invalid restriction " + key); return; @@ -3704,7 +3705,8 @@ public class UserManagerService extends IUserManager.Stub { if (type == XmlPullParser.START_TAG) { final String name = parser.getName(); if (name.equals(TAG_USER)) { - UserData userData = readUserLP(parser.getAttributeInt(null, ATTR_ID)); + UserData userData = readUserLP(parser.getAttributeInt(null, ATTR_ID), + mUserVersion); if (userData != null) { synchronized (mUsersLock) { @@ -4277,11 +4279,11 @@ public class UserManagerService extends IUserManager.Stub { UserRestrictionsUtils.writeRestrictions(serializer, mDevicePolicyUserRestrictions.getRestrictions(UserHandle.USER_ALL), - TAG_DEVICE_POLICY_RESTRICTIONS); + TAG_DEVICE_POLICY_GLOBAL_RESTRICTIONS); UserRestrictionsUtils.writeRestrictions(serializer, mDevicePolicyUserRestrictions.getRestrictions(userInfo.id), - TAG_DEVICE_POLICY_RESTRICTIONS); + TAG_DEVICE_POLICY_LOCAL_RESTRICTIONS); } if (userData.account != null) { @@ -4385,7 +4387,7 @@ public class UserManagerService extends IUserManager.Stub { } @GuardedBy({"mPackagesLock"}) - private UserData readUserLP(int id) { + private UserData readUserLP(int id, int userVersion) { try (ResilientAtomicFile file = getUserFile(id)) { FileInputStream fis = null; try { @@ -4394,19 +4396,19 @@ public class UserManagerService extends IUserManager.Stub { Slog.e(LOG_TAG, "User info not found, returning null, user id: " + id); return null; } - return readUserLP(id, fis); + return readUserLP(id, fis, userVersion); } catch (Exception e) { // Remove corrupted file and retry. Slog.e(LOG_TAG, "Error reading user info, user id: " + id); file.failRead(fis, e); - return readUserLP(id); + return readUserLP(id, userVersion); } } } @GuardedBy({"mPackagesLock"}) @VisibleForTesting - UserData readUserLP(int id, InputStream is) throws IOException, + UserData readUserLP(int id, InputStream is, int userVersion) throws IOException, XmlPullParserException { int flags = 0; String userType = null; @@ -4499,7 +4501,17 @@ public class UserManagerService extends IUserManager.Stub { } else if (TAG_DEVICE_POLICY_RESTRICTIONS.equals(tag)) { legacyLocalRestrictions = UserRestrictionsUtils.readRestrictions(parser); } else if (TAG_DEVICE_POLICY_LOCAL_RESTRICTIONS.equals(tag)) { - localRestrictions = UserRestrictionsUtils.readRestrictions(parser); + if (userVersion < 10) { + // Prior to version 10, the local user restrictions were stored as sub tags + // grouped by the user id of the source user. The source is no longer stored + // on versions 10+ as this is now stored in the DevicePolicyEngine. + RestrictionsSet oldLocalRestrictions = + RestrictionsSet.readRestrictions( + parser, TAG_DEVICE_POLICY_LOCAL_RESTRICTIONS); + localRestrictions = oldLocalRestrictions.mergeAll(); + } else { + localRestrictions = UserRestrictionsUtils.readRestrictions(parser); + } } else if (TAG_DEVICE_POLICY_GLOBAL_RESTRICTIONS.equals(tag)) { globalRestrictions = UserRestrictionsUtils.readRestrictions(parser); } else if (TAG_ACCOUNT.equals(tag)) { diff --git a/services/core/java/com/android/server/wm/AccessibilityController.java b/services/core/java/com/android/server/wm/AccessibilityController.java index f3001133338a..b3ae2ee30f22 100644 --- a/services/core/java/com/android/server/wm/AccessibilityController.java +++ b/services/core/java/com/android/server/wm/AccessibilityController.java @@ -1280,45 +1280,53 @@ final class AccessibilityController { } void drawIfNeeded(SurfaceControl.Transaction t) { + // Drawing variables (alpha, dirty rect, and bounds) access is synchronized + // using WindowManagerGlobalLock. Grab copies of these values before + // drawing on the canvas so that drawing can be performed outside of the lock. + int alpha; + Rect drawingRect = null; + Region drawingBounds = null; synchronized (mService.mGlobalLock) { if (!mInvalidated) { return; } mInvalidated = false; - if (mAlpha > 0) { - Canvas canvas = null; - try { - // Empty dirty rectangle means unspecified. - if (mDirtyRect.isEmpty()) { - mBounds.getBounds(mDirtyRect); - } - mDirtyRect.inset(-mHalfBorderWidth, -mHalfBorderWidth); - canvas = mSurface.lockCanvas(mDirtyRect); - if (DEBUG_VIEWPORT_WINDOW) { - Slog.i(LOG_TAG, "Dirty rect: " + mDirtyRect); - } - } catch (IllegalArgumentException iae) { - /* ignore */ - } catch (Surface.OutOfResourcesException oore) { - /* ignore */ - } - if (canvas == null) { - return; + + alpha = mAlpha; + if (alpha > 0) { + drawingBounds = new Region(mBounds); + // Empty dirty rectangle means unspecified. + if (mDirtyRect.isEmpty()) { + mBounds.getBounds(mDirtyRect); } + mDirtyRect.inset(-mHalfBorderWidth, -mHalfBorderWidth); + drawingRect = new Rect(mDirtyRect); if (DEBUG_VIEWPORT_WINDOW) { - Slog.i(LOG_TAG, "Bounds: " + mBounds); + Slog.i(LOG_TAG, "ViewportWindow bounds: " + mBounds); + Slog.i(LOG_TAG, "ViewportWindow dirty rect: " + mDirtyRect); } - canvas.drawColor(Color.TRANSPARENT, Mode.CLEAR); - mPaint.setAlpha(mAlpha); - Path path = mBounds.getBoundaryPath(); - canvas.drawPath(path, mPaint); - - mSurface.unlockCanvasAndPost(canvas); - t.show(mSurfaceControl); - } else { - t.hide(mSurfaceControl); } } + + // Draw without holding WindowManagerGlobalLock. + if (alpha > 0) { + Canvas canvas = null; + try { + canvas = mSurface.lockCanvas(drawingRect); + } catch (IllegalArgumentException | OutOfResourcesException e) { + /* ignore */ + } + if (canvas == null) { + return; + } + canvas.drawColor(Color.TRANSPARENT, Mode.CLEAR); + mPaint.setAlpha(alpha); + canvas.drawPath(drawingBounds.getBoundaryPath(), mPaint); + mSurface.unlockCanvasAndPost(canvas); + t.show(mSurfaceControl); + } else { + t.hide(mSurfaceControl); + } } void releaseSurface() { diff --git a/services/core/java/com/android/server/wm/AsyncRotationController.java b/services/core/java/com/android/server/wm/AsyncRotationController.java index 01158779c24f..5a7799e318c1 100644 --- a/services/core/java/com/android/server/wm/AsyncRotationController.java +++ b/services/core/java/com/android/server/wm/AsyncRotationController.java @@ -33,6 +33,7 @@ import android.view.animation.AnimationUtils; import com.android.internal.R; +import java.io.PrintWriter; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.util.function.Consumer; @@ -236,7 +237,15 @@ class AsyncRotationController extends FadeAnimationController implements Consume * operation directly to avoid waiting until timeout. */ void updateTargetWindows() { - if (mTransitionOp == OP_LEGACY || !mIsStartTransactionCommitted) return; + if (mTransitionOp == OP_LEGACY) return; + if (!mIsStartTransactionCommitted) { + if (mTimeoutRunnable == null && !mDisplayContent.hasTopFixedRotationLaunchingApp() + && !mDisplayContent.isRotationChanging() && !mDisplayContent.inTransition()) { + Slog.d(TAG, "Cancel for no change"); + mDisplayContent.finishAsyncRotationIfPossible(); + } + return; + } for (int i = mTargetWindowTokens.size() - 1; i >= 0; i--) { final Operation op = mTargetWindowTokens.valueAt(i); if (op.mIsCompletionPending || op.mAction == Operation.ACTION_SEAMLESS) { @@ -592,6 +601,16 @@ class AsyncRotationController extends FadeAnimationController implements Consume return !mAlwaysWaitForStartTransaction && op.mAction != Operation.ACTION_SEAMLESS; } + void dump(PrintWriter pw, String prefix) { + pw.println(prefix + "AsyncRotationController"); + prefix += " "; + pw.println(prefix + "mTransitionOp=" + mTransitionOp); + pw.println(prefix + "mIsStartTransactionCommitted=" + mIsStartTransactionCommitted); + pw.println(prefix + "mIsSyncDrawRequested=" + mIsSyncDrawRequested); + pw.println(prefix + "mOriginalRotation=" + mOriginalRotation); + pw.println(prefix + "mTargetWindowTokens=" + mTargetWindowTokens); + } + /** The operation to control the rotation appearance associated with window token. */ private static class Operation { @Retention(RetentionPolicy.SOURCE) @@ -619,5 +638,10 @@ class AsyncRotationController extends FadeAnimationController implements Consume Operation(@Action int action) { mAction = action; } + + @Override + public String toString() { + return "Operation{a=" + mAction + " pending=" + mIsCompletionPending + '}'; + } } } diff --git a/services/core/java/com/android/server/wm/DisplayContent.java b/services/core/java/com/android/server/wm/DisplayContent.java index ef19eef22794..d288663ab2db 100644 --- a/services/core/java/com/android/server/wm/DisplayContent.java +++ b/services/core/java/com/android/server/wm/DisplayContent.java @@ -3453,6 +3453,9 @@ class DisplayContent extends RootDisplayArea implements WindowManagerPolicy.Disp if (mFixedRotationLaunchingApp != null) { setSeamlessTransitionForFixedRotation(controller.getCollectingTransition()); } + } else if (mAsyncRotationController != null && !isRotationChanging()) { + Slog.i(TAG, "Finish AsyncRotation for previous intermediate change"); + finishAsyncRotationIfPossible(); } return; } @@ -3621,6 +3624,9 @@ class DisplayContent extends RootDisplayArea implements WindowManagerPolicy.Disp if (mFixedRotationLaunchingApp != null) { pw.println(" mFixedRotationLaunchingApp=" + mFixedRotationLaunchingApp); } + if (mAsyncRotationController != null) { + mAsyncRotationController.dump(pw, prefix); + } pw.println(); pw.print(prefix + "mHoldScreenWindow="); pw.print(mHoldScreenWindow); diff --git a/services/core/java/com/android/server/wm/Transition.java b/services/core/java/com/android/server/wm/Transition.java index 65c5c9b35ab7..2ac3125de961 100644 --- a/services/core/java/com/android/server/wm/Transition.java +++ b/services/core/java/com/android/server/wm/Transition.java @@ -1468,6 +1468,17 @@ class Transition implements BLASTSyncEngine.TransactionReadyListener { mTargetDisplays.add(dc); } + for (int i = 0; i < mTargets.size(); ++i) { + final DisplayArea da = mTargets.get(i).mContainer.asDisplayArea(); + if (da == null) continue; + if (da.isVisibleRequested()) { + mController.mValidateDisplayVis.remove(da); + } else { + // In case something accidentally hides a displayarea and nothing shows it again. + mController.mValidateDisplayVis.add(da); + } + } + if (mOverrideOptions != null) { info.setAnimationOptions(mOverrideOptions); if (mOverrideOptions.getType() == ANIM_OPEN_CROSS_PROFILE_APPS) { diff --git a/services/core/java/com/android/server/wm/TransitionController.java b/services/core/java/com/android/server/wm/TransitionController.java index b05c6b4839e5..7169c2cfa0f6 100644 --- a/services/core/java/com/android/server/wm/TransitionController.java +++ b/services/core/java/com/android/server/wm/TransitionController.java @@ -150,6 +150,13 @@ class TransitionController { final ArrayList<ActivityRecord> mValidateActivityCompat = new ArrayList<>(); /** + * List of display areas which were last sent as "closing"-type and haven't yet had a + * corresponding "opening"-type transition. A mismatch here is usually related to issues in + * keyguard unlock. + */ + final ArrayList<DisplayArea> mValidateDisplayVis = new ArrayList<>(); + + /** * Currently playing transitions (in the order they were started). When finished, records are * removed from this list. */ @@ -922,6 +929,15 @@ class TransitionController { ar.getSyncTransaction().setPosition(ar.getSurfaceControl(), tmpPos.x, tmpPos.y); } mValidateActivityCompat.clear(); + for (int i = 0; i < mValidateDisplayVis.size(); ++i) { + final DisplayArea da = mValidateDisplayVis.get(i); + if (!da.isAttached() || da.getSurfaceControl() == null) continue; + if (da.isVisibleRequested()) { + Slog.e(TAG, "DisplayArea became visible outside of a transition: " + da); + da.getSyncTransaction().show(da.getSurfaceControl()); + } + } + mValidateDisplayVis.clear(); } /** diff --git a/services/core/java/com/android/server/wm/WindowState.java b/services/core/java/com/android/server/wm/WindowState.java index 06978a5338ea..c4506d59c138 100644 --- a/services/core/java/com/android/server/wm/WindowState.java +++ b/services/core/java/com/android/server/wm/WindowState.java @@ -5664,6 +5664,12 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP // TODO(b/233286785): Add sync support to wallpaper. return false; } + if (mActivityRecord != null && mViewVisibility != View.VISIBLE + && mWinAnimator.mAttrType != TYPE_BASE_APPLICATION + && mWinAnimator.mAttrType != TYPE_APPLICATION_STARTING) { + // Skip sync for invisible app windows which are not managed by activity lifecycle. + return false; + } // In the WindowContainer implementation we immediately mark ready // since a generic WindowContainer only needs to wait for its // children to finish and is immediately ready from its own diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyEngine.java b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyEngine.java index 9c1d765fe0f9..9f65a33185b5 100644 --- a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyEngine.java +++ b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyEngine.java @@ -30,6 +30,7 @@ import android.annotation.NonNull; import android.annotation.Nullable; import android.app.AppGlobals; import android.app.BroadcastOptions; +import android.app.admin.BooleanPolicyValue; import android.app.admin.DevicePolicyIdentifiers; import android.app.admin.DevicePolicyManager; import android.app.admin.DevicePolicyState; @@ -133,6 +134,67 @@ final class DevicePolicyEngine { mEnforcingAdmins = new SparseArray<>(); } + private void maybeForceEnforcementRefreshLocked(@NonNull PolicyDefinition<?> policyDefinition) { + try { + if (shouldForceEnforcementRefresh(policyDefinition)) { + // This is okay because it's only true for user restrictions which are all <Boolean> + forceEnforcementRefreshLocked((PolicyDefinition<Boolean>) policyDefinition); + } + } catch (Throwable e) { + // Catch any possible exceptions just to be on the safe side + Log.e(TAG, "Exception throw during maybeForceEnforcementRefreshLocked", e); + } + } + + private boolean shouldForceEnforcementRefresh(@NonNull PolicyDefinition<?> policyDefinition) { + // These are all "not nullable" but for the purposes of maximum safety for a lightly tested + // change we check here + if (policyDefinition == null) { + return false; + } + PolicyKey policyKey = policyDefinition.getPolicyKey(); + if (policyKey == null) { + return false; + } + + if (policyKey instanceof UserRestrictionPolicyKey) { + // b/307481299 We must force all user restrictions to re-sync local + // + global on each set/clear + return true; + } + + return false; + } + + private void forceEnforcementRefreshLocked(PolicyDefinition<Boolean> policyDefinition) { + Binder.withCleanCallingIdentity(() -> { + // Sync global state + PolicyValue<Boolean> globalValue = new BooleanPolicyValue(false); + try { + PolicyState<Boolean> policyState = getGlobalPolicyStateLocked(policyDefinition); + globalValue = policyState.getCurrentResolvedPolicy(); + } catch (IllegalArgumentException e) { + // Expected for local-only policies + } + + enforcePolicy(policyDefinition, globalValue, UserHandle.USER_ALL); + + // Loop through each user and sync that user's state + for (UserInfo user : mUserManager.getUsers()) { + PolicyValue<Boolean> localValue = new BooleanPolicyValue(false); + try { + PolicyState<Boolean> localPolicyState = getLocalPolicyStateLocked( + policyDefinition, user.id); + localValue = localPolicyState.getCurrentResolvedPolicy(); + } catch (IllegalArgumentException e) { + // Expected for global-only policies + } + + enforcePolicy(policyDefinition, localValue, user.id); + } + }); + } + /** * Set the policy for the provided {@code policyDefinition} (see {@link PolicyDefinition}) and * {@code enforcingAdmin} to the provided {@code value}. @@ -174,6 +236,7 @@ final class DevicePolicyEngine { // No need to notify admins as no new policy is actually enforced, we're just filling in // the data structures. if (!skipEnforcePolicy) { + maybeForceEnforcementRefreshLocked(policyDefinition); if (policyChanged) { onLocalPolicyChangedLocked(policyDefinition, enforcingAdmin, userId); } @@ -262,6 +325,7 @@ final class DevicePolicyEngine { Objects.requireNonNull(enforcingAdmin); synchronized (mLock) { + maybeForceEnforcementRefreshLocked(policyDefinition); if (!hasLocalPolicyLocked(policyDefinition, userId)) { return; } @@ -425,6 +489,7 @@ final class DevicePolicyEngine { // No need to notify admins as no new policy is actually enforced, we're just filling in // the data structures. if (!skipEnforcePolicy) { + maybeForceEnforcementRefreshLocked(policyDefinition); if (policyChanged) { onGlobalPolicyChangedLocked(policyDefinition, enforcingAdmin); } @@ -474,6 +539,7 @@ final class DevicePolicyEngine { PolicyState<V> policyState = getGlobalPolicyStateLocked(policyDefinition); boolean policyChanged = policyState.removePolicy(enforcingAdmin); + maybeForceEnforcementRefreshLocked(policyDefinition); if (policyChanged) { onGlobalPolicyChangedLocked(policyDefinition, enforcingAdmin); } diff --git a/services/tests/servicestests/res/xml/user_100_v9.xml b/services/tests/servicestests/res/xml/user_100_v9.xml new file mode 100644 index 000000000000..03c08ed40828 --- /dev/null +++ b/services/tests/servicestests/res/xml/user_100_v9.xml @@ -0,0 +1,20 @@ +<user id="100" + serialNumber="0" + flags="3091" + type="android.os.usertype.full.SYSTEM" + created="0" + lastLoggedIn="0" + lastLoggedInFingerprint="0" + profileBadge="0"> + <restrictions no_oem_unlock="true" /> + <device_policy_local_restrictions> + <restrictions_user user_id="0"> + <restrictions no_camera="true" /> + </restrictions_user> + <restrictions_user user_id="100"> + <restrictions no_camera="true" /> + <restrictions no_install_unknown_sources="true" /> + </restrictions_user> + </device_policy_local_restrictions> + <ignorePrepareStorageErrors>false</ignorePrepareStorageErrors> +</user>
\ No newline at end of file diff --git a/services/tests/servicestests/src/com/android/server/pm/UserManagerServiceUserInfoTest.java b/services/tests/servicestests/src/com/android/server/pm/UserManagerServiceUserInfoTest.java index 2273fcd22b38..429c58e768bf 100644 --- a/services/tests/servicestests/src/com/android/server/pm/UserManagerServiceUserInfoTest.java +++ b/services/tests/servicestests/src/com/android/server/pm/UserManagerServiceUserInfoTest.java @@ -43,27 +43,33 @@ import android.annotation.UserIdInt; import android.app.PropertyInvalidatedCache; import android.content.pm.UserInfo; import android.content.pm.UserInfo.UserInfoFlag; +import android.content.res.Resources; import android.os.Looper; import android.os.Parcel; import android.os.UserHandle; import android.os.UserManager; import android.platform.test.annotations.Presubmit; import android.text.TextUtils; +import android.util.Xml; import androidx.test.InstrumentationRegistry; import androidx.test.filters.MediumTest; import androidx.test.runner.AndroidJUnit4; +import com.android.frameworks.servicestests.R; import com.android.server.LocalServices; import com.android.server.pm.UserManagerService.UserData; import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; +import org.xmlpull.v1.XmlPullParser; +import org.xmlpull.v1.XmlSerializer; import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; import java.io.DataOutputStream; +import java.nio.charset.StandardCharsets; import java.util.List; /** @@ -76,6 +82,7 @@ import java.util.List; @MediumTest public class UserManagerServiceUserInfoTest { private UserManagerService mUserManagerService; + private Resources mResources; @Before public void setup() { @@ -95,6 +102,8 @@ public class UserManagerServiceUserInfoTest { assertEquals("Multiple users so this test can't run.", 1, users.size()); assertEquals("Only user present isn't the system user.", UserHandle.USER_SYSTEM, users.get(0).id); + + mResources = InstrumentationRegistry.getTargetContext().getResources(); } @Test @@ -108,11 +117,51 @@ public class UserManagerServiceUserInfoTest { byte[] bytes = baos.toByteArray(); UserData read = mUserManagerService.readUserLP( - data.info.id, new ByteArrayInputStream(bytes)); + data.info.id, new ByteArrayInputStream(bytes), 0); assertUserInfoEquals(data.info, read.info, /* parcelCopy= */ false); } + /** Tests that device policy restrictions are written/read properly. */ + @Test + public void testWriteReadDevicePolicyUserRestrictions() throws Exception { + final String globalRestriction = UserManager.DISALLOW_FACTORY_RESET; + final String localRestriction = UserManager.DISALLOW_CONFIG_DATE_TIME; + + UserData data = new UserData(); + data.info = createUser(100, FLAG_FULL, "A type"); + + mUserManagerService.putUserInfo(data.info); + + // Set a global and user restriction so they get written out to the user file. + setUserRestrictions(data.info.id, globalRestriction, localRestriction, true); + + ByteArrayOutputStream baos = new ByteArrayOutputStream(); + DataOutputStream out = new DataOutputStream(baos); + mUserManagerService.writeUserLP(data, out); + byte[] bytes = baos.toByteArray(); + + // Clear the restrictions to see if they are properly read in from the user file. + setUserRestrictions(data.info.id, globalRestriction, localRestriction, false); + + final int userVersion = 10; + //read the secondary and SYSTEM user file to fetch local/global device policy restrictions. + mUserManagerService.readUserLP(data.info.id, new ByteArrayInputStream(bytes), + userVersion); + + assertTrue(mUserManagerService.hasUserRestrictionOnAnyUser(globalRestriction)); + assertTrue(mUserManagerService.hasUserRestrictionOnAnyUser(localRestriction)); + } + + /** Sets a global and local restriction and verifies they were set properly **/ + private void setUserRestrictions(int id, String global, String local, boolean enabled) { + mUserManagerService.setUserRestrictionInner(UserHandle.USER_ALL, global, enabled); + assertEquals(mUserManagerService.hasUserRestrictionOnAnyUser(global), enabled); + + mUserManagerService.setUserRestrictionInner(id, local, enabled); + assertEquals(mUserManagerService.hasUserRestrictionOnAnyUser(local), enabled); + } + @Test public void testParcelUnparcelUserInfo() throws Exception { UserInfo info = createUser(); @@ -250,6 +299,45 @@ public class UserManagerServiceUserInfoTest { assertTrue(mUserManagerService.isUserOfType(106, USER_TYPE_FULL_DEMO)); } + /** Tests readUserLP upgrading from version 9 to 10+. */ + @Test + public void testUserRestrictionsUpgradeFromV9() throws Exception { + final String[] localRestrictions = new String[] { + UserManager.DISALLOW_CAMERA, + UserManager.DISALLOW_INSTALL_UNKNOWN_SOURCES, + }; + + final int userId = 100; + UserData data = new UserData(); + data.info = createUser(userId, FLAG_FULL, "A type"); + + mUserManagerService.putUserInfo(data.info); + + for (String restriction : localRestrictions) { + assertFalse(mUserManagerService.hasBaseUserRestriction(restriction, userId)); + assertFalse(mUserManagerService.hasUserRestriction(restriction, userId)); + } + + // Convert the xml resource to the system storage xml format. + ByteArrayOutputStream baos = new ByteArrayOutputStream(); + DataOutputStream os = new DataOutputStream(baos); + XmlPullParser in = mResources.getXml(R.xml.user_100_v9); + XmlSerializer out = Xml.newBinarySerializer(); + out.setOutput(os, StandardCharsets.UTF_8.name()); + Xml.copy(in, out); + byte[] userBytes = baos.toByteArray(); + baos.reset(); + + final int userVersion = 9; + mUserManagerService.readUserLP(data.info.id, new ByteArrayInputStream(userBytes), + userVersion); + + for (String restriction : localRestrictions) { + assertFalse(mUserManagerService.hasBaseUserRestriction(restriction, userId)); + assertTrue(mUserManagerService.hasUserRestriction(restriction, userId)); + } + } + /** Creates a UserInfo with the given flags and userType. */ private UserInfo createUser(@UserIdInt int userId, @UserInfoFlag int flags, String userType) { return new UserInfo(userId, "A Name", "A path", flags, userType); diff --git a/services/tests/servicestests/src/com/android/server/power/batterysaver/BatterySaverPolicyTest.java b/services/tests/servicestests/src/com/android/server/power/batterysaver/BatterySaverPolicyTest.java index 3102639ff148..29e3d58696e6 100644 --- a/services/tests/servicestests/src/com/android/server/power/batterysaver/BatterySaverPolicyTest.java +++ b/services/tests/servicestests/src/com/android/server/power/batterysaver/BatterySaverPolicyTest.java @@ -116,7 +116,7 @@ public class BatterySaverPolicyTest extends AndroidTestCase { testServiceDefaultValue_On(ServiceType.NULL); } - @Suppress + @Suppress // TODO: b/317823111 - Remove once test fixed. @SmallTest public void testGetBatterySaverPolicy_PolicyVibration_DefaultValueCorrect() { testServiceDefaultValue_On(ServiceType.VIBRATION); @@ -202,7 +202,7 @@ public class BatterySaverPolicyTest extends AndroidTestCase { testServiceDefaultValue_On(ServiceType.QUICK_DOZE); } - @Suppress + @Suppress // TODO: b/317823111 - Remove once test fixed. @SmallTest public void testUpdateConstants_getCorrectData() { mBatterySaverPolicy.updateConstantsLocked(BATTERY_SAVER_CONSTANTS, ""); @@ -302,6 +302,7 @@ public class BatterySaverPolicyTest extends AndroidTestCase { } } + @Suppress // TODO: b/317823111 - Remove once test fixed. public void testSetPolicyLevel_Adaptive() { mBatterySaverPolicy.setPolicyLevel(POLICY_LEVEL_ADAPTIVE); 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 86d2b2fc6b56..620e362c86b8 100755 --- a/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java +++ b/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java @@ -865,13 +865,18 @@ public class NotificationManagerServiceTest extends UiServiceTestCase { } private NotificationRecord generateNotificationRecord(NotificationChannel channel, int userId) { + return generateNotificationRecord(channel, 1, userId); + } + + private NotificationRecord generateNotificationRecord(NotificationChannel channel, int id, + int userId) { if (channel == null) { channel = mTestNotificationChannel; } Notification.Builder nb = new Notification.Builder(mContext, channel.getId()) .setContentTitle("foo") .setSmallIcon(android.R.drawable.sym_def_app_icon); - StatusBarNotification sbn = new StatusBarNotification(PKG, PKG, 1, "tag", mUid, 0, + StatusBarNotification sbn = new StatusBarNotification(PKG, PKG, id, "tag", mUid, 0, nb.build(), new UserHandle(userId), null, 0); return new NotificationRecord(mContext, sbn, channel); } @@ -10795,6 +10800,51 @@ public class NotificationManagerServiceTest extends UiServiceTestCase { } @Test + public void testUngroupingAutoSummary_differentUsers() throws Exception { + NotificationRecord nr0 = + generateNotificationRecord(mTestNotificationChannel, 0, USER_SYSTEM); + NotificationRecord nr1 = + generateNotificationRecord(mTestNotificationChannel, 1, USER_SYSTEM); + + // add notifications + summary for USER_SYSTEM + mService.addNotification(nr0); + mService.addNotification(nr1); + mService.addNotification(mService.createAutoGroupSummary(nr1.getUserId(), + nr1.getSbn().getPackageName(), nr1.getKey(), GroupHelper.BASE_FLAGS)); + + // add notifications + summary for USER_ALL + NotificationRecord nr0_all = + generateNotificationRecord(mTestNotificationChannel, 2, UserHandle.USER_ALL); + NotificationRecord nr1_all = + generateNotificationRecord(mTestNotificationChannel, 3, UserHandle.USER_ALL); + + mService.addNotification(nr0_all); + mService.addNotification(nr1_all); + mService.addNotification(mService.createAutoGroupSummary(nr0_all.getUserId(), + nr0_all.getSbn().getPackageName(), nr0_all.getKey(), GroupHelper.BASE_FLAGS)); + + // cancel both children for USER_ALL + mBinderService.cancelNotificationWithTag(PKG, PKG, nr0_all.getSbn().getTag(), + nr0_all.getSbn().getId(), UserHandle.USER_ALL); + mBinderService.cancelNotificationWithTag(PKG, PKG, nr1_all.getSbn().getTag(), + nr1_all.getSbn().getId(), UserHandle.USER_ALL); + waitForIdle(); + + // group helper would send 'remove summary' event + mService.clearAutogroupSummaryLocked(UserHandle.USER_ALL, + nr0_all.getSbn().getPackageName()); + waitForIdle(); + + // make sure the right summary was removed + assertThat(mService.getNotificationCount(nr0_all.getSbn().getPackageName(), + UserHandle.USER_ALL, 0, null)).isEqualTo(0); + + // the USER_SYSTEM notifications + summary were not removed + assertThat(mService.getNotificationCount(nr0.getSbn().getPackageName(), + USER_SYSTEM, 0, null)).isEqualTo(3); + } + + @Test public void testStrongAuthTracker_isInLockDownMode() { mStrongAuthTracker.setGetStrongAuthForUserReturnValue( STRONG_AUTH_REQUIRED_AFTER_USER_LOCKDOWN); diff --git a/services/tests/wmtests/src/com/android/server/wm/DisplayContentTests.java b/services/tests/wmtests/src/com/android/server/wm/DisplayContentTests.java index 340b591e4086..b2fb891dadb3 100644 --- a/services/tests/wmtests/src/com/android/server/wm/DisplayContentTests.java +++ b/services/tests/wmtests/src/com/android/server/wm/DisplayContentTests.java @@ -2073,6 +2073,17 @@ public class DisplayContentTests extends WindowTestsBase { assertNotEquals(testPlayer.mLastReady.getChange(dcToken).getEndRotation(), testPlayer.mLastReady.getChange(dcToken).getStartRotation()); testPlayer.finish(); + + // The AsyncRotationController should only exist if there is an ongoing rotation change. + dc.finishAsyncRotationIfPossible(); + dc.setLastHasContent(); + doReturn(dr.getRotation() + 1).when(dr).rotationForOrientation(anyInt(), anyInt()); + dr.updateRotationUnchecked(true /* forceUpdate */); + assertNotNull(dc.getAsyncRotationController()); + doReturn(dr.getRotation() - 1).when(dr).rotationForOrientation(anyInt(), anyInt()); + dr.updateRotationUnchecked(true /* forceUpdate */); + assertNull("Cancel AsyncRotationController for the intermediate rotation changes 0->1->0", + dc.getAsyncRotationController()); } @Test |