summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--core/java/android/content/res/Configuration.java82
-rw-r--r--core/java/com/android/internal/app/chooser/DisplayResolveInfo.java7
-rw-r--r--packages/SystemUI/res/values/dimens.xml2
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayout.java2
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackSizeCalculator.kt108
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelViewController.java95
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationStackSizeCalculatorTest.kt88
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationPanelViewControllerTest.java109
-rw-r--r--services/core/java/com/android/server/UiModeManagerService.java16
-rw-r--r--services/core/java/com/android/server/wm/ActivityTaskManagerInternal.java11
-rw-r--r--services/core/java/com/android/server/wm/ActivityTaskManagerService.java6
-rw-r--r--services/tests/uiservicestests/src/com/android/server/UiModeManagerServiceTest.java96
-rw-r--r--services/usb/java/com/android/server/usb/UsbHostManager.java8
-rw-r--r--services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionManagerService.java38
-rw-r--r--services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionManagerServiceImpl.java15
15 files changed, 476 insertions, 207 deletions
diff --git a/core/java/android/content/res/Configuration.java b/core/java/android/content/res/Configuration.java
index 29f21f14088e..3dedc4131251 100644
--- a/core/java/android/content/res/Configuration.java
+++ b/core/java/android/content/res/Configuration.java
@@ -751,9 +751,15 @@ public final class Configuration implements Parcelable, Comparable<Configuration
public static final int SCREEN_WIDTH_DP_UNDEFINED = 0;
/**
- * The current width of the available screen space in dp units, excluding
- * the area occupied by screen decorations at the edges of the display.
- * Corresponds to the
+ * The width of the available screen space in dp units excluding the area
+ * occupied by {@link android.view.WindowInsets window insets}.
+ *
+ * <aside class="note"><b>Note:</b> The width measurement excludes window
+ * insets even when the app is displayed edge to edge using
+ * {@link android.view.Window#setDecorFitsSystemWindows(boolean)
+ * Window#setDecorFitsSystemWindows(boolean)}.</aside>
+ *
+ * <p>Corresponds to the
* <a href="{@docRoot}guide/topics/resources/providing-resources.html#AvailableWidthHeightQualifier">
* available width</a> resource qualifier. Defaults to
* {@link #SCREEN_WIDTH_DP_UNDEFINED} if no width is specified.
@@ -763,21 +769,25 @@ public final class Configuration implements Parcelable, Comparable<Configuration
* (for example, when apps are displayed side by side in split-screen mode
* in landscape orientation).
*
+ * <p>For embedded activities, equals the width of the individual
+ * activities, not the width of the app window or the device screen.
+ *
* <p>In multiple-screen scenarios, the width measurement can span screens.
* For example, if the app is spanning both screens of a dual-screen device
* (with the screens side by side), {@code screenWidthDp} represents the
- * width of both screens, excluding the area occupied by screen decorations.
- * When the app is restricted to a single screen in a multiple-screen
+ * width of both screens excluding the area occupied by window insets. When
+ * the app is restricted to a single screen in a multiple-screen
* environment, {@code screenWidthDp} is the width of the screen on which
- * the app is running.
+ * the app is displayed excluding window insets.
*
* <p>Differs from {@link android.view.WindowMetrics} by not including
- * screen decorations in the width measurement and by expressing the
- * measurement in dp rather than px. Use {@code screenWidthDp} to obtain the
- * horizontal display area available to the app, excluding the area occupied
- * by screen decorations. Use {@link android.view.WindowMetrics#getBounds()}
- * to obtain the width of the display area available to the app, including
- * the area occupied by screen decorations.
+ * window insets in the width measurement and by expressing the measurement
+ * in dp rather than px. Use {@code screenWidthDp} to obtain the width of
+ * the display area available to an app or embedded activity excluding the
+ * area occupied by window insets. Use
+ * {@link android.view.WindowMetrics#getBounds()} to obtain the horizontal
+ * display area available to an app or embedded activity including the area
+ * occupied by window insets.
*/
public int screenWidthDp;
@@ -788,9 +798,16 @@ public final class Configuration implements Parcelable, Comparable<Configuration
public static final int SCREEN_HEIGHT_DP_UNDEFINED = 0;
/**
- * The current height of the available screen space in dp units, excluding
- * the area occupied by screen decorations at the edges of the display (such
- * as the status bar, navigation bar, and cutouts). Corresponds to the
+ * The height of the available screen space in dp units excluding the area
+ * occupied by {@link android.view.WindowInsets window insets}, such as the
+ * status bar, navigation bar, and cutouts.
+ *
+ * <aside class="note"><b>Note:</b> The height measurement excludes window
+ * insets even when the app is displayed edge to edge using
+ * {@link android.view.Window#setDecorFitsSystemWindows(boolean)
+ * Window#setDecorFitsSystemWindows(boolean)}.</aside>
+ *
+ * <p>Corresponds to the
* <a href="{@docRoot}guide/topics/resources/providing-resources.html#AvailableWidthHeightQualifier">
* available height</a> resource qualifier. Defaults to
* {@link #SCREEN_HEIGHT_DP_UNDEFINED} if no height is specified.
@@ -800,22 +817,25 @@ public final class Configuration implements Parcelable, Comparable<Configuration
* (for example, when apps are displayed one above another in split-screen
* mode in portrait orientation).
*
+ * <p>For embedded activities, equals the height of the individual
+ * activities, not the height of the app window or the device screen.
+ *
* <p>In multiple-screen scenarios, the height measurement can span screens.
* For example, if the app is spanning both screens of a dual-screen device
* rotated 90 degrees (one screen above the other), {@code screenHeightDp}
- * represents the height of both screens, excluding the area occupied by
- * screen decorations. When the app is restricted to a single screen in a
+ * represents the height of both screens excluding the area occupied by
+ * window insets. When the app is restricted to a single screen in a
* multiple-screen environment, {@code screenHeightDp} is the height of the
- * screen on which the app is running.
+ * screen on which the app is displayed excluding window insets.
*
* <p>Differs from {@link android.view.WindowMetrics} by not including
- * screen decorations in the height measurement and by expressing the
- * measurement in dp rather than px. Use {@code screenHeightDp} to obtain
- * the vertical display area available to the app, excluding the area
- * occupied by screen decorations. Use
- * {@link android.view.WindowMetrics#getBounds()} to obtain the height of
- * the display area available to the app, including the area occupied by
- * screen decorations.
+ * window insets in the height measurement and by expressing the measurement
+ * in dp rather than px. Use {@code screenHeightDp} to obtain the height of
+ * the display area available to an app or embedded activity excluding the
+ * area occupied by window insets. Use
+ * {@link android.view.WindowMetrics#getBounds()} to obtain the vertical
+ * display area available to an app or embedded activity including the area
+ * occupied by window insets.
*/
public int screenHeightDp;
@@ -826,12 +846,12 @@ public final class Configuration implements Parcelable, Comparable<Configuration
public static final int SMALLEST_SCREEN_WIDTH_DP_UNDEFINED = 0;
/**
- * The smallest screen size an application will see in normal operation,
- * corresponding to
- * <a href="{@docRoot}guide/topics/resources/providing-resources.html#SmallestScreenWidthQualifier">smallest
- * screen width</a> resource qualifier.
- * This is the smallest value of both screenWidthDp and screenHeightDp
- * in both portrait and landscape. Set to
+ * The smallest screen size an application will see in normal operation.
+ * Corresponds to the
+ * <a href="{@docRoot}guide/topics/resources/providing-resources.html#SmallestScreenWidthQualifier">
+ * smallest width</a> resource qualifier. This is the smallest value of
+ * {@link #screenWidthDp} and {@link #screenHeightDp} in both portrait and
+ * landscape orientations. Defaults to
* {@link #SMALLEST_SCREEN_WIDTH_DP_UNDEFINED} if no width is specified.
*/
public int smallestScreenWidthDp;
diff --git a/core/java/com/android/internal/app/chooser/DisplayResolveInfo.java b/core/java/com/android/internal/app/chooser/DisplayResolveInfo.java
index 5ebc9154023c..96cc5e1bd7d2 100644
--- a/core/java/com/android/internal/app/chooser/DisplayResolveInfo.java
+++ b/core/java/com/android/internal/app/chooser/DisplayResolveInfo.java
@@ -35,7 +35,6 @@ import com.android.internal.app.ResolverActivity;
import com.android.internal.app.ResolverListAdapter.ResolveInfoPresentationGetter;
import java.util.ArrayList;
-import java.util.Arrays;
import java.util.List;
/**
@@ -206,7 +205,7 @@ public class DisplayResolveInfo implements TargetInfo, Parcelable {
dest.writeCharSequence(mDisplayLabel);
dest.writeCharSequence(mExtendedInfo);
dest.writeParcelable(mResolvedIntent, 0);
- dest.writeParcelableArray((Intent[]) mSourceIntents.toArray(), 0);
+ dest.writeTypedList(mSourceIntents);
dest.writeBoolean(mIsSuspended);
dest.writeBoolean(mPinned);
dest.writeParcelable(mResolveInfo, 0);
@@ -227,9 +226,7 @@ public class DisplayResolveInfo implements TargetInfo, Parcelable {
mDisplayLabel = in.readCharSequence();
mExtendedInfo = in.readCharSequence();
mResolvedIntent = in.readParcelable(null /* ClassLoader */, android.content.Intent.class);
- mSourceIntents.addAll(
- Arrays.asList((Intent[]) in.readParcelableArray(null /* ClassLoader */,
- Intent.class)));
+ in.readTypedList(mSourceIntents, Intent.CREATOR);
mIsSuspended = in.readBoolean();
mPinned = in.readBoolean();
mResolveInfo = in.readParcelable(null /* ClassLoader */, android.content.pm.ResolveInfo.class);
diff --git a/packages/SystemUI/res/values/dimens.xml b/packages/SystemUI/res/values/dimens.xml
index 45713babf034..66488415ff9f 100644
--- a/packages/SystemUI/res/values/dimens.xml
+++ b/packages/SystemUI/res/values/dimens.xml
@@ -386,7 +386,7 @@
<dimen name="split_shade_notifications_scrim_margin_bottom">0dp</dimen>
- <dimen name="shelf_and_lock_icon_overlap">5dp</dimen>
+ <dimen name="shelf_and_lock_icon_overlap">@dimen/notification_shelf_height</dimen>
<dimen name="notification_panel_margin_horizontal">0dp</dimen>
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayout.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayout.java
index ba57d57d0fd3..270eb13c4224 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayout.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayout.java
@@ -1668,7 +1668,7 @@ public class NotificationStackScrollLayout extends ViewGroup implements Dumpable
continue;
}
float childTop = slidingChild.getTranslationY();
- float top = childTop + slidingChild.getClipTopAmount();
+ float top = childTop + Math.max(0, slidingChild.getClipTopAmount());
float bottom = childTop + slidingChild.getActualHeight()
- slidingChild.getClipBottomAmount();
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackSizeCalculator.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackSizeCalculator.kt
index 22234b1ef17d..ae854e2df91a 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackSizeCalculator.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackSizeCalculator.kt
@@ -39,7 +39,11 @@ private const val TAG = "NotifStackSizeCalc"
private val DEBUG = Compile.IS_DEBUG && Log.isLoggable(TAG, Log.DEBUG)
private val SPEW = Compile.IS_DEBUG && Log.isLoggable(TAG, Log.VERBOSE)
-/** Calculates number of notifications to display and the height of the notification stack. */
+/**
+ * Calculates number of notifications to display and the height of the notification stack.
+ * "Notifications" refers to any ExpandableView that we show on lockscreen, which can include the
+ * media player.
+ */
@SysUISingleton
class NotificationStackSizeCalculator
@Inject
@@ -65,21 +69,60 @@ constructor(
}
/**
- * Given the [totalAvailableSpace] constraint, calculates how many notification to show.
- *
- * This number is only valid in keyguard.
+ * Returns whether notifications and (shelf if visible) can fit in total space available.
+ * [spaceForShelf] is extra vertical space allowed for the shelf to overlap the lock icon.
+ */
+ private fun canStackFitInSpace(
+ stackHeight: StackHeight,
+ spaceForNotifications: Float,
+ spaceForShelf: Float,
+ ): Boolean {
+
+ val (notificationsHeight, shelfHeightWithSpaceBefore) = stackHeight
+ var canFit: Boolean
+
+ if (shelfHeightWithSpaceBefore == 0f) {
+ canFit = notificationsHeight <= spaceForNotifications
+ log {
+ "canStackFitInSpace[$canFit] = notificationsHeight[$notificationsHeight]" +
+ " <= spaceForNotifications[$spaceForNotifications]"
+ }
+ } else {
+ canFit =
+ (notificationsHeight + shelfHeightWithSpaceBefore) <=
+ (spaceForNotifications + spaceForShelf)
+ log {
+ "canStackFitInSpace[$canFit] = (notificationsHeight[$notificationsHeight]" +
+ " + shelfHeightWithSpaceBefore[$shelfHeightWithSpaceBefore])" +
+ " <= (spaceForNotifications[$spaceForNotifications] " +
+ " + spaceForShelf[$spaceForShelf])"
+ }
+ }
+ return canFit
+ }
+
+ /**
+ * Given the [spaceForNotifications] and [spaceForShelf] constraints, calculate how many
+ * notifications to show. This number is only valid in keyguard.
*
* @param totalAvailableSpace space for notifications. This includes the space for the shelf.
*/
fun computeMaxKeyguardNotifications(
stack: NotificationStackScrollLayout,
- totalAvailableSpace: Float,
+ spaceForNotifications: Float,
+ spaceForShelf: Float,
shelfIntrinsicHeight: Float
): Int {
+ log { "\n" }
val stackHeightSequence = computeHeightPerNotificationLimit(stack, shelfIntrinsicHeight)
var maxNotifications =
- stackHeightSequence.lastIndexWhile { stackHeight -> stackHeight <= totalAvailableSpace }
+ stackHeightSequence.lastIndexWhile { heightResult ->
+ canStackFitInSpace(
+ heightResult,
+ spaceForNotifications = spaceForNotifications,
+ spaceForShelf = spaceForShelf)
+ }
if (onLockscreen()) {
maxNotifications = min(maxKeyguardNotifications, maxNotifications)
@@ -90,7 +133,8 @@ constructor(
log {
val sequence = if (SPEW) " stackHeightSequence=${stackHeightSequence.toList()}" else ""
"computeMaxKeyguardNotifications(" +
- "availableSpace=$totalAvailableSpace" +
+ " spaceForNotifications=$spaceForNotifications" +
+ " spaceForShelf=$spaceForShelf" +
" shelfHeight=$shelfIntrinsicHeight) -> $maxNotifications$sequence"
}
return maxNotifications
@@ -112,33 +156,51 @@ constructor(
maxNotifications: Int,
shelfIntrinsicHeight: Float
): Float {
+ log { "\n" }
val heightPerMaxNotifications =
computeHeightPerNotificationLimit(stack, shelfIntrinsicHeight)
- val height =
+
+ val (notificationsHeight, shelfHeightWithSpaceBefore) =
heightPerMaxNotifications.elementAtOrElse(maxNotifications) {
heightPerMaxNotifications.last() // Height with all notifications visible.
}
- log { "computeHeight(maxNotifications=$maxNotifications) -> $height" }
- return height
+ log {
+ "computeHeight(maxNotifications=$maxNotifications," +
+ "shelfIntrinsicHeight=$shelfIntrinsicHeight) -> " +
+ "${notificationsHeight + shelfHeightWithSpaceBefore}" +
+ " = ($notificationsHeight + $shelfHeightWithSpaceBefore)"
+ }
+ return notificationsHeight + shelfHeightWithSpaceBefore
}
- /** The ith result in the sequence is the height with ith max notifications. */
+ private data class StackHeight(
+ // Float height with ith max notifications (not including shelf)
+ val notificationsHeight: Float,
+
+ // Float height of shelf (0 if shelf is not showing), and space before the shelf that
+ // changes during the lockscreen <=> full shade transition.
+ val shelfHeightWithSpaceBefore: Float
+ )
+
private fun computeHeightPerNotificationLimit(
stack: NotificationStackScrollLayout,
- shelfIntrinsicHeight: Float
- ): Sequence<Float> = sequence {
+ shelfHeight: Float
+ ): Sequence<StackHeight> = sequence {
+ log { "computeHeightPerNotificationLimit" }
+
val children = stack.showableChildren().toList()
- var height = 0f
+ var notifications = 0f
var previous: ExpandableView? = null
val onLockscreen = onLockscreen()
- yield(dividerHeight + shelfIntrinsicHeight) // Only shelf.
+ // Only shelf. This should never happen, since we allow 1 view minimum (EmptyViewState).
+ yield(StackHeight(notificationsHeight = 0f, shelfHeightWithSpaceBefore = shelfHeight))
children.forEachIndexed { i, currentNotification ->
- height += spaceNeeded(currentNotification, i, previous, stack, onLockscreen)
+ notifications += spaceNeeded(currentNotification, i, previous, stack, onLockscreen)
previous = currentNotification
- val shelfHeight =
+ val shelfWithSpaceBefore =
if (i == children.lastIndex) {
0f // No shelf needed.
} else {
@@ -149,10 +211,16 @@ constructor(
previous = currentNotification,
current = children[firstViewInShelfIndex],
currentIndex = firstViewInShelfIndex)
- spaceBeforeShelf + shelfIntrinsicHeight
+ spaceBeforeShelf + shelfHeight
}
-
- yield(height + shelfHeight)
+ log {
+ "i=$i notificationsHeight=$notifications " +
+ "shelfHeightWithSpaceBefore=$shelfWithSpaceBefore"
+ }
+ yield(
+ StackHeight(
+ notificationsHeight = notifications,
+ shelfHeightWithSpaceBefore = shelfWithSpaceBefore))
}
}
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 c521161e85f2..ba1088fba6ed 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelViewController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelViewController.java
@@ -1520,50 +1520,77 @@ public class NotificationPanelViewController extends PanelViewController {
return (mQs != null ? mQs.getHeader().getHeight() : 0) + mQsPeekHeight;
}
- /**
- * @return Space available to show notifications on lockscreen.
- */
- @VisibleForTesting
- float getSpaceForLockscreenNotifications() {
- float staticTopPadding = mClockPositionAlgorithm.getLockscreenMinStackScrollerPadding()
- // getMinStackScrollerPadding is from the top of the screen,
- // but we need it from the top of the NSSL.
- - mNotificationStackScrollLayoutController.getTop();
-
- // Space between bottom of notifications and top of lock icon or udfps background.
- float lockIconPadding = mLockIconViewController.getTop();
- if (mLockIconViewController.getTop() != 0) {
+ /** Returns space between top of lock icon and bottom of NotificationStackScrollLayout. */
+ private float getLockIconPadding() {
+ float lockIconPadding = 0f;
+ if (mLockIconViewController.getTop() != 0f) {
lockIconPadding = mNotificationStackScrollLayoutController.getBottom()
- - mLockIconViewController.getTop()
- - mShelfAndLockIconOverlap;
+ - mLockIconViewController.getTop();
}
+ return lockIconPadding;
+ }
+
+ /** Returns space available to show notifications on lockscreen. */
+ @VisibleForTesting
+ float getVerticalSpaceForLockscreenNotifications() {
+ final float lockIconPadding = getLockIconPadding();
float bottomPadding = Math.max(lockIconPadding,
Math.max(mIndicationBottomPadding, mAmbientIndicationBottomPadding));
-
mKeyguardNotificationBottomPadding = bottomPadding;
+
+ float staticTopPadding = mClockPositionAlgorithm.getLockscreenMinStackScrollerPadding()
+ // getMinStackScrollerPadding is from the top of the screen,
+ // but we need it from the top of the NSSL.
+ - mNotificationStackScrollLayoutController.getTop();
mKeyguardNotificationTopPadding = staticTopPadding;
// To debug the available space, enable debug lines in this class. If you change how the
// available space is calculated, please also update those lines.
- float availableSpace =
+ final float verticalSpace =
mNotificationStackScrollLayoutController.getHeight()
- staticTopPadding
- bottomPadding;
if (SPEW_LOGCAT) {
- Log.d(TAG, "getSpaceForLockscreenNotifications()"
- + " availableSpace=" + availableSpace
- + " NSSL.height=" + mNotificationStackScrollLayoutController.getHeight()
- + " NSSL.top=" + mNotificationStackScrollLayoutController.getTop()
- + " staticTopPadding=" + staticTopPadding
- + " bottomPadding=" + bottomPadding
- + " lockIconPadding=" + lockIconPadding
- + " mIndicationBottomPadding=" + mIndicationBottomPadding
- + " mAmbientIndicationBottomPadding=" + mAmbientIndicationBottomPadding
+ Log.i(TAG, "\n");
+ Log.i(TAG, "staticTopPadding[" + staticTopPadding
+ + "] = Clock.padding["
+ + mClockPositionAlgorithm.getLockscreenMinStackScrollerPadding()
+ + "] - NSSLC.top[" + mNotificationStackScrollLayoutController.getTop()
+ + "]"
+ );
+ Log.i(TAG, "bottomPadding[" + bottomPadding
+ + "] = max(ambientIndicationBottomPadding[" + mAmbientIndicationBottomPadding
+ + "], mIndicationBottomPadding[" + mIndicationBottomPadding
+ + "], lockIconPadding[" + lockIconPadding
+ + "])"
+ );
+ Log.i(TAG, "verticalSpaceForNotifications[" + verticalSpace
+ + "] = NSSL.height[" + mNotificationStackScrollLayoutController.getHeight()
+ + "] - staticTopPadding[" + staticTopPadding
+ + "] - bottomPadding[" + bottomPadding
+ + "]"
);
}
- return availableSpace;
+ return verticalSpace;
+ }
+
+ /** Returns extra space available to show the shelf on lockscreen */
+ @VisibleForTesting
+ float getVerticalSpaceForLockscreenShelf() {
+ final float lockIconPadding = getLockIconPadding();
+
+ final float noShelfOverlapBottomPadding =
+ Math.max(mIndicationBottomPadding, mAmbientIndicationBottomPadding);
+
+ final float extraSpaceForShelf = lockIconPadding - noShelfOverlapBottomPadding;
+
+ if (extraSpaceForShelf > 0f) {
+ return Math.min(mNotificationShelfController.getIntrinsicHeight(),
+ extraSpaceForShelf);
+ }
+ return 0f;
}
/**
@@ -1579,16 +1606,12 @@ public class NotificationPanelViewController extends PanelViewController {
}
return mMaxAllowedKeyguardNotifications;
}
-
- final float shelfIntrinsicHeight =
- mNotificationShelfController.getVisibility() == View.GONE
- ? 0
- : mNotificationShelfController.getIntrinsicHeight();
-
return mNotificationStackSizeCalculator.computeMaxKeyguardNotifications(
mNotificationStackScrollLayoutController.getView(),
- getSpaceForLockscreenNotifications(),
- shelfIntrinsicHeight);
+ getVerticalSpaceForLockscreenNotifications(),
+ getVerticalSpaceForLockscreenShelf(),
+ mNotificationShelfController.getIntrinsicHeight()
+ );
}
private void updateClock() {
@@ -3812,7 +3835,7 @@ public class NotificationPanelViewController extends PanelViewController {
public void setAmbientIndicationTop(int ambientIndicationTop, boolean ambientTextVisible) {
int ambientIndicationBottomPadding = 0;
if (ambientTextVisible) {
- int stackBottom = mNotificationStackScrollLayoutController.getView().getBottom();
+ int stackBottom = mNotificationStackScrollLayoutController.getBottom();
ambientIndicationBottomPadding = stackBottom - ambientIndicationTop;
}
if (mAmbientIndicationBottomPadding != ambientIndicationBottomPadding) {
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationStackSizeCalculatorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationStackSizeCalculatorTest.kt
index ef680210e3fe..55dae9d04019 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationStackSizeCalculatorTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationStackSizeCalculatorTest.kt
@@ -46,7 +46,8 @@ import org.mockito.MockitoAnnotations
class NotificationStackSizeCalculatorTest : SysuiTestCase() {
@Mock private lateinit var sysuiStatusBarStateController: SysuiStatusBarStateController
- @Mock private lateinit var lockscreenShadeTransitionController: LockscreenShadeTransitionController
+ @Mock
+ private lateinit var lockscreenShadeTransitionController: LockscreenShadeTransitionController
@Mock private lateinit var stackLayout: NotificationStackScrollLayout
private val testableResources = mContext.orCreateTestableResources
@@ -74,7 +75,8 @@ class NotificationStackSizeCalculatorTest : SysuiTestCase() {
val rows = listOf(createMockRow(height = rowHeight))
val maxNotifications =
- computeMaxKeyguardNotifications(rows, availableSpace = 0f, shelfHeight = 0f)
+ computeMaxKeyguardNotifications(
+ rows, spaceForNotifications = 0f, spaceForShelf = 0f, shelfHeight = 0f)
assertThat(maxNotifications).isEqualTo(0)
}
@@ -84,7 +86,12 @@ class NotificationStackSizeCalculatorTest : SysuiTestCase() {
val numberOfRows = 30
val rows = createLockscreenRows(numberOfRows)
- val maxNotifications = computeMaxKeyguardNotifications(rows, Float.MAX_VALUE)
+ val maxNotifications =
+ computeMaxKeyguardNotifications(
+ rows,
+ spaceForNotifications = Float.MAX_VALUE,
+ spaceForShelf = Float.MAX_VALUE,
+ shelfHeight)
assertThat(maxNotifications).isEqualTo(numberOfRows)
}
@@ -93,11 +100,12 @@ class NotificationStackSizeCalculatorTest : SysuiTestCase() {
fun computeMaxKeyguardNotifications_spaceForOneAndShelf_returnsOne() {
setGapHeight(gapHeight)
val shelfHeight = rowHeight / 2 // Shelf absence won't leave room for another row.
- val availableSpace =
- listOf(rowHeight + dividerHeight, gapHeight + dividerHeight + shelfHeight).sum()
+ val spaceForNotifications = rowHeight + dividerHeight
+ val spaceForShelf = gapHeight + dividerHeight + shelfHeight
val rows = listOf(createMockRow(rowHeight), createMockRow(rowHeight))
- val maxNotifications = computeMaxKeyguardNotifications(rows, availableSpace, shelfHeight)
+ val maxNotifications =
+ computeMaxKeyguardNotifications(rows, spaceForNotifications, spaceForShelf, shelfHeight)
assertThat(maxNotifications).isEqualTo(1)
}
@@ -106,16 +114,19 @@ class NotificationStackSizeCalculatorTest : SysuiTestCase() {
fun computeMaxKeyguardNotifications_spaceForTwo_returnsTwo() {
setGapHeight(gapHeight)
val shelfHeight = shelfHeight + dividerHeight
- val availableSpace =
+ val spaceForNotifications =
listOf(
rowHeight + dividerHeight,
gapHeight + rowHeight + dividerHeight,
- gapHeight + dividerHeight + shelfHeight)
+ )
.sum()
+ val spaceForShelf = gapHeight + dividerHeight + shelfHeight
val rows =
listOf(createMockRow(rowHeight), createMockRow(rowHeight), createMockRow(rowHeight))
- val maxNotifications = computeMaxKeyguardNotifications(rows, availableSpace, shelfHeight)
+ val maxNotifications =
+ computeMaxKeyguardNotifications(
+ rows, spaceForNotifications + 1, spaceForShelf, shelfHeight)
assertThat(maxNotifications).isEqualTo(2)
}
@@ -124,19 +135,25 @@ class NotificationStackSizeCalculatorTest : SysuiTestCase() {
fun computeHeight_gapBeforeShelf_returnsSpaceUsed() {
// Each row in separate section.
setGapHeight(gapHeight)
- val spaceUsed =
- listOf(rowHeight,
+
+ val spaceForNotifications =
+ listOf(
+ rowHeight,
dividerHeight + gapHeight + rowHeight,
- dividerHeight + gapHeight + shelfHeight)
+ )
.sum()
- val availableSpace = spaceUsed + 1;
+
+ val spaceForShelf = dividerHeight + gapHeight + shelfHeight
+ val spaceUsed = spaceForNotifications + spaceForShelf
val rows =
listOf(createMockRow(rowHeight), createMockRow(rowHeight), createMockRow(rowHeight))
- val maxNotifications = computeMaxKeyguardNotifications(rows, availableSpace, shelfHeight)
+ val maxNotifications =
+ computeMaxKeyguardNotifications(rows, spaceForNotifications, spaceForShelf, shelfHeight)
assertThat(maxNotifications).isEqualTo(2)
- val height = sizeCalculator.computeHeight(stackLayout, maxNotifications, this.shelfHeight)
+ val height =
+ sizeCalculator.computeHeight(stackLayout, maxNotifications, this.shelfHeight)
assertThat(height).isEqualTo(spaceUsed)
}
@@ -145,19 +162,19 @@ class NotificationStackSizeCalculatorTest : SysuiTestCase() {
// Both rows are in the same section.
setGapHeight(0f)
- val rowHeight = rowHeight
- val shelfHeight = shelfHeight
- val spaceUsed =
- listOf(rowHeight,
- dividerHeight + shelfHeight)
- .sum()
- val availableSpace = spaceUsed + 1
+ val spaceForNotifications = rowHeight
+ val spaceForShelf = dividerHeight + shelfHeight
+ val spaceUsed = spaceForNotifications + spaceForShelf
val rows = listOf(createMockRow(rowHeight), createMockRow(rowHeight))
- val maxNotifications = computeMaxKeyguardNotifications(rows, availableSpace, shelfHeight)
+ // test that we only use space required
+ val maxNotifications =
+ computeMaxKeyguardNotifications(
+ rows, spaceForNotifications + 1, spaceForShelf, shelfHeight)
assertThat(maxNotifications).isEqualTo(1)
- val height = sizeCalculator.computeHeight(stackLayout, maxNotifications, this.shelfHeight)
+ val height =
+ sizeCalculator.computeHeight(stackLayout, maxNotifications, this.shelfHeight)
assertThat(height).isEqualTo(spaceUsed)
}
@@ -191,8 +208,13 @@ class NotificationStackSizeCalculatorTest : SysuiTestCase() {
whenever(expandableView.getMinHeight(any())).thenReturn(5)
whenever(expandableView.intrinsicHeight).thenReturn(10)
- val space = sizeCalculator.spaceNeeded(expandableView, visibleIndex = 0,
- previousView = null, stack = stackLayout, onLockscreen = true)
+ val space =
+ sizeCalculator.spaceNeeded(
+ expandableView,
+ visibleIndex = 0,
+ previousView = null,
+ stack = stackLayout,
+ onLockscreen = true)
assertThat(space).isEqualTo(5)
}
@@ -205,19 +227,25 @@ class NotificationStackSizeCalculatorTest : SysuiTestCase() {
whenever(expandableView.getMinHeight(any())).thenReturn(5)
whenever(expandableView.intrinsicHeight).thenReturn(10)
- val space = sizeCalculator.spaceNeeded(expandableView, visibleIndex = 0,
- previousView = null, stack = stackLayout, onLockscreen = false)
+ val space =
+ sizeCalculator.spaceNeeded(
+ expandableView,
+ visibleIndex = 0,
+ previousView = null,
+ stack = stackLayout,
+ onLockscreen = false)
assertThat(space).isEqualTo(10)
}
private fun computeMaxKeyguardNotifications(
rows: List<ExpandableView>,
- availableSpace: Float,
+ spaceForNotifications: Float,
+ spaceForShelf: Float,
shelfHeight: Float = this.shelfHeight
): Int {
setupChildren(rows)
return sizeCalculator.computeMaxKeyguardNotifications(
- stackLayout, availableSpace, shelfHeight)
+ stackLayout, spaceForNotifications, spaceForShelf, shelfHeight)
}
private fun setupChildren(children: List<ExpandableView>) {
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 62058a179dac..018c4531b382 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
@@ -583,7 +583,7 @@ public class NotificationPanelViewControllerTest extends SysuiTestCase {
listener.onHeightChanged(mock(ExpandableView.class), false);
verify(mNotificationStackSizeCalculator)
- .computeMaxKeyguardNotifications(any(), anyFloat(), anyFloat());
+ .computeMaxKeyguardNotifications(any(), anyFloat(), anyFloat(), anyFloat());
}
@Test
@@ -599,7 +599,7 @@ public class NotificationPanelViewControllerTest extends SysuiTestCase {
listener.onHeightChanged(mock(ExpandableView.class), false);
verify(mNotificationStackSizeCalculator, never())
- .computeMaxKeyguardNotifications(any(), anyFloat(), anyFloat());
+ .computeMaxKeyguardNotifications(any(), anyFloat(), anyFloat(), anyFloat());
}
@Test
@@ -622,25 +622,102 @@ public class NotificationPanelViewControllerTest extends SysuiTestCase {
.isNotEqualTo(-1);
}
- @Test
- public void getLockscreenSpaceForNotifications_includesOverlapWithLockIcon() {
+ private void setBottomPadding(int stackBottom, int lockIconPadding, int indicationPadding,
+ int ambientPadding) {
+
+ when(mNotificationStackScrollLayoutController.getTop()).thenReturn(0);
+ when(mNotificationStackScrollLayoutController.getHeight()).thenReturn(stackBottom);
+ when(mNotificationStackScrollLayoutController.getBottom()).thenReturn(stackBottom);
+ when(mLockIconViewController.getTop()).thenReturn((float) (stackBottom - lockIconPadding));
+
when(mResources.getDimensionPixelSize(R.dimen.keyguard_indication_bottom_padding))
- .thenReturn(0);
+ .thenReturn(indicationPadding);
+ mNotificationPanelViewController.loadDimens();
+
mNotificationPanelViewController.setAmbientIndicationTop(
- /* ambientIndicationTop= */ 0, /* ambientTextVisible */ false);
+ /* ambientIndicationTop= */ stackBottom - ambientPadding,
+ /* ambientTextVisible= */ true);
+ }
- // Use lock icon padding (100 - 80 - 5 = 15) as bottom padding
- when(mNotificationStackScrollLayoutController.getBottom()).thenReturn(100);
- when(mLockIconViewController.getTop()).thenReturn(80f);
- when(mResources.getDimensionPixelSize(R.dimen.shelf_and_lock_icon_overlap)).thenReturn(5);
+ @Test
+ public void getVerticalSpaceForLockscreenNotifications_useLockIconBottomPadding_returnsSpaceAvailable() {
+ setBottomPadding(/* stackScrollLayoutBottom= */ 100,
+ /* lockIconPadding= */ 20,
+ /* indicationPadding= */ 0,
+ /* ambientPadding= */ 0);
+
+ assertThat(mNotificationPanelViewController.getVerticalSpaceForLockscreenNotifications())
+ .isEqualTo(80);
+ }
- // Available space (100 - 0 - 15 = 85)
- when(mNotificationStackScrollLayoutController.getHeight()).thenReturn(100);
- when(mNotificationStackScrollLayoutController.getTop()).thenReturn(0);
- mNotificationPanelViewController.updateResources();
+ @Test
+ public void getVerticalSpaceForLockscreenNotifications_useIndicationBottomPadding_returnsSpaceAvailable() {
+ setBottomPadding(/* stackScrollLayoutBottom= */ 100,
+ /* lockIconPadding= */ 0,
+ /* indicationPadding= */ 30,
+ /* ambientPadding= */ 0);
+
+ assertThat(mNotificationPanelViewController.getVerticalSpaceForLockscreenNotifications())
+ .isEqualTo(70);
+ }
- assertThat(mNotificationPanelViewController.getSpaceForLockscreenNotifications())
- .isEqualTo(85);
+ @Test
+ public void getVerticalSpaceForLockscreenNotifications_useAmbientBottomPadding_returnsSpaceAvailable() {
+ setBottomPadding(/* stackScrollLayoutBottom= */ 100,
+ /* lockIconPadding= */ 0,
+ /* indicationPadding= */ 0,
+ /* ambientPadding= */ 40);
+
+ assertThat(mNotificationPanelViewController.getVerticalSpaceForLockscreenNotifications())
+ .isEqualTo(60);
+ }
+
+ @Test
+ public void getVerticalSpaceForLockscreenShelf_useLockIconBottomPadding_returnsShelfHeight() {
+ setBottomPadding(/* stackScrollLayoutBottom= */ 100,
+ /* lockIconPadding= */ 20,
+ /* indicationPadding= */ 0,
+ /* ambientPadding= */ 0);
+
+ when(mNotificationShelfController.getIntrinsicHeight()).thenReturn(5);
+ assertThat(mNotificationPanelViewController.getVerticalSpaceForLockscreenShelf())
+ .isEqualTo(5);
+ }
+
+ @Test
+ public void getVerticalSpaceForLockscreenShelf_useIndicationBottomPadding_returnsZero() {
+ setBottomPadding(/* stackScrollLayoutBottom= */ 100,
+ /* lockIconPadding= */ 0,
+ /* indicationPadding= */ 30,
+ /* ambientPadding= */ 0);
+
+ when(mNotificationShelfController.getIntrinsicHeight()).thenReturn(5);
+ assertThat(mNotificationPanelViewController.getVerticalSpaceForLockscreenShelf())
+ .isEqualTo(0);
+ }
+
+ @Test
+ public void getVerticalSpaceForLockscreenShelf_useAmbientBottomPadding_returnsZero() {
+ setBottomPadding(/* stackScrollLayoutBottom= */ 100,
+ /* lockIconPadding= */ 0,
+ /* indicationPadding= */ 0,
+ /* ambientPadding= */ 40);
+
+ when(mNotificationShelfController.getIntrinsicHeight()).thenReturn(5);
+ assertThat(mNotificationPanelViewController.getVerticalSpaceForLockscreenShelf())
+ .isEqualTo(0);
+ }
+
+ @Test
+ public void getVerticalSpaceForLockscreenShelf_useLockIconPadding_returnsLessThanShelfHeight() {
+ setBottomPadding(/* stackScrollLayoutBottom= */ 100,
+ /* lockIconPadding= */ 10,
+ /* indicationPadding= */ 8,
+ /* ambientPadding= */ 0);
+
+ when(mNotificationShelfController.getIntrinsicHeight()).thenReturn(5);
+ assertThat(mNotificationPanelViewController.getVerticalSpaceForLockscreenShelf())
+ .isEqualTo(2);
}
@Test
diff --git a/services/core/java/com/android/server/UiModeManagerService.java b/services/core/java/com/android/server/UiModeManagerService.java
index e68a0a6acaf6..c853ba93f4ab 100644
--- a/services/core/java/com/android/server/UiModeManagerService.java
+++ b/services/core/java/com/android/server/UiModeManagerService.java
@@ -635,7 +635,12 @@ final class UiModeManagerService extends SystemService {
+ "permission ENTER_CAR_MODE_PRIORITIZED");
}
- assertLegit(callingPackage);
+ // Allow the user to enable car mode using the shell,
+ // e.g. 'adb shell cmd uimode car yes'
+ boolean isShellCaller = mInjector.getCallingUid() == Process.SHELL_UID;
+ if (!isShellCaller) {
+ assertLegit(callingPackage);
+ }
final long ident = Binder.clearCallingIdentity();
try {
@@ -676,8 +681,13 @@ final class UiModeManagerService extends SystemService {
// If the caller is the system, we will allow the DISABLE_CAR_MODE_ALL_PRIORITIES car
// mode flag to be specified; this is so that the user can disable car mode at all
// priorities using the persistent notification.
- boolean isSystemCaller = mInjector.getCallingUid() == Process.SYSTEM_UID;
- if (!isSystemCaller) {
+ //
+ // We also allow the user to disable car mode using the shell,
+ // e.g. 'adb shell cmd uimode car no'
+ int callingUid = mInjector.getCallingUid();
+ boolean isSystemCaller = callingUid == Process.SYSTEM_UID;
+ boolean isShellCaller = callingUid == Process.SHELL_UID;
+ if (!isSystemCaller && !isShellCaller) {
assertLegit(callingPackage);
}
final int carModeFlags =
diff --git a/services/core/java/com/android/server/wm/ActivityTaskManagerInternal.java b/services/core/java/com/android/server/wm/ActivityTaskManagerInternal.java
index f75d73b04476..8e7dde20955b 100644
--- a/services/core/java/com/android/server/wm/ActivityTaskManagerInternal.java
+++ b/services/core/java/com/android/server/wm/ActivityTaskManagerInternal.java
@@ -338,14 +338,16 @@ public abstract class ActivityTaskManagerInternal {
private final @NonNull IBinder mAssistToken;
private final @NonNull IBinder mShareableActivityToken;
private final @NonNull IApplicationThread mAppThread;
+ private final int mUid;
public ActivityTokens(@NonNull IBinder activityToken,
@NonNull IBinder assistToken, @NonNull IApplicationThread appThread,
- @NonNull IBinder shareableActivityToken) {
+ @NonNull IBinder shareableActivityToken, int uid) {
mActivityToken = activityToken;
mAssistToken = assistToken;
mAppThread = appThread;
mShareableActivityToken = shareableActivityToken;
+ mUid = uid;
}
/**
@@ -375,6 +377,13 @@ public abstract class ActivityTaskManagerInternal {
public @NonNull IApplicationThread getApplicationThread() {
return mAppThread;
}
+
+ /**
+ * @return The UID of the activity
+ */
+ public int getUid() {
+ return mUid;
+ }
}
public abstract void sendActivityResult(int callingUid, IBinder activityToken,
diff --git a/services/core/java/com/android/server/wm/ActivityTaskManagerService.java b/services/core/java/com/android/server/wm/ActivityTaskManagerService.java
index b8162cd3d008..aa154292fe7e 100644
--- a/services/core/java/com/android/server/wm/ActivityTaskManagerService.java
+++ b/services/core/java/com/android/server/wm/ActivityTaskManagerService.java
@@ -5820,14 +5820,16 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub {
if (token == null && list.get(0).attachedToProcess()) {
ActivityRecord topRecord = list.get(0);
return new ActivityTokens(topRecord.token, topRecord.assistToken,
- topRecord.app.getThread(), topRecord.shareableActivityToken);
+ topRecord.app.getThread(), topRecord.shareableActivityToken,
+ topRecord.getUid());
}
// find the expected Activity
for (int i = 0; i < list.size(); i++) {
ActivityRecord record = list.get(i);
if (record.shareableActivityToken == token && record.attachedToProcess()) {
return new ActivityTokens(record.token, record.assistToken,
- record.app.getThread(), record.shareableActivityToken);
+ record.app.getThread(), record.shareableActivityToken,
+ record.getUid());
}
}
return null;
diff --git a/services/tests/uiservicestests/src/com/android/server/UiModeManagerServiceTest.java b/services/tests/uiservicestests/src/com/android/server/UiModeManagerServiceTest.java
index de81d6b7f443..aee27554b023 100644
--- a/services/tests/uiservicestests/src/com/android/server/UiModeManagerServiceTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/UiModeManagerServiceTest.java
@@ -75,6 +75,7 @@ import android.content.res.Configuration;
import android.content.res.Resources;
import android.os.Handler;
import android.os.IBinder;
+import android.os.Process;
import android.os.PowerManager;
import android.os.PowerManagerInternal;
import android.os.PowerSaveState;
@@ -885,7 +886,7 @@ public class UiModeManagerServiceTest extends UiServiceTestCase {
@Test
public void requestProjection_failsForBogusPackageName() throws Exception {
when(mPackageManager.getPackageUidAsUser(eq(PACKAGE_NAME), anyInt()))
- .thenReturn(TestInjector.CALLING_UID + 1);
+ .thenReturn(TestInjector.DEFAULT_CALLING_UID + 1);
assertThrows(SecurityException.class, () -> mService.requestProjection(mBinder,
PROJECTION_TYPE_AUTOMOTIVE, PACKAGE_NAME));
@@ -905,7 +906,7 @@ public class UiModeManagerServiceTest extends UiServiceTestCase {
@Test
public void requestProjection_failsIfNoProjectionTypes() throws Exception {
when(mPackageManager.getPackageUidAsUser(eq(PACKAGE_NAME), anyInt()))
- .thenReturn(TestInjector.CALLING_UID);
+ .thenReturn(TestInjector.DEFAULT_CALLING_UID);
assertThrows(IllegalArgumentException.class,
() -> mService.requestProjection(mBinder, PROJECTION_TYPE_NONE, PACKAGE_NAME));
@@ -918,7 +919,7 @@ public class UiModeManagerServiceTest extends UiServiceTestCase {
@Test
public void requestProjection_failsIfMultipleProjectionTypes() throws Exception {
when(mPackageManager.getPackageUidAsUser(eq(PACKAGE_NAME), anyInt()))
- .thenReturn(TestInjector.CALLING_UID);
+ .thenReturn(TestInjector.DEFAULT_CALLING_UID);
// Don't use PROJECTION_TYPE_ALL because that's actually == -1 and will fail the > 0 check.
int multipleProjectionTypes = PROJECTION_TYPE_AUTOMOTIVE | 0x0002 | 0x0004;
@@ -944,13 +945,13 @@ public class UiModeManagerServiceTest extends UiServiceTestCase {
@Test
public void requestProjection_automotive_failsIfAlreadySetByOtherPackage() throws Exception {
when(mPackageManager.getPackageUidAsUser(eq(PACKAGE_NAME), anyInt()))
- .thenReturn(TestInjector.CALLING_UID);
+ .thenReturn(TestInjector.DEFAULT_CALLING_UID);
mService.requestProjection(mBinder, PROJECTION_TYPE_AUTOMOTIVE, PACKAGE_NAME);
assertEquals(PROJECTION_TYPE_AUTOMOTIVE, mService.getActiveProjectionTypes());
String otherPackage = "Raconteurs";
when(mPackageManager.getPackageUidAsUser(eq(otherPackage), anyInt()))
- .thenReturn(TestInjector.CALLING_UID);
+ .thenReturn(TestInjector.DEFAULT_CALLING_UID);
assertFalse(mService.requestProjection(mBinder, PROJECTION_TYPE_AUTOMOTIVE, otherPackage));
assertThat(mService.getProjectingPackages(PROJECTION_TYPE_AUTOMOTIVE),
contains(PACKAGE_NAME));
@@ -959,7 +960,7 @@ public class UiModeManagerServiceTest extends UiServiceTestCase {
@Test
public void requestProjection_failsIfCannotLinkToDeath() throws Exception {
when(mPackageManager.getPackageUidAsUser(eq(PACKAGE_NAME), anyInt()))
- .thenReturn(TestInjector.CALLING_UID);
+ .thenReturn(TestInjector.DEFAULT_CALLING_UID);
doThrow(new RemoteException()).when(mBinder).linkToDeath(any(), anyInt());
assertFalse(mService.requestProjection(mBinder, PROJECTION_TYPE_AUTOMOTIVE, PACKAGE_NAME));
@@ -969,7 +970,7 @@ public class UiModeManagerServiceTest extends UiServiceTestCase {
@Test
public void requestProjection() throws Exception {
when(mPackageManager.getPackageUidAsUser(eq(PACKAGE_NAME), anyInt()))
- .thenReturn(TestInjector.CALLING_UID);
+ .thenReturn(TestInjector.DEFAULT_CALLING_UID);
// Should work for all powers of two.
for (int i = 0; i < Integer.SIZE; ++i) {
int projectionType = 1 << i;
@@ -985,12 +986,12 @@ public class UiModeManagerServiceTest extends UiServiceTestCase {
@Test
public void releaseProjection_failsForBogusPackageName() throws Exception {
when(mPackageManager.getPackageUidAsUser(eq(PACKAGE_NAME), anyInt()))
- .thenReturn(TestInjector.CALLING_UID);
+ .thenReturn(TestInjector.DEFAULT_CALLING_UID);
mService.requestProjection(mBinder, PROJECTION_TYPE_AUTOMOTIVE, PACKAGE_NAME);
assertEquals(PROJECTION_TYPE_AUTOMOTIVE, mService.getActiveProjectionTypes());
when(mPackageManager.getPackageUidAsUser(eq(PACKAGE_NAME), anyInt()))
- .thenReturn(TestInjector.CALLING_UID + 1);
+ .thenReturn(TestInjector.DEFAULT_CALLING_UID + 1);
assertThrows(SecurityException.class, () -> mService.releaseProjection(
PROJECTION_TYPE_AUTOMOTIVE, PACKAGE_NAME));
@@ -1000,7 +1001,7 @@ public class UiModeManagerServiceTest extends UiServiceTestCase {
@Test
public void releaseProjection_failsIfNameNotFound() throws Exception {
when(mPackageManager.getPackageUidAsUser(eq(PACKAGE_NAME), anyInt()))
- .thenReturn(TestInjector.CALLING_UID);
+ .thenReturn(TestInjector.DEFAULT_CALLING_UID);
mService.requestProjection(mBinder, PROJECTION_TYPE_AUTOMOTIVE, PACKAGE_NAME);
assertEquals(PROJECTION_TYPE_AUTOMOTIVE, mService.getActiveProjectionTypes());
when(mPackageManager.getPackageUidAsUser(eq(PACKAGE_NAME), anyInt()))
@@ -1014,7 +1015,7 @@ public class UiModeManagerServiceTest extends UiServiceTestCase {
@Test
public void releaseProjection_enforcesToggleAutomotiveProjectionPermission() throws Exception {
when(mPackageManager.getPackageUidAsUser(eq(PACKAGE_NAME), anyInt()))
- .thenReturn(TestInjector.CALLING_UID);
+ .thenReturn(TestInjector.DEFAULT_CALLING_UID);
mService.requestProjection(mBinder, PROJECTION_TYPE_AUTOMOTIVE, PACKAGE_NAME);
assertEquals(PROJECTION_TYPE_AUTOMOTIVE, mService.getActiveProjectionTypes());
doThrow(new SecurityException()).when(mContext).enforceCallingPermission(
@@ -1033,7 +1034,7 @@ public class UiModeManagerServiceTest extends UiServiceTestCase {
@Test
public void releaseProjection() throws Exception {
when(mPackageManager.getPackageUidAsUser(eq(PACKAGE_NAME), anyInt()))
- .thenReturn(TestInjector.CALLING_UID);
+ .thenReturn(TestInjector.DEFAULT_CALLING_UID);
requestAllPossibleProjectionTypes();
assertEquals(PROJECTION_TYPE_ALL, mService.getActiveProjectionTypes());
@@ -1053,7 +1054,7 @@ public class UiModeManagerServiceTest extends UiServiceTestCase {
@Test
public void binderDeath_releasesProjection() throws Exception {
when(mPackageManager.getPackageUidAsUser(eq(PACKAGE_NAME), anyInt()))
- .thenReturn(TestInjector.CALLING_UID);
+ .thenReturn(TestInjector.DEFAULT_CALLING_UID);
requestAllPossibleProjectionTypes();
assertEquals(PROJECTION_TYPE_ALL, mService.getActiveProjectionTypes());
ArgumentCaptor<IBinder.DeathRecipient> deathRecipientCaptor = ArgumentCaptor.forClass(
@@ -1069,7 +1070,7 @@ public class UiModeManagerServiceTest extends UiServiceTestCase {
public void getActiveProjectionTypes() throws Exception {
assertEquals(PROJECTION_TYPE_NONE, mService.getActiveProjectionTypes());
when(mPackageManager.getPackageUidAsUser(eq(PACKAGE_NAME), anyInt()))
- .thenReturn(TestInjector.CALLING_UID);
+ .thenReturn(TestInjector.DEFAULT_CALLING_UID);
mService.requestProjection(mBinder, PROJECTION_TYPE_AUTOMOTIVE, PACKAGE_NAME);
assertEquals(PROJECTION_TYPE_AUTOMOTIVE, mService.getActiveProjectionTypes());
mService.releaseProjection(PROJECTION_TYPE_AUTOMOTIVE, PACKAGE_NAME);
@@ -1080,7 +1081,7 @@ public class UiModeManagerServiceTest extends UiServiceTestCase {
public void getProjectingPackages() throws Exception {
assertTrue(mService.getProjectingPackages(PROJECTION_TYPE_ALL).isEmpty());
when(mPackageManager.getPackageUidAsUser(eq(PACKAGE_NAME), anyInt()))
- .thenReturn(TestInjector.CALLING_UID);
+ .thenReturn(TestInjector.DEFAULT_CALLING_UID);
mService.requestProjection(mBinder, PROJECTION_TYPE_AUTOMOTIVE, PACKAGE_NAME);
assertEquals(1, mService.getProjectingPackages(PROJECTION_TYPE_AUTOMOTIVE).size());
assertEquals(1, mService.getProjectingPackages(PROJECTION_TYPE_ALL).size());
@@ -1105,7 +1106,7 @@ public class UiModeManagerServiceTest extends UiServiceTestCase {
public void addOnProjectionStateChangedListener_callsListenerIfProjectionActive()
throws Exception {
when(mPackageManager.getPackageUidAsUser(eq(PACKAGE_NAME), anyInt()))
- .thenReturn(TestInjector.CALLING_UID);
+ .thenReturn(TestInjector.DEFAULT_CALLING_UID);
mService.requestProjection(mBinder, PROJECTION_TYPE_AUTOMOTIVE, PACKAGE_NAME);
assertEquals(PROJECTION_TYPE_AUTOMOTIVE, mService.getActiveProjectionTypes());
@@ -1135,7 +1136,7 @@ public class UiModeManagerServiceTest extends UiServiceTestCase {
mService.removeOnProjectionStateChangedListener(listener);
// Now set automotive projection, should not call back.
when(mPackageManager.getPackageUidAsUser(eq(PACKAGE_NAME), anyInt()))
- .thenReturn(TestInjector.CALLING_UID);
+ .thenReturn(TestInjector.DEFAULT_CALLING_UID);
mService.requestProjection(mBinder, PROJECTION_TYPE_AUTOMOTIVE, PACKAGE_NAME);
verify(listener, never()).onProjectionStateChanged(anyInt(), any());
}
@@ -1152,7 +1153,7 @@ public class UiModeManagerServiceTest extends UiServiceTestCase {
// Now set automotive projection, should call back.
when(mPackageManager.getPackageUidAsUser(eq(PACKAGE_NAME), anyInt()))
- .thenReturn(TestInjector.CALLING_UID);
+ .thenReturn(TestInjector.DEFAULT_CALLING_UID);
mService.requestProjection(mBinder, PROJECTION_TYPE_AUTOMOTIVE, PACKAGE_NAME);
verify(listener).onProjectionStateChanged(eq(PROJECTION_TYPE_AUTOMOTIVE),
eq(List.of(PACKAGE_NAME)));
@@ -1179,9 +1180,9 @@ public class UiModeManagerServiceTest extends UiServiceTestCase {
int otherFakeProjectionType = 0x0004;
String otherPackageName = "Internet Arms";
when(mPackageManager.getPackageUidAsUser(eq(PACKAGE_NAME), anyInt()))
- .thenReturn(TestInjector.CALLING_UID);
+ .thenReturn(TestInjector.DEFAULT_CALLING_UID);
when(mPackageManager.getPackageUidAsUser(eq(otherPackageName), anyInt()))
- .thenReturn(TestInjector.CALLING_UID);
+ .thenReturn(TestInjector.DEFAULT_CALLING_UID);
IOnProjectionStateChangedListener listener = mock(IOnProjectionStateChangedListener.class);
when(listener.asBinder()).thenReturn(mBinder); // Any binder will do.
IOnProjectionStateChangedListener listener2 = mock(IOnProjectionStateChangedListener.class);
@@ -1234,7 +1235,7 @@ public class UiModeManagerServiceTest extends UiServiceTestCase {
// Now kill the binder for the listener. This should remove it from the list of listeners.
listenerDeathRecipient.getValue().binderDied();
when(mPackageManager.getPackageUidAsUser(eq(PACKAGE_NAME), anyInt()))
- .thenReturn(TestInjector.CALLING_UID);
+ .thenReturn(TestInjector.DEFAULT_CALLING_UID);
mService.requestProjection(mBinder, PROJECTION_TYPE_AUTOMOTIVE, PACKAGE_NAME);
verify(listener, never()).onProjectionStateChanged(anyInt(), any());
}
@@ -1242,20 +1243,33 @@ public class UiModeManagerServiceTest extends UiServiceTestCase {
@Test
public void enableCarMode_failsForBogusPackageName() throws Exception {
when(mPackageManager.getPackageUidAsUser(eq(PACKAGE_NAME), anyInt()))
- .thenReturn(TestInjector.CALLING_UID + 1);
+ .thenReturn(TestInjector.DEFAULT_CALLING_UID + 1);
assertThrows(SecurityException.class, () -> mService.enableCarMode(0, 0, PACKAGE_NAME));
assertThat(mService.getCurrentModeType()).isNotEqualTo(Configuration.UI_MODE_TYPE_CAR);
}
@Test
+ public void enableCarMode_shell() throws Exception {
+ mUiManagerService = new UiModeManagerService(mContext, /* setupWizardComplete= */ true,
+ mTwilightManager, new TestInjector(Process.SHELL_UID));
+ try {
+ mUiManagerService.onBootPhase(SystemService.PHASE_SYSTEM_SERVICES_READY);
+ } catch (SecurityException e) {/* ignore for permission denial */}
+ mService = mUiManagerService.getService();
+
+ mService.enableCarMode(0, 0, PACKAGE_NAME);
+ assertThat(mService.getCurrentModeType()).isEqualTo(Configuration.UI_MODE_TYPE_CAR);
+ }
+
+ @Test
public void disableCarMode_failsForBogusPackageName() throws Exception {
when(mPackageManager.getPackageUidAsUser(eq(PACKAGE_NAME), anyInt()))
- .thenReturn(TestInjector.CALLING_UID);
+ .thenReturn(TestInjector.DEFAULT_CALLING_UID);
mService.enableCarMode(0, 0, PACKAGE_NAME);
assertThat(mService.getCurrentModeType()).isEqualTo(Configuration.UI_MODE_TYPE_CAR);
when(mPackageManager.getPackageUidAsUser(eq(PACKAGE_NAME), anyInt()))
- .thenReturn(TestInjector.CALLING_UID + 1);
+ .thenReturn(TestInjector.DEFAULT_CALLING_UID + 1);
assertThrows(SecurityException.class,
() -> mService.disableCarModeByCallingPackage(0, PACKAGE_NAME));
@@ -1263,8 +1277,24 @@ public class UiModeManagerServiceTest extends UiServiceTestCase {
// Clean up
when(mPackageManager.getPackageUidAsUser(eq(PACKAGE_NAME), anyInt()))
- .thenReturn(TestInjector.CALLING_UID);
- mService.disableCarModeByCallingPackage(0, PACKAGE_NAME);
+ .thenReturn(TestInjector.DEFAULT_CALLING_UID);
+ mService.disableCarModeByCallingPackage(0, PACKAGE_NAME);
+ assertThat(mService.getCurrentModeType()).isNotEqualTo(Configuration.UI_MODE_TYPE_CAR);
+ }
+
+ @Test
+ public void disableCarMode_shell() throws Exception {
+ mUiManagerService = new UiModeManagerService(mContext, /* setupWizardComplete= */ true,
+ mTwilightManager, new TestInjector(Process.SHELL_UID));
+ try {
+ mUiManagerService.onBootPhase(SystemService.PHASE_SYSTEM_SERVICES_READY);
+ } catch (SecurityException e) {/* ignore for permission denial */}
+ mService = mUiManagerService.getService();
+
+ mService.enableCarMode(0, 0, PACKAGE_NAME);
+ assertThat(mService.getCurrentModeType()).isEqualTo(Configuration.UI_MODE_TYPE_CAR);
+
+ mService.disableCarModeByCallingPackage(0, PACKAGE_NAME);
assertThat(mService.getCurrentModeType()).isNotEqualTo(Configuration.UI_MODE_TYPE_CAR);
}
@@ -1275,10 +1305,20 @@ public class UiModeManagerServiceTest extends UiServiceTestCase {
}
private static class TestInjector extends UiModeManagerService.Injector {
- private static final int CALLING_UID = 8675309;
+ private static final int DEFAULT_CALLING_UID = 8675309;
+
+ private final int callingUid;
+
+ public TestInjector() {
+ this(DEFAULT_CALLING_UID);
+ }
+
+ public TestInjector(int callingUid) {
+ this.callingUid = callingUid;
+ }
public int getCallingUid() {
- return CALLING_UID;
+ return callingUid;
}
}
}
diff --git a/services/usb/java/com/android/server/usb/UsbHostManager.java b/services/usb/java/com/android/server/usb/UsbHostManager.java
index b1c85fe043ec..f3892768921c 100644
--- a/services/usb/java/com/android/server/usb/UsbHostManager.java
+++ b/services/usb/java/com/android/server/usb/UsbHostManager.java
@@ -102,6 +102,7 @@ public class UsbHostManager {
private final HashMap<String, ArrayList<UsbDirectMidiDevice>>
mMidiDevices = new HashMap<String, ArrayList<UsbDirectMidiDevice>>();
private final HashSet<String> mMidiUniqueCodes = new HashSet<String>();
+ private static final int MAX_UNIQUE_CODE_GENERATION_ATTEMPTS = 10;
private final Random mRandom = new Random();
private final boolean mHasMidiFeature;
@@ -645,11 +646,18 @@ public class UsbHostManager {
// Generate a 3 digit code.
private String generateNewUsbDeviceIdentifier() {
String code;
+ int numberOfAttempts = 0;
do {
+ if (numberOfAttempts > MAX_UNIQUE_CODE_GENERATION_ATTEMPTS) {
+ Slog.w(TAG, "MIDI unique code array resetting");
+ mMidiUniqueCodes.clear();
+ numberOfAttempts = 0;
+ }
code = "";
for (int i = 0; i < 3; i++) {
code += mRandom.nextInt(10);
}
+ numberOfAttempts++;
} while (mMidiUniqueCodes.contains(code));
mMidiUniqueCodes.add(code);
return code;
diff --git a/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionManagerService.java b/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionManagerService.java
index 73b2510e6cf1..bcfee82000a4 100644
--- a/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionManagerService.java
+++ b/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionManagerService.java
@@ -35,7 +35,6 @@ import android.content.pm.ActivityInfo;
import android.content.pm.ApplicationInfo;
import android.content.pm.IPackageManager;
import android.content.pm.PackageManager;
-import android.content.pm.PackageManagerInternal;
import android.content.pm.ResolveInfo;
import android.content.pm.ServiceInfo;
import android.content.pm.ShortcutServiceInternal;
@@ -105,7 +104,6 @@ import com.android.server.pm.permission.LegacyPermissionManagerInternal;
import com.android.server.soundtrigger.SoundTriggerInternal;
import com.android.server.utils.TimingsTraceAndSlog;
import com.android.server.wm.ActivityTaskManagerInternal;
-import com.android.server.wm.ActivityTaskManagerInternal.ActivityTokens;
import java.io.FileDescriptor;
import java.io.PrintWriter;
@@ -128,7 +126,6 @@ public class VoiceInteractionManagerService extends SystemService {
final ActivityManagerInternal mAmInternal;
final ActivityTaskManagerInternal mAtmInternal;
final UserManagerInternal mUserManagerInternal;
- final PackageManagerInternal mPackageManagerInternal;
final ArrayMap<Integer, VoiceInteractionManagerServiceStub.SoundTriggerSession>
mLoadedKeyphraseIds = new ArrayMap<>();
ShortcutServiceInternal mShortcutServiceInternal;
@@ -149,8 +146,6 @@ public class VoiceInteractionManagerService extends SystemService {
LocalServices.getService(ActivityTaskManagerInternal.class));
mUserManagerInternal = Objects.requireNonNull(
LocalServices.getService(UserManagerInternal.class));
- mPackageManagerInternal = Objects.requireNonNull(
- LocalServices.getService(PackageManagerInternal.class));
LegacyPermissionManagerInternal permissionManagerInternal = LocalServices.getService(
LegacyPermissionManagerInternal.class);
@@ -374,21 +369,6 @@ public class VoiceInteractionManagerService extends SystemService {
return new SoundTriggerSessionBinderProxy(session);
}
- @GuardedBy("this")
- private void grantImplicitAccessLocked(int grantRecipientUid, @Nullable Intent intent) {
- if (mImpl == null) {
- Slog.w(TAG, "Cannot grant implicit access because mImpl is null.");
- return;
- }
-
- final int grantRecipientAppId = UserHandle.getAppId(grantRecipientUid);
- final int grantRecipientUserId = UserHandle.getUserId(grantRecipientUid);
- final int voiceInteractionUid = mImpl.mInfo.getServiceInfo().applicationInfo.uid;
- mPackageManagerInternal.grantImplicitAccess(
- grantRecipientUserId, intent, grantRecipientAppId, voiceInteractionUid,
- /* direct= */ true);
- }
-
private IVoiceInteractionSoundTriggerSession createSoundTriggerSessionForSelfIdentity(
IBinder client) {
Identity identity = new Identity();
@@ -419,9 +399,10 @@ public class VoiceInteractionManagerService extends SystemService {
@Override
public void onShown() {
synchronized (VoiceInteractionManagerServiceStub.this) {
- VoiceInteractionManagerServiceStub.this
- .grantImplicitAccessLocked(callingUid,
- /* intent= */ null);
+ if (mImpl != null) {
+ mImpl.grantImplicitAccessLocked(callingUid,
+ /* intent= */ null);
+ }
}
mAtmInternal.onLocalVoiceInteractionStarted(token,
mImpl.mActiveSession.mSession,
@@ -995,7 +976,7 @@ public class VoiceInteractionManagerService extends SystemService {
mContext.getPackageManager(), PackageManager.MATCH_ALL);
if (activityInfo != null) {
final int activityUid = activityInfo.applicationInfo.uid;
- grantImplicitAccessLocked(activityUid, intent);
+ mImpl.grantImplicitAccessLocked(activityUid, intent);
} else {
Slog.w(TAG, "Cannot find ActivityInfo in startVoiceActivity.");
}
@@ -1039,15 +1020,6 @@ public class VoiceInteractionManagerService extends SystemService {
}
final long caller = Binder.clearCallingIdentity();
try {
- // Getting the UID corresponding to the taskId, and grant the visibility to it.
- final ActivityTokens tokens = mAtmInternal
- .getAttachedNonFinishingActivityForTask(taskId, /* token= */ null);
- final ComponentName componentName = mAtmInternal.getActivityName(
- tokens.getActivityToken());
- grantImplicitAccessLocked(mPackageManagerInternal.getPackageUid(
- componentName.getPackageName(), PackageManager.MATCH_ALL,
- UserHandle.myUserId()), /* intent= */ null);
-
mImpl.requestDirectActionsLocked(token, taskId, assistToken,
cancellationCallback, resultCallback);
} finally {
diff --git a/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionManagerServiceImpl.java b/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionManagerServiceImpl.java
index 39a6868169a7..b9793cafb6fe 100644
--- a/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionManagerServiceImpl.java
+++ b/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionManagerServiceImpl.java
@@ -39,6 +39,7 @@ import android.content.Intent;
import android.content.IntentFilter;
import android.content.ServiceConnection;
import android.content.pm.PackageManager;
+import android.content.pm.PackageManagerInternal;
import android.content.pm.ParceledListSlice;
import android.content.pm.ServiceInfo;
import android.hardware.soundtrigger.IRecognitionStatusCallback;
@@ -80,6 +81,7 @@ import java.io.FileDescriptor;
import java.io.PrintWriter;
import java.util.ArrayList;
import java.util.List;
+import java.util.Objects;
class VoiceInteractionManagerServiceImpl implements VoiceInteractionSessionConnection.Callback {
final static String TAG = "VoiceInteractionServiceManager";
@@ -100,6 +102,7 @@ class VoiceInteractionManagerServiceImpl implements VoiceInteractionSessionConne
final ComponentName mComponent;
final IActivityManager mAm;
final IActivityTaskManager mAtm;
+ final PackageManagerInternal mPackageManagerInternal;
final VoiceInteractionServiceInfo mInfo;
final ComponentName mSessionComponentName;
final IWindowManager mIWindowManager;
@@ -195,6 +198,8 @@ class VoiceInteractionManagerServiceImpl implements VoiceInteractionSessionConne
mComponent = service;
mAm = ActivityManager.getService();
mAtm = ActivityTaskManager.getService();
+ mPackageManagerInternal = Objects.requireNonNull(
+ LocalServices.getService(PackageManagerInternal.class));
VoiceInteractionServiceInfo info;
try {
info = new VoiceInteractionServiceInfo(context.getPackageManager(), service, mUser);
@@ -230,6 +235,15 @@ class VoiceInteractionManagerServiceImpl implements VoiceInteractionSessionConne
Context.RECEIVER_EXPORTED);
}
+ public void grantImplicitAccessLocked(int grantRecipientUid, @Nullable Intent intent) {
+ final int grantRecipientAppId = UserHandle.getAppId(grantRecipientUid);
+ final int grantRecipientUserId = UserHandle.getUserId(grantRecipientUid);
+ final int voiceInteractionUid = mInfo.getServiceInfo().applicationInfo.uid;
+ mPackageManagerInternal.grantImplicitAccess(
+ grantRecipientUserId, intent, grantRecipientAppId, voiceInteractionUid,
+ /* direct= */ true);
+ }
+
public boolean showSessionLocked(Bundle args, int flags,
IVoiceInteractionSessionShowCallback showCallback, IBinder activityToken) {
if (mActiveSession == null) {
@@ -354,6 +368,7 @@ class VoiceInteractionManagerServiceImpl implements VoiceInteractionSessionConne
VoiceInteractionManagerServiceImpl.this, token, taskId, assistToken,
cancellationCallback, callback), REQUEST_DIRECT_ACTIONS_RETRY_TIME_MS);
} else {
+ grantImplicitAccessLocked(tokens.getUid(), /* intent= */ null);
try {
tokens.getApplicationThread().requestDirectActions(tokens.getActivityToken(),
mActiveSession.mInteractor, cancellationCallback, callback);