diff options
49 files changed, 1102 insertions, 200 deletions
diff --git a/core/java/android/hardware/camera2/CameraCharacteristics.java b/core/java/android/hardware/camera2/CameraCharacteristics.java index dac55ae08448..8db298ff8a85 100644 --- a/core/java/android/hardware/camera2/CameraCharacteristics.java +++ b/core/java/android/hardware/camera2/CameraCharacteristics.java @@ -1325,7 +1325,8 @@ public final class CameraCharacteristics extends CameraMetadata<CameraCharacteri * flashlight brightness level via * {@link android.hardware.camera2.CameraManager#turnOnTorchWithStrengthLevel }. * If this value is equal to 1, flashlight brightness control is not supported. - * The value for this key will be null for devices with no flash unit.</p> + * The value for this key will be null for devices with no flash unit. + * This level must be set to a safe value to prevent any burn out issues.</p> * <p><b>Optional</b> - The value for this key may be {@code null} on some devices.</p> */ @PublicKey diff --git a/core/java/android/view/ViewRootImpl.java b/core/java/android/view/ViewRootImpl.java index 8ec32a6d6b5b..7be8dffdf023 100644 --- a/core/java/android/view/ViewRootImpl.java +++ b/core/java/android/view/ViewRootImpl.java @@ -3759,6 +3759,7 @@ public final class ViewRootImpl implements ViewParent, } } mFirstInputStage.onWindowFocusChanged(hasWindowFocus); + mOnBackInvokedDispatcher.onWindowFocusChanged(hasWindowFocus); // NOTE: there's no view visibility (appeared / disapparead) events when the windows focus // is lost, so we don't need to to force a flush - there might be other events such as diff --git a/core/java/android/window/ImeOnBackInvokedDispatcher.java b/core/java/android/window/ImeOnBackInvokedDispatcher.java index 5924844aa3b2..f1a052b61c59 100644 --- a/core/java/android/window/ImeOnBackInvokedDispatcher.java +++ b/core/java/android/window/ImeOnBackInvokedDispatcher.java @@ -81,8 +81,10 @@ public class ImeOnBackInvokedDispatcher implements OnBackInvokedDispatcher, Parc @OnBackInvokedDispatcher.Priority int priority, @NonNull OnBackInvokedCallback callback) { final Bundle bundle = new Bundle(); + // Always invoke back for ime without checking the window focus. final IOnBackInvokedCallback iCallback = - new WindowOnBackInvokedDispatcher.OnBackInvokedCallbackWrapper(callback); + new WindowOnBackInvokedDispatcher.OnBackInvokedCallbackWrapper(callback, + () -> true); bundle.putBinder(RESULT_KEY_CALLBACK, iCallback.asBinder()); bundle.putInt(RESULT_KEY_PRIORITY, priority); bundle.putInt(RESULT_KEY_ID, callback.hashCode()); diff --git a/core/java/android/window/WindowOnBackInvokedDispatcher.java b/core/java/android/window/WindowOnBackInvokedDispatcher.java index d147524d3b3d..02c5ebcc184e 100644 --- a/core/java/android/window/WindowOnBackInvokedDispatcher.java +++ b/core/java/android/window/WindowOnBackInvokedDispatcher.java @@ -32,6 +32,7 @@ import java.util.ArrayList; import java.util.HashMap; import java.util.Objects; import java.util.TreeMap; +import java.util.function.Supplier; /** * Provides window based implementation of {@link OnBackInvokedDispatcher}. @@ -64,6 +65,7 @@ public class WindowOnBackInvokedDispatcher implements OnBackInvokedDispatcher { private final TreeMap<Integer, ArrayList<OnBackInvokedCallback>> mOnBackInvokedCallbacks = new TreeMap<>(); private final Checker mChecker; + private boolean mHasFocus; public WindowOnBackInvokedDispatcher(boolean applicationCallBackEnabled) { mChecker = new Checker(applicationCallBackEnabled); @@ -189,7 +191,7 @@ public class WindowOnBackInvokedDispatcher implements OnBackInvokedDispatcher { .ImeOnBackInvokedCallback ? ((ImeOnBackInvokedDispatcher.ImeOnBackInvokedCallback) callback).getIOnBackInvokedCallback() - : new OnBackInvokedCallbackWrapper(callback); + : new OnBackInvokedCallbackWrapper(callback, this::hasFocus); callbackInfo = new OnBackInvokedCallbackInfo(iCallback, priority); } mWindowSession.setOnBackInvokedCallbackInfo(mWindow, callbackInfo); @@ -198,6 +200,17 @@ public class WindowOnBackInvokedDispatcher implements OnBackInvokedDispatcher { } } + /** + * Called when window focus changed. + */ + public void onWindowFocusChanged(boolean hasFocus) { + mHasFocus = hasFocus; + } + + private boolean hasFocus() { + return mHasFocus; + } + public OnBackInvokedCallback getTopCallback() { if (mAllCallbacks.isEmpty()) { return null; @@ -221,9 +234,11 @@ public class WindowOnBackInvokedDispatcher implements OnBackInvokedDispatcher { static class OnBackInvokedCallbackWrapper extends IOnBackInvokedCallback.Stub { private final WeakReference<OnBackInvokedCallback> mCallback; - - OnBackInvokedCallbackWrapper(@NonNull OnBackInvokedCallback callback) { + private final Supplier<Boolean> mHasFocus; + OnBackInvokedCallbackWrapper(@NonNull OnBackInvokedCallback callback, + @NonNull Supplier<Boolean> hasFocus) { mCallback = new WeakReference<>(callback); + mHasFocus = hasFocus; } @Override @@ -263,7 +278,10 @@ public class WindowOnBackInvokedDispatcher implements OnBackInvokedDispatcher { if (callback == null) { return; } - + if (!mHasFocus.get()) { + Log.w(TAG, "Skip back invoke due to current focus has lost."); + return; + } callback.onBackInvoked(); }); } diff --git a/core/res/res/layout-car/car_alert_dialog.xml b/core/res/res/layout-car/car_alert_dialog.xml index 2e7b62ceb723..3c7a5c578fc5 100644 --- a/core/res/res/layout-car/car_alert_dialog.xml +++ b/core/res/res/layout-car/car_alert_dialog.xml @@ -54,7 +54,7 @@ android:layout_height="wrap_content" android:layout_marginStart="@dimen/text_view_start_margin" android:layout_marginEnd="@dimen/text_view_end_margin" - style="@style/CarBody4"/> + style="@style/CarDialogMessageText"/> <!-- we don't need this spacer, but the id needs to be here for compatibility --> <Space diff --git a/core/res/res/layout-car/car_alert_dialog_button_bar.xml b/core/res/res/layout-car/car_alert_dialog_button_bar.xml index 4f815b887088..43fd1eb4c25f 100644 --- a/core/res/res/layout-car/car_alert_dialog_button_bar.xml +++ b/core/res/res/layout-car/car_alert_dialog_button_bar.xml @@ -16,13 +16,13 @@ --> <ScrollView xmlns:android="http://schemas.android.com/apk/res/android" - android:id="@+id/buttonPanel" - android:layout_width="match_parent" - android:layout_height="wrap_content" - android:scrollbarAlwaysDrawVerticalTrack="true" - android:scrollIndicators="top|bottom" - android:fillViewport="true" - style="?attr/buttonBarStyle"> + android:id="@+id/buttonPanel" + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:scrollbarAlwaysDrawVerticalTrack="true" + android:scrollIndicators="top|bottom" + android:fillViewport="true" + style="?attr/buttonBarStyle"> <com.android.internal.widget.ButtonBarLayout android:layout_width="match_parent" android:layout_height="wrap_content" @@ -35,7 +35,7 @@ <Button android:id="@+id/button3" - style="@style/CarAction1" + style="@style/CarDialogButtonText" android:minWidth="@dimen/car_touch_target_size" android:paddingStart="@dimen/car_padding_2" android:paddingEnd="@dimen/car_padding_2" @@ -46,7 +46,7 @@ <Button android:id="@+id/button2" - style="@style/CarAction1" + style="@style/CarDialogButtonText" android:minWidth="@dimen/car_touch_target_size" android:paddingStart="@dimen/car_padding_2" android:paddingEnd="@dimen/car_padding_2" @@ -57,7 +57,7 @@ <Button android:id="@+id/button1" - style="@style/CarAction1" + style="@style/CarDialogButtonText" android:minWidth="@dimen/car_touch_target_size" android:paddingStart="@dimen/car_padding_2" android:paddingEnd="@dimen/car_padding_2" diff --git a/core/res/res/values/styles_car.xml b/core/res/res/values/styles_car.xml index ca3ba936630e..0655fde70934 100644 --- a/core/res/res/values/styles_car.xml +++ b/core/res/res/values/styles_car.xml @@ -14,96 +14,15 @@ limitations under the License. --> <resources> - <!-- Car text --> - <style name="CarBody1"> - <item name="textStyle">normal</item> - <item name="textSize">@dimen/car_body1_size</item> - <item name="textColor">@color/car_body1</item> - </style> - - <style name="CarBody1.Light"> - <item name="textColor">@color/car_body1_light</item> - </style> - - <style name="CarBody1.Dark"> - <item name="textColor">@color/car_body2_dark</item> - </style> - - <style name="CarBody2"> - <item name="textStyle">normal</item> - <item name="textSize">@dimen/car_body2_size</item> - <item name="textColor">@color/car_body2</item> - </style> - - <style name="CarBody2.Dark"> - <item name="textColor">@color/car_body2_dark</item> - </style> - <style name="CarBody2.Light"> - <item name="textColor">@color/car_body2_light</item> - </style> - - <style name="CarBody3"> - <item name="textStyle">normal</item> - <item name="textSize">@dimen/car_body3_size</item> - <item name="textColor">@color/car_body3</item> - </style> - - <!-- The smallest styling for body text. The color of this text changes based on the day/night - mode. --> - <style name="CarBody4"> + <!-- The Dialog message text style--> + <style name="CarDialogMessageText"> <item name="textStyle">normal</item> <item name="textSize">@dimen/car_body4_size</item> <item name="textColor">@color/car_body4</item> </style> - - <style name="CarAction1"> - <item name="textStyle">bold</item> - <item name="textSize">@dimen/car_action1_size</item> - <item name="textColor">@color/control_default_material</item> - </style> - - <style name="CarAction1.Dark"> - <item name="textColor">@color/car_highlight_dark</item> - </style> - <style name="CarAction1.Light"> - <item name="textColor">@color/car_highlight_light</item> - </style> - - <!-- The styling for title text. The color of this text changes based on day/night mode. --> - <style name="CarTitle" > - <item name="textStyle">bold</item> - <item name="textSize">@dimen/car_title2_size</item> - <item name="textColor">@color/car_title</item> - </style> - - <!-- Title text that is permanently a dark color. --> - <style name="CarTitle.Dark" > - <item name="textColor">@color/car_title_dark</item> - </style> - - <!-- Title text that is permanently a light color. --> - <style name="CarTitle.Light" > - <item name="textColor">@color/car_title_light</item> - </style> - - <!-- Action bar --> - <style name="ActionBarTitle" parent="@style/Widget.DeviceDefault.TextView"> - <item name="android:singleLine">true</item> - <item name="android:textAppearance">?attr/textAppearanceLarge</item> - </style> - - <style name="ActionBarButton" - parent="@style/Widget.DeviceDefault.Button.Borderless.Colored"> - <item name="android:textAppearance">@style/ActionBarButtonTextAppearance</item> - <!-- Button's internal horizontal padding --> - <item name="android:paddingStart">@*android:dimen/car_padding_3</item> - <item name="android:paddingEnd">@*android:dimen/car_padding_3</item> - <item name="android:drawablePadding">@*android:dimen/car_padding_2</item> - <item name="android:maxWidth">@*android:dimen/action_bar_button_max_width</item> - </style> - - <style name="ActionBarButtonTextAppearance" - parent="@style/TextAppearance.DeviceDefault.Widget.Button.Borderless.Colored"> - <item name="android:textAllCaps">false</item> + <!-- This style makes Dialog button text use medium font weight. --> + <style name="CarDialogButtonText"> + <item name="android:textAppearance">@style/TextAppearance.DeviceDefault.Widget.Button</item> + <item name="android:textColor">@color/control_default_material</item> </style> </resources> diff --git a/core/tests/coretests/src/android/window/WindowOnBackInvokedDispatcherTest.java b/core/tests/coretests/src/android/window/WindowOnBackInvokedDispatcherTest.java index f448cb3091e7..b5194f637395 100644 --- a/core/tests/coretests/src/android/window/WindowOnBackInvokedDispatcherTest.java +++ b/core/tests/coretests/src/android/window/WindowOnBackInvokedDispatcherTest.java @@ -66,6 +66,7 @@ public class WindowOnBackInvokedDispatcherTest { MockitoAnnotations.initMocks(this); mDispatcher = new WindowOnBackInvokedDispatcher(true /* applicationCallbackEnabled */); mDispatcher.attachToWindow(mWindowSession, mWindow); + mDispatcher.onWindowFocusChanged(true); } private void waitForIdle() { @@ -152,4 +153,31 @@ public class WindowOnBackInvokedDispatcherTest { waitForIdle(); verify(mCallback2).onBackStarted(); } + + @Test + public void skipBackInvokeWhenNoFocus() throws RemoteException { + ArgumentCaptor<OnBackInvokedCallbackInfo> captor = + ArgumentCaptor.forClass(OnBackInvokedCallbackInfo.class); + + mDispatcher.registerOnBackInvokedCallback( + OnBackInvokedDispatcher.PRIORITY_DEFAULT, mCallback1); + + verify(mWindowSession, times(1)).setOnBackInvokedCallbackInfo( + Mockito.eq(mWindow), + captor.capture()); + + verify(mWindowSession).setOnBackInvokedCallbackInfo(Mockito.eq(mWindow), captor.capture()); + + // Should invoke back if it's still in focused. + captor.getValue().getCallback().onBackInvoked(); + waitForIdle(); + verify(mCallback1).onBackInvoked(); + + // In case the focus has lost during back gesture. + mDispatcher.onWindowFocusChanged(false); + + captor.getValue().getCallback().onBackInvoked(); + waitForIdle(); + verifyZeroInteractions(mCallback1); + } } diff --git a/data/fonts/fonts.xml b/data/fonts/fonts.xml index 84e949a18c52..f8c015f28a50 100644 --- a/data/fonts/fonts.xml +++ b/data/fonts/fonts.xml @@ -932,7 +932,7 @@ <font weight="700" style="normal" fallbackFor="serif">NotoSerifLao-Bold.ttf</font> </family> <family lang="und-Laoo" variant="compact"> - <font weight="400" style="normal">NotoSansLaoUI-Regular.ttf + <font weight="400" style="normal" postScriptName="NotoSansLaoUI">NotoSansLaoUI-Regular.ttf </font> <font weight="700" style="normal">NotoSansLaoUI-Bold.ttf</font> </family> diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/SplitLayout.java b/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/SplitLayout.java index b69cbfa83d7c..40cf9a32d7a5 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/SplitLayout.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/SplitLayout.java @@ -727,31 +727,23 @@ public final class SplitLayout implements DisplayInsetsController.OnInsetsChange ActivityManager.RunningTaskInfo taskInfo1, ActivityManager.RunningTaskInfo taskInfo2) { if (offsetX == 0 && offsetY == 0) { wct.setBounds(taskInfo1.token, mBounds1); - wct.setAppBounds(taskInfo1.token, null); wct.setScreenSizeDp(taskInfo1.token, SCREEN_WIDTH_DP_UNDEFINED, SCREEN_HEIGHT_DP_UNDEFINED); wct.setBounds(taskInfo2.token, mBounds2); - wct.setAppBounds(taskInfo2.token, null); wct.setScreenSizeDp(taskInfo2.token, SCREEN_WIDTH_DP_UNDEFINED, SCREEN_HEIGHT_DP_UNDEFINED); } else { - mTempRect.set(taskInfo1.configuration.windowConfiguration.getBounds()); + getBounds1(mTempRect); mTempRect.offset(offsetX, offsetY); wct.setBounds(taskInfo1.token, mTempRect); - mTempRect.set(taskInfo1.configuration.windowConfiguration.getAppBounds()); - mTempRect.offset(offsetX, offsetY); - wct.setAppBounds(taskInfo1.token, mTempRect); wct.setScreenSizeDp(taskInfo1.token, taskInfo1.configuration.screenWidthDp, taskInfo1.configuration.screenHeightDp); - mTempRect.set(taskInfo2.configuration.windowConfiguration.getBounds()); + getBounds2(mTempRect); mTempRect.offset(offsetX, offsetY); wct.setBounds(taskInfo2.token, mTempRect); - mTempRect.set(taskInfo2.configuration.windowConfiguration.getAppBounds()); - mTempRect.offset(offsetX, offsetY); - wct.setAppBounds(taskInfo2.token, mTempRect); wct.setScreenSizeDp(taskInfo2.token, taskInfo2.configuration.screenWidthDp, taskInfo2.configuration.screenHeightDp); diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/onehanded/OneHandedController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/onehanded/OneHandedController.java index c67247603f2a..24f02ac1a6cf 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/onehanded/OneHandedController.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/onehanded/OneHandedController.java @@ -681,9 +681,12 @@ public class OneHandedController implements RemoteCallable<OneHandedController>, return; } + if (mState.getState() == STATE_ACTIVE) { + mOneHandedUiEventLogger.writeEvent( + OneHandedUiEventLogger.EVENT_ONE_HANDED_TRIGGER_ROTATION_OUT); + } + mDisplayAreaOrganizer.onRotateDisplay(mContext, toRotation, wct); - mOneHandedUiEventLogger.writeEvent( - OneHandedUiEventLogger.EVENT_ONE_HANDED_TRIGGER_ROTATION_OUT); } /** diff --git a/packages/SettingsLib/LayoutPreference/res/layout/settings_entity_header.xml b/packages/SettingsLib/LayoutPreference/res/layout/settings_entity_header.xml index 50f69d1d692a..c629d96bcf4b 100644 --- a/packages/SettingsLib/LayoutPreference/res/layout/settings_entity_header.xml +++ b/packages/SettingsLib/LayoutPreference/res/layout/settings_entity_header.xml @@ -71,6 +71,8 @@ <TextView android:id="@+id/entity_header_second_summary" style="@style/TextAppearance.EntityHeaderSummary" + android:singleLine="false" + android:maxLines="4" android:layout_width="wrap_content" android:layout_height="wrap_content"/> diff --git a/packages/SettingsLib/res/values/strings.xml b/packages/SettingsLib/res/values/strings.xml index f92cc8eda268..4f84c8c817cb 100644 --- a/packages/SettingsLib/res/values/strings.xml +++ b/packages/SettingsLib/res/values/strings.xml @@ -246,9 +246,9 @@ <!-- Bluetooth settings. The user-visible string that is used whenever referring to the PAN profile (accessing Internet through remote device). [CHAR LIMIT=40] --> <string name="bluetooth_profile_pan">Internet access</string> <!-- Bluetooth settings. The user-visible string that is used whenever referring to the PBAP profile. [CHAR LIMIT=40] --> - <string name="bluetooth_profile_pbap">Contact sharing</string> + <string name="bluetooth_profile_pbap">Contacts and call history sharing</string> <!-- Bluetooth settings. The user-visible summary string that is used whenever referring to the PBAP profile (sharing contacts). [CHAR LIMIT=60] --> - <string name="bluetooth_profile_pbap_summary">Use for contact sharing</string> + <string name="bluetooth_profile_pbap_summary">Use for contacts and call history sharing</string> <!-- Bluetooth settings. The user-visible string that is used whenever referring to the PAN profile (sharing this device's Internet connection). [CHAR LIMIT=40] --> <string name="bluetooth_profile_pan_nap">Internet connection sharing</string> <!-- Bluetooth settings. The user-visible string that is used whenever referring to the map profile. --> 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 dbdbdf6ee2ba..2f36ab9aa93d 100644 --- a/packages/SystemUI/animation/src/com/android/systemui/animation/DialogLaunchAnimator.kt +++ b/packages/SystemUI/animation/src/com/android/systemui/animation/DialogLaunchAnimator.kt @@ -515,11 +515,20 @@ private class AnimatedDialog( dialogContentWithBackground.setTransitionVisibility(View.INVISIBLE) // Make sure the dialog is visible instantly and does not do any window animation. - window.attributes.windowAnimations = R.style.Animation_LaunchAnimation + val attributes = window.attributes + attributes.windowAnimations = R.style.Animation_LaunchAnimation // Ensure that the animation is not clipped by the display cut-out when animating this // dialog into an app. - window.attributes.layoutInDisplayCutoutMode = LAYOUT_IN_DISPLAY_CUTOUT_MODE_ALWAYS + attributes.layoutInDisplayCutoutMode = LAYOUT_IN_DISPLAY_CUTOUT_MODE_ALWAYS + + // Ensure that the animation is not clipped by the navigation/task bars when animating this + // dialog into an app. + val wasFittingNavigationBars = + attributes.fitInsetsTypes and WindowInsets.Type.navigationBars() != 0 + attributes.fitInsetsTypes = + attributes.fitInsetsTypes and WindowInsets.Type.navigationBars().inv() + window.attributes = window.attributes // We apply the insets ourselves to make sure that the paddings are set on the correct @@ -527,7 +536,13 @@ private class AnimatedDialog( window.setDecorFitsSystemWindows(false) val viewWithInsets = (dialogContentWithBackground.parent as ViewGroup) viewWithInsets.setOnApplyWindowInsetsListener { view, windowInsets -> - val insets = windowInsets.getInsets(WindowInsets.Type.displayCutout()) + val type = if (wasFittingNavigationBars) { + WindowInsets.Type.displayCutout() or WindowInsets.Type.navigationBars() + } else { + WindowInsets.Type.displayCutout() + } + + val insets = windowInsets.getInsets(type) view.setPadding(insets.left, insets.top, insets.right, insets.bottom) WindowInsets.CONSUMED } diff --git a/packages/SystemUI/animation/src/com/android/systemui/animation/GhostedViewLaunchAnimatorController.kt b/packages/SystemUI/animation/src/com/android/systemui/animation/GhostedViewLaunchAnimatorController.kt index 47f448d503c6..eb000ad312d7 100644 --- a/packages/SystemUI/animation/src/com/android/systemui/animation/GhostedViewLaunchAnimatorController.kt +++ b/packages/SystemUI/animation/src/com/android/systemui/animation/GhostedViewLaunchAnimatorController.kt @@ -49,17 +49,16 @@ private const val TAG = "GhostedViewLaunchAnimatorController" * Note: Avoid instantiating this directly and call [ActivityLaunchAnimator.Controller.fromView] * whenever possible instead. */ -open class GhostedViewLaunchAnimatorController( +open class GhostedViewLaunchAnimatorController @JvmOverloads constructor( /** The view that will be ghosted and from which the background will be extracted. */ private val ghostedView: View, /** The [InteractionJankMonitor.CujType] associated to this animation. */ private val cujType: Int? = null, - private var interactionJankMonitor: InteractionJankMonitor? = null + private var interactionJankMonitor: InteractionJankMonitor = + InteractionJankMonitor.getInstance(), ) : ActivityLaunchAnimator.Controller { - constructor(view: View, type: Int) : this(view, type, null) - /** The container to which we will add the ghost view and expanding background. */ override var launchContainer = ghostedView.rootView as ViewGroup private val launchContainerOverlay: ViewGroupOverlay @@ -203,7 +202,7 @@ open class GhostedViewLaunchAnimatorController( val matrix = ghostView?.animationMatrix ?: Matrix.IDENTITY_MATRIX matrix.getValues(initialGhostViewMatrixValues) - cujType?.let { interactionJankMonitor?.begin(ghostedView, it) } + cujType?.let { interactionJankMonitor.begin(ghostedView, it) } } override fun onLaunchAnimationProgress( @@ -289,7 +288,7 @@ open class GhostedViewLaunchAnimatorController( return } - cujType?.let { interactionJankMonitor?.end(it) } + cujType?.let { interactionJankMonitor.end(it) } backgroundDrawable?.wrapped?.alpha = startBackgroundAlpha diff --git a/packages/SystemUI/res/layout/clipboard_overlay.xml b/packages/SystemUI/res/layout/clipboard_overlay.xml index 99a5a2e904f6..1a1fc75a41a1 100644 --- a/packages/SystemUI/res/layout/clipboard_overlay.xml +++ b/packages/SystemUI/res/layout/clipboard_overlay.xml @@ -115,6 +115,7 @@ android:autoSizeMinTextSize="@dimen/clipboard_overlay_min_font" android:autoSizeMaxTextSize="@dimen/clipboard_overlay_max_font" android:textColor="?attr/overlayButtonTextColor" + android:textColorLink="?attr/overlayButtonTextColor" android:background="?androidprv:attr/colorAccentSecondary" android:layout_width="@dimen/clipboard_preview_size" android:layout_height="@dimen/clipboard_preview_size"/> diff --git a/packages/SystemUI/shared/src/com/android/systemui/flags/Flag.kt b/packages/SystemUI/shared/src/com/android/systemui/flags/Flag.kt index b4646aee444c..a8a526a33229 100644 --- a/packages/SystemUI/shared/src/com/android/systemui/flags/Flag.kt +++ b/packages/SystemUI/shared/src/com/android/systemui/flags/Flag.kt @@ -29,6 +29,7 @@ interface Flag<T> { interface ParcelableFlag<T> : Flag<T>, Parcelable { val default: T + val overridden: Boolean override fun describeContents() = 0 } @@ -52,7 +53,8 @@ interface SysPropFlag<T> : Flag<T> { data class BooleanFlag @JvmOverloads constructor( override val id: Int, override val default: Boolean = false, - override val teamfood: Boolean = false + override val teamfood: Boolean = false, + override val overridden: Boolean = false ) : ParcelableFlag<Boolean> { companion object { @@ -65,12 +67,16 @@ data class BooleanFlag @JvmOverloads constructor( private constructor(parcel: Parcel) : this( id = parcel.readInt(), - default = parcel.readBoolean() + default = parcel.readBoolean(), + teamfood = parcel.readBoolean(), + overridden = parcel.readBoolean() ) override fun writeToParcel(parcel: Parcel, flags: Int) { parcel.writeInt(id) parcel.writeBoolean(default) + parcel.writeBoolean(teamfood) + parcel.writeBoolean(overridden) } } @@ -100,7 +106,8 @@ data class SysPropBooleanFlag @JvmOverloads constructor( data class StringFlag @JvmOverloads constructor( override val id: Int, override val default: String = "", - override val teamfood: Boolean = false + override val teamfood: Boolean = false, + override val overridden: Boolean = false ) : ParcelableFlag<String> { companion object { @JvmField @@ -130,7 +137,8 @@ data class ResourceStringFlag @JvmOverloads constructor( data class IntFlag @JvmOverloads constructor( override val id: Int, override val default: Int = 0, - override val teamfood: Boolean = false + override val teamfood: Boolean = false, + override val overridden: Boolean = false ) : ParcelableFlag<Int> { companion object { @@ -161,7 +169,8 @@ data class ResourceIntFlag @JvmOverloads constructor( data class LongFlag @JvmOverloads constructor( override val id: Int, override val default: Long = 0, - override val teamfood: Boolean = false + override val teamfood: Boolean = false, + override val overridden: Boolean = false ) : ParcelableFlag<Long> { companion object { @@ -186,7 +195,8 @@ data class LongFlag @JvmOverloads constructor( data class FloatFlag @JvmOverloads constructor( override val id: Int, override val default: Float = 0f, - override val teamfood: Boolean = false + override val teamfood: Boolean = false, + override val overridden: Boolean = false ) : ParcelableFlag<Float> { companion object { @@ -217,7 +227,8 @@ data class ResourceFloatFlag @JvmOverloads constructor( data class DoubleFlag @JvmOverloads constructor( override val id: Int, override val default: Double = 0.0, - override val teamfood: Boolean = false + override val teamfood: Boolean = false, + override val overridden: Boolean = false ) : ParcelableFlag<Double> { companion object { @@ -237,4 +248,4 @@ data class DoubleFlag @JvmOverloads constructor( parcel.writeInt(id) parcel.writeDouble(default) } -}
\ No newline at end of file +} diff --git a/packages/SystemUI/shared/src/com/android/systemui/flags/FlagManager.kt b/packages/SystemUI/shared/src/com/android/systemui/flags/FlagManager.kt index 26e40e1ecad3..d172690e2488 100644 --- a/packages/SystemUI/shared/src/com/android/systemui/flags/FlagManager.kt +++ b/packages/SystemUI/shared/src/com/android/systemui/flags/FlagManager.kt @@ -64,20 +64,29 @@ class FlagManager constructor( intent.setPackage(RECEIVING_PACKAGE) return CallbackToFutureAdapter.getFuture { - completer: CallbackToFutureAdapter.Completer<Collection<Flag<*>>> -> - context.sendOrderedBroadcast(intent, null, - object : BroadcastReceiver() { - override fun onReceive(context: Context, intent: Intent) { - val extras: Bundle? = getResultExtras(false) - val listOfFlags: java.util.ArrayList<ParcelableFlag<*>>? = - extras?.getParcelableArrayList(EXTRA_FLAGS) - if (listOfFlags != null) { - completer.set(listOfFlags) - } else { - completer.setException(NoFlagResultsException()) - } + completer: CallbackToFutureAdapter.Completer<Collection<Flag<*>>> -> + context.sendOrderedBroadcast( + intent, + null, + object : BroadcastReceiver() { + override fun onReceive(context: Context, intent: Intent) { + val extras: Bundle? = getResultExtras(false) + val listOfFlags: java.util.ArrayList<ParcelableFlag<*>>? = + extras?.getParcelableArrayList( + EXTRA_FLAGS, ParcelableFlag::class.java + ) + if (listOfFlags != null) { + completer.set(listOfFlags) + } else { + completer.setException(NoFlagResultsException()) } - }, null, Activity.RESULT_OK, "extra data", null) + } + }, + null, + Activity.RESULT_OK, + "extra data", + null + ) "QueryingFlags" } } @@ -152,7 +161,11 @@ class FlagManager constructor( } val parts = uri.pathSegments val idStr = parts[parts.size - 1] - val id = try { idStr.toInt() } catch (e: NumberFormatException) { return } + val id = try { + idStr.toInt() + } catch (e: NumberFormatException) { + return + } clearCacheAction?.accept(id) dispatchListenersAndMaybeRestart(id, onSettingsChangedAction) } @@ -188,4 +201,5 @@ class FlagManager constructor( } class NoFlagResultsException : Exception( - "SystemUI failed to communicate its flags back successfully")
\ No newline at end of file + "SystemUI failed to communicate its flags back successfully" +) diff --git a/packages/SystemUI/src/com/android/systemui/flags/FeatureFlagsDebug.java b/packages/SystemUI/src/com/android/systemui/flags/FeatureFlagsDebug.java index 49b3908558d8..c5221cd9641b 100644 --- a/packages/SystemUI/src/com/android/systemui/flags/FeatureFlagsDebug.java +++ b/packages/SystemUI/src/com/android/systemui/flags/FeatureFlagsDebug.java @@ -32,7 +32,6 @@ import android.content.res.Resources; import android.os.Bundle; import android.os.RemoteException; import android.os.UserHandle; -import android.provider.DeviceConfig; import android.util.Log; import androidx.annotation.NonNull; @@ -197,7 +196,7 @@ public class FeatureFlagsDebug implements FeatureFlags, Dumpable { /** Specific override for Boolean flags that checks against the teamfood list.*/ private boolean readFlagValue(int id, boolean defaultValue) { - Boolean result = readFlagValueInternal(id, BooleanFlagSerializer.INSTANCE); + Boolean result = readBooleanFlagOverride(id); // Only check for teamfood if the default is false. if (!defaultValue && result == null && id != Flags.TEAMFOOD.getId()) { if (mAllFlags.containsKey(id) && mAllFlags.get(id).getTeamfood()) { @@ -208,6 +207,10 @@ public class FeatureFlagsDebug implements FeatureFlags, Dumpable { return result == null ? defaultValue : result; } + private Boolean readBooleanFlagOverride(int id) { + return readFlagValueInternal(id, BooleanFlagSerializer.INSTANCE); + } + @NonNull private <T> T readFlagValue(int id, @NonNull T defaultValue, FlagSerializer<T> serializer) { requireNonNull(defaultValue, "defaultValue"); @@ -407,11 +410,18 @@ public class FeatureFlagsDebug implements FeatureFlags, Dumpable { @Nullable private ParcelableFlag<?> toParcelableFlag(Flag<?> f) { if (f instanceof BooleanFlag) { - return new BooleanFlag(f.getId(), isEnabled((BooleanFlag) f), f.getTeamfood()); + return new BooleanFlag( + f.getId(), + isEnabled((BooleanFlag) f), + f.getTeamfood(), + readBooleanFlagOverride(f.getId()) != null); } if (f instanceof ResourceBooleanFlag) { return new BooleanFlag( - f.getId(), isEnabled((ResourceBooleanFlag) f), f.getTeamfood()); + f.getId(), + isEnabled((ResourceBooleanFlag) f), + f.getTeamfood(), + readBooleanFlagOverride(f.getId()) != null); } if (f instanceof DeviceConfigBooleanFlag) { return new BooleanFlag( @@ -420,7 +430,10 @@ public class FeatureFlagsDebug implements FeatureFlags, Dumpable { if (f instanceof SysPropBooleanFlag) { // TODO(b/223379190): Teamfood not supported for sysprop flags yet. return new BooleanFlag( - f.getId(), isEnabled((SysPropBooleanFlag) f), false); + f.getId(), + ((SysPropBooleanFlag) f).getDefault(), + false, + !mSystemProperties.get(((SysPropBooleanFlag) f).getName()).isEmpty()); } // TODO: add support for other flag types. diff --git a/packages/SystemUI/src/com/android/systemui/flags/Flags.java b/packages/SystemUI/src/com/android/systemui/flags/Flags.java index 245ea219ea68..a566984748a7 100644 --- a/packages/SystemUI/src/com/android/systemui/flags/Flags.java +++ b/packages/SystemUI/src/com/android/systemui/flags/Flags.java @@ -63,6 +63,9 @@ public class Flags { public static final BooleanFlag REMOVE_UNRANKED_NOTIFICATIONS = new BooleanFlag(109, false); + public static final BooleanFlag FSI_REQUIRES_KEYGUARD = + new BooleanFlag(110, false, true); + /***************************************/ // 200 - keyguard/lockscreen @@ -142,6 +145,9 @@ public class Flags { public static final ResourceBooleanFlag STATUS_BAR_USER_SWITCHER = new ResourceBooleanFlag(602, R.bool.flag_user_switcher_chip); + public static final BooleanFlag STATUS_BAR_LETTERBOX_APPEARANCE = + new BooleanFlag(603, false); + /***************************************/ // 700 - dialer/calls public static final BooleanFlag ONGOING_CALL_STATUS_BAR_CHIP = diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotifPipelineFlags.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotifPipelineFlags.kt index c4947ca2dd90..0c6a91fe61f1 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotifPipelineFlags.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotifPipelineFlags.kt @@ -59,4 +59,7 @@ class NotifPipelineFlags @Inject constructor( fun removeUnrankedNotifs(): Boolean = featureFlags.isEnabled(Flags.REMOVE_UNRANKED_NOTIFICATIONS) + + fun fullScreenIntentRequiresKeyguard(): Boolean = + featureFlags.isEnabled(Flags.FSI_REQUIRES_KEYGUARD) } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/interruption/NotificationInterruptStateProviderImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/interruption/NotificationInterruptStateProviderImpl.java index 535dc6e2b583..a72b3814d5ad 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/interruption/NotificationInterruptStateProviderImpl.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/interruption/NotificationInterruptStateProviderImpl.java @@ -41,6 +41,7 @@ import com.android.systemui.statusbar.notification.NotificationFilter; import com.android.systemui.statusbar.notification.collection.NotificationEntry; import com.android.systemui.statusbar.policy.BatteryController; import com.android.systemui.statusbar.policy.HeadsUpManager; +import com.android.systemui.statusbar.policy.KeyguardStateController; import java.util.ArrayList; import java.util.List; @@ -58,6 +59,7 @@ public class NotificationInterruptStateProviderImpl implements NotificationInter private final List<NotificationInterruptSuppressor> mSuppressors = new ArrayList<>(); private final StatusBarStateController mStatusBarStateController; + private final KeyguardStateController mKeyguardStateController; private final NotificationFilter mNotificationFilter; private final ContentResolver mContentResolver; private final PowerManager mPowerManager; @@ -82,6 +84,7 @@ public class NotificationInterruptStateProviderImpl implements NotificationInter NotificationFilter notificationFilter, BatteryController batteryController, StatusBarStateController statusBarStateController, + KeyguardStateController keyguardStateController, HeadsUpManager headsUpManager, NotificationInterruptLogger logger, @Main Handler mainHandler, @@ -94,6 +97,7 @@ public class NotificationInterruptStateProviderImpl implements NotificationInter mAmbientDisplayConfiguration = ambientDisplayConfiguration; mNotificationFilter = notificationFilter; mStatusBarStateController = statusBarStateController; + mKeyguardStateController = keyguardStateController; mHeadsUpManager = headsUpManager; mLogger = logger; mFlags = flags; @@ -228,6 +232,28 @@ public class NotificationInterruptStateProviderImpl implements NotificationInter return false; } + // Check whether FSI requires the keyguard to be showing. + if (mFlags.fullScreenIntentRequiresKeyguard()) { + + // If notification won't HUN and keyguard is showing, launch the FSI. + if (mKeyguardStateController.isShowing()) { + if (mKeyguardStateController.isOccluded()) { + mLogger.logFullscreen(entry, "Expected not to HUN while keyguard occluded"); + } else { + // Likely LOCKED_SHADE, but launch FSI anyway + mLogger.logFullscreen(entry, "Keyguard is showing and not occluded"); + } + return true; + } + + // Detect the case determined by b/231322873 to launch FSI while device is in use, + // as blocked by the correct implementation, and report the event. + final int uid = entry.getSbn().getUid(); + android.util.EventLog.writeEvent(0x534e4554, "231322873", uid, "no hun or keyguard"); + mLogger.logNoFullscreenWarning(entry, "Expected not to HUN while not on keyguard"); + return false; + } + // If the notification won't HUN for some other reason (DND/snooze/etc), launch FSI. mLogger.logFullscreen(entry, "Expected not to HUN"); return true; diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarBoundsProvider.kt b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarBoundsProvider.kt new file mode 100644 index 000000000000..f5ba399fe51a --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarBoundsProvider.kt @@ -0,0 +1,92 @@ +/* + * Copyright (C) 2022 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.systemui.statusbar.phone + +import android.graphics.Rect +import android.view.View +import com.android.systemui.statusbar.phone.fragment.dagger.StatusBarFragmentComponent +import com.android.systemui.statusbar.phone.fragment.dagger.StatusBarFragmentModule.END_SIDE_CONTENT +import com.android.systemui.statusbar.phone.fragment.dagger.StatusBarFragmentModule.START_SIDE_CONTENT +import com.android.systemui.statusbar.phone.fragment.dagger.StatusBarFragmentScope +import com.android.systemui.util.boundsOnScreen +import javax.inject.Inject +import javax.inject.Named + +/** Provides various bounds within the status bar. */ +@StatusBarFragmentScope +class StatusBarBoundsProvider +@Inject +constructor( + private val changeListeners: Set<@JvmSuppressWildcards BoundsChangeListener>, + @Named(START_SIDE_CONTENT) private val startSideContent: View, + @Named(END_SIDE_CONTENT) private val endSideContent: View, +) : StatusBarFragmentComponent.Startable { + + interface BoundsChangeListener { + fun onStatusBarBoundsChanged() + } + + private var previousBounds = + BoundsPair(start = startSideContent.boundsOnScreen, end = endSideContent.boundsOnScreen) + + private val layoutListener = + View.OnLayoutChangeListener { _, _, _, _, _, _, _, _, _ -> + val newBounds = BoundsPair(start = visibleStartSideBounds, end = visibleEndSideBounds) + if (previousBounds != newBounds) { + previousBounds = newBounds + changeListeners.forEach { it.onStatusBarBoundsChanged() } + } + } + + override fun start() { + startSideContent.addOnLayoutChangeListener(layoutListener) + endSideContent.addOnLayoutChangeListener(layoutListener) + } + + override fun stop() { + startSideContent.removeOnLayoutChangeListener(layoutListener) + endSideContent.removeOnLayoutChangeListener(layoutListener) + } + + /** + * Returns the bounds of the end side of the status bar that are visible to the user. The end + * side is right when in LTR and is left when in RTL. + * + * Note that even though the layout might be larger, here we only return the bounds for what is + * visible to the user. + * + * The end side of the status bar contains the multi-user switcher and status icons such as + * wi-fi, battery, etc + */ + val visibleEndSideBounds: Rect + get() = endSideContent.boundsOnScreen + + /** + * Returns the bounds of the start side of the status bar that are visible to the user. The + * start side is left when in LTR and is right when in RTL. + * + * Note that even though the layout might be larger, here we only return the bounds for what is + * visible to the user. + * + * The start side of the status bar contains the operator name, clock, on-going call chip, and + * notifications. + */ + val visibleStartSideBounds: Rect + get() = startSideContent.boundsOnScreen +} + +private data class BoundsPair(val start: Rect, val end: Rect) diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarNotificationPresenter.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarNotificationPresenter.java index 4bbd69b91a0a..4c239314f70f 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarNotificationPresenter.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarNotificationPresenter.java @@ -399,7 +399,8 @@ class StatusBarNotificationPresenter implements NotificationPresenter, return true; } - if (sbn.getNotification().fullScreenIntent != null) { + if (sbn.getNotification().fullScreenIntent != null + && !mNotifPipelineFlags.fullScreenIntentRequiresKeyguard()) { // we don't allow head-up on the lockscreen (unless there's a // "showWhenLocked" activity currently showing) if // the potential HUN has a fullscreen intent diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/dagger/StatusBarViewModule.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/dagger/StatusBarViewModule.java index 41df8e3cbb31..88e985f9eeaf 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/dagger/StatusBarViewModule.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/dagger/StatusBarViewModule.java @@ -30,6 +30,7 @@ import com.android.systemui.battery.BatteryMeterViewController; import com.android.systemui.biometrics.AuthRippleView; import com.android.systemui.broadcast.BroadcastDispatcher; import com.android.systemui.dagger.qualifiers.Main; +import com.android.systemui.dump.DumpManager; import com.android.systemui.flags.FeatureFlags; import com.android.systemui.flags.Flags; import com.android.systemui.plugins.statusbar.StatusBarStateController; @@ -269,7 +270,8 @@ public abstract class StatusBarViewModule { CollapsedStatusBarFragmentLogger collapsedStatusBarFragmentLogger, OperatorNameViewController.Factory operatorNameViewControllerFactory, SecureSettings secureSettings, - @Main Executor mainExecutor + @Main Executor mainExecutor, + DumpManager dumpManager ) { return new CollapsedStatusBarFragment(statusBarFragmentComponentFactory, ongoingCallController, @@ -289,7 +291,8 @@ public abstract class StatusBarViewModule { collapsedStatusBarFragmentLogger, operatorNameViewControllerFactory, secureSettings, - mainExecutor); + mainExecutor, + dumpManager); } /** diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/fragment/CollapsedStatusBarFragment.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/fragment/CollapsedStatusBarFragment.java index 39bf92a4dcbb..0848729781bd 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/fragment/CollapsedStatusBarFragment.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/fragment/CollapsedStatusBarFragment.java @@ -34,6 +34,8 @@ import android.os.Parcelable; import android.os.UserHandle; import android.provider.Settings; import android.telephony.SubscriptionManager; +import android.util.ArrayMap; +import android.util.IndentingPrintWriter; import android.util.SparseArray; import android.view.LayoutInflater; import android.view.View; @@ -43,9 +45,11 @@ import android.widget.LinearLayout; import androidx.annotation.VisibleForTesting; +import com.android.systemui.Dumpable; import com.android.systemui.R; import com.android.systemui.animation.Interpolators; import com.android.systemui.dagger.qualifiers.Main; +import com.android.systemui.dump.DumpManager; import com.android.systemui.flags.FeatureFlags; import com.android.systemui.plugins.statusbar.StatusBarStateController; import com.android.systemui.shade.NotificationPanelViewController; @@ -66,6 +70,7 @@ import com.android.systemui.statusbar.phone.StatusBarIconController; import com.android.systemui.statusbar.phone.StatusBarIconController.DarkIconManager; import com.android.systemui.statusbar.phone.StatusBarLocationPublisher; import com.android.systemui.statusbar.phone.fragment.dagger.StatusBarFragmentComponent; +import com.android.systemui.statusbar.phone.fragment.dagger.StatusBarFragmentComponent.Startable; import com.android.systemui.statusbar.phone.ongoingcall.OngoingCallController; import com.android.systemui.statusbar.phone.ongoingcall.OngoingCallListener; import com.android.systemui.statusbar.phone.panelstate.PanelExpansionStateManager; @@ -76,9 +81,12 @@ import com.android.systemui.util.CarrierConfigTracker.CarrierConfigChangedListen import com.android.systemui.util.CarrierConfigTracker.DefaultDataSubscriptionChangedListener; import com.android.systemui.util.settings.SecureSettings; +import java.io.PrintWriter; import java.util.ArrayList; import java.util.Arrays; import java.util.List; +import java.util.Map; +import java.util.Set; import java.util.concurrent.Executor; /** @@ -89,7 +97,7 @@ import java.util.concurrent.Executor; @SuppressLint("ValidFragment") public class CollapsedStatusBarFragment extends Fragment implements CommandQueue.Callbacks, StatusBarStateController.StateListener, - SystemStatusAnimationCallback { + SystemStatusAnimationCallback, Dumpable { public static final String TAG = "CollapsedStatusBarFragment"; private static final String EXTRA_PANEL_STATE = "panel_state"; @@ -124,8 +132,10 @@ public class CollapsedStatusBarFragment extends Fragment implements CommandQueue private final StatusBarHideIconsForBouncerManager mStatusBarHideIconsForBouncerManager; private final SecureSettings mSecureSettings; private final Executor mMainExecutor; + private final DumpManager mDumpManager; private List<String> mBlockedIcons = new ArrayList<>(); + private Map<Startable, Startable.State> mStartableStates = new ArrayMap<>(); private SignalCallback mSignalCallback = new SignalCallback() { @Override @@ -185,7 +195,8 @@ public class CollapsedStatusBarFragment extends Fragment implements CommandQueue CollapsedStatusBarFragmentLogger collapsedStatusBarFragmentLogger, OperatorNameViewController.Factory operatorNameViewControllerFactory, SecureSettings secureSettings, - @Main Executor mainExecutor + @Main Executor mainExecutor, + DumpManager dumpManager ) { mStatusBarFragmentComponentFactory = statusBarFragmentComponentFactory; mOngoingCallController = ongoingCallController; @@ -206,6 +217,7 @@ public class CollapsedStatusBarFragment extends Fragment implements CommandQueue mOperatorNameViewControllerFactory = operatorNameViewControllerFactory; mSecureSettings = secureSettings; mMainExecutor = mainExecutor; + mDumpManager = dumpManager; } @Override @@ -217,8 +229,15 @@ public class CollapsedStatusBarFragment extends Fragment implements CommandQueue @Override public void onViewCreated(View view, @Nullable Bundle savedInstanceState) { super.onViewCreated(view, savedInstanceState); + mDumpManager.registerDumpable(getClass().getSimpleName(), this); mStatusBarFragmentComponent = mStatusBarFragmentComponentFactory.create(this); mStatusBarFragmentComponent.init(); + mStartableStates.clear(); + for (Startable startable : mStatusBarFragmentComponent.getStartables()) { + mStartableStates.put(startable, Startable.State.STARTING); + startable.start(); + mStartableStates.put(startable, Startable.State.STARTED); + } mStatusBar = (PhoneStatusBarView) view; View contents = mStatusBar.findViewById(R.id.status_bar_contents); @@ -321,6 +340,13 @@ public class CollapsedStatusBarFragment extends Fragment implements CommandQueue } mCarrierConfigTracker.removeCallback(mCarrierConfigCallback); mCarrierConfigTracker.removeDataSubscriptionChangedListener(mDefaultDataListener); + + for (Startable startable : mStatusBarFragmentComponent.getStartables()) { + mStartableStates.put(startable, Startable.State.STOPPING); + startable.stop(); + mStartableStates.put(startable, Startable.State.STOPPED); + } + mDumpManager.unregisterDumpable(getClass().getSimpleName()); } /** Initializes views related to the notification icon area. */ @@ -670,4 +696,23 @@ public class CollapsedStatusBarFragment extends Fragment implements CommandQueue updateStatusBarLocation(left, right); } }; + + @Override + public void dump(PrintWriter printWriter, String[] args) { + IndentingPrintWriter pw = new IndentingPrintWriter(printWriter, /* singleIndent= */" "); + StatusBarFragmentComponent component = mStatusBarFragmentComponent; + if (component == null) { + pw.println("StatusBarFragmentComponent is null"); + } else { + Set<Startable> startables = component.getStartables(); + pw.println("Startables: " + startables.size()); + pw.increaseIndent(); + for (Startable startable : startables) { + Startable.State startableState = mStartableStates.getOrDefault(startable, + Startable.State.NONE); + pw.println(startable + ", state: " + startableState); + } + pw.decreaseIndent(); + } + } } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/fragment/dagger/StatusBarFragmentComponent.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/fragment/dagger/StatusBarFragmentComponent.java index 6717bc768fbb..d9a58442d856 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/fragment/dagger/StatusBarFragmentComponent.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/fragment/dagger/StatusBarFragmentComponent.java @@ -23,9 +23,12 @@ import com.android.systemui.statusbar.phone.LightsOutNotifController; import com.android.systemui.statusbar.phone.PhoneStatusBarTransitions; import com.android.systemui.statusbar.phone.PhoneStatusBarView; import com.android.systemui.statusbar.phone.PhoneStatusBarViewController; +import com.android.systemui.statusbar.phone.StatusBarBoundsProvider; import com.android.systemui.statusbar.phone.StatusBarDemoMode; import com.android.systemui.statusbar.phone.fragment.CollapsedStatusBarFragment; +import java.util.Set; + import dagger.BindsInstance; import dagger.Subcomponent; @@ -41,7 +44,10 @@ import dagger.Subcomponent; * should be included here or in {@link StatusBarFragmentModule}. */ -@Subcomponent(modules = {StatusBarFragmentModule.class}) +@Subcomponent(modules = { + StatusBarFragmentModule.class, + StatusBarStartablesModule.class +}) @StatusBarFragmentScope public interface StatusBarFragmentComponent { /** Simple factory. */ @@ -52,6 +58,18 @@ public interface StatusBarFragmentComponent { } /** + * Performs initialization logic after {@link StatusBarFragmentComponent} has been constructed. + */ + interface Startable { + void start(); + void stop(); + + enum State { + NONE, STARTING, STARTED, STOPPING, STOPPED + } + } + + /** * Initialize anything extra for the component. Must be called after the component is created. */ default void init() { @@ -92,4 +110,10 @@ public interface StatusBarFragmentComponent { /** */ @StatusBarFragmentScope PhoneStatusBarTransitions getPhoneStatusBarTransitions(); + + /** */ + Set<Startable> getStartables(); + + /** */ + StatusBarBoundsProvider getBoundsProvider(); } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/fragment/dagger/StatusBarFragmentModule.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/fragment/dagger/StatusBarFragmentModule.java index 7252dfb76c14..41f1f9589ce4 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/fragment/dagger/StatusBarFragmentModule.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/fragment/dagger/StatusBarFragmentModule.java @@ -26,6 +26,7 @@ import com.android.systemui.statusbar.HeadsUpStatusBarView; import com.android.systemui.statusbar.phone.PhoneStatusBarTransitions; import com.android.systemui.statusbar.phone.PhoneStatusBarView; import com.android.systemui.statusbar.phone.PhoneStatusBarViewController; +import com.android.systemui.statusbar.phone.StatusBarBoundsProvider; import com.android.systemui.statusbar.phone.fragment.CollapsedStatusBarFragment; import com.android.systemui.statusbar.phone.userswitcher.StatusBarUserSwitcherContainer; import com.android.systemui.statusbar.phone.userswitcher.StatusBarUserSwitcherController; @@ -34,12 +35,14 @@ import com.android.systemui.statusbar.policy.Clock; import com.android.systemui.statusbar.window.StatusBarWindowController; import java.util.Optional; +import java.util.Set; import javax.inject.Named; import dagger.Binds; import dagger.Module; import dagger.Provides; +import dagger.multibindings.Multibinds; /** Dagger module for {@link StatusBarFragmentComponent}. */ @Module @@ -48,6 +51,8 @@ public interface StatusBarFragmentModule { String LIGHTS_OUT_NOTIF_VIEW = "lights_out_notif_view"; String OPERATOR_NAME_VIEW = "operator_name_view"; String OPERATOR_NAME_FRAME_VIEW = "operator_name_frame_view"; + String START_SIDE_CONTENT = "start_side_content"; + String END_SIDE_CONTENT = "end_side_content"; /** */ @Provides @@ -68,6 +73,22 @@ public interface StatusBarFragmentModule { /** */ @Provides @StatusBarFragmentScope + @Named(START_SIDE_CONTENT) + static View startSideContent(@RootView PhoneStatusBarView view) { + return view.findViewById(R.id.status_bar_start_side_content); + } + + /** */ + @Provides + @StatusBarFragmentScope + @Named(END_SIDE_CONTENT) + static View endSideContent(@RootView PhoneStatusBarView view) { + return view.findViewById(R.id.status_bar_end_side_content); + } + + /** */ + @Provides + @StatusBarFragmentScope @Named(LIGHTS_OUT_NOTIF_VIEW) static View provideLightsOutNotifView(@RootView PhoneStatusBarView view) { return view.findViewById(R.id.notification_lights_out); @@ -138,4 +159,8 @@ public interface StatusBarFragmentModule { static HeadsUpStatusBarView providesHeasdUpStatusBarView(@RootView PhoneStatusBarView view) { return view.findViewById(R.id.heads_up_status_bar_view); } + + /** */ + @Multibinds + Set<StatusBarBoundsProvider.BoundsChangeListener> boundsChangeListeners(); } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/fragment/dagger/StatusBarStartablesModule.kt b/packages/SystemUI/src/com/android/systemui/statusbar/phone/fragment/dagger/StatusBarStartablesModule.kt new file mode 100644 index 000000000000..9003d13df0a0 --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/fragment/dagger/StatusBarStartablesModule.kt @@ -0,0 +1,32 @@ +/* + * Copyright (C) 2022 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.systemui.statusbar.phone.fragment.dagger + +import com.android.systemui.statusbar.phone.StatusBarBoundsProvider +import dagger.Binds +import dagger.Module +import dagger.multibindings.IntoSet + +@Module +internal interface StatusBarStartablesModule { + + @Binds + @IntoSet + fun statusBarBoundsCalculator( + statusBarBoundsProvider: StatusBarBoundsProvider + ): StatusBarFragmentComponent.Startable +} diff --git a/packages/SystemUI/src/com/android/systemui/util/ConvenienceExtensions.kt b/packages/SystemUI/src/com/android/systemui/util/ConvenienceExtensions.kt index 57b3f53c48fe..ec7aabb58b49 100644 --- a/packages/SystemUI/src/com/android/systemui/util/ConvenienceExtensions.kt +++ b/packages/SystemUI/src/com/android/systemui/util/ConvenienceExtensions.kt @@ -16,7 +16,9 @@ package com.android.systemui.util +import android.graphics.Rect import android.util.IndentingPrintWriter +import android.view.View import android.view.ViewGroup import java.io.PrintWriter @@ -45,4 +47,12 @@ inline fun PrintWriter.indentIfPossible(block: PrintWriter.() -> Unit) { if (this is IndentingPrintWriter) increaseIndent() block() if (this is IndentingPrintWriter) decreaseIndent() -}
\ No newline at end of file +} + +/** Convenience extension property for [View.getBoundsOnScreen]. */ +val View.boundsOnScreen: Rect + get() { + val bounds = Rect() + getBoundsOnScreen(bounds) + return bounds + } diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/interruption/NotificationInterruptStateProviderImplTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/interruption/NotificationInterruptStateProviderImplTest.java index 72d3c2e95d75..5386171eeda2 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/interruption/NotificationInterruptStateProviderImplTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/interruption/NotificationInterruptStateProviderImplTest.java @@ -27,6 +27,7 @@ import static android.app.NotificationManager.Policy.SUPPRESSED_EFFECT_PEEK; import static com.android.systemui.statusbar.NotificationEntryHelper.modifyRanking; import static com.android.systemui.statusbar.StatusBarState.KEYGUARD; import static com.android.systemui.statusbar.StatusBarState.SHADE; +import static com.android.systemui.statusbar.StatusBarState.SHADE_LOCKED; import static com.google.common.truth.Truth.assertThat; @@ -61,6 +62,7 @@ import com.android.systemui.statusbar.notification.collection.NotificationEntry; import com.android.systemui.statusbar.notification.collection.NotificationEntryBuilder; import com.android.systemui.statusbar.policy.BatteryController; import com.android.systemui.statusbar.policy.HeadsUpManager; +import com.android.systemui.statusbar.policy.KeyguardStateController; import org.junit.Before; import org.junit.Test; @@ -87,6 +89,8 @@ public class NotificationInterruptStateProviderImplTest extends SysuiTestCase { @Mock StatusBarStateController mStatusBarStateController; @Mock + KeyguardStateController mKeyguardStateController; + @Mock HeadsUpManager mHeadsUpManager; @Mock NotificationInterruptLogger mLogger; @@ -106,6 +110,7 @@ public class NotificationInterruptStateProviderImplTest extends SysuiTestCase { @Before public void setup() { MockitoAnnotations.initMocks(this); + when(mFlags.fullScreenIntentRequiresKeyguard()).thenReturn(false); mNotifInterruptionStateProvider = new NotificationInterruptStateProviderImpl( @@ -116,6 +121,7 @@ public class NotificationInterruptStateProviderImplTest extends SysuiTestCase { mNotificationFilter, mBatteryController, mStatusBarStateController, + mKeyguardStateController, mHeadsUpManager, mLogger, mMockHandler, @@ -427,6 +433,12 @@ public class NotificationInterruptStateProviderImplTest extends SysuiTestCase { } @Test + public void testShouldNotFullScreen_notPendingIntent_withStrictFlag() throws Exception { + when(mFlags.fullScreenIntentRequiresKeyguard()).thenReturn(true); + testShouldNotFullScreen_notPendingIntent(); + } + + @Test public void testShouldNotFullScreen_notPendingIntent() throws RemoteException { NotificationEntry entry = createNotification(IMPORTANCE_HIGH); when(mPowerManager.isInteractive()).thenReturn(true); @@ -441,6 +453,12 @@ public class NotificationInterruptStateProviderImplTest extends SysuiTestCase { } @Test + public void testShouldNotFullScreen_notHighImportance_withStrictFlag() throws Exception { + when(mFlags.fullScreenIntentRequiresKeyguard()).thenReturn(true); + testShouldNotFullScreen_notHighImportance(); + } + + @Test public void testShouldNotFullScreen_notHighImportance() throws RemoteException { NotificationEntry entry = createFsiNotification(IMPORTANCE_DEFAULT, /* silenced */ false); when(mPowerManager.isInteractive()).thenReturn(true); @@ -455,6 +473,12 @@ public class NotificationInterruptStateProviderImplTest extends SysuiTestCase { } @Test + public void testShouldNotFullScreen_isGroupAlertSilenced_withStrictFlag() throws Exception { + when(mFlags.fullScreenIntentRequiresKeyguard()).thenReturn(true); + testShouldNotFullScreen_isGroupAlertSilenced(); + } + + @Test public void testShouldNotFullScreen_isGroupAlertSilenced() throws RemoteException { NotificationEntry entry = createFsiNotification(IMPORTANCE_HIGH, /* silenced */ true); when(mPowerManager.isInteractive()).thenReturn(false); @@ -469,6 +493,12 @@ public class NotificationInterruptStateProviderImplTest extends SysuiTestCase { } @Test + public void testShouldFullScreen_notInteractive_withStrictFlag() throws Exception { + when(mFlags.fullScreenIntentRequiresKeyguard()).thenReturn(true); + testShouldFullScreen_notInteractive(); + } + + @Test public void testShouldFullScreen_notInteractive() throws RemoteException { NotificationEntry entry = createFsiNotification(IMPORTANCE_HIGH, /* silenced */ false); when(mPowerManager.isInteractive()).thenReturn(false); @@ -483,6 +513,12 @@ public class NotificationInterruptStateProviderImplTest extends SysuiTestCase { } @Test + public void testShouldFullScreen_isDreaming_withStrictFlag() throws Exception { + when(mFlags.fullScreenIntentRequiresKeyguard()).thenReturn(true); + testShouldFullScreen_isDreaming(); + } + + @Test public void testShouldFullScreen_isDreaming() throws RemoteException { NotificationEntry entry = createFsiNotification(IMPORTANCE_HIGH, /* silenced */ false); when(mPowerManager.isInteractive()).thenReturn(true); @@ -497,6 +533,12 @@ public class NotificationInterruptStateProviderImplTest extends SysuiTestCase { } @Test + public void testShouldFullScreen_onKeyguard_withStrictFlag() throws Exception { + when(mFlags.fullScreenIntentRequiresKeyguard()).thenReturn(true); + testShouldFullScreen_onKeyguard(); + } + + @Test public void testShouldFullScreen_onKeyguard() throws RemoteException { NotificationEntry entry = createFsiNotification(IMPORTANCE_HIGH, /* silenced */ false); when(mPowerManager.isInteractive()).thenReturn(true); @@ -511,6 +553,12 @@ public class NotificationInterruptStateProviderImplTest extends SysuiTestCase { } @Test + public void testShouldNotFullScreen_willHun_withStrictFlag() throws Exception { + when(mFlags.fullScreenIntentRequiresKeyguard()).thenReturn(true); + testShouldNotFullScreen_willHun(); + } + + @Test public void testShouldNotFullScreen_willHun() throws RemoteException { NotificationEntry entry = createFsiNotification(IMPORTANCE_HIGH, /* silenced */ false); when(mPowerManager.isInteractive()).thenReturn(true); @@ -542,6 +590,66 @@ public class NotificationInterruptStateProviderImplTest extends SysuiTestCase { verify(mLogger).logFullscreen(entry, "Expected not to HUN"); } + @Test + public void testShouldFullScreen_snoozed_occluding_withStrictRules() throws Exception { + when(mFlags.fullScreenIntentRequiresKeyguard()).thenReturn(true); + NotificationEntry entry = createFsiNotification(IMPORTANCE_HIGH, /* silenced */ false); + when(mPowerManager.isInteractive()).thenReturn(true); + when(mPowerManager.isScreenOn()).thenReturn(true); + when(mDreamManager.isDreaming()).thenReturn(false); + when(mStatusBarStateController.getState()).thenReturn(SHADE); + when(mHeadsUpManager.isSnoozed("a")).thenReturn(true); + when(mKeyguardStateController.isShowing()).thenReturn(true); + when(mKeyguardStateController.isOccluded()).thenReturn(true); + + assertThat(mNotifInterruptionStateProvider.shouldLaunchFullScreenIntentWhenAdded(entry)) + .isTrue(); + verify(mLogger).logNoHeadsUpPackageSnoozed(entry); + verify(mLogger, never()).logNoFullscreen(any(), any()); + verify(mLogger, never()).logNoFullscreenWarning(any(), any()); + verify(mLogger).logFullscreen(entry, "Expected not to HUN while keyguard occluded"); + } + + @Test + public void testShouldFullScreen_snoozed_lockedShade_withStrictRules() throws Exception { + when(mFlags.fullScreenIntentRequiresKeyguard()).thenReturn(true); + NotificationEntry entry = createFsiNotification(IMPORTANCE_HIGH, /* silenced */ false); + when(mPowerManager.isInteractive()).thenReturn(true); + when(mPowerManager.isScreenOn()).thenReturn(true); + when(mDreamManager.isDreaming()).thenReturn(false); + when(mStatusBarStateController.getState()).thenReturn(SHADE_LOCKED); + when(mHeadsUpManager.isSnoozed("a")).thenReturn(true); + when(mKeyguardStateController.isShowing()).thenReturn(true); + when(mKeyguardStateController.isOccluded()).thenReturn(false); + + assertThat(mNotifInterruptionStateProvider.shouldLaunchFullScreenIntentWhenAdded(entry)) + .isTrue(); + verify(mLogger).logNoHeadsUpPackageSnoozed(entry); + verify(mLogger, never()).logNoFullscreen(any(), any()); + verify(mLogger, never()).logNoFullscreenWarning(any(), any()); + verify(mLogger).logFullscreen(entry, "Keyguard is showing and not occluded"); + } + + @Test + public void testShouldNotFullScreen_snoozed_unlocked_withStrictRules() throws Exception { + when(mFlags.fullScreenIntentRequiresKeyguard()).thenReturn(true); + NotificationEntry entry = createFsiNotification(IMPORTANCE_HIGH, /* silenced */ false); + when(mPowerManager.isInteractive()).thenReturn(true); + when(mPowerManager.isScreenOn()).thenReturn(true); + when(mDreamManager.isDreaming()).thenReturn(false); + when(mStatusBarStateController.getState()).thenReturn(SHADE); + when(mHeadsUpManager.isSnoozed("a")).thenReturn(true); + when(mKeyguardStateController.isShowing()).thenReturn(false); + when(mKeyguardStateController.isOccluded()).thenReturn(false); + + assertThat(mNotifInterruptionStateProvider.shouldLaunchFullScreenIntentWhenAdded(entry)) + .isFalse(); + verify(mLogger).logNoHeadsUpPackageSnoozed(entry); + verify(mLogger, never()).logNoFullscreen(any(), any()); + verify(mLogger).logNoFullscreenWarning(entry, "Expected not to HUN while not on keyguard"); + verify(mLogger, never()).logFullscreen(any(), any()); + } + /** * Bubbles can happen. */ diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/CentralSurfacesImplTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/CentralSurfacesImplTest.java index b75c52ad283e..c6fb0ce34e85 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/CentralSurfacesImplTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/CentralSurfacesImplTest.java @@ -306,8 +306,13 @@ public class CentralSurfacesImplTest extends SysuiTestCase { mNotificationInterruptStateProvider = new TestableNotificationInterruptStateProviderImpl(mContext.getContentResolver(), mPowerManager, - mDreamManager, mAmbientDisplayConfiguration, mNotificationFilter, - mStatusBarStateController, mBatteryController, mHeadsUpManager, + mDreamManager, + mAmbientDisplayConfiguration, + mNotificationFilter, + mStatusBarStateController, + mKeyguardStateController, + mBatteryController, + mHeadsUpManager, mock(NotificationInterruptLogger.class), new Handler(TestableLooper.get(this).getLooper()), mock(NotifPipelineFlags.class), @@ -1036,15 +1041,28 @@ public class CentralSurfacesImplTest extends SysuiTestCase { AmbientDisplayConfiguration ambientDisplayConfiguration, NotificationFilter filter, StatusBarStateController controller, + KeyguardStateController keyguardStateController, BatteryController batteryController, HeadsUpManager headsUpManager, NotificationInterruptLogger logger, Handler mainHandler, NotifPipelineFlags flags, KeyguardNotificationVisibilityProvider keyguardNotificationVisibilityProvider) { - super(contentResolver, powerManager, dreamManager, ambientDisplayConfiguration, filter, - batteryController, controller, headsUpManager, logger, mainHandler, - flags, keyguardNotificationVisibilityProvider); + super( + contentResolver, + powerManager, + dreamManager, + ambientDisplayConfiguration, + filter, + batteryController, + controller, + keyguardStateController, + headsUpManager, + logger, + mainHandler, + flags, + keyguardNotificationVisibilityProvider + ); mUseHeadsUp = true; } } diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarBoundsProviderTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarBoundsProviderTest.kt new file mode 100644 index 000000000000..d84010dc7771 --- /dev/null +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarBoundsProviderTest.kt @@ -0,0 +1,171 @@ +/* + * Copyright (C) 2022 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.systemui.statusbar.phone + +import android.graphics.Rect +import android.testing.AndroidTestingRunner +import android.testing.TestableLooper.RunWithLooper +import android.view.View +import android.widget.FrameLayout +import androidx.test.filters.SmallTest +import com.android.systemui.SysuiTestCase +import com.android.systemui.statusbar.phone.StatusBarBoundsProvider.BoundsChangeListener +import com.google.common.truth.Truth.assertThat +import org.junit.Before +import org.junit.Test +import org.junit.runner.RunWith +import org.mockito.Mock +import org.mockito.Mockito.any +import org.mockito.Mockito.doAnswer +import org.mockito.Mockito.never +import org.mockito.Mockito.spy +import org.mockito.Mockito.verify +import org.mockito.MockitoAnnotations + +@RunWith(AndroidTestingRunner::class) +@RunWithLooper(setAsMainLooper = true) +@SmallTest +class StatusBarBoundsProviderTest : SysuiTestCase() { + + companion object { + private val START_SIDE_BOUNDS = Rect(50, 100, 150, 200) + private val END_SIDE_BOUNDS = Rect(250, 300, 350, 400) + } + + @Mock private lateinit var boundsChangeListener: BoundsChangeListener + + private lateinit var boundsProvider: StatusBarBoundsProvider + + private lateinit var startSideContent: View + private lateinit var endSideContent: View + + @Before + fun setUp() { + MockitoAnnotations.initMocks(this) + + startSideContent = spy(FrameLayout(context)).apply { setBoundsOnScreen(START_SIDE_BOUNDS) } + endSideContent = spy(FrameLayout(context)).apply { setBoundsOnScreen(END_SIDE_BOUNDS) } + + boundsProvider = + StatusBarBoundsProvider(setOf(boundsChangeListener), startSideContent, endSideContent) + } + + @Test + fun visibleStartSideBounds_returnsBoundsFromStartSideContentView() { + assertThat(boundsProvider.visibleStartSideBounds).isEqualTo(START_SIDE_BOUNDS) + } + + @Test + fun visibleEndSideBounds_returnsBoundsFromEndSideContentView() { + assertThat(boundsProvider.visibleEndSideBounds).isEqualTo(END_SIDE_BOUNDS) + } + + @Test + fun startBoundsChange_afterStart_notifiesListener() { + boundsProvider.start() + val newBounds = Rect(START_SIDE_BOUNDS).apply { left += 1 } + + startSideContent.setBoundsOnScreen(newBounds) + + verify(boundsChangeListener).onStatusBarBoundsChanged() + } + + @Test + fun startBoundsChange_beforeStart_doesNotNotifyListener() { + val newBounds = Rect(START_SIDE_BOUNDS).apply { left += 1 } + + startSideContent.setBoundsOnScreen(newBounds) + + verify(boundsChangeListener, never()).onStatusBarBoundsChanged() + } + + @Test + fun startBoundsChange_afterStop_doesNotNotifyListener() { + boundsProvider.start() + boundsProvider.stop() + val newBounds = Rect(START_SIDE_BOUNDS).apply { left += 1 } + + startSideContent.setBoundsOnScreen(newBounds) + + verify(boundsChangeListener, never()).onStatusBarBoundsChanged() + } + + @Test + fun startLayoutChange_afterStart_boundsOnScreenSame_doesNotNotifyListener() { + boundsProvider.start() + val newBounds = Rect(START_SIDE_BOUNDS).apply { left += 1 } + + startSideContent.layout(newBounds) + + verify(boundsChangeListener, never()).onStatusBarBoundsChanged() + } + + @Test + fun endBoundsChange_afterStart_notifiesListener() { + boundsProvider.start() + val newBounds = Rect(START_SIDE_BOUNDS).apply { right += 1 } + + endSideContent.setBoundsOnScreen(newBounds) + + verify(boundsChangeListener).onStatusBarBoundsChanged() + } + + @Test + fun endBoundsChange_beforeStart_doesNotNotifyListener() { + val newBounds = Rect(START_SIDE_BOUNDS).apply { right += 1 } + + endSideContent.setBoundsOnScreen(newBounds) + + verify(boundsChangeListener, never()).onStatusBarBoundsChanged() + } + + @Test + fun endBoundsChange_afterStop_doesNotNotifyListener() { + boundsProvider.start() + boundsProvider.stop() + val newBounds = Rect(START_SIDE_BOUNDS).apply { right += 1 } + + endSideContent.setBoundsOnScreen(newBounds) + + verify(boundsChangeListener, never()).onStatusBarBoundsChanged() + } + + @Test + fun endLayoutChange_afterStart_boundsOnScreenSame_doesNotNotifyListener() { + boundsProvider.start() + val newBounds = Rect(START_SIDE_BOUNDS).apply { right += 1 } + + endSideContent.layout(newBounds) + + verify(boundsChangeListener, never()).onStatusBarBoundsChanged() + } +} + +private fun View.setBoundsOnScreen(bounds: Rect) { + doAnswer { invocation -> + val boundsOutput = invocation.arguments[0] as Rect + boundsOutput.set(bounds) + return@doAnswer Unit + } + .`when`(this) + .getBoundsOnScreen(any()) + layout(bounds.left, bounds.top, bounds.right, bounds.bottom) +} + +private fun View.layout(rect: Rect) { + layout(rect.left, rect.top, rect.right, rect.bottom) +} diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarNotificationPresenterTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarNotificationPresenterTest.java index 4b5d1f747ca0..fdb29770cbb7 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarNotificationPresenterTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarNotificationPresenterTest.java @@ -16,12 +16,14 @@ package com.android.systemui.statusbar.phone; import static android.view.Display.DEFAULT_DISPLAY; +import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertTrue; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; import android.app.Notification; +import android.app.PendingIntent; import android.app.StatusBarManager; import android.metrics.LogMaker; import android.support.test.metricshelper.MetricsAsserts; @@ -80,6 +82,8 @@ public class StatusBarNotificationPresenterTest extends SysuiTestCase { private FakeMetricsLogger mMetricsLogger; private ShadeController mShadeController = mock(ShadeController.class); private CentralSurfaces mCentralSurfaces = mock(CentralSurfaces.class); + private KeyguardStateController mKeyguardStateController = mock(KeyguardStateController.class); + private NotifPipelineFlags mNotifPipelineFlags = mock(NotifPipelineFlags.class); private InitController mInitController = new InitController(); @Before @@ -114,7 +118,7 @@ public class StatusBarNotificationPresenterTest extends SysuiTestCase { mock(ScrimController.class), mock(NotificationShadeWindowController.class), mock(DynamicPrivacyController.class), - mock(KeyguardStateController.class), + mKeyguardStateController, mock(KeyguardIndicationController.class), mCentralSurfaces, mock(ShadeControllerImpl.class), @@ -130,7 +134,7 @@ public class StatusBarNotificationPresenterTest extends SysuiTestCase { mInitController, mNotificationInterruptStateProvider, mock(NotificationRemoteInputManager.class), - mock(NotifPipelineFlags.class), + mNotifPipelineFlags, mock(NotificationRemoteInputManager.Callback.class), mock(NotificationListContainer.class)); mInitController.executePostInitTasks(); @@ -141,6 +145,19 @@ public class StatusBarNotificationPresenterTest extends SysuiTestCase { } @Test + public void testNoSuppressHeadsUp_default() { + Notification n = new Notification.Builder(getContext(), "a").build(); + NotificationEntry entry = new NotificationEntryBuilder() + .setPkg("a") + .setOpPkg("a") + .setTag("a") + .setNotification(n) + .build(); + + assertFalse(mInterruptSuppressor.suppressAwakeHeadsUp(entry)); + } + + @Test public void testSuppressHeadsUp_disabledStatusBar() { Notification n = new Notification.Builder(getContext(), "a").build(); NotificationEntry entry = new NotificationEntryBuilder() @@ -176,6 +193,63 @@ public class StatusBarNotificationPresenterTest extends SysuiTestCase { } @Test + public void testNoSuppressHeadsUp_FSI_occludedKeygaurd() { + when(mNotifPipelineFlags.fullScreenIntentRequiresKeyguard()).thenReturn(false); + Notification n = new Notification.Builder(getContext(), "a") + .setFullScreenIntent(mock(PendingIntent.class), true) + .build(); + NotificationEntry entry = new NotificationEntryBuilder() + .setPkg("a") + .setOpPkg("a") + .setTag("a") + .setNotification(n) + .build(); + + when(mKeyguardStateController.isShowing()).thenReturn(true); + when(mKeyguardStateController.isOccluded()).thenReturn(true); + when(mCentralSurfaces.isOccluded()).thenReturn(true); + assertFalse(mInterruptSuppressor.suppressAwakeHeadsUp(entry)); + } + + @Test + public void testSuppressHeadsUp_FSI_nonOccludedKeygaurd() { + when(mNotifPipelineFlags.fullScreenIntentRequiresKeyguard()).thenReturn(false); + Notification n = new Notification.Builder(getContext(), "a") + .setFullScreenIntent(mock(PendingIntent.class), true) + .build(); + NotificationEntry entry = new NotificationEntryBuilder() + .setPkg("a") + .setOpPkg("a") + .setTag("a") + .setNotification(n) + .build(); + + when(mKeyguardStateController.isShowing()).thenReturn(true); + when(mKeyguardStateController.isOccluded()).thenReturn(false); + when(mCentralSurfaces.isOccluded()).thenReturn(false); + assertTrue(mInterruptSuppressor.suppressAwakeHeadsUp(entry)); + } + + @Test + public void testNoSuppressHeadsUp_FSI_nonOccludedKeygaurd_withNewFlag() { + when(mNotifPipelineFlags.fullScreenIntentRequiresKeyguard()).thenReturn(true); + Notification n = new Notification.Builder(getContext(), "a") + .setFullScreenIntent(mock(PendingIntent.class), true) + .build(); + NotificationEntry entry = new NotificationEntryBuilder() + .setPkg("a") + .setOpPkg("a") + .setTag("a") + .setNotification(n) + .build(); + + when(mKeyguardStateController.isShowing()).thenReturn(true); + when(mKeyguardStateController.isOccluded()).thenReturn(false); + when(mCentralSurfaces.isOccluded()).thenReturn(false); + assertFalse(mInterruptSuppressor.suppressAwakeHeadsUp(entry)); + } + + @Test public void testSuppressInterruptions_vrMode() { Notification n = new Notification.Builder(getContext(), "a").build(); NotificationEntry entry = new NotificationEntryBuilder() diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/fragment/CollapsedStatusBarFragmentTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/fragment/CollapsedStatusBarFragmentTest.java index 681e998ac9e3..56804d58d241 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/fragment/CollapsedStatusBarFragmentTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/fragment/CollapsedStatusBarFragmentTest.java @@ -41,6 +41,7 @@ import androidx.test.filters.SmallTest; import com.android.systemui.R; import com.android.systemui.SysuiBaseFragmentTest; +import com.android.systemui.dump.DumpManager; import com.android.systemui.flags.FeatureFlags; import com.android.systemui.log.LogBuffer; import com.android.systemui.log.LogcatEchoTracker; @@ -107,6 +108,8 @@ public class CollapsedStatusBarFragmentTest extends SysuiBaseFragmentTest { private NotificationPanelViewController mNotificationPanelViewController; @Mock private StatusBarHideIconsForBouncerManager mStatusBarHideIconsForBouncerManager; + @Mock + private DumpManager mDumpManager; public CollapsedStatusBarFragmentTest() { super(CollapsedStatusBarFragment.class); @@ -386,7 +389,8 @@ public class CollapsedStatusBarFragmentTest extends SysuiBaseFragmentTest { ), mOperatorNameViewControllerFactory, mSecureSettings, - mExecutor); + mExecutor, + mDumpManager); } private void setUpDaggerComponent() { diff --git a/packages/SystemUI/tests/src/com/android/systemui/wmshell/BubblesTest.java b/packages/SystemUI/tests/src/com/android/systemui/wmshell/BubblesTest.java index 6a0124a3be8a..9866013093cf 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/wmshell/BubblesTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/wmshell/BubblesTest.java @@ -321,6 +321,7 @@ public class BubblesTest extends SysuiTestCase { mock(AmbientDisplayConfiguration.class), mock(NotificationFilter.class), mock(StatusBarStateController.class), + mock(KeyguardStateController.class), mock(BatteryController.class), mock(HeadsUpManager.class), mock(NotificationInterruptLogger.class), diff --git a/packages/SystemUI/tests/src/com/android/systemui/wmshell/TestableNotificationInterruptStateProviderImpl.java b/packages/SystemUI/tests/src/com/android/systemui/wmshell/TestableNotificationInterruptStateProviderImpl.java index a7f0dc22e849..d80ea154e77a 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/wmshell/TestableNotificationInterruptStateProviderImpl.java +++ b/packages/SystemUI/tests/src/com/android/systemui/wmshell/TestableNotificationInterruptStateProviderImpl.java @@ -30,6 +30,7 @@ import com.android.systemui.statusbar.notification.interruption.NotificationInte import com.android.systemui.statusbar.notification.interruption.NotificationInterruptStateProviderImpl; import com.android.systemui.statusbar.policy.BatteryController; import com.android.systemui.statusbar.policy.HeadsUpManager; +import com.android.systemui.statusbar.policy.KeyguardStateController; public class TestableNotificationInterruptStateProviderImpl extends NotificationInterruptStateProviderImpl { @@ -41,6 +42,7 @@ public class TestableNotificationInterruptStateProviderImpl AmbientDisplayConfiguration ambientDisplayConfiguration, NotificationFilter filter, StatusBarStateController statusBarStateController, + KeyguardStateController keyguardStateController, BatteryController batteryController, HeadsUpManager headsUpManager, NotificationInterruptLogger logger, @@ -54,6 +56,7 @@ public class TestableNotificationInterruptStateProviderImpl filter, batteryController, statusBarStateController, + keyguardStateController, headsUpManager, logger, mainHandler, diff --git a/services/contentcapture/Android.bp b/services/contentcapture/Android.bp index 434f239dbddc..5392c2cde3b8 100644 --- a/services/contentcapture/Android.bp +++ b/services/contentcapture/Android.bp @@ -17,6 +17,9 @@ filegroup { java_library_static { name: "services.contentcapture", defaults: ["platform_service_defaults"], - srcs: [":services.contentcapture-sources"], + srcs: [ + ":services.contentcapture-sources", + "java/**/*.logtags", + ], libs: ["services.core"], } diff --git a/services/contentcapture/java/com/android/server/contentcapture/ContentCapturePerUserService.java b/services/contentcapture/java/com/android/server/contentcapture/ContentCapturePerUserService.java index 41a759254909..0428b2322f27 100644 --- a/services/contentcapture/java/com/android/server/contentcapture/ContentCapturePerUserService.java +++ b/services/contentcapture/java/com/android/server/contentcapture/ContentCapturePerUserService.java @@ -60,6 +60,7 @@ import android.service.contentcapture.SnapshotData; import android.service.voice.VoiceInteractionManagerInternal; import android.util.ArrayMap; import android.util.ArraySet; +import android.util.EventLog; import android.util.Slog; import android.util.SparseArray; import android.util.SparseBooleanArray; @@ -88,6 +89,11 @@ final class ContentCapturePerUserService private static final String TAG = ContentCapturePerUserService.class.getSimpleName(); + private static final int EVENT_LOG_CONNECT_STATE_DIED = 0; + static final int EVENT_LOG_CONNECT_STATE_CONNECTED = 1; + static final int EVENT_LOG_CONNECT_STATE_DISCONNECTED = 2; + + @GuardedBy("mLock") private final SparseArray<ContentCaptureServerSession> mSessions = new SparseArray<>(); @@ -190,9 +196,13 @@ final class ContentCapturePerUserService Slog.w(TAG, "remote service died: " + service); synchronized (mLock) { mZombie = true; + ComponentName serviceComponent = getServiceComponentName(); writeServiceEvent( FrameworkStatsLog.CONTENT_CAPTURE_SERVICE_EVENTS__EVENT__ON_REMOTE_SERVICE_DIED, - getServiceComponentName()); + serviceComponent); + EventLog.writeEvent(EventLogTags.CC_CONNECT_STATE_CHANGED, mUserId, + serviceComponent != null ? serviceComponent.flattenToShortString() : "", + EVENT_LOG_CONNECT_STATE_DIED); } } @@ -614,11 +624,16 @@ final class ContentCapturePerUserService ? "null_activities" : activities.size() + " activities") + ")" + " for user " + mUserId); } + int packageCount = packages != null ? packages.size() : 0; + int activityCount = activities != null ? activities.size() : 0; ArraySet<String> oldList = mMaster.mGlobalContentCaptureOptions.getWhitelistedPackages(mUserId); + EventLog.writeEvent(EventLogTags.CC_CURRENT_ALLOWLIST, mUserId, oldList.size()); mMaster.mGlobalContentCaptureOptions.setWhitelist(mUserId, packages, activities); + EventLog.writeEvent(EventLogTags.CC_SET_ALLOWLIST, mUserId, + packageCount, activityCount); writeSetWhitelistEvent(getServiceComponentName(), packages, activities); updateContentCaptureOptions(oldList); @@ -699,12 +714,14 @@ final class ContentCapturePerUserService private void updateContentCaptureOptions(@Nullable ArraySet<String> oldList) { ArraySet<String> adding = mMaster.mGlobalContentCaptureOptions .getWhitelistedPackages(mUserId); + EventLog.writeEvent(EventLogTags.CC_CURRENT_ALLOWLIST, mUserId, adding.size()); if (oldList != null && adding != null) { adding.removeAll(oldList); } int N = adding != null ? adding.size() : 0; + EventLog.writeEvent(EventLogTags.CC_UPDATE_OPTIONS, mUserId, N); for (int i = 0; i < N; i++) { String packageName = adding.valueAt(i); ContentCaptureOptions options = mMaster.mGlobalContentCaptureOptions diff --git a/services/contentcapture/java/com/android/server/contentcapture/EventLogTags.logtags b/services/contentcapture/java/com/android/server/contentcapture/EventLogTags.logtags new file mode 100644 index 000000000000..6722b9ed3c5f --- /dev/null +++ b/services/contentcapture/java/com/android/server/contentcapture/EventLogTags.logtags @@ -0,0 +1,8 @@ +# See system/logging/logcat/event.logtags for a description of the format of this file. + +option java_package com.android.server.contentcapture + +53200 cc_connect_state_changed (User|1|5),(component|3),(type|1) +53201 cc_set_allowlist (User|1|5),(package_count|1),(activity_count|1) +53202 cc_current_allowlist (User|1|5),(count|1) +53203 cc_update_options (User|1|5),(count|1) diff --git a/services/contentcapture/java/com/android/server/contentcapture/RemoteContentCaptureService.java b/services/contentcapture/java/com/android/server/contentcapture/RemoteContentCaptureService.java index 1efe55aa0767..e22a9d07b4ae 100644 --- a/services/contentcapture/java/com/android/server/contentcapture/RemoteContentCaptureService.java +++ b/services/contentcapture/java/com/android/server/contentcapture/RemoteContentCaptureService.java @@ -31,6 +31,7 @@ import android.service.contentcapture.IContentCaptureService; import android.service.contentcapture.IContentCaptureServiceCallback; import android.service.contentcapture.IDataShareCallback; import android.service.contentcapture.SnapshotData; +import android.util.EventLog; import android.util.Slog; import android.view.contentcapture.ContentCaptureContext; import android.view.contentcapture.DataRemovalRequest; @@ -47,6 +48,7 @@ final class RemoteContentCaptureService private final IBinder mServerCallback; private final int mIdleUnbindTimeoutMs; private final ContentCapturePerUserService mPerUserService; + private final int mUserId; RemoteContentCaptureService(Context context, String serviceInterface, ComponentName serviceComponentName, IContentCaptureServiceCallback callback, int userId, @@ -61,6 +63,7 @@ final class RemoteContentCaptureService mPerUserService = perUserService; mServerCallback = callback.asBinder(); mIdleUnbindTimeoutMs = idleUnbindTimeoutMs; + mUserId = userId; // Bind right away, which will trigger a onConnected() on service's ensureBoundLocked(); @@ -88,6 +91,9 @@ final class RemoteContentCaptureService writeServiceEvent( FrameworkStatsLog.CONTENT_CAPTURE_SERVICE_EVENTS__EVENT__ON_CONNECTED, mComponentName); + EventLog.writeEvent(EventLogTags.CC_CONNECT_STATE_CHANGED, mUserId, + mComponentName != null ? mComponentName.flattenToShortString() : "", + ContentCapturePerUserService.EVENT_LOG_CONNECT_STATE_CONNECTED); } finally { // Update the system-service state, in case the service reconnected after // dying @@ -98,6 +104,9 @@ final class RemoteContentCaptureService writeServiceEvent( FrameworkStatsLog.CONTENT_CAPTURE_SERVICE_EVENTS__EVENT__ON_DISCONNECTED, mComponentName); + EventLog.writeEvent(EventLogTags.CC_CONNECT_STATE_CHANGED, mUserId, + mComponentName != null ? mComponentName.flattenToShortString() : "", + ContentCapturePerUserService.EVENT_LOG_CONNECT_STATE_DISCONNECTED); } } catch (Exception e) { Slog.w(mTag, "Exception calling onConnectedStateChanged(" + connected + "): " + e); diff --git a/services/core/java/com/android/server/audio/AudioService.java b/services/core/java/com/android/server/audio/AudioService.java index 0040ea9215b3..3a869f859e52 100644 --- a/services/core/java/com/android/server/audio/AudioService.java +++ b/services/core/java/com/android/server/audio/AudioService.java @@ -9181,11 +9181,8 @@ public class AudioService extends IAudioService.Stub Settings.Secure.SPATIAL_AUDIO_ENABLED, UserHandle.USER_CURRENT); if (settings == null) { Log.e(TAG, "error reading spatial audio device settings"); - } else { - Log.v(TAG, "restoring spatial audio device settings: " + settings); - mSpatializerHelper.setSADeviceSettings(settings); } - mSpatializerHelper.init(/*effectExpected*/ mHasSpatializerEffect); + mSpatializerHelper.init(/*effectExpected*/ mHasSpatializerEffect, settings); mSpatializerHelper.setFeatureEnabled(mHasSpatializerEffect); } @@ -10373,6 +10370,11 @@ public class AudioService extends IAudioService.Stub @Override public void setAccessibilityServiceUids(IntArray uids) { + // TODO(b/233287010): Fix voice interaction and a11y concurrency in audio policy service + if (isPlatformAutomotive()) { + return; + } + synchronized (mAccessibilityServiceUidsLock) { if (uids.size() == 0) { mAccessibilityServiceUids = null; diff --git a/services/core/java/com/android/server/audio/SpatializerHelper.java b/services/core/java/com/android/server/audio/SpatializerHelper.java index dd44af1b68ee..cd5960ffbf32 100644 --- a/services/core/java/com/android/server/audio/SpatializerHelper.java +++ b/services/core/java/com/android/server/audio/SpatializerHelper.java @@ -175,7 +175,7 @@ public class SpatializerHelper { mASA = asa; } - synchronized void init(boolean effectExpected) { + synchronized void init(boolean effectExpected, @Nullable String settings) { loglogi("init effectExpected=" + effectExpected); if (!effectExpected) { loglogi("init(): setting state to STATE_NOT_SUPPORTED due to effect not expected"); @@ -278,6 +278,13 @@ public class SpatializerHelper { mSACapableDeviceTypes.add(SPAT_MODE_FOR_DEVICE_TYPE.keyAt(i)); } } + + // When initialized from AudioService, the settings string will be non-null. + // Saved settings need to be applied after spatialization support is initialized above. + if (settings != null) { + setSADeviceSettings(settings); + } + // for both transaural / binaural, we are not forcing enablement as the init() method // could have been called another time after boot in case of audioserver restart addCompatibleAudioDevice( @@ -316,7 +323,7 @@ public class SpatializerHelper { mState = STATE_UNINITIALIZED; mSpatLevel = Spatializer.SPATIALIZER_IMMERSIVE_LEVEL_NONE; mActualHeadTrackingMode = Spatializer.HEAD_TRACKING_MODE_UNSUPPORTED; - init(true); + init(true, null /* settings */); setSpatializerEnabledInt(featureEnabled); } @@ -731,7 +738,7 @@ public class SpatializerHelper { return; } if (mState == STATE_UNINITIALIZED) { - init(true); + init(true, null /* settings */); } setSpatializerEnabledInt(true); } else { diff --git a/services/core/java/com/android/server/display/DisplayDeviceConfig.java b/services/core/java/com/android/server/display/DisplayDeviceConfig.java index 2322280d8a9b..b5aa7b14792b 100644 --- a/services/core/java/com/android/server/display/DisplayDeviceConfig.java +++ b/services/core/java/com/android/server/display/DisplayDeviceConfig.java @@ -287,7 +287,8 @@ public class DisplayDeviceConfig { private BrightnessThrottlingData mBrightnessThrottlingData; private BrightnessThrottlingData mOriginalBrightnessThrottlingData; - private DisplayDeviceConfig(Context context) { + @VisibleForTesting + DisplayDeviceConfig(Context context) { mContext = context; } @@ -691,7 +692,8 @@ public class DisplayDeviceConfig { return config; } - private boolean initFromFile(File configFile) { + @VisibleForTesting + boolean initFromFile(File configFile) { if (!configFile.exists()) { // Display configuration files aren't required to exist. return false; diff --git a/services/core/java/com/android/server/notification/NotificationManagerService.java b/services/core/java/com/android/server/notification/NotificationManagerService.java index 9486a45e989e..e589080786b5 100755 --- a/services/core/java/com/android/server/notification/NotificationManagerService.java +++ b/services/core/java/com/android/server/notification/NotificationManagerService.java @@ -2459,11 +2459,11 @@ public class NotificationManagerService extends SystemService { SnoozeHelper snoozeHelper = new SnoozeHelper(getContext(), (userId, r, muteOnReturn) -> { try { if (DBG) { - Slog.d(TAG, "Reposting " + r.getKey()); + Slog.d(TAG, "Reposting " + r.getKey() + " " + muteOnReturn); } enqueueNotificationInternal(r.getSbn().getPackageName(), r.getSbn().getOpPkg(), r.getSbn().getUid(), r.getSbn().getInitialPid(), r.getSbn().getTag(), - r.getSbn().getId(), r.getSbn().getNotification(), userId, true); + r.getSbn().getId(), r.getSbn().getNotification(), userId, muteOnReturn); } catch (Exception e) { Slog.e(TAG, "Cannot un-snooze notification", e); } diff --git a/services/core/java/com/android/server/pm/ScanPackageUtils.java b/services/core/java/com/android/server/pm/ScanPackageUtils.java index 0dc188b75d5e..46f0dbcdfe10 100644 --- a/services/core/java/com/android/server/pm/ScanPackageUtils.java +++ b/services/core/java/com/android/server/pm/ScanPackageUtils.java @@ -427,8 +427,7 @@ final class ScanPackageUtils { pkgSetting.setLastModifiedTime(scanFileTime); // TODO(b/135203078): Remove, move to constructor pkgSetting.setPkg(parsedPackage) - .setFlags(PackageInfoUtils.appInfoFlags(parsedPackage, pkgSetting)) - .setPrivateFlags( + .setPkgFlags(PackageInfoUtils.appInfoFlags(parsedPackage, pkgSetting), PackageInfoUtils.appInfoPrivateFlags(parsedPackage, pkgSetting)); if (parsedPackage.getLongVersionCode() != pkgSetting.getVersionCode()) { pkgSetting.setLongVersionCode(parsedPackage.getLongVersionCode()); diff --git a/services/core/java/com/android/server/pm/SettingBase.java b/services/core/java/com/android/server/pm/SettingBase.java index b952f80850bb..61a251e19db7 100644 --- a/services/core/java/com/android/server/pm/SettingBase.java +++ b/services/core/java/com/android/server/pm/SettingBase.java @@ -146,6 +146,17 @@ public abstract class SettingBase implements Watchable, Snappable { return this; } + /** + * Unconditionally set both mPkgFlags and mPkgPrivateFlags. + * Should not be used outside pkgSetting initialization or update. + */ + SettingBase setPkgFlags(int flags, int privateFlags) { + this.mPkgFlags = flags; + this.mPkgPrivateFlags = privateFlags; + onChanged(); + return this; + } + public int getFlags() { return mPkgFlags; } diff --git a/services/core/java/com/android/server/pm/Settings.java b/services/core/java/com/android/server/pm/Settings.java index 6400502f1a89..7437b145189f 100644 --- a/services/core/java/com/android/server/pm/Settings.java +++ b/services/core/java/com/android/server/pm/Settings.java @@ -987,8 +987,7 @@ public final class Settings implements Watchable, Snappable { // Update new package state. .setLastModifiedTime(codePath.lastModified()) .setDomainSetId(domainSetId); - pkgSetting.setFlags(pkgFlags) - .setPrivateFlags(pkgPrivateFlags); + pkgSetting.setPkgFlags(pkgFlags, pkgPrivateFlags); } else { pkgSetting = new PackageSetting(pkgName, realPkgName, codePath, legacyNativeLibraryPath, primaryCpuAbi, secondaryCpuAbi, @@ -1175,15 +1174,15 @@ public final class Settings implements Watchable, Snappable { .setUsesStaticLibrariesVersions(null); } - // These two flags are preserved from the existing PackageSetting. Copied from prior code, - // unclear if this is actually necessary. - boolean wasExternalStorage = (pkgSetting.getFlags() - & ApplicationInfo.FLAG_EXTERNAL_STORAGE) != 0; - if (wasExternalStorage) { - pkgFlags |= ApplicationInfo.FLAG_EXTERNAL_STORAGE; - } else { - pkgFlags &= ~ApplicationInfo.FLAG_EXTERNAL_STORAGE; - } + // If what we are scanning is a system (and possibly privileged) package, + // then make it so, regardless of whether it was previously installed only + // in the data partition. Reset first. + int newPkgFlags = pkgSetting.getFlags(); + newPkgFlags &= ~ApplicationInfo.FLAG_SYSTEM; + newPkgFlags |= pkgFlags & ApplicationInfo.FLAG_SYSTEM; + // Only set pkgFlags. + pkgSetting.setPkgFlags(newPkgFlags, pkgSetting.getPrivateFlags()); + boolean wasRequiredForSystemUser = (pkgSetting.getPrivateFlags() & ApplicationInfo.PRIVATE_FLAG_REQUIRED_FOR_SYSTEM_USER) != 0; if (wasRequiredForSystemUser) { @@ -1191,9 +1190,7 @@ public final class Settings implements Watchable, Snappable { } else { pkgPrivateFlags &= ~ApplicationInfo.PRIVATE_FLAG_REQUIRED_FOR_SYSTEM_USER; } - - pkgSetting.setFlags(pkgFlags) - .setPrivateFlags(pkgPrivateFlags); + pkgSetting.setPrivateFlags(pkgPrivateFlags); } /** diff --git a/services/tests/servicestests/src/com/android/server/display/DisplayDeviceConfigTest.java b/services/tests/servicestests/src/com/android/server/display/DisplayDeviceConfigTest.java new file mode 100644 index 000000000000..793930395daa --- /dev/null +++ b/services/tests/servicestests/src/com/android/server/display/DisplayDeviceConfigTest.java @@ -0,0 +1,182 @@ +/* + * Copyright (C) 2022 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.server.display; + + +import static org.junit.Assert.assertArrayEquals; +import static org.junit.Assert.assertEquals; +import static org.mockito.Mockito.when; + +import android.content.Context; +import android.content.res.Resources; +import android.platform.test.annotations.Presubmit; + +import androidx.test.filters.SmallTest; +import androidx.test.runner.AndroidJUnit4; + + +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.Mock; +import org.mockito.MockitoAnnotations; + +import java.io.IOException; +import java.nio.charset.StandardCharsets; +import java.nio.file.Files; +import java.nio.file.Path; + +@SmallTest +@Presubmit +@RunWith(AndroidJUnit4.class) +public final class DisplayDeviceConfigTest { + private DisplayDeviceConfig mDisplayDeviceConfig; + @Mock + private Context mContext; + + @Mock + private Resources mResources; + + @Before + public void setUp() throws IOException { + MockitoAnnotations.initMocks(this); + when(mContext.getResources()).thenReturn(mResources); + mockDeviceConfigs(); + try { + Path tempFile = Files.createTempFile("display_config", ".tmp"); + Files.write(tempFile, getContent().getBytes(StandardCharsets.UTF_8)); + mDisplayDeviceConfig = new DisplayDeviceConfig(mContext); + mDisplayDeviceConfig.initFromFile(tempFile.toFile()); + } catch (IOException e) { + throw new IOException("Failed to setup the display device config.", e); + } + } + + @Test + public void testConfigValues() { + assertEquals(mDisplayDeviceConfig.getAmbientHorizonLong(), 5000); + assertEquals(mDisplayDeviceConfig.getAmbientHorizonShort(), 50); + assertEquals(mDisplayDeviceConfig.getBrightnessRampDecreaseMaxMillis(), 3000); + assertEquals(mDisplayDeviceConfig.getBrightnessRampIncreaseMaxMillis(), 2000); + assertEquals(mDisplayDeviceConfig.getAmbientLuxBrighteningMinThreshold(), 10.0f, 0.0f); + assertEquals(mDisplayDeviceConfig.getAmbientLuxDarkeningMinThreshold(), 2.0f, 0.0f); + assertEquals(mDisplayDeviceConfig.getBrightnessRampFastDecrease(), 0.01f, 0.0f); + assertEquals(mDisplayDeviceConfig.getBrightnessRampFastIncrease(), 0.02f, 0.0f); + assertEquals(mDisplayDeviceConfig.getBrightnessRampSlowIncrease(), 0.04f, 0.0f); + assertEquals(mDisplayDeviceConfig.getBrightnessRampSlowDecrease(), 0.03f, 0.0f); + assertEquals(mDisplayDeviceConfig.getBrightnessDefault(), 0.5f, 0.0f); + assertArrayEquals(mDisplayDeviceConfig.getBrightness(), new float[]{0.0f, 0.62f, 1.0f}, + 0.0f); + assertArrayEquals(mDisplayDeviceConfig.getNits(), new float[]{2.0f, 500.0f, 800.0f}, 0.0f); + assertArrayEquals(mDisplayDeviceConfig.getBacklight(), new float[]{0.0f, 0.62f, 1.0f}, + 0.0f); + assertEquals(mDisplayDeviceConfig.getScreenBrighteningMinThreshold(), 0.001, 0.000001f); + assertEquals(mDisplayDeviceConfig.getScreenDarkeningMinThreshold(), 0.002, 0.000001f); + + // Todo(brup): Add asserts for BrightnessThrottlingData, DensityMapping, + // HighBrightnessModeData AmbientLightSensor, RefreshRateLimitations and ProximitySensor. + // Also add test for the case where optional display configs are null + } + + private String getContent() { + return "<?xml version='1.0' encoding='utf-8' standalone='yes' ?>\n" + + "<displayConfiguration>\n" + + "<screenBrightnessMap>\n" + + "<point>\n" + + "<value>0.0</value>\n" + + "<nits>2.0</nits>\n" + + "</point>\n" + + "<point>\n" + + "<value>0.62</value>\n" + + "<nits>500.0</nits>\n" + + "</point>\n" + + "<point>\n" + + "<value>1.0</value>\n" + + "<nits>800.0</nits>\n" + + "</point>\n" + + "</screenBrightnessMap>\n" + + "<highBrightnessMode enabled=\"true\">\n" + + "<transitionPoint>0.62</transitionPoint>\n" + + "<minimumLux>10000</minimumLux>\n" + + "<timing>\n" + + "<!-- allow for 5 minutes out of every 30 minutes -->\n" + + "<timeWindowSecs>1800</timeWindowSecs>\n" + + "<timeMaxSecs>300</timeMaxSecs>\n" + + "<timeMinSecs>60</timeMinSecs>\n" + + "</timing>\n" + + "<refreshRate>\n" + + "<minimum>120</minimum>\n" + + "<maximum>120</maximum>\n" + + "</refreshRate>\n" + + "<thermalStatusLimit>light</thermalStatusLimit>\n" + + "<allowInLowPowerMode>false</allowInLowPowerMode>\n" + + "</highBrightnessMode>\n" + + "<ambientBrightnessChangeThresholds>\n" + + "<brighteningThresholds>\n" + + "<minimum>10</minimum>\n" + + "</brighteningThresholds>\n" + + "<darkeningThresholds>\n" + + "<minimum>2</minimum>\n" + + "</darkeningThresholds>\n" + + "</ambientBrightnessChangeThresholds>\n" + + "<screenBrightnessRampFastDecrease>0.01</screenBrightnessRampFastDecrease> " + + "<screenBrightnessRampFastIncrease>0.02</screenBrightnessRampFastIncrease> " + + "<screenBrightnessRampSlowDecrease>0.03</screenBrightnessRampSlowDecrease>" + + "<screenBrightnessRampSlowIncrease>0.04</screenBrightnessRampSlowIncrease>" + + "<screenBrightnessRampIncreaseMaxMillis>" + + "2000" + + "</screenBrightnessRampIncreaseMaxMillis>" + + "<screenBrightnessRampDecreaseMaxMillis>" + + "3000" + + "</screenBrightnessRampDecreaseMaxMillis>" + + "<ambientLightHorizonLong>5000</ambientLightHorizonLong>\n" + + "<ambientLightHorizonShort>50</ambientLightHorizonShort>\n" + + "<displayBrightnessChangeThresholds>" + + "<brighteningThresholds>" + + "<minimum>" + + "0.001" + + "</minimum>" + + "</brighteningThresholds>" + + "<darkeningThresholds>" + + "<minimum>" + + "0.002" + + "</minimum>" + + "</darkeningThresholds>" + + "</displayBrightnessChangeThresholds>" + + "<screenBrightnessRampIncreaseMaxMillis>" + + "2000" + + "</screenBrightnessRampIncreaseMaxMillis>\n" + + "<thermalThrottling>\n" + + "<brightnessThrottlingMap>\n" + + "<brightnessThrottlingPoint>\n" + + "<thermalStatus>emergency</thermalStatus>\n" + + "<!-- Throttling to 250 nits: (250-2.0)/(500-2.0)*(0.62-0.0)+0" + + ".0 = 0.30875502 -->\n" + + "<brightness>0.30875502</brightness>\n" + + "</brightnessThrottlingPoint>\n" + + "</brightnessThrottlingMap>\n" + + "</thermalThrottling>\n" + + "</displayConfiguration>\n"; + } + + private void mockDeviceConfigs() { + when(mResources.getFloat(com.android.internal.R.dimen + .config_screenBrightnessSettingDefaultFloat)).thenReturn(0.5f); + when(mResources.getFloat(com.android.internal.R.dimen + .config_screenBrightnessSettingMaximumFloat)).thenReturn(1.0f); + } +} |