summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--libs/WindowManager/Shell/res/drawable/pip_split.xml27
-rw-r--r--libs/WindowManager/Shell/res/layout/pip_menu.xml7
-rw-r--r--libs/WindowManager/Shell/res/values/dimen.xml4
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipMenuView.java2
-rw-r--r--packages/SystemUI/animation/src/com/android/systemui/animation/DialogLaunchAnimator.kt40
-rw-r--r--packages/SystemUI/res/values-sw600dp-land/config.xml1
-rw-r--r--packages/SystemUI/res/values-sw600dp-land/dimens.xml28
-rw-r--r--packages/SystemUI/res/values/dimens.xml7
-rw-r--r--packages/SystemUI/shared/src/com/android/systemui/shared/recents/model/Task.java9
-rw-r--r--packages/SystemUI/src/com/android/keyguard/LockIconViewController.java18
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardClockPositionAlgorithm.java39
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelViewController.java7
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/animation/DialogLaunchAnimatorTest.kt9
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/keyguard/LockIconViewControllerTest.java10
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/KeyguardClockPositionAlgorithmTest.java30
-rw-r--r--services/core/java/com/android/server/display/DisplayManagerService.java31
-rwxr-xr-xservices/core/java/com/android/server/notification/NotificationManagerService.java17
-rw-r--r--services/core/java/com/android/server/wm/Task.java6
-rw-r--r--services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTest.java68
-rw-r--r--services/tests/uiservicestests/src/com/android/server/notification/BuzzBeepBlinkTest.java58
20 files changed, 340 insertions, 78 deletions
diff --git a/libs/WindowManager/Shell/res/drawable/pip_split.xml b/libs/WindowManager/Shell/res/drawable/pip_split.xml
new file mode 100644
index 000000000000..2cfdf6ed259b
--- /dev/null
+++ b/libs/WindowManager/Shell/res/drawable/pip_split.xml
@@ -0,0 +1,27 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ ~ 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.
+ -->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="@dimen/pip_expand_action_inner_size"
+ android:height="@dimen/pip_expand_action_inner_size"
+ android:viewportWidth="24"
+ android:viewportHeight="24">
+
+ <path
+ android:fillColor="#FFFFFF"
+ android:pathData="M20,18h-5V6h5V18z M22,18V6c0-1.1-0.9-2-2-2h-5c-1.1,0-2,0.9-2,2v12c0,1.1,0.9,2,2,2h5C21.1,20,22,19.1,22,18z M9,18H4V6h5
+ V18z M11,18V6c0-1.1-0.9-2-2-2H4C2.9,4,2,4.9,2,6v12c0,1.1,0.9,2,2,2h5C10.1,20,11,19.1,11,18z" />
+</vector>
diff --git a/libs/WindowManager/Shell/res/layout/pip_menu.xml b/libs/WindowManager/Shell/res/layout/pip_menu.xml
index 7dc2f31e9871..1dd17bad155b 100644
--- a/libs/WindowManager/Shell/res/layout/pip_menu.xml
+++ b/libs/WindowManager/Shell/res/layout/pip_menu.xml
@@ -81,12 +81,13 @@
<ImageButton
android:id="@+id/enter_split"
- android:layout_width="@dimen/pip_action_size"
- android:layout_height="@dimen/pip_action_size"
+ android:layout_width="@dimen/pip_split_icon_size"
+ android:layout_height="@dimen/pip_split_icon_size"
android:layout_gravity="top|start"
+ android:layout_margin="@dimen/pip_split_icon_margin"
android:gravity="center"
android:contentDescription="@string/pip_phone_enter_split"
- android:src="@drawable/pip_expand"
+ android:src="@drawable/pip_split"
android:background="?android:selectableItemBackgroundBorderless" />
</LinearLayout>
diff --git a/libs/WindowManager/Shell/res/values/dimen.xml b/libs/WindowManager/Shell/res/values/dimen.xml
index e9b9ec3f7d89..9e77578eafd8 100644
--- a/libs/WindowManager/Shell/res/values/dimen.xml
+++ b/libs/WindowManager/Shell/res/values/dimen.xml
@@ -67,6 +67,10 @@
<dimen name="pip_resize_handle_margin">4dp</dimen>
<dimen name="pip_resize_handle_padding">0dp</dimen>
+ <!-- PIP Split icon size and margin. -->
+ <dimen name="pip_split_icon_size">24dp</dimen>
+ <dimen name="pip_split_icon_margin">12dp</dimen>
+
<!-- PIP stash offset size, which is the width of visible PIP region when stashed. -->
<dimen name="pip_stash_offset">32dp</dimen>
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipMenuView.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipMenuView.java
index 7bbebe5bf287..b209699c1a19 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipMenuView.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipMenuView.java
@@ -99,7 +99,7 @@ public class PipMenuView extends FrameLayout {
private static final float MENU_BACKGROUND_ALPHA = 0.3f;
private static final float DISABLED_ACTION_ALPHA = 0.54f;
- private static final boolean ENABLE_ENTER_SPLIT = false;
+ private static final boolean ENABLE_ENTER_SPLIT = true;
private int mMenuState;
private boolean mAllowMenuTimeout = true;
diff --git a/packages/SystemUI/animation/src/com/android/systemui/animation/DialogLaunchAnimator.kt b/packages/SystemUI/animation/src/com/android/systemui/animation/DialogLaunchAnimator.kt
index faa7554525c5..413612ff9a76 100644
--- a/packages/SystemUI/animation/src/com/android/systemui/animation/DialogLaunchAnimator.kt
+++ b/packages/SystemUI/animation/src/com/android/systemui/animation/DialogLaunchAnimator.kt
@@ -54,8 +54,12 @@ class DialogLaunchAnimator(
private val TAG_LAUNCH_ANIMATION_RUNNING = R.id.launch_animation_running
}
+ /**
+ * The set of dialogs that were animated using this animator and that are still opened (not
+ * dismissed, but can be hidden).
+ */
// TODO(b/201264644): Remove this set.
- private val currentAnimations = hashSetOf<DialogLaunchAnimation>()
+ private val openedDialogs = hashSetOf<AnimatedDialog>()
/**
* Show [dialog] by expanding it from [view]. If [animateBackgroundBoundsChange] is true, then
@@ -79,21 +83,29 @@ class DialogLaunchAnimator(
"the main thread")
}
+ // If the parent of the view we are launching from is the background of some other animated
+ // dialog, then this means the caller intent is to launch a dialog from another dialog. In
+ // this case, we also animate the parent (which is the dialog background).
+ val dialogContentParent = openedDialogs
+ .firstOrNull { it.dialogContentParent == view.parent }
+ ?.dialogContentParent
+ val animateFrom = dialogContentParent ?: view
+
// Make sure we don't run the launch animation from the same view twice at the same time.
- if (view.getTag(TAG_LAUNCH_ANIMATION_RUNNING) != null) {
+ if (animateFrom.getTag(TAG_LAUNCH_ANIMATION_RUNNING) != null) {
Log.e(TAG, "Not running dialog launch animation as there is already one running")
dialog.show()
return dialog
}
- view.setTag(TAG_LAUNCH_ANIMATION_RUNNING, true)
+ animateFrom.setTag(TAG_LAUNCH_ANIMATION_RUNNING, true)
- val launchAnimation = DialogLaunchAnimation(
- context, launchAnimator, hostDialogProvider, view,
- onDialogDismissed = { currentAnimations.remove(it) }, originalDialog = dialog,
+ val launchAnimation = AnimatedDialog(
+ context, launchAnimator, hostDialogProvider, animateFrom,
+ onDialogDismissed = { openedDialogs.remove(it) }, originalDialog = dialog,
animateBackgroundBoundsChange)
val hostDialog = launchAnimation.hostDialog
- currentAnimations.add(launchAnimation)
+ openedDialogs.add(launchAnimation)
// If the dialog is dismissed/hidden/shown, then we should actually dismiss/hide/show the
// host dialog.
@@ -151,7 +163,7 @@ class DialogLaunchAnimator(
* TODO(b/193634619): Remove this function and animate dialog into opening activity instead.
*/
fun disableAllCurrentDialogsExitAnimations() {
- currentAnimations.forEach { it.exitAnimationDisabled = true }
+ openedDialogs.forEach { it.exitAnimationDisabled = true }
}
}
@@ -206,7 +218,7 @@ interface DialogListener {
fun onSizeChanged()
}
-private class DialogLaunchAnimation(
+private class AnimatedDialog(
private val context: Context,
private val launchAnimator: LaunchAnimator,
hostDialogProvider: HostDialogProvider,
@@ -215,10 +227,10 @@ private class DialogLaunchAnimation(
private val touchSurface: View,
/**
- * A callback that will be called with this [DialogLaunchAnimation] after the dialog was
+ * A callback that will be called with this [AnimatedDialog] after the dialog was
* dismissed and the exit animation is done.
*/
- private val onDialogDismissed: (DialogLaunchAnimation) -> Unit,
+ private val onDialogDismissed: (AnimatedDialog) -> Unit,
/** The original dialog whose content will be shown and animate in/out in [hostDialog]. */
private val originalDialog: Dialog,
@@ -241,7 +253,7 @@ private class DialogLaunchAnimation(
* the same size as the original dialog window and to which we will set the original dialog
* window background.
*/
- private val dialogContentParent = FrameLayout(context)
+ val dialogContentParent = FrameLayout(context)
/**
* The background color of [originalDialogView], taking into consideration the [originalDialog]
@@ -574,7 +586,7 @@ private class DialogLaunchAnimation(
}
dismissDialogs(false /* instantDismiss */)
- onDialogDismissed(this@DialogLaunchAnimation)
+ onDialogDismissed(this@AnimatedDialog)
return
}
@@ -610,7 +622,7 @@ private class DialogLaunchAnimation(
// and instantly dismiss the dialog.
GhostView.removeGhost(touchSurface)
dismissDialogs(true /* instantDismiss */)
- onDialogDismissed(this@DialogLaunchAnimation)
+ onDialogDismissed(this@AnimatedDialog)
return true
}
diff --git a/packages/SystemUI/res/values-sw600dp-land/config.xml b/packages/SystemUI/res/values-sw600dp-land/config.xml
index 040df865bfe5..362e18d785ac 100644
--- a/packages/SystemUI/res/values-sw600dp-land/config.xml
+++ b/packages/SystemUI/res/values-sw600dp-land/config.xml
@@ -33,5 +33,4 @@
<!-- Notifications are sized to match the width of two (of 4) qs tiles in landscape. -->
<bool name="config_skinnyNotifsInLandscape">false</bool>
- <dimen name="keyguard_indication_margin_bottom">25dp</dimen>
</resources>
diff --git a/packages/SystemUI/res/values-sw600dp-land/dimens.xml b/packages/SystemUI/res/values-sw600dp-land/dimens.xml
new file mode 100644
index 000000000000..3cfe05638032
--- /dev/null
+++ b/packages/SystemUI/res/values-sw600dp-land/dimens.xml
@@ -0,0 +1,28 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ * 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.
+*/
+-->
+<resources>
+
+ <!-- keyguard-->
+ <dimen name="keyguard_indication_margin_bottom">25dp</dimen>
+ <dimen name="ambient_indication_margin_bottom">115dp</dimen>
+ <dimen name="lock_icon_margin_bottom">60dp</dimen>
+
+ <!-- margin from keyguard status bar to clock. For split shade it should be
+ keyguard_split_shade_top_margin - status_bar_header_height_keyguard = 8dp -->
+ <dimen name="keyguard_clock_top_margin">8dp</dimen>
+</resources>
diff --git a/packages/SystemUI/res/values/dimens.xml b/packages/SystemUI/res/values/dimens.xml
index e5ea3688ec0d..3b3dc3899f33 100644
--- a/packages/SystemUI/res/values/dimens.xml
+++ b/packages/SystemUI/res/values/dimens.xml
@@ -753,6 +753,9 @@
<!-- Minimum distance the user has to drag down to go to the full shade. -->
<dimen name="keyguard_drag_down_min_distance">100dp</dimen>
+ <!-- The margin from the top of the screen to notifications and keyguard status view in
+ split shade on keyguard-->
+ <dimen name="keyguard_split_shade_top_margin">68dp</dimen>
<!-- The margin between the status view and the notifications on Keyguard.-->
<dimen name="keyguard_status_view_bottom_margin">20dp</dimen>
<!-- Minimum margin between clock and status bar -->
@@ -935,7 +938,9 @@
<dimen name="keyguard_lock_padding">20dp</dimen>
<dimen name="keyguard_indication_margin_bottom">32dp</dimen>
- <dimen name="lock_icon_margin_bottom">98dp</dimen>
+ <dimen name="lock_icon_margin_bottom">110dp</dimen>
+ <dimen name="ambient_indication_margin_bottom">71dp</dimen>
+
<!-- The text size for battery level -->
<dimen name="battery_level_text_size">12sp</dimen>
diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/recents/model/Task.java b/packages/SystemUI/shared/src/com/android/systemui/shared/recents/model/Task.java
index 3f2ff742d072..3128ffdbc67b 100644
--- a/packages/SystemUI/shared/src/com/android/systemui/shared/recents/model/Task.java
+++ b/packages/SystemUI/shared/src/com/android/systemui/shared/recents/model/Task.java
@@ -23,17 +23,14 @@ import android.app.ActivityManager.TaskDescription;
import android.app.TaskInfo;
import android.content.ComponentName;
import android.content.Intent;
-import android.content.pm.ActivityInfo;
-import android.graphics.Color;
import android.graphics.drawable.Drawable;
import android.os.Parcel;
import android.os.Parcelable;
import android.view.ViewDebug;
-import com.android.systemui.shared.recents.utilities.Utilities;
+import androidx.annotation.Nullable;
import java.io.PrintWriter;
-import java.util.ArrayList;
import java.util.Objects;
/**
@@ -202,8 +199,8 @@ public class Task {
* The icon is the task description icon (if provided), which falls back to the activity icon,
* which can then fall back to the application icon.
*/
- public Drawable icon;
- public ThumbnailData thumbnail;
+ @Nullable public Drawable icon;
+ @Nullable public ThumbnailData thumbnail;
@ViewDebug.ExportedProperty(category="recents")
@Deprecated
public String title;
diff --git a/packages/SystemUI/src/com/android/keyguard/LockIconViewController.java b/packages/SystemUI/src/com/android/keyguard/LockIconViewController.java
index c7be3ce01c54..88476398e09d 100644
--- a/packages/SystemUI/src/com/android/keyguard/LockIconViewController.java
+++ b/packages/SystemUI/src/com/android/keyguard/LockIconViewController.java
@@ -43,6 +43,7 @@ import android.view.GestureDetector.SimpleOnGestureListener;
import android.view.LayoutInflater;
import android.view.MotionEvent;
import android.view.View;
+import android.view.WindowManager;
import android.view.accessibility.AccessibilityManager;
import android.view.accessibility.AccessibilityNodeInfo;
@@ -85,7 +86,6 @@ public class LockIconViewController extends ViewController<LockIconView> impleme
private static final float sDefaultDensity =
(float) DisplayMetrics.DENSITY_DEVICE_STABLE / (float) DisplayMetrics.DENSITY_DEFAULT;
private static final int sLockIconRadiusPx = (int) (sDefaultDensity * 36);
- private static final float sDistAboveKgBottomAreaPx = sDefaultDensity * 12;
private static final AudioAttributes VIBRATION_SONIFICATION_ATTRIBUTES =
new AudioAttributes.Builder()
.setContentType(AudioAttributes.CONTENT_TYPE_SONIFICATION)
@@ -126,7 +126,7 @@ public class LockIconViewController extends ViewController<LockIconView> impleme
private boolean mUdfpsSupported;
private float mHeightPixels;
private float mWidthPixels;
- private int mBottomPadding; // in pixels
+ private int mBottomPaddingPx;
private boolean mShowUnlockIcon;
private boolean mShowLockIcon;
@@ -347,11 +347,11 @@ public class LockIconViewController extends ViewController<LockIconView> impleme
}
private void updateConfiguration() {
- final DisplayMetrics metrics = mView.getContext().getResources().getDisplayMetrics();
- mWidthPixels = metrics.widthPixels;
- mHeightPixels = metrics.heightPixels;
- mBottomPadding = mView.getContext().getResources().getDimensionPixelSize(
- R.dimen.lock_icon_margin_bottom);
+ WindowManager windowManager = getContext().getSystemService(WindowManager.class);
+ Rect bounds = windowManager.getCurrentWindowMetrics().getBounds();
+ mWidthPixels = bounds.right;
+ mHeightPixels = bounds.bottom;
+ mBottomPaddingPx = getResources().getDimensionPixelSize(R.dimen.lock_icon_margin_bottom);
mUnlockedLabel = mView.getContext().getResources().getString(
R.string.accessibility_unlock_button);
@@ -370,8 +370,8 @@ public class LockIconViewController extends ViewController<LockIconView> impleme
} else {
mView.setCenterLocation(
new PointF(mWidthPixels / 2,
- mHeightPixels - mBottomPadding - sDistAboveKgBottomAreaPx
- - sLockIconRadiusPx), sLockIconRadiusPx);
+ mHeightPixels - mBottomPaddingPx - sLockIconRadiusPx),
+ sLockIconRadiusPx);
}
mView.getHitRect(mSensorTouchLocation);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardClockPositionAlgorithm.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardClockPositionAlgorithm.java
index 4f3bbdbff030..7ca8652e1b3c 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardClockPositionAlgorithm.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardClockPositionAlgorithm.java
@@ -57,21 +57,6 @@ public class KeyguardClockPositionAlgorithm {
private int mUserSwitchPreferredY;
/**
- * Whether or not there is a custom clock face on keyguard.
- */
- private boolean mHasCustomClock;
-
- /**
- * Whether or not the NSSL contains any visible notifications.
- */
- private boolean mHasVisibleNotifs;
-
- /**
- * Height of notification stack: Sum of height of each notification.
- */
- private int mNotificationStackHeight;
-
- /**
* Minimum top margin to avoid overlap with status bar, lock icon, or multi-user switcher
* avatar.
*/
@@ -88,6 +73,16 @@ public class KeyguardClockPositionAlgorithm {
private int mContainerTopPadding;
/**
+ * Top margin of notifications introduced by presence of split shade header / status bar
+ */
+ private int mSplitShadeTopNotificationsMargin;
+
+ /**
+ * Target margin for notifications and clock from the top of the screen in split shade
+ */
+ private int mSplitShadeTargetTopMargin;
+
+ /**
* @see NotificationPanelViewController#getExpandedFraction()
*/
private float mPanelExpansion;
@@ -152,6 +147,10 @@ public class KeyguardClockPositionAlgorithm {
public void loadDimens(Resources res) {
mStatusViewBottomMargin = res.getDimensionPixelSize(
R.dimen.keyguard_status_view_bottom_margin);
+ mSplitShadeTopNotificationsMargin =
+ res.getDimensionPixelSize(R.dimen.split_shade_header_height);
+ mSplitShadeTargetTopMargin =
+ res.getDimensionPixelSize(R.dimen.keyguard_split_shade_top_margin);
mContainerTopPadding =
res.getDimensionPixelSize(R.dimen.keyguard_clock_top_margin);
@@ -214,7 +213,7 @@ public class KeyguardClockPositionAlgorithm {
if (mBypassEnabled) {
return (int) (mUnlockedStackScrollerPadding + mOverStretchAmount);
} else if (mIsSplitShade) {
- return clockYPosition;
+ return Math.max(0, clockYPosition - mSplitShadeTopNotificationsMargin);
} else {
return clockYPosition + mKeyguardStatusHeight;
}
@@ -224,14 +223,18 @@ public class KeyguardClockPositionAlgorithm {
if (mBypassEnabled) {
return mUnlockedStackScrollerPadding;
} else if (mIsSplitShade) {
- return mMinTopMargin;
+ return Math.max(mSplitShadeTargetTopMargin, mMinTopMargin);
} else {
return mMinTopMargin + mKeyguardStatusHeight;
}
}
private int getExpandedPreferredClockY() {
- return mMinTopMargin + mUserSwitchHeight;
+ if (mIsSplitShade) {
+ return Math.max(mSplitShadeTargetTopMargin, mMinTopMargin);
+ } else {
+ return mMinTopMargin;
+ }
}
public int getLockscreenStatusViewHeight() {
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 32659e416535..dedd6daf49a6 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelViewController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelViewController.java
@@ -3558,7 +3558,12 @@ public class NotificationPanelViewController extends PanelViewController {
mNotificationStackScrollLayoutController.setPulsing(pulsing, animatePulse);
}
- public void setAmbientIndicationBottomPadding(int ambientIndicationBottomPadding) {
+ public void setAmbientIndicationTop(int ambientIndicationTop, boolean ambientTextVisible) {
+ int ambientIndicationBottomPadding = 0;
+ if (ambientTextVisible) {
+ int stackBottom = mNotificationStackScrollLayoutController.getView().getBottom();
+ ambientIndicationBottomPadding = stackBottom - ambientIndicationTop;
+ }
if (mAmbientIndicationBottomPadding != ambientIndicationBottomPadding) {
mAmbientIndicationBottomPadding = ambientIndicationBottomPadding;
updateMaxDisplayedNotifications(true);
diff --git a/packages/SystemUI/tests/src/com/android/systemui/animation/DialogLaunchAnimatorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/animation/DialogLaunchAnimatorTest.kt
index 209df6b54f8f..d4c3840356d1 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/animation/DialogLaunchAnimatorTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/animation/DialogLaunchAnimatorTest.kt
@@ -86,7 +86,14 @@ class DialogLaunchAnimatorTest : SysuiTestCase() {
assertFalse(dialog.isShowing)
assertFalse(dialog.onStopCalled)
- runOnMainThreadAndWaitForIdleSync { dialog.dismiss() }
+ runOnMainThreadAndWaitForIdleSync {
+ // TODO(b/204561691): Remove this call to disableAllCurrentDialogsExitAnimations() and
+ // make sure that the test still pass on git_master/cf_x86_64_phone-userdebug in
+ // Forrest.
+ dialogLaunchAnimator.disableAllCurrentDialogsExitAnimations()
+
+ dialog.dismiss()
+ }
assertFalse(hostDialog.isShowing)
assertFalse(dialog.isShowing)
assertTrue(hostDialog.wasDismissed)
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/LockIconViewControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/keyguard/LockIconViewControllerTest.java
index d64319b278b4..e01583e1cb1e 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/LockIconViewControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/LockIconViewControllerTest.java
@@ -32,6 +32,7 @@ import static org.mockito.Mockito.when;
import android.content.Context;
import android.content.res.Resources;
import android.graphics.PointF;
+import android.graphics.Rect;
import android.graphics.drawable.AnimatedStateListDrawable;
import android.hardware.biometrics.BiometricSourceType;
import android.hardware.biometrics.SensorLocationInternal;
@@ -39,10 +40,10 @@ import android.hardware.fingerprint.FingerprintSensorPropertiesInternal;
import android.os.Vibrator;
import android.testing.AndroidTestingRunner;
import android.testing.TestableLooper;
-import android.util.DisplayMetrics;
import android.util.Pair;
import android.view.LayoutInflater;
import android.view.View;
+import android.view.WindowManager;
import android.view.accessibility.AccessibilityManager;
import androidx.test.filters.SmallTest;
@@ -70,6 +71,7 @@ import com.airbnb.lottie.LottieAnimationView;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
+import org.mockito.Answers;
import org.mockito.ArgumentCaptor;
import org.mockito.Captor;
import org.mockito.Mock;
@@ -88,7 +90,7 @@ public class LockIconViewControllerTest extends SysuiTestCase {
private @Mock AnimatedStateListDrawable mIconDrawable;
private @Mock Context mContext;
private @Mock Resources mResources;
- private @Mock DisplayMetrics mDisplayMetrics;
+ private @Mock(answer = Answers.RETURNS_DEEP_STUBS) WindowManager mWindowManager;
private @Mock StatusBarStateController mStatusBarStateController;
private @Mock KeyguardUpdateMonitor mKeyguardUpdateMonitor;
private @Mock KeyguardViewController mKeyguardViewController;
@@ -137,7 +139,9 @@ public class LockIconViewControllerTest extends SysuiTestCase {
when(mLockIconView.getContext()).thenReturn(mContext);
when(mLockIconView.findViewById(R.layout.udfps_aod_lock_icon)).thenReturn(mAodFp);
when(mContext.getResources()).thenReturn(mResources);
- when(mResources.getDisplayMetrics()).thenReturn(mDisplayMetrics);
+ when(mContext.getSystemService(WindowManager.class)).thenReturn(mWindowManager);
+ Rect windowBounds = new Rect(0, 0, 800, 1200);
+ when(mWindowManager.getCurrentWindowMetrics().getBounds()).thenReturn(windowBounds);
when(mResources.getString(R.string.accessibility_unlock_button)).thenReturn(UNLOCKED_LABEL);
when(mResources.getDrawable(anyInt(), any())).thenReturn(mIconDrawable);
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/KeyguardClockPositionAlgorithmTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/KeyguardClockPositionAlgorithmTest.java
index 624bedc30be9..11826954baee 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/KeyguardClockPositionAlgorithmTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/KeyguardClockPositionAlgorithmTest.java
@@ -263,6 +263,34 @@ public class KeyguardClockPositionAlgorithmTest extends SysuiTestCase {
}
@Test
+ public void clockPositionedDependingOnMarginInSplitShade() {
+ when(mResources.getDimensionPixelSize(R.dimen.keyguard_split_shade_top_margin))
+ .thenReturn(400);
+ mClockPositionAlgorithm.loadDimens(mResources);
+ givenLockScreen();
+ mIsSplitShade = true;
+ // WHEN the position algorithm is run
+ positionClock();
+
+ assertThat(mClockPosition.clockY).isEqualTo(400);
+ }
+
+ @Test
+ public void notifPaddingMakesUpToFullMarginInSplitShade() {
+ when(mResources.getDimensionPixelSize(R.dimen.keyguard_split_shade_top_margin))
+ .thenReturn(100);
+ when(mResources.getDimensionPixelSize(R.dimen.split_shade_header_height))
+ .thenReturn(70);
+ mClockPositionAlgorithm.loadDimens(mResources);
+ givenLockScreen();
+ mIsSplitShade = true;
+ // WHEN the position algorithm is run
+ positionClock();
+ // THEN the notif padding makes up lacking margin (margin - header height = 30).
+ assertThat(mClockPosition.stackScrollerPadding).isEqualTo(30);
+ }
+
+ @Test
public void notifPaddingExpandedAlignedWithClockInSplitShadeMode() {
givenLockScreen();
mIsSplitShade = true;
@@ -271,7 +299,7 @@ public class KeyguardClockPositionAlgorithmTest extends SysuiTestCase {
positionClock();
// THEN the padding DOESN'T adjust for keyguard status height.
assertThat(mClockPosition.stackScrollerPaddingExpanded)
- .isEqualTo(mClockPosition.clockYFullyDozing);
+ .isEqualTo(mClockPosition.clockY);
}
@Test
diff --git a/services/core/java/com/android/server/display/DisplayManagerService.java b/services/core/java/com/android/server/display/DisplayManagerService.java
index a3b5e79cc8c7..fadaf1082e46 100644
--- a/services/core/java/com/android/server/display/DisplayManagerService.java
+++ b/services/core/java/com/android/server/display/DisplayManagerService.java
@@ -2862,14 +2862,31 @@ public final class DisplayManagerService extends SystemService {
@Override // Binder call
public void setBrightnessConfigurationForUser(
BrightnessConfiguration c, @UserIdInt int userId, String packageName) {
- mLogicalDisplayMapper.forEachLocked(logicalDisplay -> {
- if (logicalDisplay.getDisplayInfoLocked().type != Display.TYPE_INTERNAL) {
- return;
+ mContext.enforceCallingOrSelfPermission(
+ Manifest.permission.CONFIGURE_DISPLAY_BRIGHTNESS,
+ "Permission required to change the display's brightness configuration");
+ if (userId != UserHandle.getCallingUserId()) {
+ mContext.enforceCallingOrSelfPermission(
+ Manifest.permission.INTERACT_ACROSS_USERS,
+ "Permission required to change the display brightness"
+ + " configuration of another user");
+ }
+ final long token = Binder.clearCallingIdentity();
+ try {
+ synchronized (mSyncRoot) {
+ mLogicalDisplayMapper.forEachLocked(logicalDisplay -> {
+ if (logicalDisplay.getDisplayInfoLocked().type != Display.TYPE_INTERNAL) {
+ return;
+ }
+ final DisplayDevice displayDevice =
+ logicalDisplay.getPrimaryDisplayDeviceLocked();
+ setBrightnessConfigurationForDisplayInternal(c, displayDevice.getUniqueId(),
+ userId, packageName);
+ });
}
- final DisplayDevice displayDevice = logicalDisplay.getPrimaryDisplayDeviceLocked();
- setBrightnessConfigurationForDisplay(c, displayDevice.getUniqueId(), userId,
- packageName);
- });
+ } finally {
+ Binder.restoreCallingIdentity(token);
+ }
}
@Override // Binder call
diff --git a/services/core/java/com/android/server/notification/NotificationManagerService.java b/services/core/java/com/android/server/notification/NotificationManagerService.java
index 2f550c6e9338..47d8022a5acc 100755
--- a/services/core/java/com/android/server/notification/NotificationManagerService.java
+++ b/services/core/java/com/android/server/notification/NotificationManagerService.java
@@ -7699,7 +7699,10 @@ public class NotificationManagerService extends SystemService {
final int waitMs = mAudioManager.getFocusRampTimeMs(
AudioManager.AUDIOFOCUS_GAIN_TRANSIENT_MAY_DUCK,
record.getAudioAttributes());
- if (DBG) Slog.v(TAG, "Delaying vibration by " + waitMs + "ms");
+ if (DBG) {
+ Slog.v(TAG, "Delaying vibration for notification "
+ + record.getKey() + " by " + waitMs + "ms");
+ }
try {
Thread.sleep(waitMs);
} catch (InterruptedException e) { }
@@ -7707,9 +7710,17 @@ public class NotificationManagerService extends SystemService {
// so need to check the notification still valide for vibrate.
synchronized (mNotificationLock) {
if (mNotificationsByKey.get(record.getKey()) != null) {
- vibrate(record, effect, true);
+ if (record.getKey().equals(mVibrateNotificationKey)) {
+ vibrate(record, effect, true);
+ } else {
+ if (DBG) {
+ Slog.v(TAG, "No vibration for notification "
+ + record.getKey() + ": a new notification is "
+ + "vibrating, or effects were cleared while waiting");
+ }
+ }
} else {
- Slog.e(TAG, "No vibration for canceled notification : "
+ Slog.w(TAG, "No vibration for canceled notification "
+ record.getKey());
}
}
diff --git a/services/core/java/com/android/server/wm/Task.java b/services/core/java/com/android/server/wm/Task.java
index 4190ff0b9bc7..1b7a012094f6 100644
--- a/services/core/java/com/android/server/wm/Task.java
+++ b/services/core/java/com/android/server/wm/Task.java
@@ -1126,7 +1126,11 @@ class Task extends TaskFragment {
if (inMultiWindowMode() || !hasChild()) return false;
if (intent != null) {
final int returnHomeFlags = FLAG_ACTIVITY_NEW_TASK | FLAG_ACTIVITY_TASK_ON_HOME;
- return intent != null && (intent.getFlags() & returnHomeFlags) == returnHomeFlags;
+ final Task task = getDisplayArea() != null ? getDisplayArea().getRootHomeTask() : null;
+ final boolean isLockTaskModeViolation = task != null
+ && mAtmService.getLockTaskController().isLockTaskModeViolation(task);
+ return (intent.getFlags() & returnHomeFlags) == returnHomeFlags
+ && !isLockTaskModeViolation;
}
final Task bottomTask = getBottomMostTask();
return bottomTask != this && bottomTask.returnsToHomeRootTask();
diff --git a/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTest.java b/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTest.java
index 3ac30d0258a5..cadc81600bbb 100644
--- a/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTest.java
+++ b/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTest.java
@@ -51,6 +51,7 @@ import static com.google.common.truth.Truth.assertThat;
import static com.google.common.truth.Truth.assertWithMessage;
import static org.junit.Assert.fail;
+import static org.junit.Assume.assumeTrue;
import static org.mockito.Matchers.any;
import static org.mockito.Matchers.anyBoolean;
import static org.mockito.Matchers.anyInt;
@@ -184,6 +185,8 @@ public class DevicePolicyManagerTest extends DpmTestBase {
public DevicePolicyManager parentDpm;
public DevicePolicyManagerServiceTestable dpms;
+ private boolean mIsAutomotive;
+
/*
* The CA cert below is the content of cacert.pem as generated by:
*
@@ -266,6 +269,9 @@ public class DevicePolicyManagerTest extends DpmTestBase {
setUpUserManager();
when(getServices().lockPatternUtils.hasSecureLockScreen()).thenReturn(true);
+
+ mIsAutomotive = mContext.getPackageManager()
+ .hasSystemFeature(PackageManager.FEATURE_AUTOMOTIVE);
}
private TransferOwnershipMetadataManager getMockTransferMetadataManager() {
@@ -2117,10 +2123,13 @@ public class DevicePolicyManagerTest extends DpmTestBase {
assertThat(dpm.getPasswordExpirationTimeout(admin1))
.isEqualTo(originalPasswordExpirationTimeout);
- int originalPasswordQuality = dpm.getPasswordQuality(admin1);
- assertExpectException(SecurityException.class, /* messageRegex= */ null,
- () -> dpm.setPasswordQuality(admin1, DevicePolicyManager.PASSWORD_QUALITY_NUMERIC));
- assertThat(dpm.getPasswordQuality(admin1)).isEqualTo(originalPasswordQuality);
+ if (isDeprecatedPasswordApisSupported()) {
+ int originalPasswordQuality = dpm.getPasswordQuality(admin1);
+ assertExpectException(SecurityException.class, /* messageRegex= */ null,
+ () -> dpm.setPasswordQuality(admin1,
+ DevicePolicyManager.PASSWORD_QUALITY_NUMERIC));
+ assertThat(dpm.getPasswordQuality(admin1)).isEqualTo(originalPasswordQuality);
+ }
}
@Test
@@ -5231,6 +5240,8 @@ public class DevicePolicyManagerTest extends DpmTestBase {
@Test
public void testIsActivePasswordSufficient() throws Exception {
+ assumeDeprecatedPasswordApisSupported();
+
mContext.binder.callingUid = DpmMockContext.CALLER_SYSTEM_USER_UID;
mContext.packageName = admin1.getPackageName();
setupDeviceOwner();
@@ -5283,6 +5294,8 @@ public class DevicePolicyManagerTest extends DpmTestBase {
@Test
public void testIsActivePasswordSufficient_noLockScreen() throws Exception {
+ assumeDeprecatedPasswordApisSupported();
+
// If there is no lock screen, the password is considered empty no matter what, because
// it provides no security.
when(getServices().lockPatternUtils.hasSecureLockScreen()).thenReturn(false);
@@ -5363,6 +5376,8 @@ public class DevicePolicyManagerTest extends DpmTestBase {
@Test
public void testGetAggregatedPasswordMetrics_IgnoreProfileRequirement()
throws Exception {
+ assumeDeprecatedPasswordApisSupported();
+
final int managedProfileUserId = CALLER_USER_HANDLE;
final int managedProfileAdminUid =
UserHandle.getUid(managedProfileUserId, DpmMockContext.SYSTEM_UID);
@@ -5392,6 +5407,8 @@ public class DevicePolicyManagerTest extends DpmTestBase {
@Test
public void testCanSetPasswordRequirementOnParentPreS() throws Exception {
+ assumeDeprecatedPasswordApisSupported();
+
final int managedProfileUserId = CALLER_USER_HANDLE;
final int managedProfileAdminUid =
UserHandle.getUid(managedProfileUserId, DpmMockContext.SYSTEM_UID);
@@ -5407,6 +5424,8 @@ public class DevicePolicyManagerTest extends DpmTestBase {
@Test
public void testCannotSetPasswordRequirementOnParent() throws Exception {
+ assumeDeprecatedPasswordApisSupported();
+
final int managedProfileUserId = CALLER_USER_HANDLE;
final int managedProfileAdminUid =
UserHandle.getUid(managedProfileUserId, DpmMockContext.SYSTEM_UID);
@@ -5427,6 +5446,8 @@ public class DevicePolicyManagerTest extends DpmTestBase {
@Test
public void isActivePasswordSufficient_SeparateWorkChallenge_ProfileQualityRequirementMet()
throws Exception {
+ assumeDeprecatedPasswordApisSupported();
+
// Create work profile with empty separate challenge
final int managedProfileUserId = 15;
final int managedProfileAdminUid = UserHandle.getUid(managedProfileUserId, 19436);
@@ -5450,6 +5471,8 @@ public class DevicePolicyManagerTest extends DpmTestBase {
@Test
public void isActivePasswordSufficient_SeparateWorkChallenge_ProfileComplexityRequirementMet()
throws Exception {
+ assumeDeprecatedPasswordApisSupported();
+
// Create work profile with empty separate challenge
final int managedProfileUserId = 15;
final int managedProfileAdminUid = UserHandle.getUid(managedProfileUserId, 19436);
@@ -5473,6 +5496,8 @@ public class DevicePolicyManagerTest extends DpmTestBase {
@Test
public void isActivePasswordSufficient_SeparateWorkChallenge_ParentQualityRequirementMet()
throws Exception {
+ assumeDeprecatedPasswordApisSupported();
+
// Create work profile with empty separate challenge
final int managedProfileUserId = 15;
final int managedProfileAdminUid = UserHandle.getUid(managedProfileUserId, 19436);
@@ -5519,6 +5544,8 @@ public class DevicePolicyManagerTest extends DpmTestBase {
@Test
public void isActivePasswordSufficient_UnifiedWorkChallenge_ProfileQualityRequirementMet()
throws Exception {
+ assumeDeprecatedPasswordApisSupported();
+
// Create work profile with unified challenge
final int managedProfileUserId = 15;
final int managedProfileAdminUid = UserHandle.getUid(managedProfileUserId, 19436);
@@ -5565,6 +5592,8 @@ public class DevicePolicyManagerTest extends DpmTestBase {
@Test
public void isActivePasswordSufficient_UnifiedWorkChallenge_ParentQualityRequirementMet()
throws Exception {
+ assumeDeprecatedPasswordApisSupported();
+
// Create work profile with unified challenge
final int managedProfileUserId = 15;
final int managedProfileAdminUid = UserHandle.getUid(managedProfileUserId, 19436);
@@ -5625,6 +5654,8 @@ public class DevicePolicyManagerTest extends DpmTestBase {
@Test
public void testPasswordQualityAppliesToParentPreS() throws Exception {
+ assumeDeprecatedPasswordApisSupported();
+
final int managedProfileUserId = CALLER_USER_HANDLE;
final int managedProfileAdminUid =
UserHandle.getUid(managedProfileUserId, DpmMockContext.SYSTEM_UID);
@@ -7285,6 +7316,8 @@ public class DevicePolicyManagerTest extends DpmTestBase {
@Test
public void testSetRequiredPasswordComplexity_UnauthorizedCallersOnDO() throws Exception {
+ assumeDeprecatedPasswordApisSupported();
+
mContext.binder.callingUid = DpmMockContext.CALLER_SYSTEM_USER_UID;
setupDeviceOwner();
// DO must be able to set it.
@@ -7300,6 +7333,8 @@ public class DevicePolicyManagerTest extends DpmTestBase {
@Test
public void testSetRequiredPasswordComplexity_UnauthorizedCallersOnPO() throws Exception {
+ assumeDeprecatedPasswordApisSupported();
+
mContext.binder.callingUid = DpmMockContext.CALLER_UID;
setupProfileOwner();
// PO must be able to set it.
@@ -7314,6 +7349,8 @@ public class DevicePolicyManagerTest extends DpmTestBase {
@Test
public void testSetRequiredPasswordComplexity_validValuesOnly() throws Exception {
+ assumeDeprecatedPasswordApisSupported();
+
mContext.binder.callingUid = DpmMockContext.CALLER_UID;
setupProfileOwner();
@@ -7335,6 +7372,8 @@ public class DevicePolicyManagerTest extends DpmTestBase {
@Test
public void testSetRequiredPasswordComplexity_setAndGet() throws Exception {
+ assumeDeprecatedPasswordApisSupported();
+
mContext.binder.callingUid = DpmMockContext.CALLER_UID;
setupProfileOwner();
@@ -7348,6 +7387,8 @@ public class DevicePolicyManagerTest extends DpmTestBase {
@Test
public void testSetRequiredPasswordComplexityOnParent_setAndGet() throws Exception {
+ assumeDeprecatedPasswordApisSupported();
+
final int managedProfileUserId = 15;
final int managedProfileAdminUid = UserHandle.getUid(managedProfileUserId, 19436);
@@ -7366,6 +7407,8 @@ public class DevicePolicyManagerTest extends DpmTestBase {
@Test
public void testSetRequiredPasswordComplexity_isSufficient() throws Exception {
+ assumeDeprecatedPasswordApisSupported();
+
mContext.binder.callingUid = DpmMockContext.CALLER_SYSTEM_USER_UID;
mContext.packageName = admin1.getPackageName();
setupDeviceOwner();
@@ -7395,6 +7438,8 @@ public class DevicePolicyManagerTest extends DpmTestBase {
@Test
public void testSetRequiredPasswordComplexity_resetBySettingQuality() throws Exception {
+ assumeDeprecatedPasswordApisSupported();
+
mContext.binder.callingUid = DpmMockContext.CALLER_UID;
setupProfileOwner();
@@ -7407,6 +7452,8 @@ public class DevicePolicyManagerTest extends DpmTestBase {
@Test
public void testSetRequiredPasswordComplexity_overridesQuality() throws Exception {
+ assumeDeprecatedPasswordApisSupported();
+
mContext.binder.callingUid = DpmMockContext.CALLER_UID;
setupProfileOwner();
@@ -7421,6 +7468,8 @@ public class DevicePolicyManagerTest extends DpmTestBase {
@Test
public void testSetRequiredPasswordComplexityFailsWithQualityOnParent() throws Exception {
+ assumeDeprecatedPasswordApisSupported();
+
final int managedProfileUserId = CALLER_USER_HANDLE;
final int managedProfileAdminUid =
UserHandle.getUid(managedProfileUserId, DpmMockContext.SYSTEM_UID);
@@ -7435,6 +7484,8 @@ public class DevicePolicyManagerTest extends DpmTestBase {
@Test
public void testSetQualityOnParentFailsWithComplexityOnProfile() throws Exception {
+ assumeDeprecatedPasswordApisSupported();
+
final int managedProfileUserId = CALLER_USER_HANDLE;
final int managedProfileAdminUid =
UserHandle.getUid(managedProfileUserId, DpmMockContext.SYSTEM_UID);
@@ -8015,4 +8066,13 @@ public class DevicePolicyManagerTest extends DpmTestBase {
when(mContext.getResources().getStringArray(R.array.vendor_policy_exempt_apps))
.thenReturn(new String[0]);
}
+
+ private boolean isDeprecatedPasswordApisSupported() {
+ return !mIsAutomotive;
+ }
+
+ private void assumeDeprecatedPasswordApisSupported() {
+ assumeTrue("device doesn't support deprecated password APIs",
+ isDeprecatedPasswordApisSupported());
+ }
}
diff --git a/services/tests/uiservicestests/src/com/android/server/notification/BuzzBeepBlinkTest.java b/services/tests/uiservicestests/src/com/android/server/notification/BuzzBeepBlinkTest.java
index ea46eab6e8f9..d593e8000048 100644
--- a/services/tests/uiservicestests/src/com/android/server/notification/BuzzBeepBlinkTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/BuzzBeepBlinkTest.java
@@ -32,7 +32,6 @@ import static junit.framework.Assert.assertTrue;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotEquals;
-import static org.junit.Assert.assertNotNull;
import static org.mockito.Matchers.any;
import static org.mockito.Matchers.anyBoolean;
import static org.mockito.Matchers.anyInt;
@@ -40,6 +39,7 @@ import static org.mockito.Matchers.anyObject;
import static org.mockito.Matchers.anyString;
import static org.mockito.Matchers.argThat;
import static org.mockito.Matchers.eq;
+import static org.mockito.Mockito.after;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.spy;
@@ -48,6 +48,7 @@ import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
+import android.annotation.SuppressLint;
import android.app.ActivityManager;
import android.app.KeyguardManager;
import android.app.Notification;
@@ -73,7 +74,6 @@ import android.provider.Settings;
import android.service.notification.NotificationListenerService;
import android.service.notification.StatusBarNotification;
import android.test.suitebuilder.annotation.SmallTest;
-import android.util.Slog;
import android.view.accessibility.AccessibilityEvent;
import android.view.accessibility.AccessibilityManager;
import android.view.accessibility.IAccessibilityManager;
@@ -102,6 +102,7 @@ import java.util.Objects;
@SmallTest
@RunWith(AndroidJUnit4.class)
+@SuppressLint("GuardedBy") // It's ok for this test to access guarded methods from the service.
public class BuzzBeepBlinkTest extends UiServiceTestCase {
@Mock AudioManager mAudioManager;
@@ -156,6 +157,7 @@ public class BuzzBeepBlinkTest extends UiServiceTestCase {
when(mAudioManager.getRingtonePlayer()).thenReturn(mRingtonePlayer);
when(mAudioManager.getStreamVolume(anyInt())).thenReturn(10);
when(mAudioManager.getRingerModeInternal()).thenReturn(AudioManager.RINGER_MODE_NORMAL);
+ when(mAudioManager.getFocusRampTimeMs(anyInt(), any(AudioAttributes.class))).thenReturn(50);
when(mUsageStats.isAlertRateLimited(any())).thenReturn(false);
when(mVibrator.hasFrequencyControl()).thenReturn(false);
when(mKeyguardManager.isDeviceLocked(anyInt())).thenReturn(false);
@@ -444,6 +446,11 @@ public class BuzzBeepBlinkTest extends UiServiceTestCase {
timeout(MAX_VIBRATION_DELAY).times(1));
}
+ private void verifyDelayedNeverVibrate() {
+ verify(mVibrator, after(MAX_VIBRATION_DELAY).never()).vibrate(anyInt(), anyString(), any(),
+ anyString(), any(AudioAttributes.class));
+ }
+
private void verifyVibrate(ArgumentMatcher<VibrationEffect> effectMatcher,
VerificationMode verification) {
ArgumentCaptor<AudioAttributes> captor = ArgumentCaptor.forClass(AudioAttributes.class);
@@ -1588,8 +1595,51 @@ public class BuzzBeepBlinkTest extends UiServiceTestCase {
// beep wasn't reset
verifyNeverBeep();
verifyNeverVibrate();
- verify(mRingtonePlayer, never()).stopAsync();
- verify(mVibrator, never()).cancel();
+ verifyNeverStopAudio();
+ verifyNeverStopVibrate();
+ }
+
+ @Test
+ public void testRingtoneInsistentBeep_clearEffectsStopsSoundAndVibration() throws Exception {
+ NotificationChannel ringtoneChannel =
+ new NotificationChannel("ringtone", "", IMPORTANCE_HIGH);
+ ringtoneChannel.setSound(Uri.fromParts("a", "b", "c"),
+ new AudioAttributes.Builder().setUsage(USAGE_NOTIFICATION_RINGTONE).build());
+ ringtoneChannel.enableVibration(true);
+ NotificationRecord ringtoneNotification = getCallRecord(1, ringtoneChannel, true);
+ mService.addNotification(ringtoneNotification);
+ assertFalse(mService.shouldMuteNotificationLocked(ringtoneNotification));
+ mService.buzzBeepBlinkLocked(ringtoneNotification);
+ verifyBeepLooped();
+ verifyDelayedVibrateLooped();
+
+ mService.clearSoundLocked();
+ mService.clearVibrateLocked();
+
+ verifyStopAudio();
+ verifyStopVibrate();
+ }
+
+ @Test
+ public void testRingtoneInsistentBeep_neverVibratesWhenEffectsClearedBeforeDelay()
+ throws Exception {
+ NotificationChannel ringtoneChannel =
+ new NotificationChannel("ringtone", "", IMPORTANCE_HIGH);
+ ringtoneChannel.setSound(Uri.fromParts("a", "b", "c"),
+ new AudioAttributes.Builder().setUsage(USAGE_NOTIFICATION_RINGTONE).build());
+ ringtoneChannel.enableVibration(true);
+ NotificationRecord ringtoneNotification = getCallRecord(1, ringtoneChannel, true);
+ mService.addNotification(ringtoneNotification);
+ assertFalse(mService.shouldMuteNotificationLocked(ringtoneNotification));
+ mService.buzzBeepBlinkLocked(ringtoneNotification);
+ verifyBeepLooped();
+ verifyNeverVibrate();
+
+ mService.clearSoundLocked();
+ mService.clearVibrateLocked();
+
+ verifyStopAudio();
+ verifyDelayedNeverVibrate();
}
@Test