diff options
35 files changed, 691 insertions, 227 deletions
diff --git a/data/etc/services.core.protolog.json b/data/etc/services.core.protolog.json index 791aeb73ffa3..909ca3988c5d 100644 --- a/data/etc/services.core.protolog.json +++ b/data/etc/services.core.protolog.json @@ -3505,6 +3505,12 @@ "group": "WM_ERROR", "at": "com\/android\/server\/wm\/WindowManagerService.java" }, + "1805116444": { + "message": "We don't support remote animation for Task with multiple TaskFragmentOrganizers.", + "level": "ERROR", + "group": "WM_DEBUG_APP_TRANSITIONS", + "at": "com\/android\/server\/wm\/AppTransitionController.java" + }, "1810019902": { "message": "TRANSIT_FLAG_OPEN_BEHIND, adding %s to mOpeningApps", "level": "DEBUG", diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/onehanded/OneHandedController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/onehanded/OneHandedController.java index 3ab062499845..38079aff9a6f 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/onehanded/OneHandedController.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/onehanded/OneHandedController.java @@ -537,6 +537,7 @@ public class OneHandedController implements RemoteCallable<OneHandedController>, mOneHandedSettingsUtil.getSettingsSwipeToNotificationEnabled( mContext.getContentResolver(), mUserId); setSwipeToNotificationEnabled(enabled); + notifyShortcutStateChanged(mState.getState()); mOneHandedUiEventLogger.writeEvent(enabled ? OneHandedUiEventLogger.EVENT_ONE_HANDED_SETTINGS_SHOW_NOTIFICATION_ENABLED_ON diff --git a/packages/SystemUI/res-keyguard/color/numpad_key_color_secondary.xml b/packages/SystemUI/res-keyguard/color/numpad_key_color_secondary.xml new file mode 100644 index 000000000000..08c66a24348c --- /dev/null +++ b/packages/SystemUI/res-keyguard/color/numpad_key_color_secondary.xml @@ -0,0 +1,5 @@ +<?xml version="1.0" encoding="utf-8"?> +<selector xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:androidprv="http://schemas.android.com/apk/prv/res/android"> + <item android:color="?androidprv:attr/colorAccentSecondaryVariant"/> +</selector>
\ No newline at end of file diff --git a/packages/SystemUI/res-keyguard/values/styles.xml b/packages/SystemUI/res-keyguard/values/styles.xml index 6fd83c55b656..6e89fb0169ab 100644 --- a/packages/SystemUI/res-keyguard/values/styles.xml +++ b/packages/SystemUI/res-keyguard/values/styles.xml @@ -17,8 +17,7 @@ */ --> -<resources - xmlns:androidprv="http://schemas.android.com/apk/prv/res/android"> +<resources xmlns:android="http://schemas.android.com/apk/res/android"> <!-- Keyguard PIN pad styles --> <style name="Keyguard.TextView" parent="@android:style/Widget.DeviceDefault.TextView"> <item name="android:textSize">@dimen/kg_status_line_font_size</item> @@ -59,11 +58,11 @@ <item name="android:textColor">?android:attr/textColorPrimary</item> </style> <style name="NumPadKey.Delete"> - <item name="android:colorControlNormal">?androidprv:attr/colorAccentSecondaryVariant</item> + <item name="android:colorControlNormal">@color/numpad_key_color_secondary</item> <item name="android:src">@drawable/ic_backspace_24dp</item> </style> <style name="NumPadKey.Enter"> - <item name="android:colorControlNormal">?androidprv:attr/colorAccentSecondaryVariant</item> + <item name="android:colorControlNormal">@color/numpad_key_color_secondary</item> <item name="android:src">@drawable/ic_keyboard_tab_36dp</item> </style> <style name="Widget.TextView.NumPadKey.Klondike" diff --git a/packages/SystemUI/res/layout/status_bar_expanded.xml b/packages/SystemUI/res/layout/status_bar_expanded.xml index 4b6e58fbd1e5..18315f1dff42 100644 --- a/packages/SystemUI/res/layout/status_bar_expanded.xml +++ b/packages/SystemUI/res/layout/status_bar_expanded.xml @@ -25,12 +25,6 @@ android:layout_width="match_parent" android:layout_height="match_parent" android:background="@android:color/transparent"> - <FrameLayout - android:id="@+id/big_clock_container" - android:layout_width="match_parent" - android:layout_height="match_parent" - android:visibility="gone" /> - <ViewStub android:id="@+id/keyguard_qs_user_switch_stub" android:layout="@layout/keyguard_qs_user_switch" diff --git a/packages/SystemUI/res/values/dimens.xml b/packages/SystemUI/res/values/dimens.xml index 8650654ea288..9f257466ff28 100644 --- a/packages/SystemUI/res/values/dimens.xml +++ b/packages/SystemUI/res/values/dimens.xml @@ -1559,6 +1559,13 @@ <!-- Location on the screen of the center of the fingerprint sensor. For devices with under display fingerprint sensors, this directly corresponds to the fingerprint sensor's location. For devices with sensors on the back of the device, this corresponds to the location on the + screen directly in front of the sensor. + By default, this is set to @null to use the horizontal center of the screen. --> + <dimen name="physical_fingerprint_sensor_center_screen_location_x">@null</dimen> + + <!-- Location on the screen of the center of the fingerprint sensor. For devices with under + display fingerprint sensors, this directly corresponds to the fingerprint sensor's location. + For devices with sensors on the back of the device, this corresponds to the location on the screen directly in front of the sensor. --> <dimen name="physical_fingerprint_sensor_center_screen_location_y">610px</dimen> diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/AuthController.java b/packages/SystemUI/src/com/android/systemui/biometrics/AuthController.java index 71445a7c2cfe..f4b446b50c9e 100644 --- a/packages/SystemUI/src/com/android/systemui/biometrics/AuthController.java +++ b/packages/SystemUI/src/com/android/systemui/biometrics/AuthController.java @@ -29,6 +29,7 @@ import android.content.Context; import android.content.Intent; import android.content.IntentFilter; import android.content.res.Configuration; +import android.content.res.Resources; import android.graphics.PointF; import android.hardware.biometrics.BiometricAuthenticator.Modality; import android.hardware.biometrics.BiometricConstants; @@ -97,7 +98,7 @@ public class AuthController extends SystemUI implements CommandQueue.Callbacks, private final Provider<UdfpsController> mUdfpsControllerFactory; private final Provider<SidefpsController> mSidefpsControllerFactory; @Nullable private final PointF mFaceAuthSensorLocation; - @Nullable private final PointF mFingerprintLocation; + @Nullable private PointF mFingerprintLocation; private final Set<Callback> mCallbacks = new HashSet<>(); // TODO: These should just be saved from onSaveState @@ -481,9 +482,7 @@ public class AuthController extends SystemUI implements CommandQueue.Callbacks, (float) faceAuthLocation[1]); } - mFingerprintLocation = new PointF(DisplayUtils.getWidth(mContext) / 2, - mContext.getResources().getDimensionPixelSize( - com.android.systemui.R.dimen.physical_fingerprint_sensor_center_screen_location_y)); + updateFingerprintLocation(); IntentFilter filter = new IntentFilter(); filter.addAction(Intent.ACTION_CLOSE_SYSTEM_DIALOGS); @@ -491,6 +490,21 @@ public class AuthController extends SystemUI implements CommandQueue.Callbacks, context.registerReceiver(mBroadcastReceiver, filter); } + private void updateFingerprintLocation() { + int xLocation = DisplayUtils.getWidth(mContext) / 2; + try { + xLocation = mContext.getResources().getDimensionPixelSize( + com.android.systemui.R.dimen + .physical_fingerprint_sensor_center_screen_location_x); + } catch (Resources.NotFoundException e) { + } + int yLocation = mContext.getResources().getDimensionPixelSize( + com.android.systemui.R.dimen.physical_fingerprint_sensor_center_screen_location_y); + mFingerprintLocation = new PointF( + xLocation, + yLocation); + } + @SuppressWarnings("deprecation") @Override public void start() { @@ -767,6 +781,7 @@ public class AuthController extends SystemUI implements CommandQueue.Callbacks, @Override protected void onConfigurationChanged(Configuration newConfig) { super.onConfigurationChanged(newConfig); + updateFingerprintLocation(); // Save the state of the current dialog (buttons showing, etc) if (mCurrentDialog != null) { @@ -796,6 +811,7 @@ public class AuthController extends SystemUI implements CommandQueue.Callbacks, } private void onOrientationChanged() { + updateFingerprintLocation(); if (mCurrentDialog != null) { mCurrentDialog.onOrientationChanged(); } diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/AuthRippleController.kt b/packages/SystemUI/src/com/android/systemui/biometrics/AuthRippleController.kt index ba64195ea78b..eb6b193d85a3 100644 --- a/packages/SystemUI/src/com/android/systemui/biometrics/AuthRippleController.kt +++ b/packages/SystemUI/src/com/android/systemui/biometrics/AuthRippleController.kt @@ -21,6 +21,7 @@ import android.content.Context import android.content.res.Configuration import android.graphics.PointF import android.hardware.biometrics.BiometricSourceType +import android.util.DisplayMetrics import android.util.Log import androidx.annotation.VisibleForTesting import com.android.keyguard.KeyguardUpdateMonitor @@ -45,6 +46,7 @@ import java.io.PrintWriter import javax.inject.Inject import javax.inject.Provider import com.android.systemui.plugins.statusbar.StatusBarStateController +import com.android.systemui.util.leak.RotationUtils private const val WAKE_AND_UNLOCK_FADE_DURATION = 180L @@ -182,7 +184,7 @@ class AuthRippleController @Inject constructor( } fun updateSensorLocation() { - fingerprintSensorLocation = authController.fingerprintSensorLocation + updateFingerprintLocation() faceSensorLocation = authController.faceAuthSensorLocation fingerprintSensorLocation?.let { circleReveal = CircleReveal( @@ -197,6 +199,35 @@ class AuthRippleController @Inject constructor( } } + private fun updateFingerprintLocation() { + val displayMetrics = DisplayMetrics() + sysuiContext.display?.getRealMetrics(displayMetrics) + val width = displayMetrics.widthPixels + val height = displayMetrics.heightPixels + + authController.fingerprintSensorLocation?.let { + fingerprintSensorLocation = when (RotationUtils.getRotation(sysuiContext)) { + RotationUtils.ROTATION_LANDSCAPE -> { + val normalizedYPos: Float = it.y / width + val normalizedXPos: Float = it.x / height + PointF(width * normalizedYPos, height * (1 - normalizedXPos)) + } + RotationUtils.ROTATION_UPSIDE_DOWN -> { + PointF(width - it.x, height - it.y) + } + RotationUtils.ROTATION_SEASCAPE -> { + val normalizedYPos: Float = it.y / width + val normalizedXPos: Float = it.x / height + PointF(width * (1 - normalizedYPos), height * normalizedXPos) + } + else -> { + // ROTATION_NONE + PointF(it.x, it.y) + } + } + } + } + private fun updateRippleColor() { mView.setColor( Utils.getColorAttr(sysuiContext, android.R.attr.colorAccent).defaultColor) @@ -314,10 +345,12 @@ class AuthRippleController @Inject constructor( } } "fingerprint" -> { + updateSensorLocation() pw.println("fingerprint ripple sensorLocation=$fingerprintSensorLocation") showRipple(BiometricSourceType.FINGERPRINT) } "face" -> { + updateSensorLocation() pw.println("face ripple sensorLocation=$faceSensorLocation") showRipple(BiometricSourceType.FACE) } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/lockscreen/LockscreenSmartspaceController.kt b/packages/SystemUI/src/com/android/systemui/statusbar/lockscreen/LockscreenSmartspaceController.kt index a76389b1bbc8..bdade2c6c2c9 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/lockscreen/LockscreenSmartspaceController.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/lockscreen/LockscreenSmartspaceController.kt @@ -86,6 +86,7 @@ class LockscreenSmartspaceController @Inject constructor( var stateChangeListener = object : View.OnAttachStateChangeListener { override fun onViewAttachedToWindow(v: View) { smartspaceViews.add(v as SmartspaceView) + connectSession() updateTextColorFromWallpaper() statusBarStateListener.onDozeAmountChanged(0f, statusBarStateController.dozeAmount) @@ -93,6 +94,10 @@ class LockscreenSmartspaceController @Inject constructor( override fun onViewDetachedFromWindow(v: View) { smartspaceViews.remove(v as SmartspaceView) + + if (smartspaceViews.isEmpty()) { + disconnect() + } } } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelViewController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelViewController.java index c88d9208694e..4862d1617837 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelViewController.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelViewController.java @@ -305,7 +305,6 @@ public class NotificationPanelViewController extends PanelViewController { private KeyguardUserSwitcherController mKeyguardUserSwitcherController; private KeyguardStatusBarView mKeyguardStatusBar; private KeyguardStatusBarViewController mKeyguardStatusBarViewController; - private ViewGroup mBigClockContainer; @VisibleForTesting QS mQs; private FrameLayout mQsFrame; private KeyguardStatusViewController mKeyguardStatusViewController; @@ -334,6 +333,7 @@ public class NotificationPanelViewController extends PanelViewController { private boolean mKeyguardUserSwitcherEnabled; private boolean mDozing; private boolean mDozingOnDown; + private boolean mBouncerShowing; private int mBarState; private float mInitialHeightOnTouch; private float mInitialTouchX; @@ -404,7 +404,7 @@ public class NotificationPanelViewController extends PanelViewController { private Runnable mHeadsUpExistenceChangedRunnable = () -> { setHeadsUpAnimatingAway(false); - notifyBarPanelExpansionChanged(); + updatePanelExpansionAndVisibility(); }; // TODO (b/162832756): once migrated to the new pipeline, delete legacy group manager private NotificationGroupManagerLegacy mGroupManager; @@ -801,7 +801,6 @@ public class NotificationPanelViewController extends PanelViewController { private void onFinishInflate() { loadDimens(); mKeyguardStatusBar = mView.findViewById(R.id.keyguard_header); - mBigClockContainer = mView.findViewById(R.id.big_clock_container); FrameLayout userAvatarContainer = null; KeyguardUserSwitcherView keyguardUserSwitcherView = null; @@ -1084,7 +1083,6 @@ public class NotificationPanelViewController extends PanelViewController { R.layout.keyguard_user_switcher /* layoutId */, showKeyguardUserSwitcher /* enabled */); - mBigClockContainer.removeAllViews(); updateViewControllers(mView.findViewById(R.id.keyguard_status_view), userAvatarView, keyguardUserSwitcherView); @@ -2183,7 +2181,6 @@ public class NotificationPanelViewController extends PanelViewController { if (mBarState == StatusBarState.SHADE_LOCKED || mBarState == KEYGUARD) { updateKeyguardBottomAreaAlpha(); positionClockAndNotifications(); - updateBigClockAlpha(); } if (mAccessibilityManager.isEnabled()) { @@ -2956,20 +2953,6 @@ public class NotificationPanelViewController extends PanelViewController { mLockIconViewController.setAlpha(alpha); } - /** - * Custom clock fades away when user drags up to unlock or pulls down quick settings. - * - * Updates alpha of custom clock to match the alpha of the KeyguardBottomArea. See - * {@link #updateKeyguardBottomAreaAlpha}. - */ - private void updateBigClockAlpha() { - float expansionAlpha = MathUtils.map( - isUnlockHintRunning() ? 0 : KeyguardBouncer.ALPHA_EXPANSION_THRESHOLD, 1f, 0f, 1f, - getExpandedFraction()); - float alpha = Math.min(expansionAlpha, 1 - computeQsExpansionFraction()); - mBigClockContainer.setAlpha(alpha); - } - @Override protected void onExpandingStarted() { super.onExpandingStarted(); @@ -3231,11 +3214,19 @@ public class NotificationPanelViewController extends PanelViewController { public void setHeadsUpAnimatingAway(boolean headsUpAnimatingAway) { mHeadsUpAnimatingAway = headsUpAnimatingAway; mNotificationStackScrollLayoutController.setHeadsUpAnimatingAway(headsUpAnimatingAway); - updateHeadsUpVisibility(); + updateVisibility(); } - private void updateHeadsUpVisibility() { - ((PhoneStatusBarView) mBar).setHeadsUpVisible(mHeadsUpAnimatingAway || mHeadsUpPinnedMode); + /** Set whether the bouncer is showing. */ + public void setBouncerShowing(boolean bouncerShowing) { + mBouncerShowing = bouncerShowing; + updateVisibility(); + } + + @Override + protected boolean shouldPanelBeVisible() { + boolean headsUpVisible = mHeadsUpAnimatingAway || mHeadsUpPinnedMode; + return headsUpVisible || isExpanded() || mBouncerShowing; } @Override @@ -3319,7 +3310,6 @@ public class NotificationPanelViewController extends PanelViewController { } mNotificationStackScrollLayoutController.setExpandedHeight(expandedHeight); updateKeyguardBottomAreaAlpha(); - updateBigClockAlpha(); updateStatusBarIcons(); } @@ -3645,6 +3635,11 @@ public class NotificationPanelViewController extends PanelViewController { } } + /** */ + public void setImportantForAccessibility(int mode) { + mView.setImportantForAccessibility(mode); + } + /** * Do not let the user drag the shade up and down for the current touch session. * This is necessary to avoid shade expansion while/after the bouncer is dismissed. @@ -4219,7 +4214,7 @@ public class NotificationPanelViewController extends PanelViewController { } updateGestureExclusionRect(); mHeadsUpPinnedMode = inPinnedMode; - updateHeadsUpVisibility(); + updateVisibility(); mKeyguardStatusBarViewController.updateForHeadsUp(); } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PanelBar.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PanelBar.java index e775e96de749..247ede91eeb3 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PanelBar.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PanelBar.java @@ -33,8 +33,6 @@ public abstract class PanelBar extends FrameLayout { private static final boolean SPEW = false; private static final String PANEL_BAR_SUPER_PARCELABLE = "panel_bar_super_parcelable"; private static final String STATE = "state"; - private boolean mBouncerShowing; - private boolean mExpanded; protected float mPanelFraction; public static final void LOG(String fmt, Object... args) { @@ -99,33 +97,6 @@ public abstract class PanelBar extends FrameLayout { pv.setBar(this); } - public void setBouncerShowing(boolean showing) { - mBouncerShowing = showing; - int important = showing ? IMPORTANT_FOR_ACCESSIBILITY_NO_HIDE_DESCENDANTS - : IMPORTANT_FOR_ACCESSIBILITY_AUTO; - - setImportantForAccessibility(important); - updateVisibility(); - - if (mPanel != null) mPanel.getView().setImportantForAccessibility(important); - } - - public float getExpansionFraction() { - return mPanelFraction; - } - - public boolean isExpanded() { - return mExpanded; - } - - protected void updateVisibility() { - mPanel.getView().setVisibility(shouldPanelBeVisible() ? VISIBLE : INVISIBLE); - } - - protected boolean shouldPanelBeVisible() { - return mExpanded || mBouncerShowing; - } - public boolean panelEnabled() { return true; } @@ -183,9 +154,7 @@ public abstract class PanelBar extends FrameLayout { boolean fullyClosed = true; boolean fullyOpened = false; if (SPEW) LOG("panelExpansionChanged: start state=%d, f=%.1f", mState, frac); - mExpanded = expanded; mPanelFraction = frac; - updateVisibility(); // adjust any other panels that may be partially visible if (expanded) { if (mState == STATE_CLOSED) { diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PanelViewController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PanelViewController.java index 51cae8c4af7f..b2155154d652 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PanelViewController.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PanelViewController.java @@ -16,6 +16,9 @@ package com.android.systemui.statusbar.phone; +import static android.view.View.INVISIBLE; +import static android.view.View.VISIBLE; + import static com.android.internal.jank.InteractionJankMonitor.CUJ_NOTIFICATION_SHADE_EXPAND_COLLAPSE; import static com.android.systemui.classifier.Classifier.BOUNCER_UNLOCK; import static com.android.systemui.classifier.Classifier.GENERIC; @@ -315,7 +318,7 @@ public abstract class PanelViewController { } private void startOpening(MotionEvent event) { - notifyBarPanelExpansionChanged(); + updatePanelExpansionAndVisibility(); maybeVibrateOnOpening(); //TODO: keyguard opens QS a different way; log that too? @@ -447,7 +450,7 @@ public abstract class PanelViewController { protected void onTrackingStopped(boolean expand) { mTracking = false; mBar.onTrackingStopped(expand); - notifyBarPanelExpansionChanged(); + updatePanelExpansionAndVisibility(); } protected void onTrackingStarted() { @@ -455,7 +458,7 @@ public abstract class PanelViewController { mTracking = true; mBar.onTrackingStarted(); notifyExpandingStarted(); - notifyBarPanelExpansionChanged(); + updatePanelExpansionAndVisibility(); } /** @@ -685,7 +688,7 @@ public abstract class PanelViewController { } else { cancelJankMonitoring(CUJ_NOTIFICATION_SHADE_EXPAND_COLLAPSE); } - notifyBarPanelExpansionChanged(); + updatePanelExpansionAndVisibility(); } protected abstract boolean shouldUseDismissingAnimation(); @@ -760,7 +763,7 @@ public abstract class PanelViewController { mExpandedFraction = Math.min(1f, maxPanelHeight == 0 ? 0 : mExpandedHeight / maxPanelHeight); onHeightUpdated(mExpandedHeight); - notifyBarPanelExpansionChanged(); + updatePanelExpansionAndVisibility(); } /** @@ -878,7 +881,7 @@ public abstract class PanelViewController { if (mExpanding) { notifyExpandingFinished(); } - notifyBarPanelExpansionChanged(); + updatePanelExpansionAndVisibility(); // Wait for window manager to pickup the change, so we know the maximum height of the panel // then. @@ -916,7 +919,7 @@ public abstract class PanelViewController { } if (mInstantExpanding) { mInstantExpanding = false; - notifyBarPanelExpansionChanged(); + updatePanelExpansionAndVisibility(); } } @@ -1022,7 +1025,7 @@ public abstract class PanelViewController { public void onAnimationEnd(Animator animation) { setAnimator(null); onAnimationFinished.run(); - notifyBarPanelExpansionChanged(); + updatePanelExpansionAndVisibility(); } }); animator.start(); @@ -1059,19 +1062,39 @@ public abstract class PanelViewController { return animator; } - protected void notifyBarPanelExpansionChanged() { + /** Update the visibility of {@link PanelView} if necessary. */ + public void updateVisibility() { + mView.setVisibility(shouldPanelBeVisible() ? VISIBLE : INVISIBLE); + } + + /** Returns true if {@link PanelView} should be visible. */ + abstract boolean shouldPanelBeVisible(); + + /** + * Updates the panel expansion and {@link PanelView} visibility if necessary. + * + * TODO(b/200063118): Could public calls to this method be replaced with calls to + * {@link #updateVisibility()}? That would allow us to make this method private. + */ + public void updatePanelExpansionAndVisibility() { if (mBar != null) { - mBar.panelExpansionChanged( - mExpandedFraction, - mExpandedFraction > 0f || mInstantExpanding - || isPanelVisibleBecauseOfHeadsUp() || mTracking - || mHeightAnimator != null && !mIsSpringBackAnimation); + mBar.panelExpansionChanged(mExpandedFraction, isExpanded()); } + updateVisibility(); for (int i = 0; i < mExpansionListeners.size(); i++) { mExpansionListeners.get(i).onPanelExpansionChanged(mExpandedFraction, mTracking); } } + public boolean isExpanded() { + return mExpandedFraction > 0f + || mInstantExpanding + || isPanelVisibleBecauseOfHeadsUp() + || mTracking + || mHeightAnimator != null + && !mIsSpringBackAnimation; + } + public void addExpansionListener(PanelExpansionListener panelExpansionListener) { mExpansionListeners.add(panelExpansionListener); } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarView.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarView.java index af556a26e3af..9ab6cdd3053b 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarView.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarView.java @@ -402,16 +402,6 @@ public class PhoneStatusBarView extends PanelBar { getPaddingBottom()); } - public void setHeadsUpVisible(boolean headsUpVisible) { - mHeadsUpVisible = headsUpVisible; - updateVisibility(); - } - - @Override - protected boolean shouldPanelBeVisible() { - return mHeadsUpVisible || super.shouldPanelBeVisible(); - } - /** An interface that will provide whether panel is enabled. */ interface PanelEnabledProvider { /** Returns true if the panel is enabled and false otherwise. */ diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarViewController.kt b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarViewController.kt index 28040fd8d8a1..4c0332a75df1 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarViewController.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarViewController.kt @@ -61,6 +61,10 @@ class PhoneStatusBarViewController( } } + fun setImportantForAccessibility(mode: Int) { + mView.importantForAccessibility = mode + } + private class StatusBarViewsCenterProvider : UnfoldMoveFromCenterAnimator.ViewCenterProvider { override fun getViewCenter(view: View, outPoint: Point) = when (view.id) { diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java index 3bdbc054dd45..3bdc08be63e6 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java @@ -26,6 +26,8 @@ import static android.view.WindowInsetsController.APPEARANCE_LOW_PROFILE_BARS; import static android.view.WindowInsetsController.APPEARANCE_OPAQUE_STATUS_BARS; import static android.view.WindowInsetsController.APPEARANCE_SEMI_TRANSPARENT_STATUS_BARS; +import static androidx.core.view.ViewCompat.IMPORTANT_FOR_ACCESSIBILITY_AUTO; +import static androidx.core.view.ViewCompat.IMPORTANT_FOR_ACCESSIBILITY_NO_HIDE_DESCENDANTS; import static androidx.lifecycle.Lifecycle.State.RESUMED; import static com.android.systemui.Dependency.TIME_TICK_HANDLER_NAME; @@ -1188,22 +1190,13 @@ public class StatusBar extends SystemUI implements ); mBatteryMeterViewController.init(); - // CollapsedStatusBarFragment re-inflated PhoneStatusBarView and both of - // mStatusBarView.mExpanded and mStatusBarView.mBouncerShowing are false. - // PhoneStatusBarView's new instance will set to be gone in - // PanelBar.updateVisibility after calling mStatusBarView.setBouncerShowing - // that will trigger PanelBar.updateVisibility. If there is a heads up showing, - // it needs to notify PhoneStatusBarView's new instance to update the correct - // status by calling mNotificationPanel.notifyBarPanelExpansionChanged(). - if (mHeadsUpManager.hasPinnedHeadsUp()) { - mNotificationPanelViewController.notifyBarPanelExpansionChanged(); - } - mStatusBarView.setBouncerShowing(mBouncerShowing); - if (oldStatusBarView != null) { - float fraction = oldStatusBarView.getExpansionFraction(); - boolean expanded = oldStatusBarView.isExpanded(); - mStatusBarView.panelExpansionChanged(fraction, expanded); - } + // Ensure we re-propagate panel expansion values to the panel controller and + // any listeners it may have, such as PanelBar. This will also ensure we + // re-display the notification panel if necessary (for example, if + // a heads-up notification was being displayed and should continue being + // displayed). + mNotificationPanelViewController.updatePanelExpansionAndVisibility(); + setBouncerShowingForStatusBarComponents(mBouncerShowing); HeadsUpAppearanceController oldController = mHeadsUpAppearanceController; if (mHeadsUpAppearanceController != null) { @@ -3531,7 +3524,7 @@ public class StatusBar extends SystemUI implements mBouncerShowing = bouncerShowing; mKeyguardBypassController.setBouncerShowing(bouncerShowing); mPulseExpansionHandler.setBouncerShowing(bouncerShowing); - if (mStatusBarView != null) mStatusBarView.setBouncerShowing(bouncerShowing); + setBouncerShowingForStatusBarComponents(bouncerShowing); updateHideIconsForBouncer(true /* animate */); mCommandQueue.recomputeDisableFlags(mDisplayId, true /* animate */); updateScrimController(); @@ -3541,6 +3534,23 @@ public class StatusBar extends SystemUI implements } /** + * Propagate the bouncer state to status bar components. + * + * Separate from {@link #setBouncerShowing} because we sometimes re-create the status bar and + * should update only the status bar components. + */ + private void setBouncerShowingForStatusBarComponents(boolean bouncerShowing) { + int importance = bouncerShowing + ? IMPORTANT_FOR_ACCESSIBILITY_NO_HIDE_DESCENDANTS + : IMPORTANT_FOR_ACCESSIBILITY_AUTO; + if (mPhoneStatusBarViewController != null) { + mPhoneStatusBarViewController.setImportantForAccessibility(importance); + } + mNotificationPanelViewController.setImportantForAccessibility(importance); + mNotificationPanelViewController.setBouncerShowing(bouncerShowing); + } + + /** * Collapses the notification shade if it is tracking or expanded. */ public void collapseShade() { @@ -4212,10 +4222,9 @@ public class StatusBar extends SystemUI implements } private void sendInitialExpansionAmount(ExpansionChangedListener expansionChangedListener) { - if (mStatusBarView != null) { - expansionChangedListener.onExpansionChanged(mStatusBarView.getExpansionFraction(), - mStatusBarView.isExpanded()); - } + expansionChangedListener.onExpansionChanged( + mNotificationPanelViewController.getExpandedFraction(), + mNotificationPanelViewController.isExpanded()); } public void removeExpansionChangedListener(@NonNull ExpansionChangedListener listener) { diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/lockscreen/LockscreenSmartspaceControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/lockscreen/LockscreenSmartspaceControllerTest.kt index fd932803ff37..ee9c2b82c283 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/lockscreen/LockscreenSmartspaceControllerTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/lockscreen/LockscreenSmartspaceControllerTest.kt @@ -199,8 +199,9 @@ class LockscreenSmartspaceControllerTest : SysuiTestCase() { `when`(deviceProvisionedController.isDeviceProvisioned()).thenReturn(false) `when`(deviceProvisionedController.isCurrentUserSetup()).thenReturn(false) - // WHEN a connection attempt is made - controller.buildAndConnectView(fakeParent) + // WHEN a connection attempt is made and view is attached + val view = controller.buildAndConnectView(fakeParent) + controller.stateChangeListener.onViewAttachedToWindow(view) // THEN no session is created verify(smartspaceManager, never()).createSmartspaceSession(any()) diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationPanelViewControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationPanelViewControllerTest.java index f5fb98b4c2c7..f7423bb7951d 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationPanelViewControllerTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationPanelViewControllerTest.java @@ -156,8 +156,6 @@ public class NotificationPanelViewControllerTest extends SysuiTestCase { private KeyguardBottomAreaView mQsFrame; private KeyguardStatusView mKeyguardStatusView; @Mock - private ViewGroup mBigClockContainer; - @Mock private NotificationIconAreaController mNotificationAreaController; @Mock private HeadsUpManagerPhone mHeadsUpManager; @@ -347,7 +345,6 @@ public class NotificationPanelViewControllerTest extends SysuiTestCase { when(mKeyguardBottomArea.getLeftView()).thenReturn(mock(KeyguardAffordanceView.class)); when(mKeyguardBottomArea.getRightView()).thenReturn(mock(KeyguardAffordanceView.class)); when(mKeyguardBottomArea.animate()).thenReturn(mock(ViewPropertyAnimator.class)); - when(mView.findViewById(R.id.big_clock_container)).thenReturn(mBigClockContainer); when(mView.findViewById(R.id.qs_frame)).thenReturn(mQsFrame); when(mView.findViewById(R.id.keyguard_status_view)) .thenReturn(mock(KeyguardStatusView.class)); diff --git a/services/core/java/com/android/server/wm/ActivityRecord.java b/services/core/java/com/android/server/wm/ActivityRecord.java index 168686c04d36..c1fcf71b38d2 100644 --- a/services/core/java/com/android/server/wm/ActivityRecord.java +++ b/services/core/java/com/android/server/wm/ActivityRecord.java @@ -1411,9 +1411,8 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A this.task = newTask; if (shouldStartChangeTransition(newParent, oldParent)) { - // The new parent and old parent may be in different position. Need to offset the - // animation surface to keep it in its original position. - initializeChangeTransition(getBounds(), newParent.getBounds()); + // Animate change transition on TaskFragment level to get the correct window crop. + newParent.initializeChangeTransition(getBounds(), getSurfaceControl()); } super.onParentChanged(newParent, oldParent); @@ -1482,7 +1481,7 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A // The starting window should keep covering its task when the activity is // reparented to a task fragment that may not fill the task bounds. associateStartingDataWithTask(); - overrideConfigurationPropagation(mStartingWindow, task); + attachStartingSurfaceToAssociatedTask(); } mImeInsetsFrozenUntilStartInput = false; } @@ -2383,13 +2382,20 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A } void attachStartingWindow(@NonNull WindowState startingWindow) { + startingWindow.mStartingData = mStartingData; mStartingWindow = startingWindow; if (mStartingData != null && mStartingData.mAssociatedTask != null) { - // Associate the configuration of starting window with the task. - overrideConfigurationPropagation(startingWindow, mStartingData.mAssociatedTask); + attachStartingSurfaceToAssociatedTask(); } } + private void attachStartingSurfaceToAssociatedTask() { + // Associate the configuration of starting window with the task. + overrideConfigurationPropagation(mStartingWindow, mStartingData.mAssociatedTask); + getSyncTransaction().reparent(mStartingWindow.mSurfaceControl, + mStartingData.mAssociatedTask.mSurfaceControl); + } + private void associateStartingDataWithTask() { mStartingData.mAssociatedTask = task; task.forAllActivities(r -> { diff --git a/services/core/java/com/android/server/wm/AppTransitionController.java b/services/core/java/com/android/server/wm/AppTransitionController.java index 82377b34d22a..9561de09971b 100644 --- a/services/core/java/com/android/server/wm/AppTransitionController.java +++ b/services/core/java/com/android/server/wm/AppTransitionController.java @@ -343,9 +343,6 @@ public class AppTransitionController { switch (changingType) { case TYPE_TASK: return TRANSIT_OLD_TASK_CHANGE_WINDOWING_MODE; - case TYPE_ACTIVITY: - // ActivityRecord is put in a change transition only when it is reparented - // to an organized TaskFragment. See ActivityRecord#shouldStartChangeTransition. case TYPE_TASK_FRAGMENT: return TRANSIT_OLD_TASK_FRAGMENT_CHANGE; default: @@ -533,40 +530,81 @@ public class AppTransitionController { * * @return {@code true} if the transition is overridden. */ - @VisibleForTesting - boolean overrideWithTaskFragmentRemoteAnimation(@TransitionOldType int transit, + private boolean overrideWithTaskFragmentRemoteAnimation(@TransitionOldType int transit, ArraySet<Integer> activityTypes) { final ArrayList<WindowContainer> allWindows = new ArrayList<>(); allWindows.addAll(mDisplayContent.mClosingApps); allWindows.addAll(mDisplayContent.mOpeningApps); allWindows.addAll(mDisplayContent.mChangingContainers); - // Find the common TaskFragmentOrganizer of all windows. - ITaskFragmentOrganizer organizer = null; + // It should only animated by the organizer if all windows are below the same leaf Task. + Task leafTask = null; for (int i = allWindows.size() - 1; i >= 0; i--) { final ActivityRecord r = getAppFromContainer(allWindows.get(i)); if (r == null) { return false; } + // The activity may be a child of embedded Task, but we want to find the owner Task. + // As a result, find the organized TaskFragment first. final TaskFragment organizedTaskFragment = r.getOrganizedTaskFragment(); - final ITaskFragmentOrganizer curOrganizer = organizedTaskFragment != null - ? organizedTaskFragment.getTaskFragmentOrganizer() - : null; - if (curOrganizer == null) { - // All windows must below an organized TaskFragment. + // There are also cases where the Task contains non-embedded activity, such as launching + // split TaskFragments from a non-embedded activity. + // The hierarchy may looks like this: + // - Task + // - Activity + // - TaskFragment + // - Activity + // - TaskFragment + // - Activity + // We also want to have the organizer handle the transition for such case. + final Task task = organizedTaskFragment != null + ? organizedTaskFragment.getTask() + : r.getTask(); + if (task == null) { + return false; + } + // We don't want the organizer to handle transition of other non-embedded Task. + if (leafTask != null && leafTask != task) { + return false; + } + final ActivityRecord rootActivity = task.getRootActivity(); + // We don't want the organizer to handle transition when the whole app is closing. + if (rootActivity == null) { return false; } - if (organizer == null) { - organizer = curOrganizer; - } else if (!organizer.asBinder().equals(curOrganizer.asBinder())) { - // They must be controlled by the same organizer. + // We don't want the organizer to handle transition of non-embedded activity of other + // app. + if (r.getUid() != rootActivity.getUid() && !r.isEmbedded()) { return false; } + leafTask = task; + } + if (leafTask == null) { + return false; + } + + // We don't support remote animation for Task with multiple TaskFragmentOrganizers. + final ITaskFragmentOrganizer[] organizer = new ITaskFragmentOrganizer[1]; + final boolean hasMultipleOrganizers = leafTask.forAllLeafTaskFragments(taskFragment -> { + final ITaskFragmentOrganizer tfOrganizer = taskFragment.getTaskFragmentOrganizer(); + if (tfOrganizer == null) { + return false; + } + if (organizer[0] != null && !organizer[0].asBinder().equals(tfOrganizer.asBinder())) { + return true; + } + organizer[0] = tfOrganizer; + return false; + }); + if (hasMultipleOrganizers) { + ProtoLog.e(WM_DEBUG_APP_TRANSITIONS, "We don't support remote animation for" + + " Task with multiple TaskFragmentOrganizers."); + return false; } - final RemoteAnimationDefinition definition = organizer != null + final RemoteAnimationDefinition definition = organizer[0] != null ? mDisplayContent.mAtmService.mTaskFragmentOrganizerController - .getRemoteAnimationDefinition(organizer) + .getRemoteAnimationDefinition(organizer[0]) : null; final RemoteAnimationAdapter adapter = definition != null ? definition.getAdapter(transit, activityTypes) diff --git a/services/core/java/com/android/server/wm/InputMonitor.java b/services/core/java/com/android/server/wm/InputMonitor.java index 1bc1d46a4230..1e7b676fbfe4 100644 --- a/services/core/java/com/android/server/wm/InputMonitor.java +++ b/services/core/java/com/android/server/wm/InputMonitor.java @@ -48,6 +48,8 @@ import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_INPUT; import static com.android.server.wm.WindowManagerDebugConfig.TAG_WM; import static com.android.server.wm.WindowManagerService.LOGTAG_INPUT_FOCUS; +import static java.lang.Integer.MAX_VALUE; + import android.annotation.Nullable; import android.graphics.Rect; import android.graphics.Region; @@ -581,10 +583,11 @@ final class InputMonitor { if (mAddRecentsAnimationInputConsumerHandle && shouldApplyRecentsInputConsumer) { if (recentsAnimationController.updateInputConsumerForApp( mRecentsAnimationInputConsumer.mWindowHandle)) { - final WindowState highestLayerWindow = - recentsAnimationController.getHighestLayerWindow(); - if (highestLayerWindow != null) { - mRecentsAnimationInputConsumer.show(mInputTransaction, highestLayerWindow); + final DisplayArea targetDA = + recentsAnimationController.getTargetAppDisplayArea(); + if (targetDA != null) { + mRecentsAnimationInputConsumer.reparent(mInputTransaction, targetDA); + mRecentsAnimationInputConsumer.show(mInputTransaction, MAX_VALUE - 1); mAddRecentsAnimationInputConsumerHandle = false; } } @@ -597,7 +600,7 @@ final class InputMonitor { rootTask.getSurfaceControl()); // We set the layer to z=MAX-1 so that it's always on top. mPipInputConsumer.reparent(mInputTransaction, rootTask); - mPipInputConsumer.show(mInputTransaction, Integer.MAX_VALUE - 1); + mPipInputConsumer.show(mInputTransaction, MAX_VALUE - 1); mAddPipInputConsumerHandle = false; } } diff --git a/services/core/java/com/android/server/wm/LetterboxUiController.java b/services/core/java/com/android/server/wm/LetterboxUiController.java index cf2afc93c419..c712c04b72ee 100644 --- a/services/core/java/com/android/server/wm/LetterboxUiController.java +++ b/services/core/java/com/android/server/wm/LetterboxUiController.java @@ -65,6 +65,10 @@ final class LetterboxUiController { private final LetterboxConfiguration mLetterboxConfiguration; private final ActivityRecord mActivityRecord; + // Taskbar expanded height. Used to determine whether to crop an app window to display rounded + // corners above the taskbar. + private float mExpandedTaskBarHeight; + private boolean mShowWallpaperForLetterboxBackground; @Nullable @@ -76,6 +80,8 @@ final class LetterboxUiController { // is created in its constructor. It shouldn't be used in this constructor but it's safe // to use it after since controller is only used in ActivityRecord. mActivityRecord = activityRecord; + mExpandedTaskBarHeight = + getResources().getDimensionPixelSize(R.dimen.taskbar_frame_height); } /** Cleans up {@link Letterbox} if it exists.*/ @@ -314,12 +320,27 @@ final class LetterboxUiController { final InsetsSource taskbarInsetsSource = insetsState.getSource(InsetsState.ITYPE_EXTRA_NAVIGATION_BAR); - Rect cropBounds = new Rect(mActivityRecord.getBounds()); - // Activity bounds are in screen coordinates while (0,0) for activity's surface control - // is at the top left corner of an app window so offsetting bounds accordingly. - cropBounds.offsetTo(0, 0); - // Rounded cornerners should be displayed above the taskbar. - cropBounds.bottom = Math.min(cropBounds.bottom, taskbarInsetsSource.getFrame().top); + Rect cropBounds = null; + + // 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 + // rounded corners since apps are expected to handle navigation bar inset. + // This condition checks whether the taskbar is visible. + if (taskbarInsetsSource.getFrame().height() >= mExpandedTaskBarHeight) { + cropBounds = new Rect(mActivityRecord.getBounds()); + // Activity bounds are in screen coordinates while (0,0) for activity's surface + // control is at the top left corner of an app window so offsetting bounds + // accordingly. + cropBounds.offsetTo(0, 0); + // Rounded cornerners should be displayed above the taskbar. + cropBounds.bottom = + Math.min(cropBounds.bottom, taskbarInsetsSource.getFrame().top); + if (mActivityRecord.inSizeCompatMode() + && mActivityRecord.getSizeCompatScale() < 1.0f) { + cropBounds.scale(1.0f / mActivityRecord.getSizeCompatScale()); + } + } + transaction .setWindowCrop(windowSurface, cropBounds) .setCornerRadius(windowSurface, getRoundedCorners(insetsState)); diff --git a/services/core/java/com/android/server/wm/RecentsAnimationController.java b/services/core/java/com/android/server/wm/RecentsAnimationController.java index 03ff06c9d7f1..a663c62b40e5 100644 --- a/services/core/java/com/android/server/wm/RecentsAnimationController.java +++ b/services/core/java/com/android/server/wm/RecentsAnimationController.java @@ -1125,21 +1125,11 @@ public class RecentsAnimationController implements DeathRecipient { return mTargetActivityRecord.findMainWindow(); } - /** - * Returns the window with the highest layer, or null if none is found. - */ - public WindowState getHighestLayerWindow() { - int highestLayer = Integer.MIN_VALUE; - Task highestLayerTask = null; - for (int i = mPendingAnimations.size() - 1; i >= 0; i--) { - TaskAnimationAdapter adapter = mPendingAnimations.get(i); - int layer = adapter.mTask.getPrefixOrderIndex(); - if (layer > highestLayer) { - highestLayer = layer; - highestLayerTask = adapter.mTask; - } + DisplayArea getTargetAppDisplayArea() { + if (mTargetActivityRecord == null) { + return null; } - return highestLayerTask.getTopMostActivity().getTopChild(); + return mTargetActivityRecord.getDisplayArea(); } boolean isAnimatingTask(Task task) { diff --git a/services/core/java/com/android/server/wm/SurfaceFreezer.java b/services/core/java/com/android/server/wm/SurfaceFreezer.java index 9c4f6f574487..89986cefb207 100644 --- a/services/core/java/com/android/server/wm/SurfaceFreezer.java +++ b/services/core/java/com/android/server/wm/SurfaceFreezer.java @@ -72,8 +72,11 @@ class SurfaceFreezer { * * @param startBounds The original bounds (on screen) of the surface we are snapshotting. * @param relativePosition The related position of the snapshot surface to its parent. + * @param freezeTarget The surface to take snapshot from. If {@code null}, we will take a + * snapshot from the {@link #mAnimatable} surface. */ - void freeze(SurfaceControl.Transaction t, Rect startBounds, Point relativePosition) { + void freeze(SurfaceControl.Transaction t, Rect startBounds, Point relativePosition, + @Nullable SurfaceControl freezeTarget) { mFreezeBounds.set(startBounds); mLeash = SurfaceAnimator.createAnimationLeash(mAnimatable, mAnimatable.getSurfaceControl(), @@ -82,7 +85,7 @@ class SurfaceFreezer { mWmService.mTransactionFactory); mAnimatable.onAnimationLeashCreated(t, mLeash); - SurfaceControl freezeTarget = mAnimatable.getFreezeSnapshotTarget(); + freezeTarget = freezeTarget != null ? freezeTarget : mAnimatable.getFreezeSnapshotTarget(); if (freezeTarget != null) { SurfaceControl.ScreenshotHardwareBuffer screenshotBuffer = createSnapshotBuffer( freezeTarget, startBounds); diff --git a/services/core/java/com/android/server/wm/Task.java b/services/core/java/com/android/server/wm/Task.java index c5362d38c613..d89d212bab1f 100644 --- a/services/core/java/com/android/server/wm/Task.java +++ b/services/core/java/com/android/server/wm/Task.java @@ -480,7 +480,6 @@ class Task extends TaskFragment { private Dimmer mDimmer = new Dimmer(this); private final Rect mTmpDimBoundsRect = new Rect(); - private final Point mLastSurfaceSize = new Point(); /** @see #setCanAffectSystemUiFlags */ private boolean mCanAffectSystemUiFlags = true; diff --git a/services/core/java/com/android/server/wm/TaskFragment.java b/services/core/java/com/android/server/wm/TaskFragment.java index fce279d8b805..2b5a8203d33b 100644 --- a/services/core/java/com/android/server/wm/TaskFragment.java +++ b/services/core/java/com/android/server/wm/TaskFragment.java @@ -240,6 +240,8 @@ class TaskFragment extends WindowContainer<WindowContainer> { */ private int mTaskFragmentOrganizerPid = ActivityRecord.INVALID_PID; + final Point mLastSurfaceSize = new Point(); + private final Rect mTmpInsets = new Rect(); private final Rect mTmpBounds = new Rect(); private final Rect mTmpFullBounds = new Rect(); @@ -1654,6 +1656,7 @@ class TaskFragment extends WindowContainer<WindowContainer> { } } + @Override void onChildPositionChanged(WindowContainer child) { super.onChildPositionChanged(child); @@ -2049,14 +2052,58 @@ class TaskFragment extends WindowContainer<WindowContainer> { if (shouldStartChangeTransition(mTmpPrevBounds)) { initializeChangeTransition(mTmpPrevBounds); } else if (mTaskFragmentOrganizer != null) { - // Update the surface position here instead of in the organizer so that we can make sure + // Update the surface here instead of in the organizer so that we can make sure // it can be synced with the surface freezer. - updateSurfacePosition(getSyncTransaction()); + final SurfaceControl.Transaction t = getSyncTransaction(); + updateSurfacePosition(t); + updateOrganizedTaskFragmentSurfaceSize(t, false /* forceUpdate */); } sendTaskFragmentInfoChanged(); } + /** Updates the surface size so that the sub windows cannot be shown out of bounds. */ + private void updateOrganizedTaskFragmentSurfaceSize(SurfaceControl.Transaction t, + boolean forceUpdate) { + if (mTaskFragmentOrganizer == null) { + // We only want to update for organized TaskFragment. Task will handle itself. + return; + } + if (mSurfaceControl == null || mSurfaceAnimator.hasLeash() || mSurfaceFreezer.hasLeash()) { + return; + } + + final Rect bounds = getBounds(); + final int width = bounds.width(); + final int height = bounds.height(); + if (!forceUpdate && width == mLastSurfaceSize.x && height == mLastSurfaceSize.y) { + return; + } + t.setWindowCrop(mSurfaceControl, width, height); + mLastSurfaceSize.set(width, height); + } + + @Override + public void onAnimationLeashCreated(SurfaceControl.Transaction t, SurfaceControl leash) { + super.onAnimationLeashCreated(t, leash); + // Reset surface bounds for animation. It will be taken care by the animation leash, and + // reset again onAnimationLeashLost. + if (mTaskFragmentOrganizer != null + && (mLastSurfaceSize.x != 0 || mLastSurfaceSize.y != 0)) { + t.setWindowCrop(mSurfaceControl, 0, 0); + mLastSurfaceSize.set(0, 0); + } + } + + @Override + public void onAnimationLeashLost(SurfaceControl.Transaction t) { + super.onAnimationLeashLost(t); + // Update the surface bounds after animation. + if (mTaskFragmentOrganizer != null) { + updateOrganizedTaskFragmentSurfaceSize(t, true /* forceUpdate */); + } + } + /** Whether we should prepare a transition for this {@link TaskFragment} bounds change. */ private boolean shouldStartChangeTransition(Rect startBounds) { if (mWmService.mDisableTransitionAnimation @@ -2075,9 +2122,14 @@ class TaskFragment extends WindowContainer<WindowContainer> { @Override void setSurfaceControl(SurfaceControl sc) { super.setSurfaceControl(sc); - // If the TaskFragmentOrganizer was set before we created the SurfaceControl, we need to - // emit the callbacks now. - sendTaskFragmentAppeared(); + if (mTaskFragmentOrganizer != null) { + final SurfaceControl.Transaction t = getSyncTransaction(); + updateSurfacePosition(t); + updateOrganizedTaskFragmentSurfaceSize(t, false /* forceUpdate */); + // If the TaskFragmentOrganizer was set before we created the SurfaceControl, we need to + // emit the callbacks now. + sendTaskFragmentAppeared(); + } } void sendTaskFragmentInfoChanged() { diff --git a/services/core/java/com/android/server/wm/WindowContainer.java b/services/core/java/com/android/server/wm/WindowContainer.java index 2a8fa1086799..38e20555f236 100644 --- a/services/core/java/com/android/server/wm/WindowContainer.java +++ b/services/core/java/com/android/server/wm/WindowContainer.java @@ -2620,23 +2620,27 @@ class WindowContainer<E extends WindowContainer> extends ConfigurationContainer< * For now, this will only be called for the following cases: * 1. {@link Task} is changing windowing mode between fullscreen and freeform. * 2. {@link TaskFragment} is organized and is changing window bounds. - * 3. {@link ActivityRecord} is reparented into an organized {@link TaskFragment}. + * 3. {@link ActivityRecord} is reparented into an organized {@link TaskFragment}. (The + * transition will happen on the {@link TaskFragment} for this case). * - * This shouldn't be called on other {@link WindowContainer} unless there is a valid use case. + * This shouldn't be called on other {@link WindowContainer} unless there is a valid + * use case. * * @param startBounds The original bounds (on screen) of the surface we are snapshotting. - * @param parentBounds The parent bounds (on screen) to calculate the animation surface - * position. + * @param freezeTarget The surface to take snapshot from. If {@code null}, we will take a + * snapshot from {@link #getFreezeSnapshotTarget()}. */ - void initializeChangeTransition(Rect startBounds, Rect parentBounds) { + void initializeChangeTransition(Rect startBounds, @Nullable SurfaceControl freezeTarget) { mDisplayContent.prepareAppTransition(TRANSIT_CHANGE); mDisplayContent.mChangingContainers.add(this); + // Calculate the relative position in parent container. + final Rect parentBounds = getParent().getBounds(); mTmpPoint.set(startBounds.left - parentBounds.left, startBounds.top - parentBounds.top); - mSurfaceFreezer.freeze(getSyncTransaction(), startBounds, mTmpPoint); + mSurfaceFreezer.freeze(getSyncTransaction(), startBounds, mTmpPoint, freezeTarget); } void initializeChangeTransition(Rect startBounds) { - initializeChangeTransition(startBounds, getParent().getBounds()); + initializeChangeTransition(startBounds, null /* freezeTarget */); } ArraySet<WindowContainer> getAnimationSources() { @@ -3159,7 +3163,7 @@ class WindowContainer<E extends WindowContainer> extends ConfigurationContainer< */ @VisibleForTesting(visibility = VisibleForTesting.Visibility.PROTECTED) void updateSurfacePosition(Transaction t) { - if (mSurfaceControl == null || mSurfaceAnimator.hasLeash()) { + if (mSurfaceControl == null || mSurfaceAnimator.hasLeash() || mSurfaceFreezer.hasLeash()) { return; } diff --git a/services/core/java/com/android/server/wm/WindowManagerService.java b/services/core/java/com/android/server/wm/WindowManagerService.java index b9c6e1bd73ce..0e99c0f1bad0 100644 --- a/services/core/java/com/android/server/wm/WindowManagerService.java +++ b/services/core/java/com/android/server/wm/WindowManagerService.java @@ -1770,17 +1770,15 @@ public class WindowManagerService extends IWindowManager.Stub final boolean hideSystemAlertWindows = !mHidingNonSystemOverlayWindows.isEmpty(); win.setForceHideNonSystemOverlayWindowIfNeeded(hideSystemAlertWindows); - if (type == TYPE_APPLICATION_STARTING && activity != null) { - activity.attachStartingWindow(win); - ProtoLog.v(WM_DEBUG_STARTING_WINDOW, "addWindow: %s startingWindow=%s", - activity, win); - } - boolean imMayMove = true; win.mToken.addWindow(win); displayPolicy.addWindowLw(win, attrs); - if (type == TYPE_INPUT_METHOD) { + if (type == TYPE_APPLICATION_STARTING && activity != null) { + activity.attachStartingWindow(win); + ProtoLog.v(WM_DEBUG_STARTING_WINDOW, "addWindow: %s startingWindow=%s", + activity, win); + } else if (type == TYPE_INPUT_METHOD) { displayContent.setInputMethodWindowLocked(win); imMayMove = false; } else if (type == TYPE_INPUT_METHOD_DIALOG) { diff --git a/services/core/java/com/android/server/wm/WindowState.java b/services/core/java/com/android/server/wm/WindowState.java index c3d51d5d4d1d..3adecf3474eb 100644 --- a/services/core/java/com/android/server/wm/WindowState.java +++ b/services/core/java/com/android/server/wm/WindowState.java @@ -308,6 +308,8 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP @NonNull WindowToken mToken; // The same object as mToken if this is an app window and null for non-app windows. ActivityRecord mActivityRecord; + /** Non-null if this is a starting window. */ + StartingData mStartingData; // mAttrs.flags is tested in animation without being locked. If the bits tested are ever // modified they will need to be locked. @@ -5485,6 +5487,10 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP return mWillReplaceWindow; } + private boolean isStartingWindowAssociatedToTask() { + return mStartingData != null && mStartingData.mAssociatedTask != null; + } + private void applyDims() { if (!mAnimatingExit && mAppDied) { mIsDimming = true; @@ -5634,7 +5640,9 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP outPoint.offset(-parent.mWindowFrames.mFrame.left + mTmpPoint.x, -parent.mWindowFrames.mFrame.top + mTmpPoint.y); } else if (parentWindowContainer != null) { - final Rect parentBounds = parentWindowContainer.getBounds(); + final Rect parentBounds = isStartingWindowAssociatedToTask() + ? mStartingData.mAssociatedTask.getBounds() + : parentWindowContainer.getBounds(); outPoint.offset(-parentBounds.left, -parentBounds.top); } @@ -5717,6 +5725,11 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP @Override void assignLayer(Transaction t, int layer) { + if (isStartingWindowAssociatedToTask()) { + // The starting window should cover the task. + t.setLayer(mSurfaceControl, Integer.MAX_VALUE); + return; + } // See comment in assignRelativeLayerForImeTargetChild if (needsRelativeLayeringToIme()) { getDisplayContent().assignRelativeLayerForImeTargetChild(t, this); @@ -5729,6 +5742,24 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP return mIsDimming; } + @Override + protected void reparentSurfaceControl(Transaction t, SurfaceControl newParent) { + if (isStartingWindowAssociatedToTask()) { + // Its surface is already put in task. Don't reparent when transferring starting window + // across activities. + return; + } + super.reparentSurfaceControl(t, newParent); + } + + @Override + public SurfaceControl getAnimationLeashParent() { + if (isStartingWindowAssociatedToTask()) { + return mStartingData.mAssociatedTask.mSurfaceControl; + } + return super.getAnimationLeashParent(); + } + // TODO(b/70040778): We should aim to eliminate the last user of TYPE_APPLICATION_MEDIA // then we can drop all negative layering on the windowing side and simply inherit // the default implementation here. diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java index 147889db3177..8e30f62c1b29 100644 --- a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java +++ b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java @@ -3618,6 +3618,9 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { final CallerIdentity caller = getCallerIdentity(); Preconditions.checkCallAuthorization(hasFullCrossUsersPermission(caller, userHandle)); + Preconditions.checkCallAuthorization( + isCallingFromPackage(adminReceiver.getPackageName(), caller.getUid()) + || isSystemUid(caller)); synchronized (getLockObject()) { ActiveAdmin administrator = getActiveAdminUncheckedLocked(adminReceiver, userHandle); diff --git a/services/tests/wmtests/src/com/android/server/wm/ActivityRecordTests.java b/services/tests/wmtests/src/com/android/server/wm/ActivityRecordTests.java index c434b137317e..65733d7a4129 100644 --- a/services/tests/wmtests/src/com/android/server/wm/ActivityRecordTests.java +++ b/services/tests/wmtests/src/com/android/server/wm/ActivityRecordTests.java @@ -2700,8 +2700,8 @@ public class ActivityRecordTests extends WindowTestsBase { final WindowState startingWindow = createWindowState( new WindowManager.LayoutParams(TYPE_APPLICATION_STARTING), activity1); activity1.addWindow(startingWindow); - activity1.attachStartingWindow(startingWindow); activity1.mStartingData = mock(StartingData.class); + activity1.attachStartingWindow(startingWindow); final Task task = activity1.getTask(); final Rect taskBounds = task.getBounds(); final int width = taskBounds.width(); @@ -2729,6 +2729,10 @@ public class ActivityRecordTests extends WindowTestsBase { assertTrue(activity2.isResizeable()); activity1.reparent(taskFragment1, POSITION_TOP); + verify(activity1.getSyncTransaction()).reparent(eq(startingWindow.mSurfaceControl), + eq(task.mSurfaceControl)); + assertEquals(activity1.mStartingData, startingWindow.mStartingData); + assertEquals(task.mSurfaceControl, startingWindow.getAnimationLeashParent()); assertEquals(task, activity1.mStartingData.mAssociatedTask); assertEquals(taskFragment1.getBounds(), activity1.getBounds()); // The activity was resized by task fragment, but starting window must still cover the task. diff --git a/services/tests/wmtests/src/com/android/server/wm/AppTransitionControllerTest.java b/services/tests/wmtests/src/com/android/server/wm/AppTransitionControllerTest.java index d6d7f07b2ef3..5fa76bb2e25b 100644 --- a/services/tests/wmtests/src/com/android/server/wm/AppTransitionControllerTest.java +++ b/services/tests/wmtests/src/com/android/server/wm/AppTransitionControllerTest.java @@ -27,6 +27,8 @@ import static android.view.WindowManager.TRANSIT_OLD_ACTIVITY_OPEN; import static android.view.WindowManager.TRANSIT_OLD_KEYGUARD_UNOCCLUDE; import static android.view.WindowManager.TRANSIT_OLD_TASK_CHANGE_WINDOWING_MODE; import static android.view.WindowManager.TRANSIT_OLD_TASK_FRAGMENT_CHANGE; +import static android.view.WindowManager.TRANSIT_OLD_TASK_FRAGMENT_CLOSE; +import static android.view.WindowManager.TRANSIT_OLD_TASK_FRAGMENT_OPEN; import static android.view.WindowManager.TRANSIT_OLD_TASK_OPEN; import static android.view.WindowManager.TRANSIT_OPEN; import static android.view.WindowManager.TRANSIT_TO_FRONT; @@ -44,6 +46,7 @@ import static org.mockito.Mockito.doCallRealMethod; import static org.mockito.Mockito.never; import static org.mockito.Mockito.verify; +import android.annotation.Nullable; import android.os.Binder; import android.os.IBinder; import android.os.RemoteException; @@ -763,59 +766,148 @@ public class AppTransitionControllerTest extends WindowTestsBase { } @Test - public void testGetRemoteAnimationOverrideTaskFragmentOrganizer() { - // TaskFragmentOrganizer registers remote animation. + public void testOverrideTaskFragmentAdapter_overrideWithEmbeddedActivity() { final TaskFragmentOrganizer organizer = new TaskFragmentOrganizer(Runnable::run); - final ITaskFragmentOrganizer iOrganizer = - ITaskFragmentOrganizer.Stub.asInterface(organizer.getOrganizerToken().asBinder()); - final RemoteAnimationDefinition definition = new RemoteAnimationDefinition(); final RemoteAnimationAdapter adapter = new RemoteAnimationAdapter( new TestRemoteAnimationRunner(), 10, 1); - definition.addRemoteAnimation(TRANSIT_OLD_TASK_FRAGMENT_CHANGE, adapter); - mAtm.mTaskFragmentOrganizerController.registerOrganizer(iOrganizer); - mAtm.mTaskFragmentOrganizerController.registerRemoteAnimations(iOrganizer, definition); + setupTaskFragmentRemoteAnimation(organizer, adapter); // Create a TaskFragment with embedded activity. - final TaskFragment taskFragment = new TaskFragmentBuilder(mAtm) - .setParentTask(createTask(mDisplayContent)) - .createActivityCount(1) - .setOrganizer(organizer) - .build(); + final TaskFragment taskFragment = createTaskFragmentWithEmbeddedActivity( + createTask(mDisplayContent), organizer); final ActivityRecord activity = taskFragment.getTopMostActivity(); activity.allDrawn = true; spyOn(mDisplayContent.mAppTransition); - // Prepare a transition for TaskFragment. - mDisplayContent.mAppTransition.prepareAppTransition(TRANSIT_CHANGE, 0); - mDisplayContent.mOpeningApps.add(activity); - mDisplayContent.mChangingContainers.add(taskFragment); - mDisplayContent.mAppTransitionController.handleAppTransitionReady(); + // Prepare a transition. + prepareAndTriggerAppTransition(activity, null /* closingActivity */, taskFragment); - // Check if the transition has been overridden. + // Should be overridden. verify(mDisplayContent.mAppTransition) .overridePendingAppTransitionRemote(adapter, false /* sync */); } @Test + public void testOverrideTaskFragmentAdapter_overrideWithNonEmbeddedActivity() { + final TaskFragmentOrganizer organizer = new TaskFragmentOrganizer(Runnable::run); + final RemoteAnimationAdapter adapter = new RemoteAnimationAdapter( + new TestRemoteAnimationRunner(), 10, 1); + setupTaskFragmentRemoteAnimation(organizer, adapter); + + final Task task = createTask(mDisplayContent); + // Closing non-embedded activity. + final ActivityRecord closingActivity = createActivityRecord(task); + closingActivity.allDrawn = true; + // Opening TaskFragment with embedded activity. + final TaskFragment taskFragment = createTaskFragmentWithEmbeddedActivity(task, organizer); + final ActivityRecord openingActivity = taskFragment.getTopMostActivity(); + openingActivity.allDrawn = true; + spyOn(mDisplayContent.mAppTransition); + + // Prepare a transition. + prepareAndTriggerAppTransition(openingActivity, closingActivity, taskFragment); + + // Should be overridden. + verify(mDisplayContent.mAppTransition) + .overridePendingAppTransitionRemote(adapter, false /* sync */); + } + + @Test + public void testOverrideTaskFragmentAdapter_overrideEmbeddedActivityWithDiffUid() { + final TaskFragmentOrganizer organizer = new TaskFragmentOrganizer(Runnable::run); + final RemoteAnimationAdapter adapter = new RemoteAnimationAdapter( + new TestRemoteAnimationRunner(), 10, 1); + setupTaskFragmentRemoteAnimation(organizer, adapter); + + final Task task = createTask(mDisplayContent); + // Closing TaskFragment with embedded activity. + final TaskFragment taskFragment1 = createTaskFragmentWithEmbeddedActivity(task, organizer); + final ActivityRecord closingActivity = taskFragment1.getTopMostActivity(); + closingActivity.allDrawn = true; + closingActivity.info.applicationInfo.uid = 12345; + // Opening TaskFragment with embedded activity with different UID. + final TaskFragment taskFragment2 = createTaskFragmentWithEmbeddedActivity(task, organizer); + final ActivityRecord openingActivity = taskFragment2.getTopMostActivity(); + openingActivity.info.applicationInfo.uid = 54321; + openingActivity.allDrawn = true; + spyOn(mDisplayContent.mAppTransition); + + // Prepare a transition. + prepareAndTriggerAppTransition(openingActivity, closingActivity, taskFragment1); + + // Should be overridden. + verify(mDisplayContent.mAppTransition) + .overridePendingAppTransitionRemote(adapter, false /* sync */); + } + + @Test + public void testOverrideTaskFragmentAdapter_noOverrideWithTwoApps() { + final TaskFragmentOrganizer organizer = new TaskFragmentOrganizer(Runnable::run); + final RemoteAnimationAdapter adapter = new RemoteAnimationAdapter( + new TestRemoteAnimationRunner(), 10, 1); + setupTaskFragmentRemoteAnimation(organizer, adapter); + + // Closing activity in Task1. + final ActivityRecord closingActivity = createActivityRecord(mDisplayContent); + closingActivity.allDrawn = true; + // Opening TaskFragment with embedded activity in Task2. + final TaskFragment taskFragment = createTaskFragmentWithEmbeddedActivity( + createTask(mDisplayContent), organizer); + final ActivityRecord openingActivity = taskFragment.getTopMostActivity(); + openingActivity.allDrawn = true; + spyOn(mDisplayContent.mAppTransition); + + // Prepare a transition for TaskFragment. + prepareAndTriggerAppTransition(openingActivity, closingActivity, taskFragment); + + // Should not be overridden. + verify(mDisplayContent.mAppTransition, never()) + .overridePendingAppTransitionRemote(adapter, false /* sync */); + } + + @Test + public void testOverrideTaskFragmentAdapter_noOverrideNonEmbeddedActivityWithDiffUid() { + final TaskFragmentOrganizer organizer = new TaskFragmentOrganizer(Runnable::run); + final RemoteAnimationAdapter adapter = new RemoteAnimationAdapter( + new TestRemoteAnimationRunner(), 10, 1); + setupTaskFragmentRemoteAnimation(organizer, adapter); + + final Task task = createTask(mDisplayContent); + // Closing TaskFragment with embedded activity. + final TaskFragment taskFragment = createTaskFragmentWithEmbeddedActivity(task, organizer); + final ActivityRecord closingActivity = taskFragment.getTopMostActivity(); + closingActivity.allDrawn = true; + closingActivity.info.applicationInfo.uid = 12345; + // Opening non-embedded activity with different UID. + final ActivityRecord openingActivity = createActivityRecord(task); + openingActivity.info.applicationInfo.uid = 54321; + openingActivity.allDrawn = true; + spyOn(mDisplayContent.mAppTransition); + + // Prepare a transition. + prepareAndTriggerAppTransition(openingActivity, closingActivity, taskFragment); + + // Should not be overridden + verify(mDisplayContent.mAppTransition, never()) + .overridePendingAppTransitionRemote(adapter, false /* sync */); + } + + @Test public void testTransitionGoodToGoForTaskFragments() { final TaskFragmentOrganizer organizer = new TaskFragmentOrganizer(Runnable::run); final Task task = createTask(mDisplayContent); - final TaskFragment changeTaskFragment = new TaskFragmentBuilder(mAtm) - .setParentTask(task) - .createActivityCount(1) - .setOrganizer(organizer) - .build(); + final TaskFragment changeTaskFragment = + createTaskFragmentWithEmbeddedActivity(task, organizer); final TaskFragment emptyTaskFragment = new TaskFragmentBuilder(mAtm) .setParentTask(task) .setOrganizer(organizer) .build(); changeTaskFragment.getTopMostActivity().allDrawn = true; - mDisplayContent.mAppTransition.prepareAppTransition(TRANSIT_CHANGE, 0); - mDisplayContent.mChangingContainers.add(changeTaskFragment); spyOn(mDisplayContent.mAppTransition); spyOn(emptyTaskFragment); - mDisplayContent.mAppTransitionController.handleAppTransitionReady(); + prepareAndTriggerAppTransition( + null /* openingActivity */, null /* closingActivity*/, changeTaskFragment); // Transition not ready because there is an empty non-finishing TaskFragment. verify(mDisplayContent.mAppTransition, never()).goodToGo(anyInt(), any()); @@ -829,4 +921,34 @@ public class AppTransitionControllerTest extends WindowTestsBase { // removed. verify(mDisplayContent.mAppTransition).goodToGo(anyInt(), any()); } + + /** Registers remote animation for the organizer. */ + private void setupTaskFragmentRemoteAnimation(TaskFragmentOrganizer organizer, + RemoteAnimationAdapter adapter) { + final ITaskFragmentOrganizer iOrganizer = + ITaskFragmentOrganizer.Stub.asInterface(organizer.getOrganizerToken().asBinder()); + final RemoteAnimationDefinition definition = new RemoteAnimationDefinition(); + definition.addRemoteAnimation(TRANSIT_OLD_TASK_FRAGMENT_CHANGE, adapter); + definition.addRemoteAnimation(TRANSIT_OLD_TASK_FRAGMENT_OPEN, adapter); + definition.addRemoteAnimation(TRANSIT_OLD_TASK_FRAGMENT_CLOSE, adapter); + mAtm.mTaskFragmentOrganizerController.registerOrganizer(iOrganizer); + mAtm.mTaskFragmentOrganizerController.registerRemoteAnimations(iOrganizer, definition); + } + + private void prepareAndTriggerAppTransition(@Nullable ActivityRecord openingActivity, + @Nullable ActivityRecord closingActivity, @Nullable TaskFragment changingTaskFragment) { + if (openingActivity != null) { + mDisplayContent.mAppTransition.prepareAppTransition(TRANSIT_OPEN, 0); + mDisplayContent.mOpeningApps.add(openingActivity); + } + if (closingActivity != null) { + mDisplayContent.mAppTransition.prepareAppTransition(TRANSIT_CLOSE, 0); + mDisplayContent.mClosingApps.add(closingActivity); + } + if (changingTaskFragment != null) { + mDisplayContent.mAppTransition.prepareAppTransition(TRANSIT_CHANGE, 0); + mDisplayContent.mChangingContainers.add(changingTaskFragment); + } + mDisplayContent.mAppTransitionController.handleAppTransitionReady(); + } }
\ No newline at end of file diff --git a/services/tests/wmtests/src/com/android/server/wm/AppTransitionTests.java b/services/tests/wmtests/src/com/android/server/wm/AppTransitionTests.java index 405d714256ab..fb8bc7be38ce 100644 --- a/services/tests/wmtests/src/com/android/server/wm/AppTransitionTests.java +++ b/services/tests/wmtests/src/com/android/server/wm/AppTransitionTests.java @@ -35,6 +35,8 @@ import static androidx.test.platform.app.InstrumentationRegistry.getInstrumentat import static com.android.dx.mockito.inline.extended.ExtendedMockito.doNothing; import static com.android.dx.mockito.inline.extended.ExtendedMockito.doReturn; +import static com.android.dx.mockito.inline.extended.ExtendedMockito.spyOn; +import static com.android.dx.mockito.inline.extended.ExtendedMockito.verify; import static com.android.server.wm.WindowContainer.POSITION_TOP; import static org.junit.Assert.assertEquals; @@ -42,6 +44,7 @@ import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertNull; import static org.junit.Assert.assertTrue; import static org.mockito.ArgumentMatchers.any; +import static org.mockito.Mockito.mock; import android.graphics.Rect; import android.os.Binder; @@ -54,6 +57,7 @@ import android.view.IRemoteAnimationFinishedCallback; import android.view.IRemoteAnimationRunner; import android.view.RemoteAnimationAdapter; import android.view.RemoteAnimationTarget; +import android.view.SurfaceControl; import android.view.WindowManager; import android.window.ITaskFragmentOrganizer; import android.window.TaskFragmentOrganizer; @@ -397,7 +401,9 @@ public class AppTransitionTests extends WindowTestsBase { @Test public void testActivityRecordReparentToTaskFragment() { final ActivityRecord activity = createActivityRecord(mDc); + final SurfaceControl activityLeash = mock(SurfaceControl.class); activity.setVisibility(true); + activity.setSurfaceControl(activityLeash); final Task task = activity.getTask(); // Add a TaskFragment of half of the Task size. @@ -412,15 +418,20 @@ public class AppTransitionTests extends WindowTestsBase { final Rect taskBounds = new Rect(); task.getBounds(taskBounds); taskFragment.setBounds(0, 0, taskBounds.right / 2, taskBounds.bottom); + spyOn(taskFragment); assertTrue(mDc.mChangingContainers.isEmpty()); assertFalse(mDc.mAppTransition.isTransitionSet()); // Schedule app transition when reparent activity to a TaskFragment of different size. + final Rect startBounds = new Rect(activity.getBounds()); activity.reparent(taskFragment, POSITION_TOP); - assertTrue(mDc.mChangingContainers.contains(activity)); + // It should transit at TaskFragment level with snapshot on the activity surface. + verify(taskFragment).initializeChangeTransition(activity.getBounds(), activityLeash); + assertTrue(mDc.mChangingContainers.contains(taskFragment)); assertTrue(mDc.mAppTransition.containsTransitRequest(TRANSIT_CHANGE)); + assertEquals(startBounds, taskFragment.mSurfaceFreezer.mFreezeBounds); } private class TestRemoteAnimationRunner implements IRemoteAnimationRunner { diff --git a/services/tests/wmtests/src/com/android/server/wm/TaskFragmentTest.java b/services/tests/wmtests/src/com/android/server/wm/TaskFragmentTest.java new file mode 100644 index 000000000000..cb209abf6aa9 --- /dev/null +++ b/services/tests/wmtests/src/com/android/server/wm/TaskFragmentTest.java @@ -0,0 +1,105 @@ +/* + * Copyright (C) 2021 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.server.wm; + +import static com.android.dx.mockito.inline.extended.ExtendedMockito.doReturn; +import static com.android.dx.mockito.inline.extended.ExtendedMockito.spyOn; +import static com.android.dx.mockito.inline.extended.ExtendedMockito.verify; + +import static org.mockito.Mockito.clearInvocations; + +import android.graphics.Rect; +import android.platform.test.annotations.Presubmit; +import android.view.SurfaceControl; +import android.window.ITaskFragmentOrganizer; +import android.window.TaskFragmentOrganizer; + +import androidx.test.filters.MediumTest; + +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.Mock; +import org.mockito.MockitoAnnotations; + +/** + * Test class for {@link TaskFragment}. + * + * Build/Install/Run: + * atest WmTests:TaskFragmentTest + */ +@MediumTest +@Presubmit +@RunWith(WindowTestRunner.class) +public class TaskFragmentTest extends WindowTestsBase { + + private TaskFragmentOrganizer mOrganizer; + private TaskFragment mTaskFragment; + private SurfaceControl mLeash; + @Mock + private SurfaceControl.Transaction mTransaction; + + @Before + public void setup() { + MockitoAnnotations.initMocks(this); + mOrganizer = new TaskFragmentOrganizer(Runnable::run); + final ITaskFragmentOrganizer iOrganizer = + ITaskFragmentOrganizer.Stub.asInterface(mOrganizer.getOrganizerToken().asBinder()); + mAtm.mWindowOrganizerController.mTaskFragmentOrganizerController + .registerOrganizer(iOrganizer); + mTaskFragment = new TaskFragmentBuilder(mAtm) + .setCreateParentTask() + .setOrganizer(mOrganizer) + .build(); + mLeash = mTaskFragment.getSurfaceControl(); + spyOn(mTaskFragment); + doReturn(mTransaction).when(mTaskFragment).getSyncTransaction(); + doReturn(mTransaction).when(mTaskFragment).getPendingTransaction(); + } + + @Test + public void testOnConfigurationChanged_updateSurface() { + final Rect bounds = new Rect(100, 100, 1100, 1100); + mTaskFragment.setBounds(bounds); + + verify(mTransaction).setPosition(mLeash, 100, 100); + verify(mTransaction).setWindowCrop(mLeash, 1000, 1000); + } + + @Test + public void testStartChangeTransition_resetSurface() { + final Rect startBounds = new Rect(0, 0, 1000, 1000); + final Rect endBounds = new Rect(500, 500, 1000, 1000); + mTaskFragment.setBounds(startBounds); + doReturn(true).when(mTaskFragment).isVisible(); + + clearInvocations(mTransaction); + mTaskFragment.setBounds(endBounds); + + // Surface reset when prepare transition. + verify(mTaskFragment).initializeChangeTransition(startBounds); + verify(mTransaction).setPosition(mLeash, 0, 0); + verify(mTransaction).setWindowCrop(mLeash, 0, 0); + + clearInvocations(mTransaction); + mTaskFragment.mSurfaceFreezer.unfreeze(mTransaction); + + // Update surface after animation. + verify(mTransaction).setPosition(mLeash, 500, 500); + verify(mTransaction).setWindowCrop(mLeash, 500, 500); + } +} diff --git a/services/tests/wmtests/src/com/android/server/wm/WindowTestsBase.java b/services/tests/wmtests/src/com/android/server/wm/WindowTestsBase.java index 6626aa46e7da..8ec1bd6c5787 100644 --- a/services/tests/wmtests/src/com/android/server/wm/WindowTestsBase.java +++ b/services/tests/wmtests/src/com/android/server/wm/WindowTestsBase.java @@ -699,6 +699,15 @@ class WindowTestsBase extends SystemServiceTestsBase { return builder.build(); } + static TaskFragment createTaskFragmentWithEmbeddedActivity(@NonNull Task parentTask, + TaskFragmentOrganizer organizer) { + return new TaskFragmentBuilder(parentTask.mAtmService) + .setParentTask(parentTask) + .createActivityCount(1) + .setOrganizer(organizer) + .build(); + } + /** Creates a {@link DisplayContent} that supports IME and adds it to the system. */ DisplayContent createNewDisplay() { return createNewDisplayWithImeSupport(DISPLAY_IME_POLICY_LOCAL); diff --git a/telephony/common/com/android/internal/telephony/TelephonyPermissions.java b/telephony/common/com/android/internal/telephony/TelephonyPermissions.java index 4d81b5e54470..7a424c87d1d6 100644 --- a/telephony/common/com/android/internal/telephony/TelephonyPermissions.java +++ b/telephony/common/com/android/internal/telephony/TelephonyPermissions.java @@ -308,6 +308,12 @@ public final class TelephonyPermissions { return checkPrivilegedReadPermissionOrCarrierPrivilegePermission( context, subId, callingPackage, callingFeatureId, message, false, reportFailure); } + + private static void throwSecurityExceptionAsUidDoesNotHaveAccess(String message, int uid) { + throw new SecurityException(message + ": The uid " + uid + + " does not meet the requirements to access device identifiers."); + } + /** * Checks whether the app with the given pid/uid can read device identifiers. * @@ -343,9 +349,14 @@ public final class TelephonyPermissions { LegacyPermissionManager permissionManager = (LegacyPermissionManager) context.getSystemService(Context.LEGACY_PERMISSION_SERVICE); - if (permissionManager.checkDeviceIdentifierAccess(callingPackage, message, callingFeatureId, - pid, uid) == PackageManager.PERMISSION_GRANTED) { - return true; + try { + if (permissionManager.checkDeviceIdentifierAccess(callingPackage, message, + callingFeatureId, + pid, uid) == PackageManager.PERMISSION_GRANTED) { + return true; + } + } catch (SecurityException se) { + throwSecurityExceptionAsUidDoesNotHaveAccess(message, uid); } if (reportFailure) { @@ -410,8 +421,8 @@ public final class TelephonyPermissions { return false; } } - throw new SecurityException(message + ": The user " + uid - + " does not meet the requirements to access device identifiers."); + throwSecurityExceptionAsUidDoesNotHaveAccess(message, uid); + return true; } /** |