summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--core/java/android/hardware/camera2/CameraCharacteristics.java3
-rw-r--r--core/java/android/view/ViewRootImpl.java1
-rw-r--r--core/java/android/window/ImeOnBackInvokedDispatcher.java4
-rw-r--r--core/java/android/window/WindowOnBackInvokedDispatcher.java26
-rw-r--r--core/res/res/layout-car/car_alert_dialog.xml2
-rw-r--r--core/res/res/layout-car/car_alert_dialog_button_bar.xml20
-rw-r--r--core/res/res/values/styles_car.xml93
-rw-r--r--core/tests/coretests/src/android/window/WindowOnBackInvokedDispatcherTest.java28
-rw-r--r--data/fonts/fonts.xml2
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/common/split/SplitLayout.java12
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/onehanded/OneHandedController.java7
-rw-r--r--packages/SettingsLib/LayoutPreference/res/layout/settings_entity_header.xml2
-rw-r--r--packages/SettingsLib/res/values/strings.xml4
-rw-r--r--packages/SystemUI/animation/src/com/android/systemui/animation/DialogLaunchAnimator.kt21
-rw-r--r--packages/SystemUI/animation/src/com/android/systemui/animation/GhostedViewLaunchAnimatorController.kt11
-rw-r--r--packages/SystemUI/res/layout/clipboard_overlay.xml1
-rw-r--r--packages/SystemUI/shared/src/com/android/systemui/flags/Flag.kt27
-rw-r--r--packages/SystemUI/shared/src/com/android/systemui/flags/FlagManager.kt44
-rw-r--r--packages/SystemUI/src/com/android/systemui/flags/FeatureFlagsDebug.java23
-rw-r--r--packages/SystemUI/src/com/android/systemui/flags/Flags.java6
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/NotifPipelineFlags.kt3
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/interruption/NotificationInterruptStateProviderImpl.java26
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarBoundsProvider.kt92
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarNotificationPresenter.java3
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/dagger/StatusBarViewModule.java7
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/fragment/CollapsedStatusBarFragment.java49
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/fragment/dagger/StatusBarFragmentComponent.java26
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/fragment/dagger/StatusBarFragmentModule.java25
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/fragment/dagger/StatusBarStartablesModule.kt32
-rw-r--r--packages/SystemUI/src/com/android/systemui/util/ConvenienceExtensions.kt12
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/interruption/NotificationInterruptStateProviderImplTest.java108
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/CentralSurfacesImplTest.java28
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarBoundsProviderTest.kt171
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarNotificationPresenterTest.java78
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/fragment/CollapsedStatusBarFragmentTest.java6
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/wmshell/BubblesTest.java1
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/wmshell/TestableNotificationInterruptStateProviderImpl.java3
-rw-r--r--services/contentcapture/Android.bp5
-rw-r--r--services/contentcapture/java/com/android/server/contentcapture/ContentCapturePerUserService.java19
-rw-r--r--services/contentcapture/java/com/android/server/contentcapture/EventLogTags.logtags8
-rw-r--r--services/contentcapture/java/com/android/server/contentcapture/RemoteContentCaptureService.java9
-rw-r--r--services/core/java/com/android/server/audio/AudioService.java10
-rw-r--r--services/core/java/com/android/server/audio/SpatializerHelper.java13
-rw-r--r--services/core/java/com/android/server/display/DisplayDeviceConfig.java6
-rwxr-xr-xservices/core/java/com/android/server/notification/NotificationManagerService.java4
-rw-r--r--services/core/java/com/android/server/pm/ScanPackageUtils.java3
-rw-r--r--services/core/java/com/android/server/pm/SettingBase.java11
-rw-r--r--services/core/java/com/android/server/pm/Settings.java25
-rw-r--r--services/tests/servicestests/src/com/android/server/display/DisplayDeviceConfigTest.java182
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);
+ }
+}