summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--AconfigFlags.bp17
-rw-r--r--core/java/android/app/Notification.java5
-rw-r--r--core/java/android/app/WindowConfiguration.java51
-rw-r--r--core/java/android/os/IHintManager.aidl9
-rw-r--r--core/java/android/service/autofill/AutofillService.java5
-rw-r--r--core/java/android/service/autofill/IAutoFillService.aidl4
-rw-r--r--core/java/android/service/notification/flags.aconfig10
-rw-r--r--core/java/android/view/TextureView.java24
-rw-r--r--core/java/android/view/View.java217
-rw-r--r--core/java/android/view/ViewRootImpl.java139
-rw-r--r--core/res/res/values/strings.xml6
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/back/CrossActivityBackAnimation.kt16
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/compatui/UserAspectRatioSettingsWindowManager.java3
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/transition/DefaultTransitionHandler.java5
-rw-r--r--libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/compatui/UserAspectRatioSettingsWindowManagerTest.java18
-rw-r--r--libs/hwui/SkiaInterpolator.cpp1
-rw-r--r--libs/hwui/effects/GainmapRenderer.cpp22
-rw-r--r--libs/hwui/renderthread/CanvasContext.cpp10
-rw-r--r--libs/hwui/utils/Color.cpp2
-rw-r--r--native/android/performance_hint.cpp44
-rw-r--r--native/android/tests/performance_hint/PerformanceHintNativeTest.cpp26
-rw-r--r--packages/CredentialManager/src/com/android/credentialmanager/autofill/CredentialAutofillService.kt6
-rw-r--r--packages/SettingsLib/src/com/android/settingslib/Utils.java6
-rw-r--r--packages/SettingsLib/src/com/android/settingslib/bluetooth/LocalBluetoothLeBroadcast.java30
-rw-r--r--packages/SystemUI/compose/features/src/com/android/systemui/media/controls/ui/composable/MediaCarousel.kt5
-rw-r--r--packages/SystemUI/compose/features/src/com/android/systemui/scene/ui/composable/SceneContainer.kt6
-rw-r--r--packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/Element.kt12
-rw-r--r--packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/Scene.kt5
-rw-r--r--packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/TransitionDsl.kt50
-rw-r--r--packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/MovableElementScenePickerTest.kt18
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/qs/ui/viewmodel/QuickSettingsSceneViewModelTest.kt2
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/scene/SceneFrameworkIntegrationTest.kt1
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/scene/domain/interactor/SceneInteractorTest.kt17
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/scene/ui/viewmodel/SceneContainerViewModelTest.kt22
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/shade/domain/interactor/ShadeBackActionInteractorImplTest.kt47
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/util/kotlin/BooleanFlowOperatorsTest.kt31
-rw-r--r--packages/SystemUI/res/drawable/notification_material_bg.xml5
-rw-r--r--packages/SystemUI/res/layout/media_carousel.xml4
-rw-r--r--packages/SystemUI/res/values/dimens.xml3
-rw-r--r--packages/SystemUI/src/com/android/systemui/SystemUIApplication.java12
-rw-r--r--packages/SystemUI/src/com/android/systemui/bouncer/domain/interactor/BouncerInteractor.kt2
-rw-r--r--packages/SystemUI/src/com/android/systemui/communal/ui/viewmodel/BaseCommunalViewModel.kt5
-rw-r--r--packages/SystemUI/src/com/android/systemui/communal/widgets/EditWidgetsActivity.kt6
-rw-r--r--packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromDreamingTransitionInteractor.kt11
-rw-r--r--packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/TransitionInteractor.kt20
-rw-r--r--packages/SystemUI/src/com/android/systemui/media/controls/ui/controller/MediaCarouselController.kt6
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/ui/viewmodel/QuickSettingsSceneViewModel.kt2
-rw-r--r--packages/SystemUI/src/com/android/systemui/scene/domain/interactor/SceneInteractor.kt23
-rw-r--r--packages/SystemUI/src/com/android/systemui/scene/domain/startable/SceneContainerStartable.kt3
-rw-r--r--packages/SystemUI/src/com/android/systemui/scene/ui/viewmodel/SceneContainerViewModel.kt25
-rw-r--r--packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotShelfViewProxy.kt23
-rw-r--r--packages/SystemUI/src/com/android/systemui/screenshot/ui/ScreenshotAnimationController.kt42
-rw-r--r--packages/SystemUI/src/com/android/systemui/screenshot/ui/ScreenshotShelfView.kt9
-rw-r--r--packages/SystemUI/src/com/android/systemui/screenshot/ui/SwipeGestureListener.kt74
-rw-r--r--packages/SystemUI/src/com/android/systemui/screenshot/ui/binder/ScreenshotShelfViewBinder.kt8
-rw-r--r--packages/SystemUI/src/com/android/systemui/shade/GlanceableHubContainerController.kt4
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationBackgroundView.java25
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayout.java17
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutController.java4
-rw-r--r--packages/SystemUI/src/com/android/systemui/util/kotlin/BooleanFlowOperators.kt5
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/FromDreamingTransitionInteractorTest.kt14
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardTransitionScenariosTest.kt52
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/keyguard/util/KeyguardTransitionRepositorySpySubject.kt25
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/shade/GlanceableHubContainerControllerTest.kt14
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/ongoingcall/OngoingCallControllerTest.kt2
-rw-r--r--packages/SystemUI/tests/utils/src/com/android/systemui/flags/SceneContainerRule.kt15
-rw-r--r--packages/SystemUI/tests/utils/src/com/android/systemui/scene/SceneKosmos.kt15
-rw-r--r--packages/SystemUI/tests/utils/src/com/android/systemui/scene/shared/model/FakeScene.kt52
-rw-r--r--services/autofill/java/com/android/server/autofill/RemoteFillService.java5
-rw-r--r--services/autofill/java/com/android/server/autofill/SecondaryProviderHandler.java5
-rw-r--r--services/autofill/java/com/android/server/autofill/Session.java51
-rw-r--r--services/core/Android.bp1
-rw-r--r--services/core/java/com/android/server/display/feature/display_flags.aconfig2
-rw-r--r--services/core/java/com/android/server/feature/Android.bp13
-rw-r--r--services/core/java/com/android/server/media/MediaServerUtils.java5
-rw-r--r--services/core/java/com/android/server/media/MediaSessionRecord.java205
-rwxr-xr-xservices/core/java/com/android/server/notification/NotificationManagerService.java25
-rw-r--r--services/core/java/com/android/server/notification/NotificationRecord.java3
-rw-r--r--services/core/java/com/android/server/pm/PackageManagerNative.java5
-rw-r--r--services/core/java/com/android/server/power/hint/HintManagerService.java59
-rw-r--r--services/core/java/com/android/server/wm/ActivityRecord.java8
-rw-r--r--services/core/java/com/android/server/wm/ConfigurationContainer.java7
-rw-r--r--services/core/java/com/android/server/wm/DisplayContent.java9
-rw-r--r--services/core/java/com/android/server/wm/InsetsStateController.java1
-rw-r--r--services/core/java/com/android/server/wm/TaskDisplayArea.java1
-rw-r--r--services/core/java/com/android/server/wm/TaskFragment.java9
-rw-r--r--services/core/java/com/android/server/wm/WindowOrganizerController.java4
-rw-r--r--services/core/jni/com_android_server_hint_HintManagerService.cpp66
-rw-r--r--services/foldables/devicestateprovider/src/com/android/server/policy/BookStyleDeviceStatePolicy.java6
-rw-r--r--services/foldables/devicestateprovider/src/com/android/server/policy/FoldableDeviceStateProvider.java15
-rw-r--r--services/foldables/devicestateprovider/tests/src/com/android/server/policy/BookStyleDeviceStatePolicyTest.java20
-rw-r--r--services/tests/servicestests/src/com/android/server/power/hint/HintManagerServiceTest.java159
-rw-r--r--services/tests/uiservicestests/src/com/android/server/notification/NotificationListenersTest.java69
-rwxr-xr-xservices/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java53
-rw-r--r--services/tests/uiservicestests/src/com/android/server/notification/ZenEnumTest.java2
-rw-r--r--services/tests/wmtests/src/com/android/server/wm/SizeCompatTests.java21
-rw-r--r--services/tests/wmtests/src/com/android/server/wm/TaskFragmentOrganizerControllerTest.java2
-rw-r--r--services/tests/wmtests/src/com/android/server/wm/TaskFragmentTest.java21
-rw-r--r--services/tests/wmtests/src/com/android/server/wm/WindowConfigurationTests.java39
-rw-r--r--tests/FsVerityTest/FsVerityTestApp/src/com/android/fsverity/Helper.java9
100 files changed, 1666 insertions, 653 deletions
diff --git a/AconfigFlags.bp b/AconfigFlags.bp
index d3e80aea6fed..d98e191dbe9e 100644
--- a/AconfigFlags.bp
+++ b/AconfigFlags.bp
@@ -92,6 +92,7 @@ aconfig_declarations_group {
"com.android.window.flags.window-aconfig-java",
"device_policy_aconfig_flags_lib",
"display_flags_lib",
+ "dropbox_flags_lib",
"framework-jobscheduler-job.flags-aconfig-java",
"framework_graphics_flags_java_lib",
"hwui_flags_java_lib",
@@ -1351,3 +1352,19 @@ java_aconfig_library {
aconfig_declarations: "backstage_power_flags",
defaults: ["framework-minus-apex-aconfig-java-defaults"],
}
+
+// Dropbox data
+aconfig_declarations {
+ name: "dropbox_flags",
+ package: "com.android.server.feature.flags",
+ container: "system",
+ srcs: [
+ "services/core/java/com/android/server/feature/dropbox_flags.aconfig",
+ ],
+}
+
+java_aconfig_library {
+ name: "dropbox_flags_lib",
+ aconfig_declarations: "dropbox_flags",
+ defaults: ["framework-minus-apex-aconfig-java-defaults"],
+}
diff --git a/core/java/android/app/Notification.java b/core/java/android/app/Notification.java
index e39928b5e091..f7e0e9f6a416 100644
--- a/core/java/android/app/Notification.java
+++ b/core/java/android/app/Notification.java
@@ -17,15 +17,14 @@
package android.app;
import static android.annotation.Dimension.DP;
+import static android.app.Flags.evenlyDividedCallStyleActionLayout;
+import static android.app.Flags.updateRankingTime;
import static android.app.admin.DevicePolicyResources.Drawables.Source.NOTIFICATION;
import static android.app.admin.DevicePolicyResources.Drawables.Style.SOLID_COLORED;
import static android.app.admin.DevicePolicyResources.Drawables.WORK_PROFILE_ICON;
import static android.app.admin.DevicePolicyResources.UNDEFINED;
import static android.graphics.drawable.Icon.TYPE_URI;
import static android.graphics.drawable.Icon.TYPE_URI_ADAPTIVE_BITMAP;
-import static android.app.Flags.cleanUpSpansAndNewLines;
-import static android.app.Flags.evenlyDividedCallStyleActionLayout;
-import static android.app.Flags.updateRankingTime;
import static java.util.Objects.requireNonNull;
diff --git a/core/java/android/app/WindowConfiguration.java b/core/java/android/app/WindowConfiguration.java
index aa3b71a28eba..a12faca71bf6 100644
--- a/core/java/android/app/WindowConfiguration.java
+++ b/core/java/android/app/WindowConfiguration.java
@@ -100,9 +100,6 @@ public class WindowConfiguration implements Parcelable, Comparable<WindowConfigu
/** The current windowing mode of the configuration. */
private @WindowingMode int mWindowingMode;
- /** The display windowing mode of the configuration */
- private @WindowingMode int mDisplayWindowingMode;
-
/** Windowing mode is currently not defined. */
public static final int WINDOWING_MODE_UNDEFINED = 0;
/** Occupies the full area of the screen or the parent container. */
@@ -193,12 +190,9 @@ public class WindowConfiguration implements Parcelable, Comparable<WindowConfigu
/** Bit that indicates that the {@link #mRotation} changed.
* @hide */
public static final int WINDOW_CONFIG_ROTATION = 1 << 6;
- /** Bit that indicates that the {@link #mDisplayWindowingMode} changed.
- * @hide */
- public static final int WINDOW_CONFIG_DISPLAY_WINDOWING_MODE = 1 << 7;
/** Bit that indicates that the apparent-display changed.
* @hide */
- public static final int WINDOW_CONFIG_DISPLAY_ROTATION = 1 << 8;
+ public static final int WINDOW_CONFIG_DISPLAY_ROTATION = 1 << 7;
/** @hide */
@IntDef(flag = true, prefix = { "WINDOW_CONFIG_" }, value = {
@@ -209,7 +203,6 @@ public class WindowConfiguration implements Parcelable, Comparable<WindowConfigu
WINDOW_CONFIG_ACTIVITY_TYPE,
WINDOW_CONFIG_ALWAYS_ON_TOP,
WINDOW_CONFIG_ROTATION,
- WINDOW_CONFIG_DISPLAY_WINDOWING_MODE,
WINDOW_CONFIG_DISPLAY_ROTATION,
})
public @interface WindowConfig {}
@@ -237,7 +230,6 @@ public class WindowConfiguration implements Parcelable, Comparable<WindowConfigu
dest.writeInt(mActivityType);
dest.writeInt(mAlwaysOnTop);
dest.writeInt(mRotation);
- dest.writeInt(mDisplayWindowingMode);
dest.writeInt(mDisplayRotation);
}
@@ -250,7 +242,6 @@ public class WindowConfiguration implements Parcelable, Comparable<WindowConfigu
mActivityType = source.readInt();
mAlwaysOnTop = source.readInt();
mRotation = source.readInt();
- mDisplayWindowingMode = source.readInt();
mDisplayRotation = source.readInt();
}
@@ -411,17 +402,6 @@ public class WindowConfiguration implements Parcelable, Comparable<WindowConfigu
return mWindowingMode;
}
- /** @hide */
- public void setDisplayWindowingMode(@WindowingMode int windowingMode) {
- mDisplayWindowingMode = windowingMode;
- }
-
- /** @hide */
- @WindowingMode
- public int getDisplayWindowingMode() {
- return mDisplayWindowingMode;
- }
-
public void setActivityType(@ActivityType int activityType) {
if (mActivityType == activityType) {
return;
@@ -453,7 +433,6 @@ public class WindowConfiguration implements Parcelable, Comparable<WindowConfigu
setActivityType(other.mActivityType);
setAlwaysOnTop(other.mAlwaysOnTop);
setRotation(other.mRotation);
- setDisplayWindowingMode(other.mDisplayWindowingMode);
}
/** Set this object to completely undefined.
@@ -472,7 +451,6 @@ public class WindowConfiguration implements Parcelable, Comparable<WindowConfigu
setActivityType(ACTIVITY_TYPE_UNDEFINED);
setAlwaysOnTop(ALWAYS_ON_TOP_UNDEFINED);
setRotation(ROTATION_UNDEFINED);
- setDisplayWindowingMode(WINDOWING_MODE_UNDEFINED);
}
/** @hide */
@@ -543,11 +521,6 @@ public class WindowConfiguration implements Parcelable, Comparable<WindowConfigu
changed |= WINDOW_CONFIG_ROTATION;
setRotation(delta.mRotation);
}
- if (delta.mDisplayWindowingMode != WINDOWING_MODE_UNDEFINED
- && mDisplayWindowingMode != delta.mDisplayWindowingMode) {
- changed |= WINDOW_CONFIG_DISPLAY_WINDOWING_MODE;
- setDisplayWindowingMode(delta.mDisplayWindowingMode);
- }
if (delta.mDisplayRotation != ROTATION_UNDEFINED
&& delta.mDisplayRotation != mDisplayRotation) {
changed |= WINDOW_CONFIG_DISPLAY_ROTATION;
@@ -582,9 +555,6 @@ public class WindowConfiguration implements Parcelable, Comparable<WindowConfigu
if ((mask & WINDOW_CONFIG_ROTATION) != 0) {
setRotation(delta.mRotation);
}
- if ((mask & WINDOW_CONFIG_DISPLAY_WINDOWING_MODE) != 0) {
- setDisplayWindowingMode(delta.mDisplayWindowingMode);
- }
if ((mask & WINDOW_CONFIG_DISPLAY_ROTATION) != 0) {
setDisplayRotation(delta.mDisplayRotation);
}
@@ -639,11 +609,6 @@ public class WindowConfiguration implements Parcelable, Comparable<WindowConfigu
changes |= WINDOW_CONFIG_ROTATION;
}
- if ((compareUndefined || other.mDisplayWindowingMode != WINDOWING_MODE_UNDEFINED)
- && mDisplayWindowingMode != other.mDisplayWindowingMode) {
- changes |= WINDOW_CONFIG_DISPLAY_WINDOWING_MODE;
- }
-
if ((compareUndefined || other.mDisplayRotation != ROTATION_UNDEFINED)
&& mDisplayRotation != other.mDisplayRotation) {
changes |= WINDOW_CONFIG_DISPLAY_ROTATION;
@@ -697,8 +662,6 @@ public class WindowConfiguration implements Parcelable, Comparable<WindowConfigu
n = mRotation - that.mRotation;
if (n != 0) return n;
- n = mDisplayWindowingMode - that.mDisplayWindowingMode;
- if (n != 0) return n;
n = mDisplayRotation - that.mDisplayRotation;
if (n != 0) return n;
@@ -728,7 +691,6 @@ public class WindowConfiguration implements Parcelable, Comparable<WindowConfigu
result = 31 * result + mActivityType;
result = 31 * result + mAlwaysOnTop;
result = 31 * result + mRotation;
- result = 31 * result + mDisplayWindowingMode;
result = 31 * result + mDisplayRotation;
return result;
}
@@ -742,7 +704,6 @@ public class WindowConfiguration implements Parcelable, Comparable<WindowConfigu
+ " mDisplayRotation=" + (mRotation == ROTATION_UNDEFINED
? "undefined" : rotationToString(mDisplayRotation))
+ " mWindowingMode=" + windowingModeToString(mWindowingMode)
- + " mDisplayWindowingMode=" + windowingModeToString(mDisplayWindowingMode)
+ " mActivityType=" + activityTypeToString(mActivityType)
+ " mAlwaysOnTop=" + alwaysOnTopToString(mAlwaysOnTop)
+ " mRotation=" + (mRotation == ROTATION_UNDEFINED
@@ -818,16 +779,6 @@ public class WindowConfiguration implements Parcelable, Comparable<WindowConfigu
}
/**
- * Returns true if the activities associated with this window configuration display a decor
- * view.
- * @hide
- */
- public boolean hasWindowDecorCaption() {
- return mActivityType == ACTIVITY_TYPE_STANDARD && (mWindowingMode == WINDOWING_MODE_FREEFORM
- || mDisplayWindowingMode == WINDOWING_MODE_FREEFORM);
- }
-
- /**
* Returns true if the tasks associated with this window configuration can be resized
* independently of their parent container.
* @hide
diff --git a/core/java/android/os/IHintManager.aidl b/core/java/android/os/IHintManager.aidl
index d97ea541f73d..e057a8536fab 100644
--- a/core/java/android/os/IHintManager.aidl
+++ b/core/java/android/os/IHintManager.aidl
@@ -18,16 +18,21 @@
package android.os;
import android.os.IHintSession;
+import android.hardware.power.SessionConfig;
+import android.hardware.power.SessionTag;
/** {@hide} */
interface IHintManager {
/**
* Creates a {@link Session} for the given set of threads and associates to a binder token.
+ * Returns a config if creation is not supported, and HMS had to use the
+ * legacy creation method.
*/
- IHintSession createHintSession(in IBinder token, in int[] tids, long durationNanos);
+ IHintSession createHintSessionWithConfig(in IBinder token, in int[] threadIds,
+ in long durationNanos, in SessionTag tag, out @nullable SessionConfig config);
/**
- * Get preferred rate limit in nano second.
+ * Get preferred rate limit in nanoseconds.
*/
long getHintSessionPreferredRate();
diff --git a/core/java/android/service/autofill/AutofillService.java b/core/java/android/service/autofill/AutofillService.java
index e6a84df16c27..269839b61bef 100644
--- a/core/java/android/service/autofill/AutofillService.java
+++ b/core/java/android/service/autofill/AutofillService.java
@@ -37,7 +37,6 @@ import android.view.ViewStructure;
import android.view.autofill.AutofillId;
import android.view.autofill.AutofillManager;
import android.view.autofill.AutofillValue;
-import android.view.autofill.IAutoFillManagerClient;
import com.android.internal.os.IResultReceiver;
@@ -642,7 +641,7 @@ public abstract class AutofillService extends Service {
@Override
public void onFillCredentialRequest(FillRequest request, IFillCallback callback,
- IAutoFillManagerClient autofillClientCallback) {
+ IBinder autofillClientCallback) {
ICancellationSignal transport = CancellationSignal.createTransport();
try {
callback.onCancellable(transport);
@@ -724,7 +723,7 @@ public abstract class AutofillService extends Service {
*/
public void onFillCredentialRequest(@NonNull FillRequest request,
@NonNull CancellationSignal cancellationSignal, @NonNull FillCallback callback,
- @NonNull IAutoFillManagerClient autofillClientCallback) {}
+ @NonNull IBinder autofillClientCallback) {}
/**
* Called by the Android system to convert a credential manager response to a dataset
diff --git a/core/java/android/service/autofill/IAutoFillService.aidl b/core/java/android/service/autofill/IAutoFillService.aidl
index 2c2feae7aeea..3b64b8a0ec5e 100644
--- a/core/java/android/service/autofill/IAutoFillService.aidl
+++ b/core/java/android/service/autofill/IAutoFillService.aidl
@@ -16,13 +16,13 @@
package android.service.autofill;
+import android.os.IBinder;
import android.service.autofill.ConvertCredentialRequest;
import android.service.autofill.IConvertCredentialCallback;
import android.service.autofill.FillRequest;
import android.service.autofill.IFillCallback;
import android.service.autofill.ISaveCallback;
import android.service.autofill.SaveRequest;
-import android.view.autofill.IAutoFillManagerClient;
import com.android.internal.os.IResultReceiver;
/**
@@ -34,7 +34,7 @@ oneway interface IAutoFillService {
void onConnectedStateChanged(boolean connected);
void onFillRequest(in FillRequest request, in IFillCallback callback);
void onFillCredentialRequest(in FillRequest request, in IFillCallback callback,
- in IAutoFillManagerClient client);
+ in IBinder client);
void onSaveRequest(in SaveRequest request, in ISaveCallback callback);
void onSavedPasswordCountRequest(in IResultReceiver receiver);
void onConvertCredentialRequest(in ConvertCredentialRequest convertCredentialRequest, in IConvertCredentialCallback convertCredentialCallback);
diff --git a/core/java/android/service/notification/flags.aconfig b/core/java/android/service/notification/flags.aconfig
index 35cd3edcafcb..595e41e8d71b 100644
--- a/core/java/android/service/notification/flags.aconfig
+++ b/core/java/android/service/notification/flags.aconfig
@@ -14,7 +14,17 @@ flag {
namespace: "systemui"
description: "This flag controls the redacting of sensitive notifications from untrusted NotificationListenerServices"
bug: "306271190"
+}
+
+flag {
+ name: "redact_sensitive_notifications_big_text_style"
is_exported: true
+ namespace: "systemui"
+ description: "This flag controls the redacting of BigTextStyle fields in sensitive notifications"
+ bug: "335488909"
+ metadata {
+ purpose: PURPOSE_BUGFIX
+ }
}
flag {
diff --git a/core/java/android/view/TextureView.java b/core/java/android/view/TextureView.java
index 5466bf542f91..ebc86ee96f75 100644
--- a/core/java/android/view/TextureView.java
+++ b/core/java/android/view/TextureView.java
@@ -202,6 +202,14 @@ public class TextureView extends View {
// Set by native code, do not write!
@UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
private long mNativeWindow;
+ // Used for VRR detecting "normal" frame rate rather than "high". This is the previous
+ // interval for drawing. This can be removed when NORMAL is the default rate for Views.
+ // (b/329156944)
+ private long mMinusTwoFrameIntervalMillis = 0;
+ // Used for VRR detecting "normal" frame rate rather than "high". This is the last
+ // frame time for drawing. This can be removed when NORMAL is the default rate for Views.
+ // (b/329156944)
+ private long mLastFrameTimeMillis = 0;
/**
* Creates a new TextureView.
@@ -890,12 +898,26 @@ public class TextureView extends View {
*/
@Override
protected int calculateFrameRateCategory() {
- if (mMinusTwoFrameIntervalMillis > 15 && mMinusOneFrameIntervalMillis > 15) {
+ long now = getDrawingTime();
+ // This isn't necessary when the default frame rate is NORMAL (b/329156944)
+ if (mMinusTwoFrameIntervalMillis > 15 && (now - mLastFrameTimeMillis) > 15) {
return FRAME_RATE_CATEGORY_NORMAL;
}
return super.calculateFrameRateCategory();
}
+ /**
+ * @hide
+ */
+ @Override
+ protected void votePreferredFrameRate() {
+ super.votePreferredFrameRate();
+ // This isn't necessary when the default frame rate is NORMAL (b/329156944)
+ long now = getDrawingTime();
+ mMinusTwoFrameIntervalMillis = now - mLastFrameTimeMillis;
+ mLastFrameTimeMillis = now;
+ }
+
@UnsupportedAppUsage
private final SurfaceTexture.OnFrameAvailableListener mUpdateListener =
surfaceTexture -> {
diff --git a/core/java/android/view/View.java b/core/java/android/view/View.java
index 4c4a22cc96e9..47816f412a62 100644
--- a/core/java/android/view/View.java
+++ b/core/java/android/view/View.java
@@ -1133,7 +1133,7 @@ public class View implements Drawable.Callback, KeyEvent.Callback,
private static final int FOCUSABLE_MASK = 0x00000011;
/**
- * This view will adjust its padding to fit sytem windows (e.g. status bar)
+ * This view will adjust its padding to fit system windows (e.g. status bar)
*/
private static final int FITS_SYSTEM_WINDOWS = 0x00000002;
@@ -5764,23 +5764,10 @@ public class View implements Drawable.Callback, KeyEvent.Callback,
static final float MAX_FRAME_RATE = 140;
- private static final int INFREQUENT_UPDATE_INTERVAL_MILLIS = 100;
- private static final int INFREQUENT_UPDATE_COUNTS = 2;
-
// The preferred frame rate of the view that is mainly used for
// touch boosting, view velocity handling, and TextureView.
private float mPreferredFrameRate = REQUESTED_FRAME_RATE_CATEGORY_DEFAULT;
- private int mInfrequentUpdateCount = 0;
- private long mLastUpdateTimeMillis = 0;
- /**
- * @hide
- */
- protected int mMinusOneFrameIntervalMillis = 0;
- /**
- * @hide
- */
- protected int mMinusTwoFrameIntervalMillis = 0;
private int mLastFrameRateCategory = FRAME_RATE_CATEGORY_NO_PREFERENCE;
@FlaggedApi(FLAG_TOOLKIT_SET_FRAME_RATE_READ_ONLY)
@@ -23651,7 +23638,6 @@ public class View implements Drawable.Callback, KeyEvent.Callback,
if (sToolkitSetFrameRateReadOnlyFlagValue
&& sToolkitFrameRateViewEnablingReadOnlyFlagValue) {
votePreferredFrameRate();
- updateInfrequentCount();
}
mPrivateFlags4 = (mPrivateFlags4 & ~PFLAG4_HAS_MOVED) | PFLAG4_HAS_DRAWN;
@@ -32835,6 +32821,13 @@ public class View implements Drawable.Callback, KeyEvent.Callback,
SENSITIVE_CONTENT_AUTOFILL_HINTS.add(View.AUTOFILL_HINT_USERNAME);
SENSITIVE_CONTENT_AUTOFILL_HINTS.add(View.AUTOFILL_HINT_PASSWORD_AUTO);
SENSITIVE_CONTENT_AUTOFILL_HINTS.add(View.AUTOFILL_HINT_PASSWORD);
+ SENSITIVE_CONTENT_AUTOFILL_HINTS.add(View.AUTOFILL_HINT_CREDIT_CARD_NUMBER);
+ SENSITIVE_CONTENT_AUTOFILL_HINTS.add(View.AUTOFILL_HINT_CREDIT_CARD_SECURITY_CODE);
+ SENSITIVE_CONTENT_AUTOFILL_HINTS.add(View.AUTOFILL_HINT_CREDIT_CARD_EXPIRATION_DATE);
+ SENSITIVE_CONTENT_AUTOFILL_HINTS.add(View.AUTOFILL_HINT_CREDIT_CARD_EXPIRATION_DAY);
+ SENSITIVE_CONTENT_AUTOFILL_HINTS.add(View.AUTOFILL_HINT_CREDIT_CARD_EXPIRATION_MONTH);
+ SENSITIVE_CONTENT_AUTOFILL_HINTS.add(View.AUTOFILL_HINT_CREDIT_CARD_EXPIRATION_YEAR);
+ SENSITIVE_CONTENT_AUTOFILL_HINTS.add(View.AUTOFILL_HINT_CREDENTIAL_MANAGER);
}
/**
@@ -33903,15 +33896,15 @@ public class View implements Drawable.Callback, KeyEvent.Callback,
* @hide
*/
protected int calculateFrameRateCategory() {
- if (mMinusTwoFrameIntervalMillis + mMinusOneFrameIntervalMillis
- < INFREQUENT_UPDATE_INTERVAL_MILLIS) {
- return mSizeBasedFrameRateCategoryAndReason;
- }
-
- if (mInfrequentUpdateCount == INFREQUENT_UPDATE_COUNTS) {
- return FRAME_RATE_CATEGORY_NORMAL | FRAME_RATE_CATEGORY_REASON_INTERMITTENT;
+ int category;
+ switch (getViewRootImpl().intermittentUpdateState()) {
+ case ViewRootImpl.INTERMITTENT_STATE_INTERMITTENT ->
+ category = FRAME_RATE_CATEGORY_NORMAL | FRAME_RATE_CATEGORY_REASON_INTERMITTENT;
+ case ViewRootImpl.INTERMITTENT_STATE_NOT_INTERMITTENT ->
+ category = mSizeBasedFrameRateCategoryAndReason;
+ default -> category = mLastFrameRateCategory;
}
- return mLastFrameRateCategory;
+ return category;
}
/**
@@ -33922,76 +33915,99 @@ public class View implements Drawable.Callback, KeyEvent.Callback,
protected void votePreferredFrameRate() {
// use toolkitSetFrameRate flag to gate the change
ViewRootImpl viewRootImpl = getViewRootImpl();
- int width = mRight - mLeft;
- int height = mBottom - mTop;
-
- if (viewRootImpl != null && (width != 0 && height != 0)) {
- if (viewRootImpl.shouldCheckFrameRate(mPreferredFrameRate > 0f)) {
- float velocityFrameRate = 0f;
- if (mAttachInfo.mViewVelocityApi) {
- float velocity = mFrameContentVelocity;
-
- if (velocity < 0f
- && (mPrivateFlags4 & (PFLAG4_HAS_MOVED | PFLAG4_HAS_DRAWN)) == (
- PFLAG4_HAS_MOVED | PFLAG4_HAS_DRAWN)
- && mParent instanceof View
- && ((View) mParent).mFrameContentVelocity <= 0
- ) {
- // This current calculation is very simple. If something on the screen
- // moved, then it votes for the highest velocity.
- velocityFrameRate = MAX_FRAME_RATE;
- } else if (velocity > 0f) {
- velocityFrameRate = convertVelocityToFrameRate(velocity);
- }
- }
- if (velocityFrameRate > 0f || mPreferredFrameRate > 0f) {
- int compatibility = FRAME_RATE_COMPATIBILITY_GTE;
- float frameRate = velocityFrameRate;
- if (mPreferredFrameRate > velocityFrameRate) {
- compatibility = FRAME_RATE_COMPATIBILITY_FIXED_SOURCE;
- frameRate = mPreferredFrameRate;
- }
- viewRootImpl.votePreferredFrameRate(frameRate, compatibility);
+ if (viewRootImpl == null) {
+ return; // can't vote if not connected
+ }
+ float velocity = mFrameContentVelocity;
+ float frameRate = mPreferredFrameRate;
+ ViewParent parent = mParent;
+ if (velocity <= 0 && Float.isNaN(frameRate)) {
+ // The most common case is when nothing is set, so this special case is called
+ // often.
+ if (mAttachInfo.mViewVelocityApi
+ && (mPrivateFlags4 & (PFLAG4_HAS_MOVED | PFLAG4_HAS_DRAWN)) == (
+ PFLAG4_HAS_MOVED | PFLAG4_HAS_DRAWN)
+ && viewRootImpl.shouldCheckFrameRate(false)
+ && parent instanceof View
+ && ((View) parent).mFrameContentVelocity <= 0) {
+ viewRootImpl.votePreferredFrameRate(MAX_FRAME_RATE, FRAME_RATE_COMPATIBILITY_GTE);
+ }
+ if (!willNotDraw() && viewRootImpl.shouldCheckFrameRateCategory()) {
+ int frameRateCategory = calculateFrameRateCategory();
+ int category = frameRateCategory & ~FRAME_RATE_CATEGORY_REASON_MASK;
+ int reason = frameRateCategory & FRAME_RATE_CATEGORY_REASON_MASK;
+ viewRootImpl.votePreferredFrameRateCategory(category, reason, this);
+ mLastFrameRateCategory = frameRateCategory;
+ }
+ return;
+ }
+ if (viewRootImpl.shouldCheckFrameRate(frameRate > 0f)) {
+ float velocityFrameRate = 0f;
+ if (mAttachInfo.mViewVelocityApi) {
+ if (velocity < 0f
+ && (mPrivateFlags4 & (PFLAG4_HAS_MOVED | PFLAG4_HAS_DRAWN)) == (
+ PFLAG4_HAS_MOVED | PFLAG4_HAS_DRAWN)
+ && mParent instanceof View
+ && ((View) mParent).mFrameContentVelocity <= 0
+ ) {
+ // This current calculation is very simple. If something on the screen
+ // moved, then it votes for the highest velocity.
+ velocityFrameRate = MAX_FRAME_RATE;
+ } else if (velocity > 0f) {
+ velocityFrameRate = convertVelocityToFrameRate(velocity);
}
}
- if (!willNotDraw() && isDirty() && viewRootImpl.shouldCheckFrameRateCategory()) {
- if (sToolkitMetricsForFrameRateDecisionFlagValue) {
- float sizePercentage = width * height / mAttachInfo.mDisplayPixelCount;
- viewRootImpl.recordViewPercentage(sizePercentage);
+ if (velocityFrameRate > 0f || frameRate > 0f) {
+ int compatibility;
+ if (frameRate >= velocityFrameRate) {
+ compatibility = FRAME_RATE_COMPATIBILITY_FIXED_SOURCE;
+ } else {
+ compatibility = FRAME_RATE_COMPATIBILITY_GTE;
+ frameRate = velocityFrameRate;
}
+ viewRootImpl.votePreferredFrameRate(frameRate, compatibility);
+ }
+ }
- int frameRateCategory = FRAME_RATE_CATEGORY_NO_PREFERENCE;
- if (Float.isNaN(mPreferredFrameRate)) {
- frameRateCategory = calculateFrameRateCategory();
- } else if (mPreferredFrameRate < 0) {
- switch ((int) mPreferredFrameRate) {
- case (int) REQUESTED_FRAME_RATE_CATEGORY_NO_PREFERENCE ->
- frameRateCategory = FRAME_RATE_CATEGORY_NO_PREFERENCE
- | FRAME_RATE_CATEGORY_REASON_REQUESTED;
- case (int) REQUESTED_FRAME_RATE_CATEGORY_LOW ->
- frameRateCategory = FRAME_RATE_CATEGORY_LOW
- | FRAME_RATE_CATEGORY_REASON_REQUESTED;
- case (int) REQUESTED_FRAME_RATE_CATEGORY_NORMAL ->
- frameRateCategory = FRAME_RATE_CATEGORY_NORMAL
- | FRAME_RATE_CATEGORY_REASON_REQUESTED;
- case (int) REQUESTED_FRAME_RATE_CATEGORY_HIGH ->
- frameRateCategory = FRAME_RATE_CATEGORY_HIGH
- | FRAME_RATE_CATEGORY_REASON_REQUESTED;
- default -> {
- // invalid frame rate, use default
- int category = sToolkitFrameRateDefaultNormalReadOnlyFlagValue
- ? FRAME_RATE_CATEGORY_NORMAL : FRAME_RATE_CATEGORY_HIGH;
- frameRateCategory = category
- | FRAME_RATE_CATEGORY_REASON_INVALID;
- }
+ if (!willNotDraw() && viewRootImpl.shouldCheckFrameRateCategory()) {
+ if (sToolkitMetricsForFrameRateDecisionFlagValue) {
+ int width = mRight - mLeft;
+ int height = mBottom - mTop;
+ float sizePercentage = width * height / mAttachInfo.mDisplayPixelCount;
+ viewRootImpl.recordViewPercentage(sizePercentage);
+ }
+
+ int frameRateCategory = FRAME_RATE_CATEGORY_NO_PREFERENCE;
+ if (Float.isNaN(frameRate)) {
+ frameRateCategory = calculateFrameRateCategory();
+ } else if (frameRate < 0) {
+ switch ((int) frameRate) {
+ case (int) REQUESTED_FRAME_RATE_CATEGORY_NO_PREFERENCE ->
+ frameRateCategory = FRAME_RATE_CATEGORY_NO_PREFERENCE
+ | FRAME_RATE_CATEGORY_REASON_REQUESTED;
+ case (int) REQUESTED_FRAME_RATE_CATEGORY_LOW ->
+ frameRateCategory = FRAME_RATE_CATEGORY_LOW
+ | FRAME_RATE_CATEGORY_REASON_REQUESTED;
+ case (int) REQUESTED_FRAME_RATE_CATEGORY_NORMAL ->
+ frameRateCategory = FRAME_RATE_CATEGORY_NORMAL
+ | FRAME_RATE_CATEGORY_REASON_REQUESTED;
+ case (int) REQUESTED_FRAME_RATE_CATEGORY_HIGH ->
+ frameRateCategory = FRAME_RATE_CATEGORY_HIGH
+ | FRAME_RATE_CATEGORY_REASON_REQUESTED;
+ default -> {
+ // invalid frame rate, use default
+ int category = sToolkitFrameRateDefaultNormalReadOnlyFlagValue
+ ? FRAME_RATE_CATEGORY_NORMAL : FRAME_RATE_CATEGORY_HIGH;
+ frameRateCategory = category
+ | FRAME_RATE_CATEGORY_REASON_INVALID;
}
}
-
- int category = frameRateCategory & ~FRAME_RATE_CATEGORY_REASON_MASK;
- int reason = frameRateCategory & FRAME_RATE_CATEGORY_REASON_MASK;
- viewRootImpl.votePreferredFrameRateCategory(category, reason, this);
- mLastFrameRateCategory = frameRateCategory;
}
+
+ int category = frameRateCategory & ~FRAME_RATE_CATEGORY_REASON_MASK;
+ int reason = frameRateCategory & FRAME_RATE_CATEGORY_REASON_MASK;
+ viewRootImpl.votePreferredFrameRateCategory(category, reason, this);
+ mLastFrameRateCategory = frameRateCategory;
}
}
@@ -34074,33 +34090,4 @@ public class View implements Drawable.Callback, KeyEvent.Callback,
}
return 0;
}
-
- /**
- * This function is mainly used for migrating infrequent layer logic
- * from SurfaceFlinger to Toolkit.
- * The infrequent layer logic includes:
- * - NORMAL for infrequent update: FT2-FT1 > 100 && FT3-FT2 > 100.
- * - HIGH/NORMAL based on size for frequent update: (FT3-FT2) + (FT2 - FT1) < 100.
- * - otherwise, use the previous category value.
- */
- private void updateInfrequentCount() {
- if (!willNotDraw()) {
- long currentTimeMillis = getDrawingTime();
- int timeIntervalMillis =
- (int) Math.min(Integer.MAX_VALUE, currentTimeMillis - mLastUpdateTimeMillis);
- mMinusTwoFrameIntervalMillis = mMinusOneFrameIntervalMillis;
- mMinusOneFrameIntervalMillis = timeIntervalMillis;
-
- mLastUpdateTimeMillis = currentTimeMillis;
- if (mMinusTwoFrameIntervalMillis >= 30 && timeIntervalMillis < 2) {
- return;
- }
- if (timeIntervalMillis >= INFREQUENT_UPDATE_INTERVAL_MILLIS) {
- mInfrequentUpdateCount = mInfrequentUpdateCount == INFREQUENT_UPDATE_COUNTS
- ? mInfrequentUpdateCount : mInfrequentUpdateCount + 1;
- } else {
- mInfrequentUpdateCount = 0;
- }
- }
- }
}
diff --git a/core/java/android/view/ViewRootImpl.java b/core/java/android/view/ViewRootImpl.java
index a8619ab4539c..e2ed2b8097f5 100644
--- a/core/java/android/view/ViewRootImpl.java
+++ b/core/java/android/view/ViewRootImpl.java
@@ -392,6 +392,26 @@ public final class ViewRootImpl implements ViewParent,
private static final int UNSET_SYNC_ID = -1;
+ private static final int INFREQUENT_UPDATE_INTERVAL_MILLIS = 100;
+ private static final int INFREQUENT_UPDATE_COUNTS = 2;
+
+ /**
+ * The {@link #intermittentUpdateState()} value when the ViewRootImpl isn't intermittent.
+ */
+ public static final int INTERMITTENT_STATE_NOT_INTERMITTENT = 1;
+
+ /**
+ * The {@link #intermittentUpdateState()} value when the ViewRootImpl is transitioning either
+ * to or from intermittent to not intermittent. This indicates that the frame rate shouldn't
+ * change.
+ */
+ public static final int INTERMITTENT_STATE_IN_TRANSITION = -1;
+
+ /**
+ * The {@link #intermittentUpdateState()} value when the ViewRootImpl is intermittent.
+ */
+ public static final int INTERMITTENT_STATE_INTERMITTENT = 0;
+
/**
* Minimum time to wait before reporting changes to keep clear areas.
*/
@@ -623,6 +643,15 @@ public final class ViewRootImpl implements ViewParent,
// Is the stylus pointer icon enabled
private final boolean mIsStylusPointerIconEnabled;
+ // VRR check for number of infrequent updates
+ private int mInfrequentUpdateCount = 0;
+ // VRR time of last update
+ private long mLastUpdateTimeMillis = 0;
+ // VRR interval since the previous
+ private int mMinusOneFrameIntervalMillis = 0;
+ // VRR interval between the previous and the frame before
+ private int mMinusTwoFrameIntervalMillis = 0;
+
/**
* Update the Choreographer's FrameInfo object with the timing information for the current
* ViewRootImpl instance. Erase the data in the current ViewFrameInfo to prepare for the next
@@ -1068,6 +1097,7 @@ public final class ViewRootImpl implements ViewParent,
// Used to check if there is a message in the message queue
// for idleness handling.
private boolean mHasIdledMessage = false;
+ private boolean mDrawnThisFrame = false;
// Used to check if there is a conflict between different frame rate voting.
// Take 24 and 30 as an example, 24 is not a divisor of 30.
// We consider there is a conflict.
@@ -4220,25 +4250,29 @@ public final class ViewRootImpl implements ViewParent,
// For the variable refresh rate project.
// We set the preferred frame rate and frame rate category at the end of performTraversals
// when the values are applicable.
- setCategoryFromCategoryCounts();
- setPreferredFrameRate(mPreferredFrameRate);
- setPreferredFrameRateCategory(mPreferredFrameRateCategory);
- if (!mIsFrameRateConflicted) {
- mHandler.removeMessages(MSG_FRAME_RATE_SETTING);
- mHandler.sendEmptyMessageDelayed(MSG_FRAME_RATE_SETTING,
- FRAME_RATE_SETTING_REEVALUATE_TIME);
- }
- checkIdleness();
- mFrameRateCategoryHighCount = mFrameRateCategoryHighCount > 0
- ? mFrameRateCategoryHighCount - 1 : mFrameRateCategoryHighCount;
- mFrameRateCategoryNormalCount = mFrameRateCategoryNormalCount > 0
- ? mFrameRateCategoryNormalCount - 1 : mFrameRateCategoryNormalCount;
- mFrameRateCategoryLowCount = mFrameRateCategoryLowCount > 0
- ? mFrameRateCategoryLowCount - 1 : mFrameRateCategoryLowCount;
- mPreferredFrameRateCategory = FRAME_RATE_CATEGORY_DEFAULT;
- mPreferredFrameRate = -1;
- mIsFrameRateConflicted = false;
- mFrameRateCategoryChangeReason = FRAME_RATE_CATEGORY_REASON_UNKNOWN;
+ if (mDrawnThisFrame) {
+ mDrawnThisFrame = false;
+ updateInfrequentCount();
+ setCategoryFromCategoryCounts();
+ setPreferredFrameRate(mPreferredFrameRate);
+ setPreferredFrameRateCategory(mPreferredFrameRateCategory);
+ if (!mIsFrameRateConflicted) {
+ mHandler.removeMessages(MSG_FRAME_RATE_SETTING);
+ mHandler.sendEmptyMessageDelayed(MSG_FRAME_RATE_SETTING,
+ FRAME_RATE_SETTING_REEVALUATE_TIME);
+ }
+ checkIdleness();
+ mFrameRateCategoryHighCount = mFrameRateCategoryHighCount > 0
+ ? mFrameRateCategoryHighCount - 1 : mFrameRateCategoryHighCount;
+ mFrameRateCategoryNormalCount = mFrameRateCategoryNormalCount > 0
+ ? mFrameRateCategoryNormalCount - 1 : mFrameRateCategoryNormalCount;
+ mFrameRateCategoryLowCount = mFrameRateCategoryLowCount > 0
+ ? mFrameRateCategoryLowCount - 1 : mFrameRateCategoryLowCount;
+ mPreferredFrameRateCategory = FRAME_RATE_CATEGORY_DEFAULT;
+ mPreferredFrameRate = -1;
+ mIsFrameRateConflicted = false;
+ mFrameRateCategoryChangeReason = FRAME_RATE_CATEGORY_REASON_UNKNOWN;
+ }
}
private void createSyncIfNeeded() {
@@ -12516,6 +12550,15 @@ public final class ViewRootImpl implements ViewParent,
* Sets the mPreferredFrameRateCategory from the high, high_hint, normal, and low counts.
*/
private void setCategoryFromCategoryCounts() {
+ switch (mPreferredFrameRateCategory) {
+ case FRAME_RATE_CATEGORY_LOW -> mFrameRateCategoryLowCount = FRAME_RATE_CATEGORY_COUNT;
+ case FRAME_RATE_CATEGORY_NORMAL ->
+ mFrameRateCategoryNormalCount = FRAME_RATE_CATEGORY_COUNT;
+ case FRAME_RATE_CATEGORY_HIGH_HINT ->
+ mFrameRateCategoryHighHintCount = FRAME_RATE_CATEGORY_COUNT;
+ case FRAME_RATE_CATEGORY_HIGH ->
+ mFrameRateCategoryHighCount = FRAME_RATE_CATEGORY_COUNT;
+ }
if (mFrameRateCategoryHighCount > 0) {
mPreferredFrameRateCategory = FRAME_RATE_CATEGORY_HIGH;
} else if (mFrameRateCategoryHighHintCount > 0) {
@@ -12661,21 +12704,31 @@ public final class ViewRootImpl implements ViewParent,
*/
@VisibleForTesting(visibility = VisibleForTesting.Visibility.PROTECTED)
public void votePreferredFrameRateCategory(int frameRateCategory, int reason, View view) {
- switch (frameRateCategory) {
- case FRAME_RATE_CATEGORY_LOW -> mFrameRateCategoryLowCount = FRAME_RATE_CATEGORY_COUNT;
- case FRAME_RATE_CATEGORY_NORMAL ->
- mFrameRateCategoryNormalCount = FRAME_RATE_CATEGORY_COUNT;
- case FRAME_RATE_CATEGORY_HIGH_HINT ->
- mFrameRateCategoryHighHintCount = FRAME_RATE_CATEGORY_COUNT;
- case FRAME_RATE_CATEGORY_HIGH ->
- mFrameRateCategoryHighCount = FRAME_RATE_CATEGORY_COUNT;
- }
if (frameRateCategory > mPreferredFrameRateCategory) {
mPreferredFrameRateCategory = frameRateCategory;
mFrameRateCategoryChangeReason = reason;
- mFrameRateCategoryView = view == null ? "-" : view.getClass().getSimpleName();
+// mFrameRateCategoryView = view == null ? "-" : view.getClass().getSimpleName();
}
mHasInvalidation = true;
+ mDrawnThisFrame = true;
+ }
+
+ /**
+ * Returns {@link #INTERMITTENT_STATE_INTERMITTENT} when the ViewRootImpl has only been
+ * updated intermittently, {@link #INTERMITTENT_STATE_NOT_INTERMITTENT} when it is
+ * not updated intermittently, and {@link #INTERMITTENT_STATE_IN_TRANSITION} when it
+ * is transitioning between {@link #INTERMITTENT_STATE_NOT_INTERMITTENT} and
+ * {@link #INTERMITTENT_STATE_INTERMITTENT}.
+ */
+ int intermittentUpdateState() {
+ if (mMinusOneFrameIntervalMillis + mMinusTwoFrameIntervalMillis
+ < INFREQUENT_UPDATE_INTERVAL_MILLIS) {
+ return INTERMITTENT_STATE_NOT_INTERMITTENT;
+ }
+ if (mInfrequentUpdateCount == INFREQUENT_UPDATE_COUNTS) {
+ return INTERMITTENT_STATE_INTERMITTENT;
+ }
+ return INTERMITTENT_STATE_IN_TRANSITION;
}
/**
@@ -12730,6 +12783,8 @@ public final class ViewRootImpl implements ViewParent,
mFrameRateCategoryHighCount = FRAME_RATE_CATEGORY_COUNT;
mFrameRateCategoryChangeReason = FRAME_RATE_CATEGORY_REASON_VELOCITY;
mFrameRateCategoryView = null;
+ mHasInvalidation = true;
+ mDrawnThisFrame = true;
return;
}
}
@@ -12761,6 +12816,7 @@ public final class ViewRootImpl implements ViewParent,
mPreferredFrameRate = nextFrameRate;
mFrameRateCompatibility = nextFrameRateCompatibility;
mHasInvalidation = true;
+ mDrawnThisFrame = true;
}
/**
@@ -12894,4 +12950,29 @@ public final class ViewRootImpl implements ViewParent,
mHandler.removeMessages(MSG_CHECK_INVALIDATION_IDLE);
mHandler.removeMessages(MSG_FRAME_RATE_SETTING);
}
+
+ /**
+ * This function is mainly used for migrating infrequent layer logic
+ * from SurfaceFlinger to Toolkit.
+ * The infrequent layer logic includes:
+ * - NORMAL for infrequent update: FT2-FT1 > 100 && FT3-FT2 > 100.
+ * - HIGH/NORMAL based on size for frequent update: (FT3-FT2) + (FT2 - FT1) < 100.
+ * - otherwise, use the previous category value.
+ */
+ private void updateInfrequentCount() {
+ long currentTimeMillis = mAttachInfo.mDrawingTime;
+ int timeIntervalMillis =
+ (int) Math.min(Integer.MAX_VALUE, currentTimeMillis - mLastUpdateTimeMillis);
+ mMinusTwoFrameIntervalMillis = mMinusOneFrameIntervalMillis;
+ mMinusOneFrameIntervalMillis = timeIntervalMillis;
+
+ mLastUpdateTimeMillis = currentTimeMillis;
+ if (timeIntervalMillis >= INFREQUENT_UPDATE_INTERVAL_MILLIS) {
+ int infrequentUpdateCount = mInfrequentUpdateCount;
+ mInfrequentUpdateCount = infrequentUpdateCount == INFREQUENT_UPDATE_COUNTS
+ ? infrequentUpdateCount : infrequentUpdateCount + 1;
+ } else {
+ mInfrequentUpdateCount = 0;
+ }
+ }
}
diff --git a/core/res/res/values/strings.xml b/core/res/res/values/strings.xml
index f31d390d376c..60289c1921c2 100644
--- a/core/res/res/values/strings.xml
+++ b/core/res/res/values/strings.xml
@@ -6436,10 +6436,8 @@ ul.</string>
<!-- Communal profile label on a screen. This can be used as a tab label for this profile in tabbed views and can be used to represent the profile in sharing surfaces, etc. [CHAR LIMIT=20] -->
<string name="profile_label_communal">Communal</string>
- <!-- Notification message used when a notification's normal message contains sensitive information. -->
- <!-- TODO b/301960090: replace with redacted message string and action title, when/if UX provides one -->
- <!-- DO NOT TRANSLATE -->
- <string name="redacted_notification_message"></string>
+ <!-- Notification message used when a notification's normal message contains sensitive information [CHAR_LIMIT=NOTIF_BODY] -->
+ <string name="redacted_notification_message">Sensitive notification content hidden</string>
<!-- Notification action title used instead of a notification's normal title sensitive [CHAR_LIMIT=NOTIF_BODY] -->
<string name="redacted_notification_action_title"></string>
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/back/CrossActivityBackAnimation.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/back/CrossActivityBackAnimation.kt
index 7cb56605cc12..0799fe3b6eb2 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/back/CrossActivityBackAnimation.kt
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/back/CrossActivityBackAnimation.kt
@@ -94,6 +94,8 @@ class CrossActivityBackAnimation @Inject constructor(
private var scrimLayer: SurfaceControl? = null
private var maxScrimAlpha: Float = 0f
+ private var isLetterboxed = false
+
override fun onConfigurationChanged(newConfiguration: Configuration) {
cornerRadius = ScreenDecorationsUtils.getWindowCornerRadius(context)
}
@@ -112,9 +114,15 @@ class CrossActivityBackAnimation @Inject constructor(
initialTouchPos.set(backMotionEvent.touchX, backMotionEvent.touchY)
transaction.setAnimationTransaction()
-
+ isLetterboxed = closingTarget!!.taskInfo.appCompatTaskInfo.topActivityBoundsLetterboxed
+ if (isLetterboxed) {
+ // Include letterbox in back animation
+ backAnimRect.set(closingTarget!!.windowConfiguration.bounds)
+ } else {
+ // otherwise play animation on localBounds only
+ backAnimRect.set(closingTarget!!.localBounds)
+ }
// Offset start rectangle to align task bounds.
- backAnimRect.set(closingTarget!!.localBounds)
backAnimRect.offsetTo(0, 0)
startClosingRect.set(backAnimRect)
@@ -241,6 +249,7 @@ class CrossActivityBackAnimation @Inject constructor(
}
finishCallback = null
removeScrimLayer()
+ isLetterboxed = false
}
private fun applyTransform(leash: SurfaceControl?, rect: RectF, alpha: Float) {
@@ -274,10 +283,11 @@ class CrossActivityBackAnimation @Inject constructor(
scrimLayer = scrimBuilder.build()
val colorComponents = floatArrayOf(0f, 0f, 0f)
maxScrimAlpha = if (isDarkTheme) MAX_SCRIM_ALPHA_DARK else MAX_SCRIM_ALPHA_LIGHT
+ val scrimCrop = if (isLetterboxed) backAnimRect else closingTarget!!.localBounds
transaction
.setColor(scrimLayer, colorComponents)
.setAlpha(scrimLayer!!, maxScrimAlpha)
- .setCrop(scrimLayer!!, closingTarget!!.localBounds)
+ .setCrop(scrimLayer!!, scrimCrop)
.setRelativeLayer(scrimLayer!!, closingTarget!!.leash, -1)
.show(scrimLayer)
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/compatui/UserAspectRatioSettingsWindowManager.java b/libs/WindowManager/Shell/src/com/android/wm/shell/compatui/UserAspectRatioSettingsWindowManager.java
index 7c280994042b..8fb4bdbea933 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/compatui/UserAspectRatioSettingsWindowManager.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/compatui/UserAspectRatioSettingsWindowManager.java
@@ -237,7 +237,8 @@ class UserAspectRatioSettingsWindowManager extends CompatUIWindowManagerAbstract
final int letterboxWidth = taskInfo.topActivityLetterboxWidth;
// App is not visibly letterboxed if it covers status bar/bottom insets or matches the
// stable bounds, so don't show the button
- if (stableBounds.height() <= letterboxHeight && stableBounds.width() <= letterboxWidth) {
+ if (stableBounds.height() <= letterboxHeight && stableBounds.width() <= letterboxWidth
+ && !taskInfo.isUserFullscreenOverrideEnabled) {
return false;
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/transition/DefaultTransitionHandler.java b/libs/WindowManager/Shell/src/com/android/wm/shell/transition/DefaultTransitionHandler.java
index 9adb67c8a65e..2d6ba6ee7217 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/transition/DefaultTransitionHandler.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/transition/DefaultTransitionHandler.java
@@ -594,7 +594,6 @@ public class DefaultTransitionHandler implements Transitions.TransitionHandler {
.setName("animation-background")
.setCallsite("DefaultTransitionHandler")
.setColorLayer();
- final SurfaceControl backgroundSurface = colorLayerBuilder.build();
// Attaching the background surface to the transition root could unexpectedly make it
// cover one of the split root tasks. To avoid this, put the background surface just
@@ -605,8 +604,10 @@ public class DefaultTransitionHandler implements Transitions.TransitionHandler {
if (isSplitTaskInvolved) {
mRootTDAOrganizer.attachToDisplayArea(displayId, colorLayerBuilder);
} else {
- startTransaction.reparent(backgroundSurface, info.getRootLeash());
+ colorLayerBuilder.setParent(info.getRootLeash());
}
+
+ final SurfaceControl backgroundSurface = colorLayerBuilder.build();
startTransaction.setColor(backgroundSurface, colorArray)
.setLayer(backgroundSurface, -1)
.show(backgroundSurface);
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/compatui/UserAspectRatioSettingsWindowManagerTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/compatui/UserAspectRatioSettingsWindowManagerTest.java
index 81ba4b37d13b..94e168ed70ed 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/compatui/UserAspectRatioSettingsWindowManagerTest.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/compatui/UserAspectRatioSettingsWindowManagerTest.java
@@ -292,6 +292,24 @@ public class UserAspectRatioSettingsWindowManagerTest extends ShellTestCase {
}
@Test
+ public void testUserFullscreenOverrideEnabled_buttonAlwaysShown() {
+ TaskInfo taskInfo = createTaskInfo(/* eligibleForUserAspectRatioButton= */
+ true, /* topActivityBoundsLetterboxed */ true, ACTION_MAIN, CATEGORY_LAUNCHER);
+
+ final Rect stableBounds = mWindowManager.getTaskStableBounds();
+
+ // Letterboxed activity that has user fullscreen override should always show button,
+ // layout should be inflated
+ taskInfo.appCompatTaskInfo.topActivityLetterboxHeight = stableBounds.height();
+ taskInfo.appCompatTaskInfo.topActivityLetterboxWidth = stableBounds.width();
+ taskInfo.appCompatTaskInfo.isUserFullscreenOverrideEnabled = true;
+
+ mWindowManager.updateCompatInfo(taskInfo, mTaskListener, /* canShow= */ true);
+
+ verify(mWindowManager).inflateLayout();
+ }
+
+ @Test
public void testUpdateDisplayLayout() {
final DisplayInfo displayInfo = new DisplayInfo();
displayInfo.logicalWidth = 1000;
diff --git a/libs/hwui/SkiaInterpolator.cpp b/libs/hwui/SkiaInterpolator.cpp
index c67b135855f7..5a45ad9085e7 100644
--- a/libs/hwui/SkiaInterpolator.cpp
+++ b/libs/hwui/SkiaInterpolator.cpp
@@ -20,6 +20,7 @@
#include "include/core/SkTypes.h"
#include <cstdlib>
+#include <cstring>
#include <log/log.h>
typedef int Dot14;
diff --git a/libs/hwui/effects/GainmapRenderer.cpp b/libs/hwui/effects/GainmapRenderer.cpp
index 3ebf7d19202d..0a30c6c14c4c 100644
--- a/libs/hwui/effects/GainmapRenderer.cpp
+++ b/libs/hwui/effects/GainmapRenderer.cpp
@@ -32,6 +32,8 @@
#include "src/core/SkColorFilterPriv.h"
#include "src/core/SkImageInfoPriv.h"
#include "src/core/SkRuntimeEffectPriv.h"
+
+#include <cmath>
#endif
namespace android::uirenderer {
@@ -206,12 +208,12 @@ private:
void setupGenericUniforms(const sk_sp<const SkImage>& gainmapImage,
const SkGainmapInfo& gainmapInfo) {
- const SkColor4f logRatioMin({sk_float_log(gainmapInfo.fGainmapRatioMin.fR),
- sk_float_log(gainmapInfo.fGainmapRatioMin.fG),
- sk_float_log(gainmapInfo.fGainmapRatioMin.fB), 1.f});
- const SkColor4f logRatioMax({sk_float_log(gainmapInfo.fGainmapRatioMax.fR),
- sk_float_log(gainmapInfo.fGainmapRatioMax.fG),
- sk_float_log(gainmapInfo.fGainmapRatioMax.fB), 1.f});
+ const SkColor4f logRatioMin({std::log(gainmapInfo.fGainmapRatioMin.fR),
+ std::log(gainmapInfo.fGainmapRatioMin.fG),
+ std::log(gainmapInfo.fGainmapRatioMin.fB), 1.f});
+ const SkColor4f logRatioMax({std::log(gainmapInfo.fGainmapRatioMax.fR),
+ std::log(gainmapInfo.fGainmapRatioMax.fG),
+ std::log(gainmapInfo.fGainmapRatioMax.fB), 1.f});
const int noGamma = gainmapInfo.fGainmapGamma.fR == 1.f &&
gainmapInfo.fGainmapGamma.fG == 1.f &&
gainmapInfo.fGainmapGamma.fB == 1.f;
@@ -248,10 +250,10 @@ private:
float W = 0.f;
if (targetHdrSdrRatio > mGainmapInfo.fDisplayRatioSdr) {
if (targetHdrSdrRatio < mGainmapInfo.fDisplayRatioHdr) {
- W = (sk_float_log(targetHdrSdrRatio) -
- sk_float_log(mGainmapInfo.fDisplayRatioSdr)) /
- (sk_float_log(mGainmapInfo.fDisplayRatioHdr) -
- sk_float_log(mGainmapInfo.fDisplayRatioSdr));
+ W = (std::log(targetHdrSdrRatio) -
+ std::log(mGainmapInfo.fDisplayRatioSdr)) /
+ (std::log(mGainmapInfo.fDisplayRatioHdr) -
+ std::log(mGainmapInfo.fDisplayRatioSdr));
} else {
W = 1.f;
}
diff --git a/libs/hwui/renderthread/CanvasContext.cpp b/libs/hwui/renderthread/CanvasContext.cpp
index 66e089627a7b..8bb11badb607 100644
--- a/libs/hwui/renderthread/CanvasContext.cpp
+++ b/libs/hwui/renderthread/CanvasContext.cpp
@@ -1010,7 +1010,15 @@ void CanvasContext::destroyHardwareResources() {
}
void CanvasContext::onContextDestroyed() {
- destroyHardwareResources();
+ // We don't want to destroyHardwareResources as that will invalidate display lists which
+ // the client may not be expecting. Instead just purge all scratch resources
+ if (mRenderPipeline->isContextReady()) {
+ freePrefetchedLayers();
+ for (const sp<RenderNode>& node : mRenderNodes) {
+ node->destroyLayers();
+ }
+ mRenderPipeline->onDestroyHardwareResources();
+ }
}
DeferredLayerUpdater* CanvasContext::createTextureLayer() {
diff --git a/libs/hwui/utils/Color.cpp b/libs/hwui/utils/Color.cpp
index f6c57927cc85..6a560b365247 100644
--- a/libs/hwui/utils/Color.cpp
+++ b/libs/hwui/utils/Color.cpp
@@ -403,7 +403,7 @@ skcms_TransferFunction GetPQSkTransferFunction(float sdr_white_level) {
}
static skcms_TransferFunction trfn_apply_gain(const skcms_TransferFunction trfn, float gain) {
- float pow_gain_ginv = sk_float_pow(gain, 1 / trfn.g);
+ float pow_gain_ginv = std::pow(gain, 1 / trfn.g);
skcms_TransferFunction result;
result.g = trfn.g;
result.a = trfn.a * pow_gain_ginv;
diff --git a/native/android/performance_hint.cpp b/native/android/performance_hint.cpp
index 882afcab6290..fbb35e2bc355 100644
--- a/native/android/performance_hint.cpp
+++ b/native/android/performance_hint.cpp
@@ -59,7 +59,8 @@ public:
~APerformanceHintManager() = default;
APerformanceHintSession* createSession(const int32_t* threadIds, size_t size,
- int64_t initialTargetWorkDurationNanos);
+ int64_t initialTargetWorkDurationNanos,
+ hal::SessionTag tag = hal::SessionTag::OTHER);
int64_t getPreferredRateNanos() const;
private:
@@ -84,7 +85,8 @@ struct APerformanceHintSession {
public:
APerformanceHintSession(std::shared_ptr<IHintManager> hintManager,
std::shared_ptr<IHintSession> session, int64_t preferredRateNanos,
- int64_t targetDurationNanos);
+ int64_t targetDurationNanos,
+ std::optional<hal::SessionConfig> sessionConfig);
APerformanceHintSession() = delete;
~APerformanceHintSession();
@@ -116,9 +118,10 @@ private:
// Cached samples
std::vector<hal::WorkDuration> mActualWorkDurations;
std::string mSessionName;
- static int32_t sIDCounter;
+ static int64_t sIDCounter;
// The most recent set of thread IDs
std::vector<int32_t> mLastThreadIDs;
+ std::optional<hal::SessionConfig> mSessionConfig;
// Tracing helpers
void traceThreads(std::vector<int32_t>& tids);
void tracePowerEfficient(bool powerEfficient);
@@ -129,7 +132,8 @@ private:
static std::shared_ptr<IHintManager>* gIHintManagerForTesting = nullptr;
static APerformanceHintManager* gHintManagerForTesting = nullptr;
-int32_t APerformanceHintSession::sIDCounter = 0;
+// Start above the int32 range so we don't collide with config sessions
+int64_t APerformanceHintSession::sIDCounter = INT32_MAX;
// ===================================== APerformanceHintManager implementation
APerformanceHintManager::APerformanceHintManager(std::shared_ptr<IHintManager> manager,
@@ -174,16 +178,20 @@ APerformanceHintManager* APerformanceHintManager::create(std::shared_ptr<IHintMa
}
APerformanceHintSession* APerformanceHintManager::createSession(
- const int32_t* threadIds, size_t size, int64_t initialTargetWorkDurationNanos) {
+ const int32_t* threadIds, size_t size, int64_t initialTargetWorkDurationNanos,
+ hal::SessionTag tag) {
std::vector<int32_t> tids(threadIds, threadIds + size);
std::shared_ptr<IHintSession> session;
- ndk::ScopedAStatus ret =
- mHintManager->createHintSession(mToken, tids, initialTargetWorkDurationNanos, &session);
+ ndk::ScopedAStatus ret;
+ std::optional<hal::SessionConfig> sessionConfig;
+ ret = mHintManager->createHintSessionWithConfig(mToken, tids, initialTargetWorkDurationNanos,
+ tag, &sessionConfig, &session);
+
if (!ret.isOk() || !session) {
return nullptr;
}
auto out = new APerformanceHintSession(mHintManager, std::move(session), mPreferredRateNanos,
- initialTargetWorkDurationNanos);
+ initialTargetWorkDurationNanos, sessionConfig);
out->traceThreads(tids);
out->traceTargetDuration(initialTargetWorkDurationNanos);
out->tracePowerEfficient(false);
@@ -199,19 +207,23 @@ int64_t APerformanceHintManager::getPreferredRateNanos() const {
APerformanceHintSession::APerformanceHintSession(std::shared_ptr<IHintManager> hintManager,
std::shared_ptr<IHintSession> session,
int64_t preferredRateNanos,
- int64_t targetDurationNanos)
+ int64_t targetDurationNanos,
+ std::optional<hal::SessionConfig> sessionConfig)
: mHintManager(hintManager),
mHintSession(std::move(session)),
mPreferredRateNanos(preferredRateNanos),
mTargetDurationNanos(targetDurationNanos),
mFirstTargetMetTimestamp(0),
- mLastTargetMetTimestamp(0) {
- const std::vector<hal::SessionHint> sessionHintRange{ndk::enum_range<hal::SessionHint>()
- .begin(),
- ndk::enum_range<hal::SessionHint>().end()};
-
- mLastHintSentTimestamp = std::vector<int64_t>(sessionHintRange.size(), 0);
- mSessionName = android::base::StringPrintf("ADPF Session %" PRId32, ++sIDCounter);
+ mLastTargetMetTimestamp(0),
+ mSessionConfig(sessionConfig) {
+ if (sessionConfig->id > INT32_MAX) {
+ ALOGE("Session ID too large, must fit 32-bit integer");
+ }
+ constexpr int numEnums =
+ ndk::enum_range<hal::SessionHint>().end() - ndk::enum_range<hal::SessionHint>().begin();
+ mLastHintSentTimestamp = std::vector<int64_t>(numEnums, 0);
+ int64_t traceId = sessionConfig.has_value() ? sessionConfig->id : ++sIDCounter;
+ mSessionName = android::base::StringPrintf("ADPF Session %" PRId64, traceId);
}
APerformanceHintSession::~APerformanceHintSession() {
diff --git a/native/android/tests/performance_hint/PerformanceHintNativeTest.cpp b/native/android/tests/performance_hint/PerformanceHintNativeTest.cpp
index bfbe34e7a8a1..974e6e63b424 100644
--- a/native/android/tests/performance_hint/PerformanceHintNativeTest.cpp
+++ b/native/android/tests/performance_hint/PerformanceHintNativeTest.cpp
@@ -16,6 +16,8 @@
#define LOG_TAG "PerformanceHintNativeTest"
+#include <aidl/android/hardware/power/SessionConfig.h>
+#include <aidl/android/hardware/power/SessionTag.h>
#include <aidl/android/hardware/power/WorkDuration.h>
#include <aidl/android/os/IHintManager.h>
#include <android/binder_manager.h>
@@ -28,6 +30,8 @@
#include <memory>
#include <vector>
+using aidl::android::hardware::power::SessionConfig;
+using aidl::android::hardware::power::SessionTag;
using aidl::android::hardware::power::WorkDuration;
using aidl::android::os::IHintManager;
using aidl::android::os::IHintSession;
@@ -39,8 +43,9 @@ using namespace testing;
class MockIHintManager : public IHintManager {
public:
- MOCK_METHOD(ScopedAStatus, createHintSession,
+ MOCK_METHOD(ScopedAStatus, createHintSessionWithConfig,
(const SpAIBinder& token, const ::std::vector<int32_t>& tids, int64_t durationNanos,
+ SessionTag tag, std::optional<SessionConfig>* config,
std::shared_ptr<IHintSession>* _aidl_return),
(override));
MOCK_METHOD(ScopedAStatus, getHintSessionPreferredRate, (int64_t * _aidl_return), (override));
@@ -92,14 +97,18 @@ public:
APerformanceHintSession* createSession(APerformanceHintManager* manager,
int64_t targetDuration = 56789L) {
mMockSession = ndk::SharedRefBase::make<NiceMock<MockIHintSession>>();
-
+ int64_t sessionId = 123;
std::vector<int32_t> tids;
tids.push_back(1);
tids.push_back(2);
- ON_CALL(*mMockIHintManager, createHintSession(_, Eq(tids), Eq(targetDuration), _))
- .WillByDefault(DoAll(SetArgPointee<3>(std::shared_ptr<IHintSession>(mMockSession)),
+ ON_CALL(*mMockIHintManager,
+ createHintSessionWithConfig(_, Eq(tids), Eq(targetDuration), _, _, _))
+ .WillByDefault(DoAll(SetArgPointee<4>(
+ std::make_optional<SessionConfig>({.id = sessionId})),
+ SetArgPointee<5>(std::shared_ptr<IHintSession>(mMockSession)),
[] { return ScopedAStatus::ok(); }));
+
ON_CALL(*mMockIHintManager, setHintSessionThreads(_, _)).WillByDefault([] {
return ScopedAStatus::ok();
});
@@ -115,7 +124,6 @@ public:
ON_CALL(*mMockSession, reportActualWorkDuration2(_)).WillByDefault([] {
return ScopedAStatus::ok();
});
-
return APerformanceHint_createSession(manager, tids.data(), tids.size(), targetDuration);
}
@@ -178,6 +186,14 @@ TEST_F(PerformanceHintTest, TestSession) {
APerformanceHint_closeSession(session);
}
+TEST_F(PerformanceHintTest, TestUpdatedSessionCreation) {
+ EXPECT_CALL(*mMockIHintManager, createHintSessionWithConfig(_, _, _, _, _, _)).Times(1);
+ APerformanceHintManager* manager = createManager();
+ APerformanceHintSession* session = createSession(manager);
+ ASSERT_TRUE(session);
+ APerformanceHint_closeSession(session);
+}
+
TEST_F(PerformanceHintTest, SetThreads) {
APerformanceHintManager* manager = createManager();
diff --git a/packages/CredentialManager/src/com/android/credentialmanager/autofill/CredentialAutofillService.kt b/packages/CredentialManager/src/com/android/credentialmanager/autofill/CredentialAutofillService.kt
index 4109079e20a5..50ebdd5e3ce7 100644
--- a/packages/CredentialManager/src/com/android/credentialmanager/autofill/CredentialAutofillService.kt
+++ b/packages/CredentialManager/src/com/android/credentialmanager/autofill/CredentialAutofillService.kt
@@ -47,9 +47,9 @@ import android.service.autofill.SaveRequest
import android.service.credentials.CredentialProviderService
import android.util.Log
import android.content.Intent
+import android.os.IBinder
import android.view.autofill.AutofillId
import android.view.autofill.AutofillManager
-import android.view.autofill.IAutoFillManagerClient
import android.widget.RemoteViews
import android.widget.inline.InlinePresentationSpec
import androidx.autofill.inline.v1.InlineSuggestionUi
@@ -95,7 +95,7 @@ class CredentialAutofillService : AutofillService() {
request: FillRequest,
cancellationSignal: CancellationSignal,
callback: FillCallback,
- autofillCallback: IAutoFillManagerClient
+ autofillCallback: IBinder
) {
val context = request.fillContexts
val structure = context[context.size - 1].structure
@@ -160,7 +160,7 @@ class CredentialAutofillService : AutofillService() {
CancellationSignal(),
Executors.newSingleThreadExecutor(),
outcome,
- autofillCallback.asBinder()
+ autofillCallback
)
}
diff --git a/packages/SettingsLib/src/com/android/settingslib/Utils.java b/packages/SettingsLib/src/com/android/settingslib/Utils.java
index 563f02d95f3c..c2506d353d14 100644
--- a/packages/SettingsLib/src/com/android/settingslib/Utils.java
+++ b/packages/SettingsLib/src/com/android/settingslib/Utils.java
@@ -765,7 +765,11 @@ public class Utils {
return false;
}
- final List<UsbPort> usbPortList = context.getSystemService(UsbManager.class).getPorts();
+ final UsbManager usbManager = context.getSystemService(UsbManager.class);
+ if (usbManager == null) {
+ return false;
+ }
+ final List<UsbPort> usbPortList = usbManager.getPorts();
if (usbPortList == null || usbPortList.isEmpty()) {
return false;
}
diff --git a/packages/SettingsLib/src/com/android/settingslib/bluetooth/LocalBluetoothLeBroadcast.java b/packages/SettingsLib/src/com/android/settingslib/bluetooth/LocalBluetoothLeBroadcast.java
index 9df23aa2fe29..a6b1dd3ae578 100644
--- a/packages/SettingsLib/src/com/android/settingslib/bluetooth/LocalBluetoothLeBroadcast.java
+++ b/packages/SettingsLib/src/com/android/settingslib/bluetooth/LocalBluetoothLeBroadcast.java
@@ -292,7 +292,6 @@ public class LocalBluetoothLeBroadcast implements LocalBluetoothProfile {
+ ", sourceId = "
+ sourceId);
}
- updateFallbackActiveDeviceIfNeeded();
}
@Override
@@ -314,7 +313,18 @@ public class LocalBluetoothLeBroadcast implements LocalBluetoothProfile {
public void onSourceAddFailed(
@NonNull BluetoothDevice sink,
@NonNull BluetoothLeBroadcastMetadata source,
- int reason) {}
+ int reason) {
+ if (DEBUG) {
+ Log.d(
+ TAG,
+ "onSourceAddFailed(), sink = "
+ + sink
+ + ", reason = "
+ + reason
+ + ", source = "
+ + source);
+ }
+ }
@Override
public void onSourceModified(
@@ -369,6 +379,9 @@ public class LocalBluetoothLeBroadcast implements LocalBluetoothProfile {
+ ", state = "
+ state);
}
+ if (BluetoothUtils.isConnected(state)) {
+ updateFallbackActiveDeviceIfNeeded();
+ }
}
};
@@ -1056,7 +1069,9 @@ public class LocalBluetoothLeBroadcast implements LocalBluetoothProfile {
List<BluetoothLeBroadcastReceiveState> sourceList =
mServiceBroadcastAssistant.getAllSources(
bluetoothDevice);
- return !sourceList.isEmpty();
+ return !sourceList.isEmpty()
+ && sourceList.stream()
+ .anyMatch(BluetoothUtils::isConnected);
})
.collect(Collectors.toList());
if (devicesInSharing.isEmpty()) {
@@ -1091,7 +1106,8 @@ public class LocalBluetoothLeBroadcast implements LocalBluetoothProfile {
return;
}
int fallbackActiveGroupId = getFallbackActiveGroupId();
- if (getGroupId(targetCachedDevice) == fallbackActiveGroupId) {
+ if (fallbackActiveGroupId != BluetoothCsipSetCoordinator.GROUP_ID_INVALID
+ && getGroupId(targetCachedDevice) == fallbackActiveGroupId) {
Log.d(
TAG,
"Skip updateFallbackActiveDeviceIfNeeded, already is fallback: "
@@ -1101,12 +1117,6 @@ public class LocalBluetoothLeBroadcast implements LocalBluetoothProfile {
targetCachedDevice.setActive();
}
- private boolean isDecryptedSource(BluetoothLeBroadcastReceiveState state) {
- return state.getPaSyncState() == BluetoothLeBroadcastReceiveState.PA_SYNC_STATE_SYNCHRONIZED
- && state.getBigEncryptionState()
- == BluetoothLeBroadcastReceiveState.BIG_ENCRYPTION_STATE_DECRYPTING;
- }
-
private int getFallbackActiveGroupId() {
return Settings.Secure.getInt(
mContext.getContentResolver(),
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/media/controls/ui/composable/MediaCarousel.kt b/packages/SystemUI/compose/features/src/com/android/systemui/media/controls/ui/composable/MediaCarousel.kt
index d3e4553be209..f0d356c889a9 100644
--- a/packages/SystemUI/compose/features/src/com/android/systemui/media/controls/ui/composable/MediaCarousel.kt
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/media/controls/ui/composable/MediaCarousel.kt
@@ -53,6 +53,11 @@ fun SceneScope.MediaCarousel(
val mediaFrame = carouselController.mediaFrame
(mediaFrame.parent as? ViewGroup)?.removeView(mediaFrame)
addView(mediaFrame)
+ layoutParams =
+ FrameLayout.LayoutParams(
+ FrameLayout.LayoutParams.MATCH_PARENT,
+ FrameLayout.LayoutParams.MATCH_PARENT,
+ )
}
},
update = {
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/scene/ui/composable/SceneContainer.kt b/packages/SystemUI/compose/features/src/com/android/systemui/scene/ui/composable/SceneContainer.kt
index fe6701cc8d89..7af9b7bb90e9 100644
--- a/packages/SystemUI/compose/features/src/com/android/systemui/scene/ui/composable/SceneContainer.kt
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/scene/ui/composable/SceneContainer.kt
@@ -37,8 +37,6 @@ import androidx.compose.ui.input.pointer.pointerInput
import com.android.compose.animation.scene.MutableSceneTransitionLayoutState
import com.android.compose.animation.scene.SceneKey
import com.android.compose.animation.scene.SceneTransitionLayout
-import com.android.compose.animation.scene.UserAction
-import com.android.compose.animation.scene.UserActionResult
import com.android.compose.animation.scene.observableTransitionState
import com.android.systemui.ribbon.ui.composable.BottomRightCornerRibbon
import com.android.systemui.scene.shared.model.SceneDataSourceDelegator
@@ -71,9 +69,7 @@ fun SceneContainer(
) {
val coroutineScope = rememberCoroutineScope()
val currentSceneKey: SceneKey by viewModel.currentScene.collectAsState()
- val currentScene = checkNotNull(sceneByKey[currentSceneKey])
- val currentDestinations: Map<UserAction, UserActionResult> by
- currentScene.destinationScenes.collectAsState()
+ val currentDestinations by viewModel.currentDestinationScenes(coroutineScope).collectAsState()
val state: MutableSceneTransitionLayoutState = remember {
MutableSceneTransitionLayoutState(
initialScene = currentSceneKey,
diff --git a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/Element.kt b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/Element.kt
index a8a1d881b907..f4009ee56737 100644
--- a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/Element.kt
+++ b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/Element.kt
@@ -300,14 +300,16 @@ internal fun shouldDrawOrComposeSharedElement(
val fromScene = transition.fromScene
val toScene = transition.toScene
- val chosenByPicker =
+ val pickedScene =
scenePicker.sceneDuringTransition(
element = element,
transition = transition,
fromSceneZIndex = layoutImpl.scenes.getValue(fromScene).zIndex,
toSceneZIndex = layoutImpl.scenes.getValue(toScene).zIndex,
- ) == scene
- return chosenByPicker || transition.currentOverscrollSpec?.scene == scene
+ )
+ ?: return false
+
+ return pickedScene == scene || transition.currentOverscrollSpec?.scene == scene
}
private fun isSharedElementEnabled(
@@ -356,7 +358,9 @@ private fun isElementOpaque(
val toState = element.sceneStates[toScene]
if (fromState == null && toState == null) {
- error("This should not happen, element $element is neither in $fromScene or $toScene")
+ // TODO(b/311600838): Throw an exception instead once layers of disposed elements are not
+ // run anymore.
+ return true
}
val isSharedElement = fromState != null && toState != null
diff --git a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/Scene.kt b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/Scene.kt
index 418c6bb9af70..7fb5a4d0cc27 100644
--- a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/Scene.kt
+++ b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/Scene.kt
@@ -59,7 +59,10 @@ internal class Scene(
): Map<UserAction, UserActionResult> {
userActions.forEach { (action, result) ->
if (key == result.toScene) {
- error("Transition to the same scene is not supported. Scene $key, action $action")
+ error(
+ "Transition to the same scene is not supported. Scene $key, action $action," +
+ " result $result"
+ )
}
}
return userActions
diff --git a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/TransitionDsl.kt b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/TransitionDsl.kt
index 2c109a337f65..6bc397e86cfa 100644
--- a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/TransitionDsl.kt
+++ b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/TransitionDsl.kt
@@ -205,7 +205,9 @@ interface OverscrollScope {
interface ElementScenePicker {
/**
* Return the scene in which [element] should be drawn (when using `Modifier.element(key)`) or
- * composed (when using `MovableElement(key)`) during the given [transition].
+ * composed (when using `MovableElement(key)`) during the given [transition]. If this element
+ * should not be drawn or composed in neither [transition.fromScene] nor [transition.toScene],
+ * return `null`.
*
* Important: For [MovableElements][SceneScope.MovableElement], this scene picker will *always*
* be used during transitions to decide whether we should compose that element in a given scene
@@ -217,12 +219,13 @@ interface ElementScenePicker {
transition: TransitionState.Transition,
fromSceneZIndex: Float,
toSceneZIndex: Float,
- ): SceneKey
+ ): SceneKey?
/**
* Return [transition.fromScene] if it is in [scenes] and [transition.toScene] is not, or return
- * [transition.toScene] if it is in [scenes] and [transition.fromScene] is not, otherwise throw
- * an exception (i.e. if neither or both of fromScene and toScene are in [scenes]).
+ * [transition.toScene] if it is in [scenes] and [transition.fromScene] is not. If neither
+ * [transition.fromScene] and [transition.toScene] are in [scenes], return `null`. If both
+ * [transition.fromScene] and [transition.toScene] are in [scenes], throw an exception.
*
* This function can be useful when computing the scene in which a movable element should be
* composed.
@@ -231,31 +234,22 @@ interface ElementScenePicker {
scenes: Set<SceneKey>,
transition: TransitionState.Transition,
element: ElementKey,
- ): SceneKey {
+ ): SceneKey? {
val fromScene = transition.fromScene
val toScene = transition.toScene
val fromSceneInScenes = scenes.contains(fromScene)
val toSceneInScenes = scenes.contains(toScene)
- if (fromSceneInScenes && toSceneInScenes) {
- error(
- "Element $element can be in both $fromScene and $toScene. You should add a " +
- "special case for this transition before calling pickSingleSceneIn()."
- )
- }
- if (!fromSceneInScenes && !toSceneInScenes) {
- error(
- "Element $element can be neither in $fromScene and $toScene. This either means " +
- "that you should add one of them in the scenes set passed to " +
- "pickSingleSceneIn(), or there is an internal error and this element was " +
- "composed when it shouldn't be."
- )
- }
-
- return if (fromSceneInScenes) {
- fromScene
- } else {
- toScene
+ return when {
+ fromSceneInScenes && toSceneInScenes -> {
+ error(
+ "Element $element can be in both $fromScene and $toScene. You should add a " +
+ "special case for this transition before calling pickSingleSceneIn()."
+ )
+ }
+ fromSceneInScenes -> fromScene
+ toSceneInScenes -> toScene
+ else -> null
}
}
}
@@ -312,8 +306,12 @@ class MovableElementScenePicker(private val scenes: Set<SceneKey>) : ElementScen
transition: TransitionState.Transition,
fromSceneZIndex: Float,
toSceneZIndex: Float,
- ): SceneKey {
- return if (scenes.contains(transition.toScene)) transition.toScene else transition.fromScene
+ ): SceneKey? {
+ return when {
+ scenes.contains(transition.toScene) -> transition.toScene
+ scenes.contains(transition.fromScene) -> transition.fromScene
+ else -> null
+ }
}
}
diff --git a/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/MovableElementScenePickerTest.kt b/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/MovableElementScenePickerTest.kt
index fb46a34e3cab..6745fbe064ff 100644
--- a/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/MovableElementScenePickerTest.kt
+++ b/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/MovableElementScenePickerTest.kt
@@ -38,8 +38,8 @@ class MovableElementScenePickerTest {
}
@Test
- fun toSceneNotInScenes() {
- val picker = MovableElementScenePicker(scenes = emptySet())
+ fun fromSceneInScenes() {
+ val picker = MovableElementScenePicker(scenes = setOf(TestScenes.SceneA))
assertThat(
picker.sceneDuringTransition(
TestElements.Foo,
@@ -50,4 +50,18 @@ class MovableElementScenePickerTest {
)
.isEqualTo(TestScenes.SceneA)
}
+
+ @Test
+ fun noneInScenes() {
+ val picker = MovableElementScenePicker(scenes = emptySet())
+ assertThat(
+ picker.sceneDuringTransition(
+ TestElements.Foo,
+ transition(from = TestScenes.SceneA, to = TestScenes.SceneB),
+ fromSceneZIndex = 0f,
+ toSceneZIndex = 1f,
+ )
+ )
+ .isEqualTo(null)
+ }
}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/ui/viewmodel/QuickSettingsSceneViewModelTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/ui/viewmodel/QuickSettingsSceneViewModelTest.kt
index 3727c113a58e..ab1f458b2f5b 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/ui/viewmodel/QuickSettingsSceneViewModelTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/ui/viewmodel/QuickSettingsSceneViewModelTest.kt
@@ -106,7 +106,7 @@ class QuickSettingsSceneViewModelTest : SysuiTestCase() {
val destinations by collectLastValue(underTest.destinationScenes)
val currentScene by collectLastValue(sceneInteractor.currentScene)
- val previousScene by collectLastValue(sceneInteractor.previousScene)
+ val previousScene by collectLastValue(sceneInteractor.previousScene())
sceneInteractor.changeScene(Scenes.Lockscreen, "reason")
sceneInteractor.changeScene(Scenes.QuickSettings, "reason")
assertThat(currentScene).isEqualTo(Scenes.QuickSettings)
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/scene/SceneFrameworkIntegrationTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/scene/SceneFrameworkIntegrationTest.kt
index 65fd1010ec38..a8890078a6f3 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/scene/SceneFrameworkIntegrationTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/scene/SceneFrameworkIntegrationTest.kt
@@ -150,6 +150,7 @@ class SceneFrameworkIntegrationTest : SysuiTestCase() {
sceneInteractor = sceneInteractor,
falsingInteractor = kosmos.falsingInteractor,
powerInteractor = kosmos.powerInteractor,
+ scenes = kosmos.scenes,
)
.apply { setTransitionState(transitionState) }
}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/scene/domain/interactor/SceneInteractorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/scene/domain/interactor/SceneInteractorTest.kt
index 63f481695232..871ce6d56e97 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/scene/domain/interactor/SceneInteractorTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/scene/domain/interactor/SceneInteractorTest.kt
@@ -296,7 +296,7 @@ class SceneInteractorTest : SysuiTestCase() {
fun previousScene() =
testScope.runTest {
val currentScene by collectLastValue(underTest.currentScene)
- val previousScene by collectLastValue(underTest.previousScene)
+ val previousScene by collectLastValue(underTest.previousScene())
assertThat(previousScene).isNull()
val firstScene = currentScene
@@ -306,4 +306,19 @@ class SceneInteractorTest : SysuiTestCase() {
underTest.changeScene(toScene = Scenes.QuickSettings, "reason")
assertThat(previousScene).isEqualTo(Scenes.Shade)
}
+
+ @Test
+ fun previousScene_withIgnoredScene() =
+ testScope.runTest {
+ val currentScene by collectLastValue(underTest.currentScene)
+ val previousScene by collectLastValue(underTest.previousScene(ignored = Scenes.Shade))
+ assertThat(previousScene).isNull()
+
+ val firstScene = currentScene
+ underTest.changeScene(toScene = Scenes.Shade, "reason")
+ assertThat(previousScene).isEqualTo(firstScene)
+
+ underTest.changeScene(toScene = Scenes.QuickSettings, "reason")
+ assertThat(previousScene).isNull()
+ }
}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/scene/ui/viewmodel/SceneContainerViewModelTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/scene/ui/viewmodel/SceneContainerViewModelTest.kt
index ea95aab4a1c4..5c30379ea2fb 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/scene/ui/viewmodel/SceneContainerViewModelTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/scene/ui/viewmodel/SceneContainerViewModelTest.kt
@@ -28,8 +28,10 @@ import com.android.systemui.kosmos.testScope
import com.android.systemui.power.data.repository.fakePowerRepository
import com.android.systemui.power.domain.interactor.powerInteractor
import com.android.systemui.scene.domain.interactor.sceneInteractor
+import com.android.systemui.scene.fakeScenes
import com.android.systemui.scene.sceneContainerConfig
import com.android.systemui.scene.sceneKeys
+import com.android.systemui.scene.scenes
import com.android.systemui.scene.shared.model.Scenes
import com.android.systemui.scene.shared.model.fakeSceneDataSource
import com.android.systemui.testKosmos
@@ -64,6 +66,7 @@ class SceneContainerViewModelTest : SysuiTestCase() {
sceneInteractor = sceneInteractor,
falsingInteractor = kosmos.falsingInteractor,
powerInteractor = kosmos.powerInteractor,
+ scenes = kosmos.scenes,
)
}
@@ -214,4 +217,23 @@ class SceneContainerViewModelTest : SysuiTestCase() {
assertThat(isVisible).isFalse()
}
+
+ @Test
+ fun currentDestinationScenes_onlyTheCurrentSceneIsCollected() =
+ testScope.runTest {
+ val unused by collectLastValue(underTest.currentDestinationScenes(backgroundScope))
+ val currentScene by collectLastValue(sceneInteractor.currentScene)
+ kosmos.fakeScenes.forEach { scene ->
+ fakeSceneDataSource.changeScene(toScene = scene.key)
+ runCurrent()
+ assertThat(currentScene).isEqualTo(scene.key)
+
+ assertThat(scene.isDestinationScenesBeingCollected).isTrue()
+ kosmos.fakeScenes
+ .filter { it.key != scene.key }
+ .forEach { otherScene ->
+ assertThat(otherScene.isDestinationScenesBeingCollected).isFalse()
+ }
+ }
+ }
}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/shade/domain/interactor/ShadeBackActionInteractorImplTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/shade/domain/interactor/ShadeBackActionInteractorImplTest.kt
index 26f342aa05ce..468c39daa282 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/shade/domain/interactor/ShadeBackActionInteractorImplTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/shade/domain/interactor/ShadeBackActionInteractorImplTest.kt
@@ -22,7 +22,12 @@ import androidx.test.filters.SmallTest
import com.android.compose.animation.scene.ObservableTransitionState
import com.android.compose.animation.scene.SceneKey
import com.android.systemui.SysuiTestCase
+import com.android.systemui.authentication.data.repository.fakeAuthenticationRepository
+import com.android.systemui.authentication.shared.model.AuthenticationMethodModel
+import com.android.systemui.coroutines.collectLastValue
import com.android.systemui.flags.EnableSceneContainer
+import com.android.systemui.keyguard.data.repository.deviceEntryFingerprintAuthRepository
+import com.android.systemui.keyguard.shared.model.SuccessFingerprintAuthenticationStatus
import com.android.systemui.kosmos.testScope
import com.android.systemui.scene.domain.interactor.sceneInteractor
import com.android.systemui.scene.shared.model.Scenes
@@ -31,77 +36,93 @@ import com.android.systemui.testKosmos
import com.google.common.truth.Truth.assertThat
import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.flow.MutableStateFlow
+import kotlinx.coroutines.test.TestScope
import kotlinx.coroutines.test.runCurrent
import kotlinx.coroutines.test.runTest
import org.junit.Assume
import org.junit.Before
-import org.junit.Ignore
import org.junit.Test
import org.junit.runner.RunWith
@OptIn(ExperimentalCoroutinesApi::class)
@SmallTest
@RunWith(AndroidJUnit4::class)
-@Ignore("b/328827631")
@EnableSceneContainer
class ShadeBackActionInteractorImplTest : SysuiTestCase() {
val kosmos = testKosmos()
val testScope = kosmos.testScope
- val sceneInteractor = kosmos.sceneInteractor
- val underTest = kosmos.shadeBackActionInteractor
+ val sceneInteractor by lazy { kosmos.sceneInteractor }
+ val shadeInteractor by lazy { kosmos.shadeInteractor }
+ val fakeAuthenticationRepository by lazy { kosmos.fakeAuthenticationRepository }
+ val deviceEntryFingerprintAuthRepository by lazy { kosmos.deviceEntryFingerprintAuthRepository }
+
+ lateinit var underTest: ShadeBackActionInteractor
@Before
- fun ignoreSplitShade() {
+ fun ignoreSplitShadeAndSetup() {
Assume.assumeFalse(Utilities.isLargeScreen(kosmos.applicationContext))
+ underTest = kosmos.shadeBackActionInteractor
}
@Test
fun animateCollapseQs_notOnQs() =
testScope.runTest {
+ val actual by collectLastValue(sceneInteractor.currentScene)
setScene(Scenes.Shade)
underTest.animateCollapseQs(true)
runCurrent()
- assertThat(sceneInteractor.currentScene.value).isEqualTo(Scenes.Shade)
+ assertThat(actual).isEqualTo(Scenes.Shade)
}
@Test
fun animateCollapseQs_fullyCollapse_entered() =
testScope.runTest {
+ val actual by collectLastValue(sceneInteractor.currentScene)
enterDevice()
setScene(Scenes.QuickSettings)
underTest.animateCollapseQs(true)
runCurrent()
- assertThat(sceneInteractor.currentScene.value).isEqualTo(Scenes.Gone)
+ assertThat(actual).isEqualTo(Scenes.Gone)
}
@Test
fun animateCollapseQs_fullyCollapse_locked() =
testScope.runTest {
+ val actual by collectLastValue(sceneInteractor.currentScene)
setScene(Scenes.QuickSettings)
underTest.animateCollapseQs(true)
runCurrent()
- assertThat(sceneInteractor.currentScene.value).isEqualTo(Scenes.Lockscreen)
+ assertThat(actual).isEqualTo(Scenes.Lockscreen)
}
@Test
fun animateCollapseQs_notFullyCollapse() =
testScope.runTest {
+ val actual by collectLastValue(sceneInteractor.currentScene)
setScene(Scenes.QuickSettings)
underTest.animateCollapseQs(false)
runCurrent()
- assertThat(sceneInteractor.currentScene.value).isEqualTo(Scenes.Shade)
+ assertThat(actual).isEqualTo(Scenes.Shade)
}
- private fun enterDevice() {
- testScope.runCurrent()
+ private fun TestScope.enterDevice() {
+ // configure device unlocked state
+ fakeAuthenticationRepository.setAuthenticationMethod(AuthenticationMethodModel.Pin)
+ runCurrent()
+ deviceEntryFingerprintAuthRepository.setAuthenticationStatus(
+ SuccessFingerprintAuthenticationStatus(0, true)
+ )
+ runCurrent()
setScene(Scenes.Gone)
}
- private fun setScene(key: SceneKey) {
+ private fun TestScope.setScene(key: SceneKey) {
+ val actual by collectLastValue(sceneInteractor.currentScene)
sceneInteractor.changeScene(key, "test")
sceneInteractor.setTransitionState(
MutableStateFlow<ObservableTransitionState>(ObservableTransitionState.Idle(key))
)
- testScope.runCurrent()
+ runCurrent()
+ assertThat(actual).isEqualTo(key)
}
}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/util/kotlin/BooleanFlowOperatorsTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/util/kotlin/BooleanFlowOperatorsTest.kt
index 96d1c0de9b66..03a39f8f07d8 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/util/kotlin/BooleanFlowOperatorsTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/util/kotlin/BooleanFlowOperatorsTest.kt
@@ -20,6 +20,7 @@ import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
import com.android.systemui.SysuiTestCase
import com.android.systemui.coroutines.collectLastValue
+import com.android.systemui.coroutines.collectValues
import com.android.systemui.kosmos.testScope
import com.android.systemui.testKosmos
import com.android.systemui.util.kotlin.BooleanFlowOperators.and
@@ -27,6 +28,7 @@ import com.android.systemui.util.kotlin.BooleanFlowOperators.not
import com.android.systemui.util.kotlin.BooleanFlowOperators.or
import com.google.common.truth.Truth.assertThat
import kotlinx.coroutines.flow.Flow
+import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.flowOf
import kotlinx.coroutines.test.runTest
import org.junit.Test
@@ -62,6 +64,21 @@ class BooleanFlowOperatorsTest : SysuiTestCase() {
}
@Test
+ fun and_onlyEmitsWhenValueChanges() =
+ testScope.runTest {
+ val flow1 = MutableStateFlow(false)
+ val flow2 = MutableStateFlow(false)
+ val values by collectValues(and(flow1, flow2))
+
+ assertThat(values).containsExactly(false)
+ flow1.value = true
+ // Overall value is still false, we should not have emitted again.
+ assertThat(values).containsExactly(false)
+ flow2.value = true
+ assertThat(values).containsExactly(false, true).inOrder()
+ }
+
+ @Test
fun or_allTrue_returnsTrue() =
testScope.runTest {
val result by collectLastValue(or(TRUE, TRUE))
@@ -83,6 +100,20 @@ class BooleanFlowOperatorsTest : SysuiTestCase() {
}
@Test
+ fun or_onlyEmitsWhenValueChanges() =
+ testScope.runTest {
+ val flow1 = MutableStateFlow(false)
+ val flow2 = MutableStateFlow(false)
+ val values by collectValues(or(flow1, flow2))
+
+ assertThat(values).containsExactly(false)
+ flow1.value = true
+ assertThat(values).containsExactly(false, true).inOrder()
+ flow2.value = true
+ assertThat(values).containsExactly(false, true).inOrder()
+ }
+
+ @Test
fun not_true_returnsFalse() =
testScope.runTest {
val result by collectLastValue(not(TRUE))
diff --git a/packages/SystemUI/res/drawable/notification_material_bg.xml b/packages/SystemUI/res/drawable/notification_material_bg.xml
index 587a5a05458f..715be074eaa8 100644
--- a/packages/SystemUI/res/drawable/notification_material_bg.xml
+++ b/packages/SystemUI/res/drawable/notification_material_bg.xml
@@ -28,9 +28,10 @@
<solid android:color="@color/notification_state_color_default" />
</shape>
</item>
- <item>
+ <item android:id="@+id/notification_focus_overlay">
<shape>
- <stroke android:width="3dp" android:color="@color/notification_focus_overlay_color"/>
+ <stroke android:width="@dimen/notification_focus_stroke_width"
+ android:color="@color/notification_focus_overlay_color"/>
</shape>
</item>
</layer-list> \ No newline at end of file
diff --git a/packages/SystemUI/res/layout/media_carousel.xml b/packages/SystemUI/res/layout/media_carousel.xml
index 825ece856ed2..ffe269abe08f 100644
--- a/packages/SystemUI/res/layout/media_carousel.xml
+++ b/packages/SystemUI/res/layout/media_carousel.xml
@@ -19,7 +19,7 @@
<FrameLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
- android:layout_height="wrap_content"
+ android:layout_height="match_parent"
android:clipChildren="false"
android:clipToPadding="false"
android:forceHasOverlappingRendering="false"
@@ -27,7 +27,7 @@
<com.android.systemui.media.controls.ui.view.MediaScrollView
android:id="@+id/media_carousel_scroller"
android:layout_width="match_parent"
- android:layout_height="wrap_content"
+ android:layout_height="match_parent"
android:scrollbars="none"
android:clipChildren="false"
android:clipToPadding="false"
diff --git a/packages/SystemUI/res/values/dimens.xml b/packages/SystemUI/res/values/dimens.xml
index 82395e48de20..df57f2a27761 100644
--- a/packages/SystemUI/res/values/dimens.xml
+++ b/packages/SystemUI/res/values/dimens.xml
@@ -310,6 +310,9 @@
<!-- Radius for notifications corners without adjacent notifications -->
<dimen name="notification_corner_radius">28dp</dimen>
+ <!-- Stroke width for notifications focus state overlay, see id/notification_focus_outline -->
+ <dimen name="notification_focus_stroke_width">3dp</dimen>
+
<!-- Distance over which notification corner animations run, near the shelf while scrolling. -->
<dimen name="notification_corner_animation_distance">48dp</dimen>
diff --git a/packages/SystemUI/src/com/android/systemui/SystemUIApplication.java b/packages/SystemUI/src/com/android/systemui/SystemUIApplication.java
index fb88f0e4e093..ba869bd56f31 100644
--- a/packages/SystemUI/src/com/android/systemui/SystemUIApplication.java
+++ b/packages/SystemUI/src/com/android/systemui/SystemUIApplication.java
@@ -204,7 +204,7 @@ public class SystemUIApplication extends Application implements
*/
public void startSystemUserServicesIfNeeded() {
- if (!mProcessWrapper.isSystemUser()) {
+ if (!shouldStartSystemUserServices()) {
Log.wtf(TAG, "Tried starting SystemUser services on non-SystemUser");
return; // Per-user startables are handled in #startSystemUserServicesIfNeeded.
}
@@ -227,7 +227,7 @@ public class SystemUIApplication extends Application implements
* <p>This method must only be called from the main thread.</p>
*/
void startSecondaryUserServicesIfNeeded() {
- if (mProcessWrapper.isSystemUser()) {
+ if (!shouldStartSecondaryUserServices()) {
return; // Per-user startables are handled in #startSystemUserServicesIfNeeded.
}
// Sort the startables so that we get a deterministic ordering.
@@ -238,6 +238,14 @@ public class SystemUIApplication extends Application implements
sortedStartables, "StartSecondaryServices", null);
}
+ protected boolean shouldStartSystemUserServices() {
+ return mProcessWrapper.isSystemUser();
+ }
+
+ protected boolean shouldStartSecondaryUserServices() {
+ return !mProcessWrapper.isSystemUser();
+ }
+
private void startServicesIfNeeded(
Map<Class<?>, Provider<CoreStartable>> startables,
String metricsPrefix,
diff --git a/packages/SystemUI/src/com/android/systemui/bouncer/domain/interactor/BouncerInteractor.kt b/packages/SystemUI/src/com/android/systemui/bouncer/domain/interactor/BouncerInteractor.kt
index dd71bc782c1c..cb458efbdf69 100644
--- a/packages/SystemUI/src/com/android/systemui/bouncer/domain/interactor/BouncerInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/bouncer/domain/interactor/BouncerInteractor.kt
@@ -95,7 +95,7 @@ constructor(
/** The scene to show when bouncer is dismissed. */
val dismissDestination: Flow<SceneKey> =
- sceneInteractor.previousScene.map { it ?: Scenes.Lockscreen }
+ sceneInteractor.previousScene(Scenes.Bouncer).map { it ?: Scenes.Lockscreen }
/** Notifies that the user has places down a pointer, not necessarily dragging just yet. */
fun onDown() {
diff --git a/packages/SystemUI/src/com/android/systemui/communal/ui/viewmodel/BaseCommunalViewModel.kt b/packages/SystemUI/src/com/android/systemui/communal/ui/viewmodel/BaseCommunalViewModel.kt
index 71d719df45ff..4ac43bcc4abd 100644
--- a/packages/SystemUI/src/com/android/systemui/communal/ui/viewmodel/BaseCommunalViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/communal/ui/viewmodel/BaseCommunalViewModel.kt
@@ -20,6 +20,7 @@ import android.content.ComponentName
import android.os.UserHandle
import com.android.compose.animation.scene.ObservableTransitionState
import com.android.compose.animation.scene.SceneKey
+import com.android.compose.animation.scene.TransitionKey
import com.android.systemui.communal.domain.interactor.CommunalInteractor
import com.android.systemui.communal.domain.model.CommunalContentModel
import com.android.systemui.communal.widgets.WidgetConfigurator
@@ -52,8 +53,8 @@ abstract class BaseCommunalViewModel(
communalInteractor.signalUserInteraction()
}
- fun changeScene(scene: SceneKey) {
- communalInteractor.changeScene(scene)
+ fun changeScene(scene: SceneKey, transitionKey: TransitionKey? = null) {
+ communalInteractor.changeScene(scene, transitionKey)
}
/**
diff --git a/packages/SystemUI/src/com/android/systemui/communal/widgets/EditWidgetsActivity.kt b/packages/SystemUI/src/com/android/systemui/communal/widgets/EditWidgetsActivity.kt
index 5f4b394a05b9..f20fafccfd19 100644
--- a/packages/SystemUI/src/com/android/systemui/communal/widgets/EditWidgetsActivity.kt
+++ b/packages/SystemUI/src/com/android/systemui/communal/widgets/EditWidgetsActivity.kt
@@ -36,6 +36,7 @@ import com.android.compose.theme.PlatformTheme
import com.android.internal.logging.UiEventLogger
import com.android.systemui.communal.shared.log.CommunalUiEvent
import com.android.systemui.communal.shared.model.CommunalScenes
+import com.android.systemui.communal.shared.model.CommunalTransitionKeys
import com.android.systemui.communal.ui.compose.CommunalHub
import com.android.systemui.communal.ui.viewmodel.CommunalEditModeViewModel
import com.android.systemui.communal.util.WidgetPickerIntentUtils.getWidgetExtraFromIntent
@@ -149,7 +150,10 @@ constructor(
private fun onEditDone() {
try {
- communalViewModel.changeScene(CommunalScenes.Communal)
+ communalViewModel.changeScene(
+ CommunalScenes.Communal,
+ CommunalTransitionKeys.SimpleFade
+ )
checkNotNull(windowManagerService).lockNow(/* options */ null)
finish()
} catch (e: RemoteException) {
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromDreamingTransitionInteractor.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromDreamingTransitionInteractor.kt
index b8ceab3f0a38..2c869bf8255e 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromDreamingTransitionInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromDreamingTransitionInteractor.kt
@@ -65,6 +65,7 @@ constructor(
) {
override fun start() {
+ listenForDreamingToAlternateBouncer()
listenForDreamingToOccluded()
listenForDreamingToGoneWhenDismissable()
listenForDreamingToGoneFromBiometricUnlock()
@@ -75,6 +76,16 @@ constructor(
listenForDreamingToPrimaryBouncer()
}
+ private fun listenForDreamingToAlternateBouncer() {
+ scope.launch("$TAG#listenForDreamingToAlternateBouncer") {
+ keyguardInteractor.alternateBouncerShowing
+ .filterRelevantKeyguardStateAnd { isAlternateBouncerShowing ->
+ isAlternateBouncerShowing
+ }
+ .collect { startTransitionTo(KeyguardState.ALTERNATE_BOUNCER) }
+ }
+ }
+
private fun listenForDreamingToGlanceableHub() {
if (!communalHub()) return
scope.launch("$TAG#listenForDreamingToGlanceableHub", mainDispatcher) {
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/TransitionInteractor.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/TransitionInteractor.kt
index e456a550cad7..2850165b0d1a 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/TransitionInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/TransitionInteractor.kt
@@ -224,20 +224,12 @@ sealed class TransitionInteractor(
) {
if (!KeyguardWmStateRefactor.isEnabled) {
scope.launch {
- keyguardInteractor.onCameraLaunchDetected
- .sample(transitionInteractor.finishedKeyguardState)
- .collect { finishedKeyguardState ->
- // Other keyguard state transitions may trigger on the first power button
- // push,
- // so use the last finishedKeyguardState to determine the overriding FROM
- // state
- if (finishedKeyguardState == fromState) {
- startTransitionTo(
- toState = KeyguardState.OCCLUDED,
- modeOnCanceled = TransitionModeOnCanceled.RESET,
- )
- }
- }
+ keyguardInteractor.onCameraLaunchDetected.filterRelevantKeyguardState().collect {
+ startTransitionTo(
+ toState = KeyguardState.OCCLUDED,
+ modeOnCanceled = TransitionModeOnCanceled.RESET,
+ )
+ }
}
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/media/controls/ui/controller/MediaCarouselController.kt b/packages/SystemUI/src/com/android/systemui/media/controls/ui/controller/MediaCarouselController.kt
index d15d45a477d7..eb716d4c6eef 100644
--- a/packages/SystemUI/src/com/android/systemui/media/controls/ui/controller/MediaCarouselController.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/controls/ui/controller/MediaCarouselController.kt
@@ -656,8 +656,14 @@ constructor(
if (width == widthInSceneContainerPx && height == heightInSceneContainerPx) {
return
}
+ if (width <= 0 || height <= 0) {
+ // reject as invalid
+ return
+ }
widthInSceneContainerPx = width
heightInSceneContainerPx = height
+ mediaCarouselScrollHandler.playerWidthPlusPadding =
+ width + context.resources.getDimensionPixelSize(R.dimen.qs_media_padding)
updatePlayers(recreateMedia = true)
}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/ui/viewmodel/QuickSettingsSceneViewModel.kt b/packages/SystemUI/src/com/android/systemui/qs/ui/viewmodel/QuickSettingsSceneViewModel.kt
index ab0b0b7b7c6c..2f6919f2fb12 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/ui/viewmodel/QuickSettingsSceneViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/ui/viewmodel/QuickSettingsSceneViewModel.kt
@@ -56,7 +56,7 @@ constructor(
// TODO(b/330200163) Add an Up from Bottom to be able to collapse the shade
// while customizing
} else {
- sceneInteractor.previousScene.map { previousScene ->
+ sceneInteractor.previousScene(ignored = Scenes.QuickSettings).map { previousScene ->
mapOf(
Back to UserActionResult(previousScene ?: Scenes.Shade),
Swipe(SwipeDirection.Up) to UserActionResult(previousScene ?: Scenes.Shade),
diff --git a/packages/SystemUI/src/com/android/systemui/scene/domain/interactor/SceneInteractor.kt b/packages/SystemUI/src/com/android/systemui/scene/domain/interactor/SceneInteractor.kt
index 02394552e8f9..8ced22223527 100644
--- a/packages/SystemUI/src/com/android/systemui/scene/domain/interactor/SceneInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/scene/domain/interactor/SceneInteractor.kt
@@ -140,12 +140,31 @@ constructor(
)
/**
- * The previous scene.
+ * The previous scene (or `null` if the previous scene is the [ignored] scene).
*
* This is effectively the previous value of [currentScene] which means that all caveats, for
* example regarding when in a transition the current scene changes, apply.
+ *
+ * @param ignored If the previous scene is the same as [ignored], `null` is emitted. This is
+ * designed to reduce the chances of a scene using [previousScene] naively to then set up a
+ * user action that ends up leading to itself, which is an illegal operation that would cause
+ * a crash.
*/
- val previousScene: StateFlow<SceneKey?> = repository.previousScene
+ fun previousScene(
+ ignored: SceneKey? = null,
+ ): StateFlow<SceneKey?> {
+ fun SceneKey?.nullifyIfIgnored(): SceneKey? {
+ return this?.takeIf { this != ignored }
+ }
+
+ return repository.previousScene
+ .map { it.nullifyIfIgnored() }
+ .stateIn(
+ scope = applicationScope,
+ started = SharingStarted.WhileSubscribed(),
+ initialValue = repository.previousScene.value.nullifyIfIgnored(),
+ )
+ }
/**
* Returns the keys of all scenes in the container.
diff --git a/packages/SystemUI/src/com/android/systemui/scene/domain/startable/SceneContainerStartable.kt b/packages/SystemUI/src/com/android/systemui/scene/domain/startable/SceneContainerStartable.kt
index 4774eb32a445..98b4ab88141c 100644
--- a/packages/SystemUI/src/com/android/systemui/scene/domain/startable/SceneContainerStartable.kt
+++ b/packages/SystemUI/src/com/android/systemui/scene/domain/startable/SceneContainerStartable.kt
@@ -253,7 +253,8 @@ constructor(
// Track the previous scene (sans Bouncer), so that we know where to go when the device
// is unlocked whilst on the bouncer.
val previousScene =
- sceneInteractor.previousScene
+ sceneInteractor
+ .previousScene()
.filterNot { it == Scenes.Bouncer }
.stateIn(this, SharingStarted.Eagerly, initialValue = null)
deviceUnlockedInteractor.deviceUnlockStatus
diff --git a/packages/SystemUI/src/com/android/systemui/scene/ui/viewmodel/SceneContainerViewModel.kt b/packages/SystemUI/src/com/android/systemui/scene/ui/viewmodel/SceneContainerViewModel.kt
index 231b28424691..ef7829f91723 100644
--- a/packages/SystemUI/src/com/android/systemui/scene/ui/viewmodel/SceneContainerViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/scene/ui/viewmodel/SceneContainerViewModel.kt
@@ -19,15 +19,22 @@ package com.android.systemui.scene.ui.viewmodel
import android.view.MotionEvent
import com.android.compose.animation.scene.ObservableTransitionState
import com.android.compose.animation.scene.SceneKey
+import com.android.compose.animation.scene.UserAction
+import com.android.compose.animation.scene.UserActionResult
import com.android.systemui.classifier.Classifier
import com.android.systemui.classifier.domain.interactor.FalsingInteractor
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.power.domain.interactor.PowerInteractor
import com.android.systemui.scene.domain.interactor.SceneInteractor
+import com.android.systemui.scene.shared.model.Scene
import com.android.systemui.scene.shared.model.Scenes
+import com.android.systemui.utils.coroutines.flow.flatMapLatestConflated
import javax.inject.Inject
+import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.flow.Flow
+import kotlinx.coroutines.flow.SharingStarted
import kotlinx.coroutines.flow.StateFlow
+import kotlinx.coroutines.flow.stateIn
/** Models UI state for the scene container. */
@SysUISingleton
@@ -37,6 +44,7 @@ constructor(
private val sceneInteractor: SceneInteractor,
private val falsingInteractor: FalsingInteractor,
private val powerInteractor: PowerInteractor,
+ scenes: Set<@JvmSuppressWildcards Scene>,
) {
/**
* Keys of all scenes in the container.
@@ -52,6 +60,23 @@ constructor(
/** Whether the container is visible. */
val isVisible: StateFlow<Boolean> = sceneInteractor.isVisible
+ private val destinationScenesBySceneKey =
+ scenes.associate { scene -> scene.key to scene.destinationScenes }
+
+ fun currentDestinationScenes(
+ scope: CoroutineScope,
+ ): StateFlow<Map<UserAction, UserActionResult>> {
+ return currentScene
+ .flatMapLatestConflated { currentSceneKey ->
+ checkNotNull(destinationScenesBySceneKey[currentSceneKey])
+ }
+ .stateIn(
+ scope = scope,
+ started = SharingStarted.WhileSubscribed(),
+ initialValue = emptyMap(),
+ )
+ }
+
/**
* Binds the given flow so the system remembers it.
*
diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotShelfViewProxy.kt b/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotShelfViewProxy.kt
index 254c13367ce2..e2dc0928b823 100644
--- a/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotShelfViewProxy.kt
+++ b/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotShelfViewProxy.kt
@@ -42,6 +42,7 @@ import com.android.systemui.screenshot.ScreenshotEvent.SCREENSHOT_DISMISSED_OTHE
import com.android.systemui.screenshot.scroll.ScrollCaptureController
import com.android.systemui.screenshot.ui.ScreenshotAnimationController
import com.android.systemui.screenshot.ui.ScreenshotShelfView
+import com.android.systemui.screenshot.ui.SwipeGestureListener
import com.android.systemui.screenshot.ui.binder.ScreenshotShelfViewBinder
import com.android.systemui.screenshot.ui.viewmodel.ScreenshotViewModel
import dagger.assisted.Assisted
@@ -75,9 +76,17 @@ constructor(
override var isPendingSharedTransition = false
private val animationController = ScreenshotAnimationController(view)
+ private val swipeGestureListener =
+ SwipeGestureListener(
+ view,
+ onDismiss = { requestDismissal(ScreenshotEvent.SCREENSHOT_SWIPE_DISMISSED, it) },
+ onCancel = { animationController.getSwipeReturnAnimation().start() }
+ )
init {
- ScreenshotShelfViewBinder.bind(view, viewModel, LayoutInflater.from(context))
+ ScreenshotShelfViewBinder.bind(view, viewModel, LayoutInflater.from(context)) {
+ swipeGestureListener.onMotionEvent(it)
+ }
addPredictiveBackListener { requestDismissal(SCREENSHOT_DISMISSED_OTHER) }
setOnKeyListener { requestDismissal(SCREENSHOT_DISMISSED_OTHER) }
debugLog(DEBUG_WINDOW) { "adding OnComputeInternalInsetsListener" }
@@ -111,6 +120,10 @@ constructor(
override fun setChipIntents(imageData: SavedImageData) {}
override fun requestDismissal(event: ScreenshotEvent?) {
+ requestDismissal(event, getDismissalVelocity())
+ }
+
+ private fun requestDismissal(event: ScreenshotEvent?, velocity: Float) {
debugLog(DEBUG_DISMISS) { "screenshot dismissal requested: $event" }
// If we're already animating out, don't restart the animation
@@ -119,7 +132,7 @@ constructor(
return
}
event?.let { logger.log(it, 0, packageName) }
- val animator = animationController.getExitAnimation()
+ val animator = animationController.getSwipeDismissAnimation(velocity)
animator.addListener(
object : AnimatorListenerAdapter() {
override fun onAnimationStart(animator: Animator) {
@@ -222,6 +235,12 @@ constructor(
)
}
+ private fun getDismissalVelocity(): Float {
+ val isLTR = view.resources.configuration.layoutDirection == View.LAYOUT_DIRECTION_LTR
+ // dismiss to the left in LTR locales, to the right in RTL
+ return if (isLTR) -1.5f else 1.5f
+ }
+
@AssistedFactory
interface Factory : ScreenshotViewProxy.Factory {
override fun getProxy(context: Context, displayId: Int): ScreenshotShelfViewProxy
diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/ui/ScreenshotAnimationController.kt b/packages/SystemUI/src/com/android/systemui/screenshot/ui/ScreenshotAnimationController.kt
index 2c178736d9c4..f3c421e4896e 100644
--- a/packages/SystemUI/src/com/android/systemui/screenshot/ui/ScreenshotAnimationController.kt
+++ b/packages/SystemUI/src/com/android/systemui/screenshot/ui/ScreenshotAnimationController.kt
@@ -20,9 +20,12 @@ import android.animation.Animator
import android.animation.AnimatorListenerAdapter
import android.animation.ValueAnimator
import android.view.View
+import com.android.systemui.res.R
+import kotlin.math.abs
-class ScreenshotAnimationController(private val view: View) {
+class ScreenshotAnimationController(private val view: ScreenshotShelfView) {
private var animator: Animator? = null
+ private val actionContainer = view.requireViewById<View>(R.id.actions_container_background)
fun getEntranceAnimation(): Animator {
val animator = ValueAnimator.ofFloat(0f, 1f)
@@ -41,19 +44,32 @@ class ScreenshotAnimationController(private val view: View) {
return animator
}
- fun getExitAnimation(): Animator {
- val animator = ValueAnimator.ofFloat(1f, 0f)
- animator.addUpdateListener { view.alpha = it.animatedValue as Float }
- animator.addListener(
- object : AnimatorListenerAdapter() {
- override fun onAnimationStart(animator: Animator) {
- view.alpha = 1f
- }
- override fun onAnimationEnd(animator: Animator) {
- view.alpha = 0f
- }
+ fun getSwipeReturnAnimation(): Animator {
+ animator?.cancel()
+ val animator = ValueAnimator.ofFloat(view.translationX, 0f)
+ animator.addUpdateListener { view.translationX = it.animatedValue as Float }
+ this.animator = animator
+ return animator
+ }
+
+ fun getSwipeDismissAnimation(velocity: Float): Animator {
+ val screenWidth = view.resources.displayMetrics.widthPixels
+ // translation at which point the visible UI is fully off the screen (in the direction
+ // according to velocity)
+ val endX =
+ if (velocity < 0) {
+ -1f * actionContainer.right
+ } else {
+ (screenWidth - actionContainer.left).toFloat()
}
- )
+ val distance = endX - view.translationX
+ val animator = ValueAnimator.ofFloat(view.translationX, endX)
+ animator.addUpdateListener {
+ view.translationX = it.animatedValue as Float
+ view.alpha = 1f - it.animatedFraction
+ }
+ animator.duration = ((abs(distance / velocity))).toLong()
+
this.animator = animator
return animator
}
diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/ui/ScreenshotShelfView.kt b/packages/SystemUI/src/com/android/systemui/screenshot/ui/ScreenshotShelfView.kt
index b7a03ef42245..16e23c54dfb2 100644
--- a/packages/SystemUI/src/com/android/systemui/screenshot/ui/ScreenshotShelfView.kt
+++ b/packages/SystemUI/src/com/android/systemui/screenshot/ui/ScreenshotShelfView.kt
@@ -21,6 +21,7 @@ import android.graphics.Insets
import android.graphics.Rect
import android.graphics.Region
import android.util.AttributeSet
+import android.view.MotionEvent
import android.view.View
import android.widget.ImageView
import androidx.constraintlayout.widget.ConstraintLayout
@@ -30,6 +31,7 @@ import com.android.systemui.screenshot.FloatingWindowUtil
class ScreenshotShelfView(context: Context, attrs: AttributeSet? = null) :
ConstraintLayout(context, attrs) {
lateinit var screenshotPreview: ImageView
+ var onTouchInterceptListener: ((MotionEvent) -> Boolean)? = null
private val displayMetrics = context.resources.displayMetrics
private val tmpRect = Rect()
@@ -83,4 +85,11 @@ class ScreenshotShelfView(context: Context, attrs: AttributeSet? = null) :
companion object {
private const val TOUCH_PADDING_DP = 12f
}
+
+ override fun onInterceptTouchEvent(ev: MotionEvent): Boolean {
+ if (onTouchInterceptListener?.invoke(ev) == true) {
+ return true
+ }
+ return super.onInterceptTouchEvent(ev)
+ }
}
diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/ui/SwipeGestureListener.kt b/packages/SystemUI/src/com/android/systemui/screenshot/ui/SwipeGestureListener.kt
new file mode 100644
index 000000000000..f2889602bc5d
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/screenshot/ui/SwipeGestureListener.kt
@@ -0,0 +1,74 @@
+/*
+ * Copyright (C) 2024 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.screenshot.ui
+
+import android.view.MotionEvent
+import android.view.VelocityTracker
+import android.view.View
+import com.android.systemui.screenshot.FloatingWindowUtil
+import kotlin.math.abs
+import kotlin.math.sign
+
+class SwipeGestureListener(
+ private val view: View,
+ private val onDismiss: (Float) -> Unit,
+ private val onCancel: () -> Unit
+) {
+ private val velocityTracker = VelocityTracker.obtain()
+ private val displayMetrics = view.resources.displayMetrics
+
+ private var startX = 0f
+
+ fun onMotionEvent(ev: MotionEvent): Boolean {
+ ev.offsetLocation(view.translationX, 0f)
+ when (ev.actionMasked) {
+ MotionEvent.ACTION_DOWN -> {
+ velocityTracker.addMovement(ev)
+ startX = ev.rawX
+ }
+ MotionEvent.ACTION_UP -> {
+ velocityTracker.computeCurrentVelocity(1)
+ val xVelocity = velocityTracker.xVelocity
+ if (
+ abs(xVelocity) > FloatingWindowUtil.dpToPx(displayMetrics, FLING_THRESHOLD_DP)
+ ) {
+ onDismiss.invoke(xVelocity)
+ return true
+ } else if (
+ abs(view.translationX) >
+ FloatingWindowUtil.dpToPx(displayMetrics, DISMISS_THRESHOLD_DP)
+ ) {
+ onDismiss.invoke(1.5f * sign(view.translationX))
+ return true
+ } else {
+ velocityTracker.clear()
+ onCancel.invoke()
+ }
+ }
+ MotionEvent.ACTION_MOVE -> {
+ velocityTracker.addMovement(ev)
+ view.translationX = ev.rawX - startX
+ }
+ }
+ return false
+ }
+
+ companion object {
+ private const val DISMISS_THRESHOLD_DP = 80f
+ private const val FLING_THRESHOLD_DP = .8f // dp per ms
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/ui/binder/ScreenshotShelfViewBinder.kt b/packages/SystemUI/src/com/android/systemui/screenshot/ui/binder/ScreenshotShelfViewBinder.kt
index 5f835b3697a1..b8b9ce580796 100644
--- a/packages/SystemUI/src/com/android/systemui/screenshot/ui/binder/ScreenshotShelfViewBinder.kt
+++ b/packages/SystemUI/src/com/android/systemui/screenshot/ui/binder/ScreenshotShelfViewBinder.kt
@@ -17,8 +17,8 @@
package com.android.systemui.screenshot.ui.binder
import android.view.LayoutInflater
+import android.view.MotionEvent
import android.view.View
-import android.view.ViewGroup
import android.widget.ImageView
import android.widget.LinearLayout
import androidx.lifecycle.Lifecycle
@@ -26,16 +26,20 @@ import androidx.lifecycle.lifecycleScope
import androidx.lifecycle.repeatOnLifecycle
import com.android.systemui.lifecycle.repeatWhenAttached
import com.android.systemui.res.R
+import com.android.systemui.screenshot.ui.ScreenshotShelfView
import com.android.systemui.screenshot.ui.viewmodel.ScreenshotViewModel
import com.android.systemui.util.children
import kotlinx.coroutines.launch
object ScreenshotShelfViewBinder {
fun bind(
- view: ViewGroup,
+ view: ScreenshotShelfView,
viewModel: ScreenshotViewModel,
layoutInflater: LayoutInflater,
+ onTouchListener: (MotionEvent) -> Boolean,
) {
+ view.onTouchInterceptListener = onTouchListener
+
val previewView: ImageView = view.requireViewById(R.id.screenshot_preview)
val previewBorder = view.requireViewById<View>(R.id.screenshot_preview_border)
previewView.clipToOutline = true
diff --git a/packages/SystemUI/src/com/android/systemui/shade/GlanceableHubContainerController.kt b/packages/SystemUI/src/com/android/systemui/shade/GlanceableHubContainerController.kt
index 8f6b9d0d19e9..a8481cde8ff0 100644
--- a/packages/SystemUI/src/com/android/systemui/shade/GlanceableHubContainerController.kt
+++ b/packages/SystemUI/src/com/android/systemui/shade/GlanceableHubContainerController.kt
@@ -45,6 +45,7 @@ import com.android.systemui.res.R
import com.android.systemui.scene.shared.model.SceneDataSourceDelegator
import com.android.systemui.shade.domain.interactor.ShadeInteractor
import com.android.systemui.statusbar.phone.SystemUIDialogFactory
+import com.android.systemui.util.kotlin.BooleanFlowOperators.or
import com.android.systemui.util.kotlin.collectFlow
import javax.inject.Inject
import kotlinx.coroutines.flow.Flow
@@ -137,7 +138,8 @@ constructor(
private var isDreaming = false
/** Returns a flow that tracks whether communal hub is available. */
- fun communalAvailable(): Flow<Boolean> = communalInteractor.isCommunalAvailable
+ fun communalAvailable(): Flow<Boolean> =
+ or(communalInteractor.isCommunalAvailable, communalInteractor.editModeOpen)
/**
* Creates the container view containing the glanceable hub UI.
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationBackgroundView.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationBackgroundView.java
index ed3a38d3305b..d0db5145e0ff 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationBackgroundView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationBackgroundView.java
@@ -53,6 +53,8 @@ public class NotificationBackgroundView extends View implements Dumpable {
private int mTintColor;
@Nullable private Integer mRippleColor;
private final float[] mCornerRadii = new float[8];
+ private final float[] mFocusOverlayCornerRadii = new float[8];
+ private float mFocusOverlayStroke = 0;
private boolean mBottomIsRounded;
private boolean mBottomAmountClips = true;
private int mActualHeight = -1;
@@ -74,6 +76,7 @@ public class NotificationBackgroundView extends View implements Dumpable {
R.color.notification_state_color_dark);
mNormalColor = Utils.getColorAttrDefaultColor(mContext,
com.android.internal.R.attr.materialColorSurfaceContainerHigh);
+ mFocusOverlayStroke = getResources().getDimension(R.dimen.notification_focus_stroke_width);
}
@Override
@@ -290,14 +293,28 @@ public class NotificationBackgroundView extends View implements Dumpable {
if (mDontModifyCorners) {
return;
}
- if (mBackground instanceof LayerDrawable) {
- int numberOfLayers = ((LayerDrawable) mBackground).getNumberOfLayers();
+ if (mBackground instanceof LayerDrawable layerDrawable) {
+ int numberOfLayers = layerDrawable.getNumberOfLayers();
for (int i = 0; i < numberOfLayers; i++) {
- GradientDrawable gradientDrawable =
- (GradientDrawable) ((LayerDrawable) mBackground).getDrawable(i);
+ GradientDrawable gradientDrawable = (GradientDrawable) layerDrawable.getDrawable(i);
gradientDrawable.setCornerRadii(mCornerRadii);
}
+ updateFocusOverlayRadii(layerDrawable);
+ }
+ }
+
+ private void updateFocusOverlayRadii(LayerDrawable background) {
+ GradientDrawable overlay =
+ (GradientDrawable) background.findDrawableByLayerId(
+ R.id.notification_focus_overlay);
+ for (int i = 0; i < mCornerRadii.length; i++) {
+ // in theory subtracting mFocusOverlayStroke/2 should be enough but notification
+ // background is still peeking a bit from below - probably due to antialiasing or
+ // overlay uneven scaling. So let's subtract full mFocusOverlayStroke to make sure the
+ // radius is a bit smaller and covers background corners fully
+ mFocusOverlayCornerRadii[i] = Math.max(0, mCornerRadii[i] - mFocusOverlayStroke);
}
+ overlay.setCornerRadii(mFocusOverlayCornerRadii);
}
/** Set the current expand animation size. */
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayout.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayout.java
index 8a1a4f1e4cd8..fd675866fa82 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayout.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayout.java
@@ -482,7 +482,6 @@ public class NotificationStackScrollLayout
private Interpolator mHideXInterpolator = Interpolators.FAST_OUT_SLOW_IN;
private final NotificationSectionsManager mSectionsManager;
- private boolean mAnimateBottomOnLayout;
private float mLastSentAppear;
private float mLastSentExpandedHeight;
private boolean mWillExpand;
@@ -2941,23 +2940,11 @@ public class NotificationStackScrollLayout
}
private void updateFirstAndLastBackgroundViews() {
- NotificationSection firstSection = getFirstVisibleSection();
- NotificationSection lastSection = getLastVisibleSection();
- ExpandableView previousFirstChild =
- firstSection == null ? null : firstSection.getFirstVisibleChild();
- ExpandableView previousLastChild =
- lastSection == null ? null : lastSection.getLastVisibleChild();
-
- ExpandableView firstChild = getFirstChildWithBackground();
ExpandableView lastChild = getLastChildWithBackground();
boolean sectionViewsChanged = mSectionsManager.updateFirstAndLastViewsForAllSections(
mSections, getChildrenWithBackground());
- if (mAnimationsEnabled && mIsExpanded) {
- } else {
- }
mAmbientState.setLastVisibleBackgroundChild(lastChild);
- mAnimateBottomOnLayout = false;
invalidate();
}
@@ -5465,10 +5452,6 @@ public class NotificationStackScrollLayout
}
}
- void setAnimateBottomOnLayout(boolean animateBottomOnLayout) {
- mAnimateBottomOnLayout = animateBottomOnLayout;
- }
-
public void setOnPulseHeightChangedListener(Runnable listener) {
mAmbientState.setOnPulseHeightChangedListener(listener);
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutController.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutController.java
index 5e719b1f0667..d8a16ce5a6e9 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutController.java
@@ -306,10 +306,6 @@ public class NotificationStackScrollLayoutController implements Dumpable {
};
private final DynamicPrivacyController.Listener mDynamicPrivacyControllerListener = () -> {
- if (mView.isExpanded()) {
- // The bottom might change because we're using the final actual height of the view
- mView.setAnimateBottomOnLayout(true);
- }
if (!FooterViewRefactor.isEnabled()) {
// Let's update the footer once the notifications have been updated (in the next frame)
mView.post(this::updateFooter);
diff --git a/packages/SystemUI/src/com/android/systemui/util/kotlin/BooleanFlowOperators.kt b/packages/SystemUI/src/com/android/systemui/util/kotlin/BooleanFlowOperators.kt
index 693a835e25d2..b3008856d370 100644
--- a/packages/SystemUI/src/com/android/systemui/util/kotlin/BooleanFlowOperators.kt
+++ b/packages/SystemUI/src/com/android/systemui/util/kotlin/BooleanFlowOperators.kt
@@ -18,6 +18,7 @@ package com.android.systemui.util.kotlin
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.combine
+import kotlinx.coroutines.flow.distinctUntilChanged
import kotlinx.coroutines.flow.map
object BooleanFlowOperators {
@@ -31,7 +32,7 @@ object BooleanFlowOperators {
* ```
*/
fun and(vararg flows: Flow<Boolean>): Flow<Boolean> =
- combine(flows.asIterable()) { values -> values.all { it } }
+ combine(flows.asIterable()) { values -> values.all { it } }.distinctUntilChanged()
/**
* Logical NOT operator for a boolean flow.
@@ -48,5 +49,5 @@ object BooleanFlowOperators {
* determine the result.
*/
fun or(vararg flows: Flow<Boolean>): Flow<Boolean> =
- combine(flows.asIterable()) { values -> values.any { it } }
+ combine(flows.asIterable()) { values -> values.any { it } }.distinctUntilChanged()
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/FromDreamingTransitionInteractorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/FromDreamingTransitionInteractorTest.kt
index f534ba5bc68c..8435a03fae9a 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/FromDreamingTransitionInteractorTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/FromDreamingTransitionInteractorTest.kt
@@ -37,6 +37,7 @@ import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
import com.android.systemui.Flags
import com.android.systemui.SysuiTestCase
+import com.android.systemui.bouncer.data.repository.fakeKeyguardBouncerRepository
import com.android.systemui.keyguard.data.repository.FakeKeyguardTransitionRepository
import com.android.systemui.keyguard.data.repository.fakeKeyguardRepository
import com.android.systemui.keyguard.data.repository.fakeKeyguardTransitionRepository
@@ -160,4 +161,17 @@ class FromDreamingTransitionInteractorTest : SysuiTestCase() {
to = KeyguardState.LOCKSCREEN,
)
}
+
+ @Test
+ fun testTransitionToAlternateBouncer() =
+ testScope.runTest {
+ kosmos.fakeKeyguardBouncerRepository.setAlternateVisible(true)
+ runCurrent()
+
+ assertThat(transitionRepository)
+ .startedTransition(
+ from = KeyguardState.DREAMING,
+ to = KeyguardState.ALTERNATE_BOUNCER,
+ )
+ }
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardTransitionScenariosTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardTransitionScenariosTest.kt
index 085b70e61339..671b09b257ce 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardTransitionScenariosTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardTransitionScenariosTest.kt
@@ -755,35 +755,6 @@ class KeyguardTransitionScenariosTest : SysuiTestCase() {
}
@Test
- fun goneToDreamingLockscreenHosted() =
- testScope.runTest {
- // GIVEN a device that is not dreaming or dozing
- keyguardRepository.setDreamingWithOverlay(false)
- keyguardRepository.setDozeTransitionModel(
- DozeTransitionModel(from = DozeStateModel.DOZE, to = DozeStateModel.FINISH)
- )
- runCurrent()
-
- // GIVEN a prior transition has run to GONE
- runTransitionAndSetWakefulness(KeyguardState.LOCKSCREEN, KeyguardState.GONE)
-
- // WHEN the device begins to dream with the lockscreen hosted dream
- keyguardRepository.setDreamingWithOverlay(true)
- keyguardRepository.setIsActiveDreamLockscreenHosted(true)
- advanceTimeBy(100L)
-
- assertThat(transitionRepository)
- .startedTransition(
- to = KeyguardState.DREAMING_LOCKSCREEN_HOSTED,
- from = KeyguardState.GONE,
- ownerName = "FromGoneTransitionInteractor",
- animatorAssertion = { it.isNotNull() }
- )
-
- coroutineContext.cancelChildren()
- }
-
- @Test
fun goneToGlanceableHub() =
testScope.runTest {
// GIVEN a prior transition has run to GONE
@@ -1244,9 +1215,15 @@ class KeyguardTransitionScenariosTest : SysuiTestCase() {
keyguardRepository.setKeyguardOccluded(true)
runCurrent()
- // GIVEN device is docked
+ // GIVEN device is docked/communal is available
dockManager.setIsDocked(true)
dockManager.setDockEvent(DockManager.STATE_DOCKED)
+ val idleTransitionState =
+ MutableStateFlow<ObservableTransitionState>(
+ ObservableTransitionState.Idle(CommunalScenes.Communal)
+ )
+ communalInteractor.setTransitionState(idleTransitionState)
+ runCurrent()
// WHEN occlusion ends
keyguardRepository.setKeyguardOccluded(false)
@@ -1372,6 +1349,8 @@ class KeyguardTransitionScenariosTest : SysuiTestCase() {
// WHEN the keyguard is occluded and device wakes up and is no longer dreaming
keyguardRepository.setDreaming(false)
+ testScheduler.advanceTimeBy(150) // The dreaming signal is debounced.
+ runCurrent()
keyguardRepository.setKeyguardOccluded(true)
powerInteractor.setAwakeForTest()
runCurrent()
@@ -1379,7 +1358,7 @@ class KeyguardTransitionScenariosTest : SysuiTestCase() {
// THEN a transition to OCCLUDED should occur
assertThat(transitionRepository)
.startedTransition(
- ownerName = "FromDreamingTransitionInteractor",
+ ownerName = "FromDreamingTransitionInteractor(Occluded but no longer dreaming)",
from = KeyguardState.DREAMING,
to = KeyguardState.OCCLUDED,
animatorAssertion = { it.isNotNull() },
@@ -1516,7 +1495,7 @@ class KeyguardTransitionScenariosTest : SysuiTestCase() {
// THEN a transition to OCCLUDED should occur
assertThat(transitionRepository)
.startedTransition(
- ownerName = "FromAodTransitionInteractor",
+ ownerName = "FromAodTransitionInteractor(isOccluded = true)",
from = KeyguardState.AOD,
to = KeyguardState.OCCLUDED,
animatorAssertion = { it.isNotNull() },
@@ -1555,7 +1534,8 @@ class KeyguardTransitionScenariosTest : SysuiTestCase() {
runTransitionAndSetWakefulness(KeyguardState.AOD, KeyguardState.LOCKSCREEN)
runCurrent()
- // WHEN the device begins to sleep (first power button press)...
+ // WHEN the device begins to sleep (first power button press), which starts
+ // LS -> DOZING...
powerInteractor.setAsleepForTest()
runCurrent()
reset(transitionRepository)
@@ -1568,11 +1548,11 @@ class KeyguardTransitionScenariosTest : SysuiTestCase() {
}
runCurrent()
- // THEN a transition from LOCKSCREEN => OCCLUDED should occur
+ // THEN a transition from DOZING => OCCLUDED should occur
assertThat(transitionRepository)
.startedTransition(
- ownerName = "FromLockscreenTransitionInteractor",
- from = KeyguardState.LOCKSCREEN,
+ ownerName = "FromDozingTransitionInteractor",
+ from = KeyguardState.DOZING,
to = KeyguardState.OCCLUDED,
animatorAssertion = { it.isNotNull() },
)
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/util/KeyguardTransitionRepositorySpySubject.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/util/KeyguardTransitionRepositorySpySubject.kt
index a08e4913d553..af785c24bb5c 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/util/KeyguardTransitionRepositorySpySubject.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/util/KeyguardTransitionRepositorySpySubject.kt
@@ -21,6 +21,8 @@ import com.android.systemui.keyguard.data.repository.KeyguardTransitionRepositor
import com.android.systemui.keyguard.shared.model.KeyguardState
import com.android.systemui.keyguard.shared.model.TransitionInfo
import com.android.systemui.keyguard.shared.model.TransitionModeOnCanceled
+import com.android.systemui.util.mockito.any
+import com.android.systemui.util.mockito.withArgCaptor
import com.google.common.truth.FailureMetadata
import com.google.common.truth.Subject
import com.google.common.truth.Truth
@@ -28,6 +30,8 @@ import com.google.common.truth.Truth.assertAbout
import junit.framework.Assert.assertEquals
import kotlin.test.fail
import org.mockito.Mockito
+import org.mockito.Mockito.never
+import org.mockito.Mockito.verify
/** [Subject] used to make assertions about a [Mockito.spy] KeyguardTransitionRepository. */
class KeyguardTransitionRepositorySpySubject
@@ -75,34 +79,19 @@ private constructor(
animatorAssertion: (Subject) -> Unit,
modeOnCanceled: TransitionModeOnCanceled? = null,
) {
- // TODO(b/331799060): Remove this workaround once atest supports mocking suspend functions.
- Mockito.mockingDetails(repository).invocations.forEach { invocation ->
- if (invocation.method.equals(KeyguardTransitionRepository::startTransition.name)) {
- val transitionInfo = invocation.arguments.firstOrNull() as TransitionInfo
+ withArgCaptor<TransitionInfo> { verify(repository).startTransition(capture()) }
+ .also { transitionInfo ->
assertEquals(to, transitionInfo.to)
animatorAssertion.invoke(Truth.assertThat(transitionInfo.animator))
from?.let { assertEquals(it, transitionInfo.from) }
ownerName?.let { assertEquals(it, transitionInfo.ownerName) }
modeOnCanceled?.let { assertEquals(it, transitionInfo.modeOnCanceled) }
- invocation.markVerified()
}
- }
}
/** Verifies that [KeyguardTransitionRepository.startTransition] was never called. */
suspend fun noTransitionsStarted() {
- // TODO(b/331799060): Remove this workaround once atest supports mocking suspend functions.
- Mockito.mockingDetails(repository).invocations.forEach {
- if (
- it.method.equals(KeyguardTransitionRepository::startTransition.name) &&
- !it.isVerified
- ) {
- fail(
- "Expected no transitions started, however this transition was started: " +
- it.arguments.firstOrNull()
- )
- }
- }
+ verify(repository, never()).startTransition(any())
}
companion object {
diff --git a/packages/SystemUI/tests/src/com/android/systemui/shade/GlanceableHubContainerControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/shade/GlanceableHubContainerControllerTest.kt
index ee03236d00b3..fd9daf862190 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/shade/GlanceableHubContainerControllerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/shade/GlanceableHubContainerControllerTest.kt
@@ -37,6 +37,7 @@ import com.android.systemui.communal.domain.interactor.setCommunalAvailable
import com.android.systemui.communal.shared.model.CommunalScenes
import com.android.systemui.communal.ui.viewmodel.CommunalViewModel
import com.android.systemui.communal.util.CommunalColors
+import com.android.systemui.coroutines.collectLastValue
import com.android.systemui.keyguard.data.repository.fakeKeyguardRepository
import com.android.systemui.keyguard.data.repository.fakeKeyguardTransitionRepository
import com.android.systemui.keyguard.domain.interactor.KeyguardInteractor
@@ -325,6 +326,19 @@ class GlanceableHubContainerControllerTest : SysuiTestCase() {
}
}
+ @Test
+ fun editMode_communalAvailable() =
+ with(kosmos) {
+ testScope.runTest {
+ val available by collectLastValue(underTest.communalAvailable())
+ setCommunalAvailable(false)
+
+ assertThat(available).isFalse()
+ communalInteractor.setEditModeOpen(true)
+ assertThat(available).isTrue()
+ }
+ }
+
private fun initAndAttachContainerView() {
containerView = View(context)
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/ongoingcall/OngoingCallControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/ongoingcall/OngoingCallControllerTest.kt
index 7b73528cd932..efd7f9969d49 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/ongoingcall/OngoingCallControllerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/ongoingcall/OngoingCallControllerTest.kt
@@ -22,6 +22,7 @@ import android.app.IUidObserver
import android.app.Notification
import android.app.PendingIntent
import android.app.Person
+import android.platform.test.annotations.DisableFlags
import android.service.notification.NotificationListenerService.REASON_USER_STOPPED
import android.testing.AndroidTestingRunner
import android.testing.TestableLooper
@@ -193,6 +194,7 @@ class OngoingCallControllerTest : SysuiTestCase() {
/** Regression test for b/192379214. */
@Test
+ @DisableFlags(android.app.Flags.FLAG_UPDATE_RANKING_TIME)
fun onEntryUpdated_notificationWhenIsZero_timeHidden() {
val notification = NotificationEntryBuilder(createOngoingCallNotifEntry())
notification.modifyNotification(context).setWhen(0)
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/flags/SceneContainerRule.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/flags/SceneContainerRule.kt
index 9ec1481355a5..5f9f6b43be2a 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/flags/SceneContainerRule.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/flags/SceneContainerRule.kt
@@ -43,6 +43,21 @@ class SceneContainerRule : TestRule {
SceneContainerFlag.isEnabled
)
}
+ // Get the flag value, treating the unset error as false.
+ val sceneContainerAconfigEnabled = try {
+ com.android.systemui.Flags.sceneContainer()
+ } catch (e: Exception) {
+ false
+ }
+ if (sceneContainerAconfigEnabled) {
+ Assert.assertTrue(
+ "FLAG_SCENE_CONTAINER is enabled but SceneContainerFlag.isEnabled" +
+ " is false. Use `.andSceneContainer()` from" +
+ " SceneContainerFlagParameterization.kt to parameterize this" +
+ " flag correctly.",
+ SceneContainerFlag.isEnabled
+ )
+ }
if (
description.hasAnnotation<BrokenWithSceneContainer>() &&
SceneContainerFlag.isEnabled
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/scene/SceneKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/scene/SceneKosmos.kt
index 2cdf76d50299..7f6a7bd3f7d8 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/scene/SceneKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/scene/SceneKosmos.kt
@@ -2,6 +2,8 @@ package com.android.systemui.scene
import com.android.systemui.kosmos.Kosmos
import com.android.systemui.kosmos.Kosmos.Fixture
+import com.android.systemui.kosmos.testScope
+import com.android.systemui.scene.shared.model.FakeScene
import com.android.systemui.scene.shared.model.SceneContainerConfig
import com.android.systemui.scene.shared.model.Scenes
@@ -16,5 +18,18 @@ var Kosmos.sceneKeys by Fixture {
)
}
+val Kosmos.fakeScenes by Fixture {
+ sceneKeys
+ .map { key ->
+ FakeScene(
+ scope = testScope.backgroundScope,
+ key = key,
+ )
+ }
+ .toSet()
+}
+
+val Kosmos.scenes by Fixture { fakeScenes }
+
val Kosmos.initialSceneKey by Fixture { Scenes.Lockscreen }
val Kosmos.sceneContainerConfig by Fixture { SceneContainerConfig(sceneKeys, initialSceneKey) }
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/scene/shared/model/FakeScene.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/scene/shared/model/FakeScene.kt
new file mode 100644
index 000000000000..eeaa9db16730
--- /dev/null
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/scene/shared/model/FakeScene.kt
@@ -0,0 +1,52 @@
+/*
+ * Copyright (C) 2024 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.scene.shared.model
+
+import com.android.compose.animation.scene.SceneKey
+import com.android.compose.animation.scene.UserAction
+import com.android.compose.animation.scene.UserActionResult
+import kotlinx.coroutines.CoroutineScope
+import kotlinx.coroutines.channels.Channel
+import kotlinx.coroutines.flow.SharingStarted
+import kotlinx.coroutines.flow.onCompletion
+import kotlinx.coroutines.flow.onStart
+import kotlinx.coroutines.flow.receiveAsFlow
+import kotlinx.coroutines.flow.stateIn
+
+class FakeScene(
+ val scope: CoroutineScope,
+ override val key: SceneKey,
+) : Scene {
+ var isDestinationScenesBeingCollected = false
+
+ private val destinationScenesChannel = Channel<Map<UserAction, UserActionResult>>()
+
+ override val destinationScenes =
+ destinationScenesChannel
+ .receiveAsFlow()
+ .onStart { isDestinationScenesBeingCollected = true }
+ .onCompletion { isDestinationScenesBeingCollected = false }
+ .stateIn(
+ scope = scope,
+ started = SharingStarted.WhileSubscribed(),
+ initialValue = emptyMap(),
+ )
+
+ suspend fun setDestinationScenes(value: Map<UserAction, UserActionResult>) {
+ destinationScenesChannel.send(value)
+ }
+}
diff --git a/services/autofill/java/com/android/server/autofill/RemoteFillService.java b/services/autofill/java/com/android/server/autofill/RemoteFillService.java
index c96688c1b9ae..7ceb3bb56403 100644
--- a/services/autofill/java/com/android/server/autofill/RemoteFillService.java
+++ b/services/autofill/java/com/android/server/autofill/RemoteFillService.java
@@ -28,6 +28,7 @@ import android.content.Context;
import android.content.Intent;
import android.content.IntentSender;
import android.os.Handler;
+import android.os.IBinder;
import android.os.ICancellationSignal;
import android.os.RemoteException;
import android.service.autofill.AutofillService;
@@ -42,7 +43,6 @@ import android.service.autofill.ISaveCallback;
import android.service.autofill.SaveRequest;
import android.text.format.DateUtils;
import android.util.Slog;
-import android.view.autofill.IAutoFillManagerClient;
import com.android.internal.infra.AbstractRemoteService;
import com.android.internal.infra.ServiceConnector;
@@ -283,8 +283,7 @@ final class RemoteFillService extends ServiceConnector.Impl<IAutoFillService> {
return callback;
}
- public void onFillCredentialRequest(@NonNull FillRequest request,
- IAutoFillManagerClient autofillCallback) {
+ public void onFillCredentialRequest(@NonNull FillRequest request, IBinder autofillCallback) {
if (sVerbose) {
Slog.v(TAG, "onFillRequest:" + request);
}
diff --git a/services/autofill/java/com/android/server/autofill/SecondaryProviderHandler.java b/services/autofill/java/com/android/server/autofill/SecondaryProviderHandler.java
index ce9d1803d764..044a06417c00 100644
--- a/services/autofill/java/com/android/server/autofill/SecondaryProviderHandler.java
+++ b/services/autofill/java/com/android/server/autofill/SecondaryProviderHandler.java
@@ -21,11 +21,11 @@ import android.annotation.Nullable;
import android.content.ComponentName;
import android.content.Context;
import android.content.IntentSender;
+import android.os.IBinder;
import android.service.autofill.ConvertCredentialResponse;
import android.service.autofill.FillRequest;
import android.service.autofill.FillResponse;
import android.util.Slog;
-import android.view.autofill.IAutoFillManagerClient;
/**
* Requests autofill response from a Remote Autofill Service. This autofill service can be
@@ -105,8 +105,7 @@ final class SecondaryProviderHandler implements RemoteFillService.FillServiceCal
/**
* Requests a new fill response.
*/
- public void onFillRequest(FillRequest pendingFillRequest, int flag,
- IAutoFillManagerClient client) {
+ public void onFillRequest(FillRequest pendingFillRequest, int flag, IBinder client) {
Slog.v(TAG, "Requesting fill response to secondary provider.");
mLastFlag = flag;
if (mRemoteFillService != null && mRemoteFillService.isCredentialAutofillService()) {
diff --git a/services/autofill/java/com/android/server/autofill/Session.java b/services/autofill/java/com/android/server/autofill/Session.java
index 3a384065217e..cd1ef882868a 100644
--- a/services/autofill/java/com/android/server/autofill/Session.java
+++ b/services/autofill/java/com/android/server/autofill/Session.java
@@ -757,13 +757,14 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState
mPendingInlineSuggestionsRequest, id);
}
mSecondaryProviderHandler.onFillRequest(mPendingFillRequest,
- mPendingFillRequest.getFlags(), mClient);
+ mPendingFillRequest.getFlags(), mClient.asBinder());
} else if (mRemoteFillService != null) {
if (mIsPrimaryCredential) {
mPendingFillRequest = addCredentialManagerDataToClientState(
mPendingFillRequest,
mPendingInlineSuggestionsRequest, id);
- mRemoteFillService.onFillCredentialRequest(mPendingFillRequest, mClient);
+ mRemoteFillService.onFillCredentialRequest(mPendingFillRequest,
+ mClient.asBinder());
} else {
mRemoteFillService.onFillRequest(mPendingFillRequest);
}
@@ -2897,7 +2898,7 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState
+ ", clientState=" + newClientState + ", authenticationId=" + authenticationId);
}
if (Flags.autofillCredmanDevIntegration() && exception != null
- && exception instanceof GetCredentialException) {
+ && !exception.getType().equals(GetCredentialException.TYPE_USER_CANCELED)) {
if (dataset != null && dataset.getFieldIds().size() == 1) {
if (sDebug) {
Slog.d(TAG, "setAuthenticationResultLocked(): result returns with"
@@ -6494,21 +6495,15 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState
}
}
if (exception != null) {
- mClient.onGetCredentialException(id, viewId, exception.getType(),
- exception.getMessage());
+ if (viewId.isVirtualInt()) {
+ sendResponseToViewNode(viewId, /*response=*/ null, exception);
+ } else {
+ mClient.onGetCredentialException(id, viewId, exception.getType(),
+ exception.getMessage());
+ }
} else if (response != null) {
if (viewId.isVirtualInt()) {
- ViewNode viewNode = getViewNodeFromContextsLocked(viewId);
- if (viewNode != null && viewNode.getPendingCredentialCallback() != null) {
- Bundle resultData = new Bundle();
- resultData.putParcelable(
- CredentialProviderService.EXTRA_GET_CREDENTIAL_RESPONSE,
- response);
- viewNode.getPendingCredentialCallback().send(SUCCESS_CREDMAN_SELECTOR,
- resultData);
- } else {
- Slog.w(TAG, "View node not found after GetCredentialResponse");
- }
+ sendResponseToViewNode(viewId, response, /*exception=*/ null);
} else {
mClient.onGetCredentialResponse(id, viewId, response);
}
@@ -6522,6 +6517,30 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState
}
}
+ @GuardedBy("mLock")
+ private void sendResponseToViewNode(AutofillId viewId, GetCredentialResponse response,
+ GetCredentialException exception) {
+ ViewNode viewNode = getViewNodeFromContextsLocked(viewId);
+ if (viewNode != null && viewNode.getPendingCredentialCallback() != null) {
+ Bundle resultData = new Bundle();
+ if (response != null) {
+ resultData.putParcelable(
+ CredentialProviderService.EXTRA_GET_CREDENTIAL_RESPONSE,
+ response);
+ viewNode.getPendingCredentialCallback().send(SUCCESS_CREDMAN_SELECTOR,
+ resultData);
+ } else if (exception != null) {
+ resultData.putStringArray(
+ CredentialProviderService.EXTRA_GET_CREDENTIAL_EXCEPTION,
+ new String[] {exception.getType(), exception.getMessage()});
+ viewNode.getPendingCredentialCallback().send(FAILURE_CREDMAN_SELECTOR,
+ resultData);
+ }
+ } else {
+ Slog.w(TAG, "View node not found after GetCredentialResponse");
+ }
+ }
+
void autoFillApp(Dataset dataset) {
synchronized (mLock) {
if (mDestroyed) {
diff --git a/services/core/Android.bp b/services/core/Android.bp
index e095fa3e8539..300b147509a7 100644
--- a/services/core/Android.bp
+++ b/services/core/Android.bp
@@ -234,7 +234,6 @@ java_library_static {
"android.hidl.manager-V1.2-java",
"cbor-java",
"com.android.media.audio-aconfig-java",
- "dropbox_flags_lib",
"icu4j_calendar_astronomer",
"android.security.aaid_aidl-java",
"netd-client",
diff --git a/services/core/java/com/android/server/display/feature/display_flags.aconfig b/services/core/java/com/android/server/display/feature/display_flags.aconfig
index c68ef9bd4cce..a7dd2430901f 100644
--- a/services/core/java/com/android/server/display/feature/display_flags.aconfig
+++ b/services/core/java/com/android/server/display/feature/display_flags.aconfig
@@ -126,7 +126,7 @@ flag {
name: "even_dimmer"
namespace: "display_manager"
description: "Feature flag for extending the brightness below traditional range"
- bug: "179428400"
+ bug: "294760970"
is_fixed_read_only: true
}
diff --git a/services/core/java/com/android/server/feature/Android.bp b/services/core/java/com/android/server/feature/Android.bp
deleted file mode 100644
index b0fbab6657b0..000000000000
--- a/services/core/java/com/android/server/feature/Android.bp
+++ /dev/null
@@ -1,13 +0,0 @@
-aconfig_declarations {
- name: "dropbox_flags",
- package: "com.android.server.feature.flags",
- container: "system",
- srcs: [
- "dropbox_flags.aconfig",
- ],
-}
-
-java_aconfig_library {
- name: "dropbox_flags_lib",
- aconfig_declarations: "dropbox_flags",
-}
diff --git a/services/core/java/com/android/server/media/MediaServerUtils.java b/services/core/java/com/android/server/media/MediaServerUtils.java
index 6a954d66d18d..f16d426d634a 100644
--- a/services/core/java/com/android/server/media/MediaServerUtils.java
+++ b/services/core/java/com/android/server/media/MediaServerUtils.java
@@ -74,10 +74,7 @@ import java.util.List;
}
final PackageManagerInternal packageManagerInternal =
LocalServices.getService(PackageManagerInternal.class);
- final int actualUid =
- packageManagerInternal.getPackageUid(
- packageName, 0 /* flags */, UserHandle.getUserId(uid));
- if (!UserHandle.isSameApp(uid, actualUid)) {
+ if (!packageManagerInternal.isSameApp(packageName, uid, UserHandle.getUserId(uid))) {
String[] uidPackages = context.getPackageManager().getPackagesForUid(uid);
throw new IllegalArgumentException(
"packageName does not belong to the calling uid; "
diff --git a/services/core/java/com/android/server/media/MediaSessionRecord.java b/services/core/java/com/android/server/media/MediaSessionRecord.java
index a3c5d2d336f2..3d6855547bcd 100644
--- a/services/core/java/com/android/server/media/MediaSessionRecord.java
+++ b/services/core/java/com/android/server/media/MediaSessionRecord.java
@@ -66,6 +66,7 @@ import android.net.Uri;
import android.os.Binder;
import android.os.Build;
import android.os.Bundle;
+import android.os.DeadObjectException;
import android.os.Handler;
import android.os.IBinder;
import android.os.Looper;
@@ -89,6 +90,7 @@ import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.util.ArrayList;
import java.util.Arrays;
+import java.util.Collection;
import java.util.List;
import java.util.NoSuchElementException;
import java.util.Objects;
@@ -189,9 +191,6 @@ public class MediaSessionRecord extends MediaSessionRecordImpl implements IBinde
private final ForegroundServiceDelegationOptions mForegroundServiceDelegationOptions;
private final Object mLock = new Object();
- // This field is partially guarded by mLock. Writes and non-atomic iterations (for example:
- // index-based-iterations) must be guarded by mLock. But it is safe to acquire an iterator
- // without acquiring mLock.
private final CopyOnWriteArrayList<ISessionControllerCallbackHolder>
mControllerCallbackHolders = new CopyOnWriteArrayList<>();
@@ -887,9 +886,24 @@ public class MediaSessionRecord extends MediaSessionRecordImpl implements IBinde
}
playbackState = mPlaybackState;
}
- performOnCallbackHolders(
- "pushPlaybackStateUpdate",
- holder -> holder.mCallback.onPlaybackStateChanged(playbackState));
+ Collection<ISessionControllerCallbackHolder> deadCallbackHolders = null;
+ for (ISessionControllerCallbackHolder holder : mControllerCallbackHolders) {
+ try {
+ holder.mCallback.onPlaybackStateChanged(playbackState);
+ } catch (DeadObjectException e) {
+ if (deadCallbackHolders == null) {
+ deadCallbackHolders = new ArrayList<>();
+ }
+ deadCallbackHolders.add(holder);
+ logCallbackException("Removing dead callback in pushPlaybackStateUpdate", holder,
+ e);
+ } catch (RemoteException e) {
+ logCallbackException("unexpected exception in pushPlaybackStateUpdate", holder, e);
+ }
+ }
+ if (deadCallbackHolders != null) {
+ removeControllerHoldersSafely(deadCallbackHolders);
+ }
}
private void pushMetadataUpdate() {
@@ -900,8 +914,23 @@ public class MediaSessionRecord extends MediaSessionRecordImpl implements IBinde
}
metadata = mMetadata;
}
- performOnCallbackHolders(
- "pushMetadataUpdate", holder -> holder.mCallback.onMetadataChanged(metadata));
+ Collection<ISessionControllerCallbackHolder> deadCallbackHolders = null;
+ for (ISessionControllerCallbackHolder holder : mControllerCallbackHolders) {
+ try {
+ holder.mCallback.onMetadataChanged(metadata);
+ } catch (DeadObjectException e) {
+ if (deadCallbackHolders == null) {
+ deadCallbackHolders = new ArrayList<>();
+ }
+ deadCallbackHolders.add(holder);
+ logCallbackException("Removing dead callback in pushMetadataUpdate", holder, e);
+ } catch (RemoteException e) {
+ logCallbackException("unexpected exception in pushMetadataUpdate", holder, e);
+ }
+ }
+ if (deadCallbackHolders != null) {
+ removeControllerHoldersSafely(deadCallbackHolders);
+ }
}
private void pushQueueUpdate() {
@@ -912,18 +941,31 @@ public class MediaSessionRecord extends MediaSessionRecordImpl implements IBinde
}
toSend = mQueue == null ? null : new ArrayList<>(mQueue);
}
- performOnCallbackHolders(
- "pushQueueUpdate",
- holder -> {
- ParceledListSlice<QueueItem> parcelableQueue = null;
- if (toSend != null) {
- parcelableQueue = new ParceledListSlice<>(toSend);
- // Limit the size of initial Parcel to prevent binder buffer overflow
- // as onQueueChanged is an async binder call.
- parcelableQueue.setInlineCountLimit(1);
- }
- holder.mCallback.onQueueChanged(parcelableQueue);
- });
+ Collection<ISessionControllerCallbackHolder> deadCallbackHolders = null;
+ for (ISessionControllerCallbackHolder holder : mControllerCallbackHolders) {
+ ParceledListSlice<QueueItem> parcelableQueue = null;
+ if (toSend != null) {
+ parcelableQueue = new ParceledListSlice<>(toSend);
+ // Limit the size of initial Parcel to prevent binder buffer overflow
+ // as onQueueChanged is an async binder call.
+ parcelableQueue.setInlineCountLimit(1);
+ }
+
+ try {
+ holder.mCallback.onQueueChanged(parcelableQueue);
+ } catch (DeadObjectException e) {
+ if (deadCallbackHolders == null) {
+ deadCallbackHolders = new ArrayList<>();
+ }
+ deadCallbackHolders.add(holder);
+ logCallbackException("Removing dead callback in pushQueueUpdate", holder, e);
+ } catch (RemoteException e) {
+ logCallbackException("unexpected exception in pushQueueUpdate", holder, e);
+ }
+ }
+ if (deadCallbackHolders != null) {
+ removeControllerHoldersSafely(deadCallbackHolders);
+ }
}
private void pushQueueTitleUpdate() {
@@ -934,8 +976,23 @@ public class MediaSessionRecord extends MediaSessionRecordImpl implements IBinde
}
queueTitle = mQueueTitle;
}
- performOnCallbackHolders(
- "pushQueueTitleUpdate", holder -> holder.mCallback.onQueueTitleChanged(queueTitle));
+ Collection<ISessionControllerCallbackHolder> deadCallbackHolders = null;
+ for (ISessionControllerCallbackHolder holder : mControllerCallbackHolders) {
+ try {
+ holder.mCallback.onQueueTitleChanged(queueTitle);
+ } catch (DeadObjectException e) {
+ if (deadCallbackHolders == null) {
+ deadCallbackHolders = new ArrayList<>();
+ }
+ deadCallbackHolders.add(holder);
+ logCallbackException("Removing dead callback in pushQueueTitleUpdate", holder, e);
+ } catch (RemoteException e) {
+ logCallbackException("unexpected exception in pushQueueTitleUpdate", holder, e);
+ }
+ }
+ if (deadCallbackHolders != null) {
+ removeControllerHoldersSafely(deadCallbackHolders);
+ }
}
private void pushExtrasUpdate() {
@@ -946,8 +1003,23 @@ public class MediaSessionRecord extends MediaSessionRecordImpl implements IBinde
}
extras = mExtras;
}
- performOnCallbackHolders(
- "pushExtrasUpdate", holder -> holder.mCallback.onExtrasChanged(extras));
+ Collection<ISessionControllerCallbackHolder> deadCallbackHolders = null;
+ for (ISessionControllerCallbackHolder holder : mControllerCallbackHolders) {
+ try {
+ holder.mCallback.onExtrasChanged(extras);
+ } catch (DeadObjectException e) {
+ if (deadCallbackHolders == null) {
+ deadCallbackHolders = new ArrayList<>();
+ }
+ deadCallbackHolders.add(holder);
+ logCallbackException("Removing dead callback in pushExtrasUpdate", holder, e);
+ } catch (RemoteException e) {
+ logCallbackException("unexpected exception in pushExtrasUpdate", holder, e);
+ }
+ }
+ if (deadCallbackHolders != null) {
+ removeControllerHoldersSafely(deadCallbackHolders);
+ }
}
private void pushVolumeUpdate() {
@@ -958,8 +1030,23 @@ public class MediaSessionRecord extends MediaSessionRecordImpl implements IBinde
}
info = getVolumeAttributes();
}
- performOnCallbackHolders(
- "pushVolumeUpdate", holder -> holder.mCallback.onVolumeInfoChanged(info));
+ Collection<ISessionControllerCallbackHolder> deadCallbackHolders = null;
+ for (ISessionControllerCallbackHolder holder : mControllerCallbackHolders) {
+ try {
+ holder.mCallback.onVolumeInfoChanged(info);
+ } catch (DeadObjectException e) {
+ if (deadCallbackHolders == null) {
+ deadCallbackHolders = new ArrayList<>();
+ }
+ deadCallbackHolders.add(holder);
+ logCallbackException("Removing dead callback in pushVolumeUpdate", holder, e);
+ } catch (RemoteException e) {
+ logCallbackException("unexpected exception in pushVolumeUpdate", holder, e);
+ }
+ }
+ if (deadCallbackHolders != null) {
+ removeControllerHoldersSafely(deadCallbackHolders);
+ }
}
private void pushEvent(String event, Bundle data) {
@@ -968,7 +1055,23 @@ public class MediaSessionRecord extends MediaSessionRecordImpl implements IBinde
return;
}
}
- performOnCallbackHolders("pushEvent", holder -> holder.mCallback.onEvent(event, data));
+ Collection<ISessionControllerCallbackHolder> deadCallbackHolders = null;
+ for (ISessionControllerCallbackHolder holder : mControllerCallbackHolders) {
+ try {
+ holder.mCallback.onEvent(event, data);
+ } catch (DeadObjectException e) {
+ if (deadCallbackHolders == null) {
+ deadCallbackHolders = new ArrayList<>();
+ }
+ deadCallbackHolders.add(holder);
+ logCallbackException("Removing dead callback in pushEvent", holder, e);
+ } catch (RemoteException e) {
+ logCallbackException("unexpected exception in pushEvent", holder, e);
+ }
+ }
+ if (deadCallbackHolders != null) {
+ removeControllerHoldersSafely(deadCallbackHolders);
+ }
}
private void pushSessionDestroyed() {
@@ -979,37 +1082,20 @@ public class MediaSessionRecord extends MediaSessionRecordImpl implements IBinde
return;
}
}
- performOnCallbackHolders(
- "pushSessionDestroyed",
- holder -> {
- holder.mCallback.asBinder().unlinkToDeath(holder.mDeathMonitor, 0);
- holder.mCallback.onSessionDestroyed();
- });
- // After notifying clear all listeners
- synchronized (mLock) {
- mControllerCallbackHolders.clear();
- }
- }
-
- private interface ControllerCallbackCall {
-
- void performOn(ISessionControllerCallbackHolder holder) throws RemoteException;
- }
-
- private void performOnCallbackHolders(String operationName, ControllerCallbackCall call) {
- ArrayList<ISessionControllerCallbackHolder> deadCallbackHolders = new ArrayList<>();
for (ISessionControllerCallbackHolder holder : mControllerCallbackHolders) {
try {
- call.performOn(holder);
- } catch (RemoteException | NoSuchElementException exception) {
- deadCallbackHolders.add(holder);
- logCallbackException(
- "Exception while executing: " + operationName, holder, exception);
+ holder.mCallback.asBinder().unlinkToDeath(holder.mDeathMonitor, 0);
+ holder.mCallback.onSessionDestroyed();
+ } catch (NoSuchElementException e) {
+ logCallbackException("error unlinking to binder death", holder, e);
+ } catch (DeadObjectException e) {
+ logCallbackException("Removing dead callback in pushSessionDestroyed", holder, e);
+ } catch (RemoteException e) {
+ logCallbackException("unexpected exception in pushSessionDestroyed", holder, e);
}
}
- synchronized (mLock) {
- mControllerCallbackHolders.removeAll(deadCallbackHolders);
- }
+ // After notifying clear all listeners
+ removeControllerHoldersSafely(null);
}
private PlaybackState getStateWithUpdatedPosition() {
@@ -1057,6 +1143,17 @@ public class MediaSessionRecord extends MediaSessionRecordImpl implements IBinde
return -1;
}
+ private void removeControllerHoldersSafely(
+ Collection<ISessionControllerCallbackHolder> holders) {
+ synchronized (mLock) {
+ if (holders == null) {
+ mControllerCallbackHolders.clear();
+ } else {
+ mControllerCallbackHolders.removeAll(holders);
+ }
+ }
+ }
+
private PlaybackInfo getVolumeAttributes() {
int volumeType;
AudioAttributes attributes;
diff --git a/services/core/java/com/android/server/notification/NotificationManagerService.java b/services/core/java/com/android/server/notification/NotificationManagerService.java
index ebea05ddfd0c..0c658379fa4d 100755
--- a/services/core/java/com/android/server/notification/NotificationManagerService.java
+++ b/services/core/java/com/android/server/notification/NotificationManagerService.java
@@ -98,6 +98,7 @@ import static android.os.UserHandle.USER_NULL;
import static android.os.UserHandle.USER_SYSTEM;
import static android.service.notification.Flags.callstyleCallbackApi;
import static android.service.notification.Flags.redactSensitiveNotificationsFromUntrustedListeners;
+import static android.service.notification.Flags.redactSensitiveNotificationsBigTextStyle;
import static android.service.notification.NotificationListenerService.FLAG_FILTER_TYPE_ALERTING;
import static android.service.notification.NotificationListenerService.FLAG_FILTER_TYPE_CONVERSATIONS;
import static android.service.notification.NotificationListenerService.FLAG_FILTER_TYPE_ONGOING;
@@ -139,6 +140,7 @@ import static android.service.notification.NotificationListenerService.TRIM_FULL
import static android.service.notification.NotificationListenerService.TRIM_LIGHT;
import static android.view.WindowManager.LayoutParams.TYPE_TOAST;
import static android.view.contentprotection.flags.Flags.rapidClearNotificationsByListenerAppOpEnabled;
+
import static com.android.internal.util.FrameworkStatsLog.DND_MODE_RULE;
import static com.android.internal.util.FrameworkStatsLog.PACKAGE_NOTIFICATION_CHANNEL_GROUP_PREFERENCES;
import static com.android.internal.util.FrameworkStatsLog.PACKAGE_NOTIFICATION_CHANNEL_PREFERENCES;
@@ -306,6 +308,7 @@ import android.view.Display;
import android.view.accessibility.AccessibilityManager;
import android.widget.RemoteViews;
import android.widget.Toast;
+
import com.android.internal.R;
import com.android.internal.annotations.GuardedBy;
import com.android.internal.annotations.VisibleForTesting;
@@ -357,7 +360,9 @@ import com.android.server.utils.quota.MultiRateLimiter;
import com.android.server.wm.ActivityTaskManagerInternal;
import com.android.server.wm.BackgroundActivityStartCallback;
import com.android.server.wm.WindowManagerInternal;
+
import libcore.io.IoUtils;
+
import org.json.JSONException;
import org.json.JSONObject;
import org.xmlpull.v1.XmlPullParserException;
@@ -11775,10 +11780,18 @@ public class NotificationManagerService extends SystemService {
}
if (lifetimeExtensionRefactor()) {
+ if (sendRedacted && redactedSbn == null) {
+ redactedSbn = redactStatusBarNotification(sbn);
+ redactedCache = new TrimCache(redactedSbn);
+ }
+ final StatusBarNotification sbnToPost = sendRedacted
+ ? redactedCache.ForListener(info) : trimCache.ForListener(info);
+
// Checks if this is a request to notify system UI about a notification that
// has been lifetime extended.
// (We only need to check old for the flag, because in both cancellation and
- // update cases, old should have the flag.)
+ // update cases, old should have the flag, whereas in update cases the
+ // new will NOT have the flag.)
// If it is such a request, and this is system UI, we send the post request
// only to System UI, and break as we don't need to continue checking other
// Managed Services.
@@ -11786,7 +11799,7 @@ public class NotificationManagerService extends SystemService {
&& (old.getNotification().flags
& FLAG_LIFETIME_EXTENDED_BY_DIRECT_REPLY) > 0) {
final NotificationRankingUpdate update = makeRankingUpdateLocked(info);
- listenerCalls.add(() -> notifyPosted(info, oldSbn, update));
+ listenerCalls.add(() -> notifyPosted(info, sbnToPost, update));
break;
}
}
@@ -11917,6 +11930,14 @@ public class NotificationManagerService extends SystemService {
redactedText, System.currentTimeMillis(), empty));
redactedNotifBuilder.setStyle(messageStyle);
}
+ if (redactSensitiveNotificationsBigTextStyle()
+ && oldNotif.isStyle(Notification.BigTextStyle.class)) {
+ Notification.BigTextStyle bigTextStyle = new Notification.BigTextStyle();
+ bigTextStyle.bigText(mContext.getString(R.string.redacted_notification_message));
+ bigTextStyle.setBigContentTitle("");
+ bigTextStyle.setSummaryText("");
+ redactedNotifBuilder.setStyle(bigTextStyle);
+ }
Notification redacted = redactedNotifBuilder.build();
// Notification extras can't always be overridden by a builder (configured by a system
diff --git a/services/core/java/com/android/server/notification/NotificationRecord.java b/services/core/java/com/android/server/notification/NotificationRecord.java
index c69bead309f1..8dd3e52c1039 100644
--- a/services/core/java/com/android/server/notification/NotificationRecord.java
+++ b/services/core/java/com/android/server/notification/NotificationRecord.java
@@ -66,15 +66,16 @@ import android.service.notification.StatusBarNotification;
import android.text.TextUtils;
import android.util.ArraySet;
import android.util.Log;
-import android.util.TimeUtils;
import android.util.proto.ProtoOutputStream;
import android.widget.RemoteViews;
+
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.logging.MetricsLogger;
import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
import com.android.server.EventLogTags;
import com.android.server.LocalServices;
import com.android.server.uri.UriGrantsManagerInternal;
+
import dalvik.annotation.optimization.NeverCompile;
import java.io.PrintWriter;
diff --git a/services/core/java/com/android/server/pm/PackageManagerNative.java b/services/core/java/com/android/server/pm/PackageManagerNative.java
index d035084d8b02..66ecd6e67e56 100644
--- a/services/core/java/com/android/server/pm/PackageManagerNative.java
+++ b/services/core/java/com/android/server/pm/PackageManagerNative.java
@@ -68,6 +68,11 @@ final class PackageManagerNative extends IPackageManagerNative.Stub {
}
}
+ @Override
+ public int getPackageUid(String packageName, long flags, int userId) throws RemoteException {
+ return mPm.snapshotComputer().getPackageUid(packageName, flags, userId);
+ }
+
// NB: this differentiates between preloads and sideloads
@Override
public String getInstallerForPackage(String packageName) throws RemoteException {
diff --git a/services/core/java/com/android/server/power/hint/HintManagerService.java b/services/core/java/com/android/server/power/hint/HintManagerService.java
index 101983e6ecf1..6e17fd3d8c87 100644
--- a/services/core/java/com/android/server/power/hint/HintManagerService.java
+++ b/services/core/java/com/android/server/power/hint/HintManagerService.java
@@ -20,11 +20,14 @@ import static com.android.internal.util.ConcurrentUtils.DIRECT_EXECUTOR;
import static com.android.server.power.hint.Flags.powerhintThreadCleanup;
import android.annotation.NonNull;
+import android.annotation.Nullable;
import android.app.ActivityManager;
import android.app.ActivityManagerInternal;
import android.app.StatsManager;
import android.app.UidObserver;
import android.content.Context;
+import android.hardware.power.SessionConfig;
+import android.hardware.power.SessionTag;
import android.hardware.power.WorkDuration;
import android.os.Binder;
import android.os.Handler;
@@ -67,6 +70,7 @@ import java.util.NoSuchElementException;
import java.util.Objects;
import java.util.Set;
import java.util.concurrent.TimeUnit;
+import java.util.concurrent.atomic.AtomicBoolean;
/** An hint service implementation that runs in System Server process. */
public final class HintManagerService extends SystemService {
@@ -87,7 +91,7 @@ public final class HintManagerService extends SystemService {
@GuardedBy("mLock")
private final ArrayMap<Integer, ArrayMap<IBinder, ArraySet<AppHintSession>>> mActiveSessions;
- /** Lock to protect HAL handles and listen list. */
+ /** Lock to protect mActiveSessions. */
private final Object mLock = new Object();
@GuardedBy("mNonIsolatedTidsLock")
@@ -104,6 +108,8 @@ public final class HintManagerService extends SystemService {
private final Context mContext;
+ private AtomicBoolean mConfigCreationSupport = new AtomicBoolean(true);
+
private static final String PROPERTY_SF_ENABLE_CPU_HINT = "debug.sf.enable_adpf_cpu_hint";
private static final String PROPERTY_HWUI_ENABLE_HINT_MANAGER = "debug.hwui.use_hint_manager";
@@ -217,6 +223,9 @@ public final class HintManagerService extends SystemService {
private static native long nativeCreateHintSession(int tgid, int uid, int[] tids,
long durationNanos);
+ private static native long nativeCreateHintSessionWithConfig(int tgid, int uid, int[] tids,
+ long durationNanos, int tag, SessionConfig config);
+
private static native void nativePauseHintSession(long halPtr);
private static native void nativeResumeHintSession(long halPtr);
@@ -253,6 +262,12 @@ public final class HintManagerService extends SystemService {
return nativeCreateHintSession(tgid, uid, tids, durationNanos);
}
+ /** Wrapper for HintManager.nativeCreateHintSessionWithConfig */
+ public long halCreateHintSessionWithConfig(
+ int tgid, int uid, int[] tids, long durationNanos, int tag, SessionConfig config) {
+ return nativeCreateHintSessionWithConfig(tgid, uid, tids, durationNanos, tag, config);
+ }
+
/** Wrapper for HintManager.nativePauseHintSession */
public void halPauseHintSession(long halPtr) {
nativePauseHintSession(halPtr);
@@ -612,8 +627,12 @@ public final class HintManagerService extends SystemService {
@VisibleForTesting
final class BinderService extends IHintManager.Stub {
@Override
- public IHintSession createHintSession(IBinder token, int[] tids, long durationNanos) {
- if (!isHalSupported()) return null;
+ public IHintSession createHintSessionWithConfig(@NonNull IBinder token,
+ @NonNull int[] tids, long durationNanos, @SessionTag int tag,
+ @Nullable SessionConfig config) {
+ if (!isHalSupported()) {
+ throw new UnsupportedOperationException("PowerHAL is not supported!");
+ }
java.util.Objects.requireNonNull(token);
java.util.Objects.requireNonNull(tids);
@@ -634,8 +653,35 @@ public final class HintManagerService extends SystemService {
throw new SecurityException(errMsg);
}
- long halSessionPtr = mNativeWrapper.halCreateHintSession(callingTgid, callingUid,
- tids, durationNanos);
+ Long halSessionPtr = null;
+ if (mConfigCreationSupport.get()) {
+ try {
+ halSessionPtr = mNativeWrapper.halCreateHintSessionWithConfig(
+ callingTgid, callingUid, tids, durationNanos, tag, config);
+ } catch (UnsupportedOperationException e) {
+ mConfigCreationSupport.set(false);
+ } catch (IllegalStateException e) {
+ Slog.e("createHintSessionWithConfig failed: ", e.getMessage());
+ throw new IllegalStateException(
+ "createHintSessionWithConfig failed: " + e.getMessage());
+ }
+ }
+
+ if (halSessionPtr == null) {
+ try {
+ halSessionPtr = mNativeWrapper.halCreateHintSession(callingTgid,
+ callingUid, tids, durationNanos);
+ } catch (UnsupportedOperationException e) {
+ Slog.w("createHintSession unsupported: ", e.getMessage());
+ throw new UnsupportedOperationException(
+ "createHintSession unsupported: " + e.getMessage());
+ } catch (IllegalStateException e) {
+ Slog.e("createHintSession failed: ", e.getMessage());
+ throw new IllegalStateException(
+ "createHintSession failed: " + e.getMessage());
+ }
+ }
+
if (powerhintThreadCleanup()) {
synchronized (mNonIsolatedTidsLock) {
for (int i = nonIsolated.size() - 1; i >= 0; i--) {
@@ -644,9 +690,6 @@ public final class HintManagerService extends SystemService {
}
}
}
- if (halSessionPtr == 0) {
- return null;
- }
AppHintSession hs = new AppHintSession(callingUid, callingTgid, tids, token,
halSessionPtr, durationNanos);
diff --git a/services/core/java/com/android/server/wm/ActivityRecord.java b/services/core/java/com/android/server/wm/ActivityRecord.java
index 54e932a80ee9..8c998c3bfdb9 100644
--- a/services/core/java/com/android/server/wm/ActivityRecord.java
+++ b/services/core/java/com/android/server/wm/ActivityRecord.java
@@ -8363,7 +8363,7 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A
* aspect ratio.
*/
boolean shouldCreateCompatDisplayInsets() {
- if (mLetterboxUiController.shouldApplyUserFullscreenOverride()) {
+ if (mLetterboxUiController.hasFullscreenOverride()) {
// If the user has forced the applications aspect ratio to be fullscreen, don't use size
// compatibility mode in any situation. The user has been warned and therefore accepts
// the risk of the application misbehaving.
@@ -8378,7 +8378,11 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A
default:
// Fall through
}
- if (inMultiWindowMode() || getWindowConfiguration().hasWindowDecorCaption()) {
+ // Use root activity's info for tasks in multi-window mode, or fullscreen tasks in freeform
+ // task display areas, to ensure visual consistency across activity launches and exits in
+ // the same task.
+ final TaskDisplayArea tda = getTaskDisplayArea();
+ if (inMultiWindowMode() || (tda != null && tda.inFreeformWindowingMode())) {
final ActivityRecord root = task != null ? task.getRootActivity() : null;
if (root != null && root != this && !root.shouldCreateCompatDisplayInsets()) {
// If the root activity doesn't use size compatibility mode, the activities above
diff --git a/services/core/java/com/android/server/wm/ConfigurationContainer.java b/services/core/java/com/android/server/wm/ConfigurationContainer.java
index be7c18c49373..31754bf67cc8 100644
--- a/services/core/java/com/android/server/wm/ConfigurationContainer.java
+++ b/services/core/java/com/android/server/wm/ConfigurationContainer.java
@@ -467,13 +467,6 @@ public abstract class ConfigurationContainer<E extends ConfigurationContainer> {
onRequestedOverrideConfigurationChanged(mRequestsTmpConfig);
}
- /** Sets the windowing mode for the configuration container. */
- void setDisplayWindowingMode(int windowingMode) {
- mRequestsTmpConfig.setTo(getRequestedOverrideConfiguration());
- mRequestsTmpConfig.windowConfiguration.setDisplayWindowingMode(windowingMode);
- onRequestedOverrideConfigurationChanged(mRequestsTmpConfig);
- }
-
/**
* Returns true if this container is currently in multi-window mode. I.e. sharing the screen
* with another activity.
diff --git a/services/core/java/com/android/server/wm/DisplayContent.java b/services/core/java/com/android/server/wm/DisplayContent.java
index 00d42e0eb109..6c757a31f323 100644
--- a/services/core/java/com/android/server/wm/DisplayContent.java
+++ b/services/core/java/com/android/server/wm/DisplayContent.java
@@ -2456,7 +2456,6 @@ class DisplayContent extends RootDisplayArea implements WindowManagerPolicy.Disp
config.windowConfiguration.setBounds(mTmpRect);
config.windowConfiguration.setMaxBounds(mTmpRect);
config.windowConfiguration.setWindowingMode(getWindowingMode());
- config.windowConfiguration.setDisplayWindowingMode(getWindowingMode());
computeScreenAppConfiguration(config, dw, dh, displayInfo.rotation);
@@ -2834,11 +2833,6 @@ class DisplayContent extends RootDisplayArea implements WindowManagerPolicy.Disp
}
}
- @Override
- void setDisplayWindowingMode(int windowingMode) {
- setWindowingMode(windowingMode);
- }
-
/**
* See {@code WindowState#applyImeWindowsIfNeeded} for the details that we won't traverse the
* IME window in some cases.
@@ -2956,7 +2950,6 @@ class DisplayContent extends RootDisplayArea implements WindowManagerPolicy.Disp
void onDisplayChanged(DisplayContent dc) {
super.onDisplayChanged(dc);
updateSystemGestureExclusionLimit();
- updateKeepClearAreas();
}
void updateSystemGestureExclusionLimit() {
@@ -4035,7 +4028,6 @@ class DisplayContent extends RootDisplayArea implements WindowManagerPolicy.Disp
}
adjustForImeIfNeeded();
- updateKeepClearAreas();
// We may need to schedule some toast windows to be removed. The toasts for an app that
// does not have input focus are removed within a timeout to prevent apps to redress
@@ -6156,6 +6148,7 @@ class DisplayContent extends RootDisplayArea implements WindowManagerPolicy.Disp
Region touchableRegion = Region.obtain();
w.getEffectiveTouchableRegion(touchableRegion);
RegionUtils.forEachRect(touchableRegion, rect -> outUnrestricted.add(rect));
+ touchableRegion.recycle();
}
}
diff --git a/services/core/java/com/android/server/wm/InsetsStateController.java b/services/core/java/com/android/server/wm/InsetsStateController.java
index 3a04bcd1df7d..dfee16440518 100644
--- a/services/core/java/com/android/server/wm/InsetsStateController.java
+++ b/services/core/java/com/android/server/wm/InsetsStateController.java
@@ -224,7 +224,6 @@ class InsetsStateController {
if (changed) {
notifyInsetsChanged();
mDisplayContent.updateSystemGestureExclusion();
- mDisplayContent.updateKeepClearAreas();
mDisplayContent.getDisplayPolicy().updateSystemBarAttributes();
}
}
diff --git a/services/core/java/com/android/server/wm/TaskDisplayArea.java b/services/core/java/com/android/server/wm/TaskDisplayArea.java
index 90a3b25303f5..2c27b98250c9 100644
--- a/services/core/java/com/android/server/wm/TaskDisplayArea.java
+++ b/services/core/java/com/android/server/wm/TaskDisplayArea.java
@@ -1880,7 +1880,6 @@ final class TaskDisplayArea extends DisplayArea<WindowContainer> {
mTempConfiguration.setTo(getRequestedOverrideConfiguration());
WindowConfiguration tempRequestWindowConfiguration = mTempConfiguration.windowConfiguration;
tempRequestWindowConfiguration.setWindowingMode(windowingMode);
- tempRequestWindowConfiguration.setDisplayWindowingMode(windowingMode);
onRequestedOverrideConfigurationChanged(mTempConfiguration);
}
diff --git a/services/core/java/com/android/server/wm/TaskFragment.java b/services/core/java/com/android/server/wm/TaskFragment.java
index fc85af5c2303..7a8bea0c726b 100644
--- a/services/core/java/com/android/server/wm/TaskFragment.java
+++ b/services/core/java/com/android/server/wm/TaskFragment.java
@@ -1971,6 +1971,15 @@ class TaskFragment extends WindowContainer<WindowContainer> {
return SCREEN_ORIENTATION_UNSET;
}
+ @ActivityInfo.ScreenOrientation
+ @Override
+ protected int getOverrideOrientation() {
+ if (isEmbedded() && !isVisibleRequested()) {
+ return SCREEN_ORIENTATION_UNSPECIFIED;
+ }
+ return super.getOverrideOrientation();
+ }
+
/**
* Whether or not to allow this container to specify an app requested orientation.
*
diff --git a/services/core/java/com/android/server/wm/WindowOrganizerController.java b/services/core/java/com/android/server/wm/WindowOrganizerController.java
index 41804751ed4b..7afd34c90b0d 100644
--- a/services/core/java/com/android/server/wm/WindowOrganizerController.java
+++ b/services/core/java/com/android/server/wm/WindowOrganizerController.java
@@ -1025,7 +1025,9 @@ class WindowOrganizerController extends IWindowOrganizerController.Stub
break;
}
final Task task = wc.asTask();
-
+ if (task.isVisibleRequested() || task.isVisible()) {
+ effects |= TRANSACT_EFFECTS_LIFECYCLE;
+ }
if (task.isLeafTask()) {
mService.mTaskSupervisor
.removeTask(task, true, REMOVE_FROM_RECENTS, "remove-task"
diff --git a/services/core/jni/com_android_server_hint_HintManagerService.cpp b/services/core/jni/com_android_server_hint_HintManagerService.cpp
index 5b8ef19ea179..be188358b90b 100644
--- a/services/core/jni/com_android_server_hint_HintManagerService.cpp
+++ b/services/core/jni/com_android_server_hint_HintManagerService.cpp
@@ -34,13 +34,13 @@
#include "jni.h"
+using aidl::android::hardware::power::SessionConfig;
using aidl::android::hardware::power::SessionHint;
using aidl::android::hardware::power::SessionMode;
+using aidl::android::hardware::power::SessionTag;
using aidl::android::hardware::power::WorkDuration;
using android::power::PowerHintSessionWrapper;
-using android::base::StringPrintf;
-
namespace android {
static struct {
@@ -66,6 +66,15 @@ static int64_t getHintSessionPreferredRate() {
return rate;
}
+void throwUnsupported(JNIEnv* env, const char* msg) {
+ env->ThrowNew(env->FindClass("java/lang/UnsupportedOperationException"), msg);
+}
+
+void throwFailed(JNIEnv* env, const char* msg) {
+ // We throw IllegalStateException for all errors other than the "unsupported" ones
+ env->ThrowNew(env->FindClass("java/lang/IllegalStateException"), msg);
+}
+
static jlong createHintSession(JNIEnv* env, int32_t tgid, int32_t uid,
std::vector<int32_t>& threadIds, int64_t durationNanos) {
auto result = gPowerHalController.createHintSession(tgid, uid, threadIds, durationNanos);
@@ -76,10 +85,38 @@ static jlong createHintSession(JNIEnv* env, int32_t tgid, int32_t uid,
return res.second ? session_ptr : 0;
} else if (result.isFailed()) {
ALOGW("createHintSession failed with message: %s", result.errorMessage());
+ throwFailed(env, result.errorMessage());
+ } else if (result.isUnsupported()) {
+ throwUnsupported(env, result.errorMessage());
+ return -1;
}
return 0;
}
+static jlong createHintSessionWithConfig(JNIEnv* env, int32_t tgid, int32_t uid,
+ std::vector<int32_t> threadIds, int64_t durationNanos,
+ int32_t sessionTag, SessionConfig& config) {
+ auto result =
+ gPowerHalController.createHintSessionWithConfig(tgid, uid, threadIds, durationNanos,
+ static_cast<SessionTag>(sessionTag),
+ &config);
+ if (result.isOk()) {
+ jlong session_ptr = reinterpret_cast<jlong>(result.value().get());
+ std::scoped_lock sessionLock(gSessionMapLock);
+ auto res = gSessionMap.insert({session_ptr, result.value()});
+ if (!res.second) {
+ throwFailed(env, "PowerHAL provided an invalid session");
+ return 0;
+ }
+ return session_ptr;
+ } else if (result.isUnsupported()) {
+ throwUnsupported(env, result.errorMessage());
+ return -1;
+ }
+ throwFailed(env, result.errorMessage());
+ return 0;
+}
+
static void pauseHintSession(JNIEnv* env, int64_t session_ptr) {
auto appSession = reinterpret_cast<PowerHintSessionWrapper*>(session_ptr);
appSession->pause();
@@ -136,13 +173,34 @@ static jlong nativeCreateHintSession(JNIEnv* env, jclass /* clazz */, jint tgid,
jintArray tids, jlong durationNanos) {
ScopedIntArrayRO tidArray(env, tids);
if (nullptr == tidArray.get() || tidArray.size() == 0) {
- ALOGW("GetIntArrayElements returns nullptr.");
+ ALOGW("nativeCreateHintSession: GetIntArrayElements returns nullptr.");
return 0;
}
std::vector<int32_t> threadIds(tidArray.get(), tidArray.get() + tidArray.size());
return createHintSession(env, tgid, uid, threadIds, durationNanos);
}
+static jlong nativeCreateHintSessionWithConfig(JNIEnv* env, jclass /* clazz */, jint tgid, jint uid,
+ jintArray tids, jlong durationNanos, jint sessionTag,
+ jobject sessionConfig) {
+ ScopedIntArrayRO tidArray(env, tids);
+ if (nullptr == tidArray.get() || tidArray.size() == 0) {
+ ALOGW("nativeCreateHintSessionWithConfig: GetIntArrayElements returns nullptr.");
+ return 0;
+ }
+ std::vector<int32_t> threadIds(tidArray.get(), tidArray.get() + tidArray.size());
+ SessionConfig config;
+ jlong out = createHintSessionWithConfig(env, tgid, uid, std::move(threadIds), durationNanos,
+ sessionTag, config);
+ if (out <= 0) {
+ return out;
+ }
+ static jclass configClass = env->FindClass("android/hardware/power/SessionConfig");
+ static jfieldID fid = env->GetFieldID(configClass, "id", "J");
+ env->SetLongField(sessionConfig, fid, config.id);
+ return out;
+}
+
static void nativePauseHintSession(JNIEnv* env, jclass /* clazz */, jlong session_ptr) {
pauseHintSession(env, session_ptr);
}
@@ -215,6 +273,8 @@ static const JNINativeMethod sHintManagerServiceMethods[] = {
{"nativeInit", "()V", (void*)nativeInit},
{"nativeGetHintSessionPreferredRate", "()J", (void*)nativeGetHintSessionPreferredRate},
{"nativeCreateHintSession", "(II[IJ)J", (void*)nativeCreateHintSession},
+ {"nativeCreateHintSessionWithConfig", "(II[IJILandroid/hardware/power/SessionConfig;)J",
+ (void*)nativeCreateHintSessionWithConfig},
{"nativePauseHintSession", "(J)V", (void*)nativePauseHintSession},
{"nativeResumeHintSession", "(J)V", (void*)nativeResumeHintSession},
{"nativeCloseHintSession", "(J)V", (void*)nativeCloseHintSession},
diff --git a/services/foldables/devicestateprovider/src/com/android/server/policy/BookStyleDeviceStatePolicy.java b/services/foldables/devicestateprovider/src/com/android/server/policy/BookStyleDeviceStatePolicy.java
index 95c4407e3963..cc5573bb01d8 100644
--- a/services/foldables/devicestateprovider/src/com/android/server/policy/BookStyleDeviceStatePolicy.java
+++ b/services/foldables/devicestateprovider/src/com/android/server/policy/BookStyleDeviceStatePolicy.java
@@ -103,6 +103,12 @@ public class BookStyleDeviceStatePolicy extends DeviceStatePolicy implements
final DisplayManager displayManager = mContext.getSystemService(DisplayManager.class);
mEnablePostureBasedClosedState = featureFlags.enableFoldablesPostureBasedClosedState();
+ if (mEnablePostureBasedClosedState) {
+ // This configuration doesn't require listening to hall sensor, it solely relies
+ // on the fused hinge angle sensor
+ hallSensor = null;
+ }
+
mIsDualDisplayBlockingEnabled = featureFlags.enableDualDisplayBlocking();
final DeviceStatePredicateWrapper[] configuration = createConfiguration(
diff --git a/services/foldables/devicestateprovider/src/com/android/server/policy/FoldableDeviceStateProvider.java b/services/foldables/devicestateprovider/src/com/android/server/policy/FoldableDeviceStateProvider.java
index bc8643f3d173..daeaa9833d78 100644
--- a/services/foldables/devicestateprovider/src/com/android/server/policy/FoldableDeviceStateProvider.java
+++ b/services/foldables/devicestateprovider/src/com/android/server/policy/FoldableDeviceStateProvider.java
@@ -95,6 +95,7 @@ public final class FoldableDeviceStateProvider implements DeviceStateProvider,
private final Sensor mHingeAngleSensor;
private final DisplayManager mDisplayManager;
+ @Nullable
private final Sensor mHallSensor;
private static final Predicate<FoldableDeviceStateProvider> ALLOWED = p -> true;
@@ -122,7 +123,7 @@ public final class FoldableDeviceStateProvider implements DeviceStateProvider,
@NonNull Context context,
@NonNull SensorManager sensorManager,
@NonNull Sensor hingeAngleSensor,
- @NonNull Sensor hallSensor,
+ @Nullable Sensor hallSensor,
@NonNull DisplayManager displayManager,
@NonNull DeviceStatePredicateWrapper[] deviceStatePredicateWrappers) {
this(new FeatureFlagsImpl(), context, sensorManager, hingeAngleSensor, hallSensor,
@@ -135,7 +136,7 @@ public final class FoldableDeviceStateProvider implements DeviceStateProvider,
@NonNull Context context,
@NonNull SensorManager sensorManager,
@NonNull Sensor hingeAngleSensor,
- @NonNull Sensor hallSensor,
+ @Nullable Sensor hallSensor,
@NonNull DisplayManager displayManager,
@NonNull DeviceStatePredicateWrapper[] deviceStatePredicateWrappers) {
@@ -149,7 +150,9 @@ public final class FoldableDeviceStateProvider implements DeviceStateProvider,
mIsDualDisplayBlockingEnabled = featureFlags.enableDualDisplayBlocking();
sensorManager.registerListener(this, mHingeAngleSensor, SENSOR_DELAY_FASTEST);
- sensorManager.registerListener(this, mHallSensor, SENSOR_DELAY_FASTEST);
+ if (hallSensor != null) {
+ sensorManager.registerListener(this, mHallSensor, SENSOR_DELAY_FASTEST);
+ }
mOrderedStates = new DeviceState[deviceStatePredicateWrappers.length];
for (int i = 0; i < deviceStatePredicateWrappers.length; i++) {
@@ -324,7 +327,7 @@ public final class FoldableDeviceStateProvider implements DeviceStateProvider,
@Override
public void onSensorChanged(SensorEvent event) {
synchronized (mLock) {
- if (event.sensor == mHallSensor) {
+ if (mHallSensor != null && event.sensor == mHallSensor) {
mLastHallSensorEvent = event;
} else if (event.sensor == mHingeAngleSensor) {
mLastHingeAngleSensorEvent = event;
@@ -359,11 +362,11 @@ public final class FoldableDeviceStateProvider implements DeviceStateProvider,
}
@GuardedBy("mLock")
- private void dumpSensorValues(Sensor sensor, @Nullable SensorEvent event) {
+ private void dumpSensorValues(@Nullable Sensor sensor, @Nullable SensorEvent event) {
Slog.i(TAG, toSensorValueString(sensor, event));
}
- private String toSensorValueString(Sensor sensor, @Nullable SensorEvent event) {
+ private String toSensorValueString(@Nullable Sensor sensor, @Nullable SensorEvent event) {
String sensorString = sensor == null ? "null" : sensor.getName();
String eventValues = event == null ? "null" : Arrays.toString(event.values);
return sensorString + " : " + eventValues;
diff --git a/services/foldables/devicestateprovider/tests/src/com/android/server/policy/BookStyleDeviceStatePolicyTest.java b/services/foldables/devicestateprovider/tests/src/com/android/server/policy/BookStyleDeviceStatePolicyTest.java
index 901f24dd9b0b..9f07aa8c1a41 100644
--- a/services/foldables/devicestateprovider/tests/src/com/android/server/policy/BookStyleDeviceStatePolicyTest.java
+++ b/services/foldables/devicestateprovider/tests/src/com/android/server/policy/BookStyleDeviceStatePolicyTest.java
@@ -219,6 +219,26 @@ public final class BookStyleDeviceStatePolicyTest {
}
@Test
+ public void test_postureBasedClosedState_createPolicy_doesNotRegisterHallSensor() {
+ mFakeFeatureFlags.setFlag(Flags.FLAG_ENABLE_FOLDABLES_POSTURE_BASED_CLOSED_STATE, true);
+ clearInvocations(mSensorManager);
+
+ mInstrumentation.runOnMainSync(() -> mProvider = createProvider());
+
+ verify(mSensorManager, never()).registerListener(any(), eq(mHallSensor), anyInt());
+ }
+
+ @Test
+ public void test_postureBasedClosedStateDisabled_createPolicy_registersHallSensor() {
+ mFakeFeatureFlags.setFlag(Flags.FLAG_ENABLE_FOLDABLES_POSTURE_BASED_CLOSED_STATE, false);
+ clearInvocations(mSensorManager);
+
+ mInstrumentation.runOnMainSync(() -> mProvider = createProvider());
+
+ verify(mSensorManager).registerListener(any(), eq(mHallSensor), anyInt());
+ }
+
+ @Test
public void test_noSensorEventsYet_reportOpenedState() {
mProvider.setListener(mListener);
verify(mListener).onStateChanged(mDeviceStateCaptor.capture());
diff --git a/services/tests/servicestests/src/com/android/server/power/hint/HintManagerServiceTest.java b/services/tests/servicestests/src/com/android/server/power/hint/HintManagerServiceTest.java
index 510e7c42f12d..5902caae443d 100644
--- a/services/tests/servicestests/src/com/android/server/power/hint/HintManagerServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/power/hint/HintManagerServiceTest.java
@@ -22,6 +22,7 @@ import static com.android.server.power.hint.HintManagerService.CLEAN_UP_UID_DELA
import static com.google.common.truth.Truth.assertThat;
import static org.junit.Assert.assertArrayEquals;
+import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNotEquals;
import static org.junit.Assert.assertNotNull;
@@ -41,6 +42,8 @@ import static org.mockito.Mockito.when;
import android.app.ActivityManager;
import android.app.ActivityManagerInternal;
import android.content.Context;
+import android.hardware.power.SessionConfig;
+import android.hardware.power.SessionTag;
import android.hardware.power.WorkDuration;
import android.os.Binder;
import android.os.IBinder;
@@ -63,6 +66,8 @@ import org.junit.Rule;
import org.junit.Test;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
+import org.mockito.invocation.InvocationOnMock;
+import org.mockito.stubbing.Answer;
import java.util.ArrayList;
import java.util.Arrays;
@@ -97,6 +102,7 @@ public class HintManagerServiceTest {
private static final long DEFAULT_HINT_PREFERRED_RATE = 16666666L;
private static final long DEFAULT_TARGET_DURATION = 16666666L;
+ private static final long DOUBLED_TARGET_DURATION = 33333333L;
private static final long CONCURRENCY_TEST_DURATION_SEC = 10;
private static final int UID = Process.myUid();
private static final int TID = Process.myPid();
@@ -106,6 +112,8 @@ public class HintManagerServiceTest {
private static final int[] SESSION_TIDS_C = new int[] {TID};
private static final long[] DURATIONS_THREE = new long[] {1L, 100L, 1000L};
private static final long[] TIMESTAMPS_THREE = new long[] {1L, 2L, 3L};
+ private static final long[] SESSION_PTRS = new long[] {11L, 22L, 33L};
+ private static final long[] SESSION_IDS = new long[] {1L, 11L, 111L};
private static final long[] DURATIONS_ZERO = new long[] {};
private static final long[] TIMESTAMPS_ZERO = new long[] {};
private static final long[] TIMESTAMPS_TWO = new long[] {1L, 2L};
@@ -129,21 +137,61 @@ public class HintManagerServiceTest {
private HintManagerService mService;
+ private static Answer<Long> fakeCreateWithConfig(Long ptr, Long sessionId) {
+ return new Answer<Long>() {
+ public Long answer(InvocationOnMock invocation) {
+ ((SessionConfig) invocation.getArguments()[5]).id = sessionId;
+ return ptr;
+ }
+ };
+ }
+
@Before
public void setUp() throws Exception {
MockitoAnnotations.initMocks(this);
when(mNativeWrapperMock.halGetHintSessionPreferredRate())
.thenReturn(DEFAULT_HINT_PREFERRED_RATE);
when(mNativeWrapperMock.halCreateHintSession(eq(TGID), eq(UID), eq(SESSION_TIDS_A),
- eq(DEFAULT_TARGET_DURATION))).thenReturn(1L);
+ eq(DEFAULT_TARGET_DURATION))).thenReturn(SESSION_PTRS[0]);
when(mNativeWrapperMock.halCreateHintSession(eq(TGID), eq(UID), eq(SESSION_TIDS_B),
- eq(DEFAULT_TARGET_DURATION))).thenReturn(2L);
+ eq(DOUBLED_TARGET_DURATION))).thenReturn(SESSION_PTRS[1]);
when(mNativeWrapperMock.halCreateHintSession(eq(TGID), eq(UID), eq(SESSION_TIDS_C),
- eq(0L))).thenReturn(1L);
+ eq(0L))).thenReturn(SESSION_PTRS[2]);
+ when(mNativeWrapperMock.halCreateHintSessionWithConfig(eq(TGID), eq(UID),
+ eq(SESSION_TIDS_A), eq(DEFAULT_TARGET_DURATION), anyInt(),
+ any(SessionConfig.class))).thenAnswer(fakeCreateWithConfig(SESSION_PTRS[0],
+ SESSION_IDS[0]));
+ when(mNativeWrapperMock.halCreateHintSessionWithConfig(eq(TGID), eq(UID),
+ eq(SESSION_TIDS_B), eq(DOUBLED_TARGET_DURATION), anyInt(),
+ any(SessionConfig.class))).thenAnswer(fakeCreateWithConfig(SESSION_PTRS[1],
+ SESSION_IDS[1]));
+ when(mNativeWrapperMock.halCreateHintSessionWithConfig(eq(TGID), eq(UID),
+ eq(SESSION_TIDS_C), eq(0L), anyInt(),
+ any(SessionConfig.class))).thenAnswer(fakeCreateWithConfig(SESSION_PTRS[2],
+ SESSION_IDS[2]));
+
LocalServices.removeServiceForTest(ActivityManagerInternal.class);
LocalServices.addService(ActivityManagerInternal.class, mAmInternalMock);
}
+ /**
+ * Mocks the creation calls, but without support for new createHintSessionWithConfig method
+ */
+ public void makeConfigCreationUnsupported() {
+ reset(mNativeWrapperMock);
+ when(mNativeWrapperMock.halGetHintSessionPreferredRate())
+ .thenReturn(DEFAULT_HINT_PREFERRED_RATE);
+ when(mNativeWrapperMock.halCreateHintSession(eq(TGID), eq(UID), eq(SESSION_TIDS_A),
+ eq(DEFAULT_TARGET_DURATION))).thenReturn(SESSION_PTRS[0]);
+ when(mNativeWrapperMock.halCreateHintSession(eq(TGID), eq(UID), eq(SESSION_TIDS_B),
+ eq(DOUBLED_TARGET_DURATION))).thenReturn(SESSION_PTRS[1]);
+ when(mNativeWrapperMock.halCreateHintSession(eq(TGID), eq(UID), eq(SESSION_TIDS_C),
+ eq(0L))).thenReturn(SESSION_PTRS[2]);
+ when(mNativeWrapperMock.halCreateHintSessionWithConfig(anyInt(), anyInt(),
+ any(int[].class), anyLong(), anyInt(),
+ any(SessionConfig.class))).thenThrow(new UnsupportedOperationException());
+ }
+
static class NativeWrapperFake extends NativeWrapper {
@Override
public void halInit() {
@@ -160,6 +208,12 @@ public class HintManagerServiceTest {
}
@Override
+ public long halCreateHintSessionWithConfig(int tgid, int uid, int[] tids,
+ long durationNanos, int tag, SessionConfig config) {
+ return 1;
+ }
+
+ @Override
public void halPauseHintSession(long halPtr) {
}
@@ -224,27 +278,57 @@ public class HintManagerServiceTest {
IBinder token = new Binder();
// Make sure we throw exception when adding a TID doesn't belong to the processes
// In this case, we add `init` PID into the list.
+ SessionConfig config = new SessionConfig();
assertThrows(SecurityException.class,
- () -> service.getBinderServiceInstance().createHintSession(token,
- new int[]{TID, 1}, DEFAULT_TARGET_DURATION));
+ () -> service.getBinderServiceInstance().createHintSessionWithConfig(token,
+ new int[]{TID, 1}, DEFAULT_TARGET_DURATION, SessionTag.OTHER, config));
+ }
+
+ @Test
+ public void testCreateHintSessionFallback() throws Exception {
+ HintManagerService service = createService();
+ IBinder token = new Binder();
+ makeConfigCreationUnsupported();
+
+ IHintSession a = service.getBinderServiceInstance().createHintSessionWithConfig(token,
+ SESSION_TIDS_A, DEFAULT_TARGET_DURATION, SessionTag.OTHER, new SessionConfig());
+ assertNotNull(a);
+
+ IHintSession b = service.getBinderServiceInstance().createHintSessionWithConfig(token,
+ SESSION_TIDS_B, DOUBLED_TARGET_DURATION, SessionTag.OTHER, new SessionConfig());
+ assertNotEquals(a, b);
+
+ IHintSession c = service.getBinderServiceInstance().createHintSessionWithConfig(token,
+ SESSION_TIDS_C, 0L, SessionTag.OTHER, new SessionConfig());
+ assertNotNull(c);
+ verify(mNativeWrapperMock, times(3)).halCreateHintSession(anyInt(), anyInt(),
+ any(int[].class), anyLong());
}
@Test
- public void testCreateHintSession() throws Exception {
+ public void testCreateHintSessionWithConfig() throws Exception {
HintManagerService service = createService();
IBinder token = new Binder();
- IHintSession a = service.getBinderServiceInstance().createHintSession(token,
- SESSION_TIDS_A, DEFAULT_TARGET_DURATION);
+ SessionConfig config = new SessionConfig();
+ IHintSession a = service.getBinderServiceInstance().createHintSessionWithConfig(token,
+ SESSION_TIDS_A, DEFAULT_TARGET_DURATION, SessionTag.OTHER, config);
assertNotNull(a);
+ assertEquals(SESSION_IDS[0], config.id);
- IHintSession b = service.getBinderServiceInstance().createHintSession(token,
- SESSION_TIDS_B, DEFAULT_TARGET_DURATION);
+ SessionConfig config2 = new SessionConfig();
+ IHintSession b = service.getBinderServiceInstance().createHintSessionWithConfig(token,
+ SESSION_TIDS_B, DOUBLED_TARGET_DURATION, SessionTag.APP, config2);
assertNotEquals(a, b);
+ assertEquals(SESSION_IDS[1], config2.id);
- IHintSession c = service.getBinderServiceInstance().createHintSession(token,
- SESSION_TIDS_C, 0L);
+ SessionConfig config3 = new SessionConfig();
+ IHintSession c = service.getBinderServiceInstance().createHintSessionWithConfig(token,
+ SESSION_TIDS_C, 0L, SessionTag.GAME, config3);
assertNotNull(c);
+ assertEquals(SESSION_IDS[2], config3.id);
+ verify(mNativeWrapperMock, times(3)).halCreateHintSessionWithConfig(anyInt(), anyInt(),
+ any(int[].class), anyLong(), anyInt(), any(SessionConfig.class));
}
@Test
@@ -253,7 +337,8 @@ public class HintManagerServiceTest {
IBinder token = new Binder();
AppHintSession a = (AppHintSession) service.getBinderServiceInstance()
- .createHintSession(token, SESSION_TIDS_A, DEFAULT_TARGET_DURATION);
+ .createHintSessionWithConfig(token, SESSION_TIDS_A, DEFAULT_TARGET_DURATION,
+ SessionTag.OTHER, new SessionConfig());
// Set session to background and calling updateHintAllowed() would invoke pause();
service.mUidObserver.onUidStateChanged(
@@ -288,8 +373,8 @@ public class HintManagerServiceTest {
HintManagerService service = createService();
IBinder token = new Binder();
- IHintSession a = service.getBinderServiceInstance().createHintSession(token,
- SESSION_TIDS_A, DEFAULT_TARGET_DURATION);
+ IHintSession a = service.getBinderServiceInstance().createHintSessionWithConfig(token,
+ SESSION_TIDS_A, DEFAULT_TARGET_DURATION, SessionTag.OTHER, new SessionConfig());
a.close();
verify(mNativeWrapperMock, times(1)).halCloseHintSession(anyLong());
@@ -300,8 +385,8 @@ public class HintManagerServiceTest {
HintManagerService service = createService();
IBinder token = new Binder();
- IHintSession a = service.getBinderServiceInstance().createHintSession(token,
- SESSION_TIDS_A, DEFAULT_TARGET_DURATION);
+ IHintSession a = service.getBinderServiceInstance().createHintSessionWithConfig(token,
+ SESSION_TIDS_A, DEFAULT_TARGET_DURATION, SessionTag.OTHER, new SessionConfig());
assertThrows(IllegalArgumentException.class, () -> {
a.updateTargetWorkDuration(-1L);
@@ -321,7 +406,8 @@ public class HintManagerServiceTest {
IBinder token = new Binder();
AppHintSession a = (AppHintSession) service.getBinderServiceInstance()
- .createHintSession(token, SESSION_TIDS_A, DEFAULT_TARGET_DURATION);
+ .createHintSessionWithConfig(token, SESSION_TIDS_A, DEFAULT_TARGET_DURATION,
+ SessionTag.OTHER, new SessionConfig());
a.updateTargetWorkDuration(100L);
a.reportActualWorkDuration(DURATIONS_THREE, TIMESTAMPS_THREE);
@@ -363,7 +449,8 @@ public class HintManagerServiceTest {
IBinder token = new Binder();
AppHintSession a = (AppHintSession) service.getBinderServiceInstance()
- .createHintSession(token, SESSION_TIDS_A, DEFAULT_TARGET_DURATION);
+ .createHintSessionWithConfig(token, SESSION_TIDS_A, DEFAULT_TARGET_DURATION,
+ SessionTag.OTHER, new SessionConfig());
a.sendHint(PerformanceHintManager.Session.CPU_LOAD_RESET);
verify(mNativeWrapperMock, times(1)).halSendHint(anyLong(),
@@ -389,7 +476,8 @@ public class HintManagerServiceTest {
IBinder token = new Binder();
AppHintSession a = (AppHintSession) service.getBinderServiceInstance()
- .createHintSession(token, SESSION_TIDS_A, DEFAULT_TARGET_DURATION);
+ .createHintSessionWithConfig(token, SESSION_TIDS_A, DEFAULT_TARGET_DURATION,
+ SessionTag.OTHER, new SessionConfig());
service.mUidObserver.onUidStateChanged(
a.mUid, ActivityManager.PROCESS_STATE_IMPORTANT_BACKGROUND, 0, 0);
@@ -410,7 +498,8 @@ public class HintManagerServiceTest {
IBinder token = new Binder();
AppHintSession a = (AppHintSession) service.getBinderServiceInstance()
- .createHintSession(token, SESSION_TIDS_A, DEFAULT_TARGET_DURATION);
+ .createHintSessionWithConfig(token, SESSION_TIDS_A, DEFAULT_TARGET_DURATION,
+ SessionTag.OTHER, new SessionConfig());
service.mUidObserver.onUidStateChanged(
a.mUid, ActivityManager.PROCESS_STATE_IMPORTANT_FOREGROUND, 0, 0);
@@ -423,7 +512,8 @@ public class HintManagerServiceTest {
IBinder token = new Binder();
AppHintSession a = (AppHintSession) service.getBinderServiceInstance()
- .createHintSession(token, SESSION_TIDS_A, DEFAULT_TARGET_DURATION);
+ .createHintSessionWithConfig(token, SESSION_TIDS_A, DEFAULT_TARGET_DURATION,
+ SessionTag.OTHER, new SessionConfig());
a.updateTargetWorkDuration(100L);
@@ -454,10 +544,12 @@ public class HintManagerServiceTest {
int threadCount = 3;
int[] tids1 = createThreads(threadCount, stopLatch1);
long sessionPtr1 = 111;
- when(mNativeWrapperMock.halCreateHintSession(eq(TGID), eq(UID), eq(tids1),
- eq(DEFAULT_TARGET_DURATION))).thenReturn(sessionPtr1);
+ when(mNativeWrapperMock.halCreateHintSessionWithConfig(eq(TGID), eq(UID), eq(tids1),
+ eq(DEFAULT_TARGET_DURATION), anyInt(), any(SessionConfig.class)))
+ .thenReturn(sessionPtr1);
AppHintSession session1 = (AppHintSession) service.getBinderServiceInstance()
- .createHintSession(token, tids1, DEFAULT_TARGET_DURATION);
+ .createHintSessionWithConfig(token, tids1, DEFAULT_TARGET_DURATION,
+ SessionTag.OTHER, new SessionConfig());
assertNotNull(session1);
// for test only to avoid conflicting with any real thread that exists on device
@@ -473,10 +565,12 @@ public class HintManagerServiceTest {
tids2WithIsolated[threadCount] = isoProc1;
tids2WithIsolated[threadCount + 1] = isoProc2;
long sessionPtr2 = 222;
- when(mNativeWrapperMock.halCreateHintSession(eq(TGID), eq(UID), eq(tids2WithIsolated),
- eq(DEFAULT_TARGET_DURATION))).thenReturn(sessionPtr2);
+ when(mNativeWrapperMock.halCreateHintSessionWithConfig(eq(TGID), eq(UID),
+ eq(tids2WithIsolated), eq(DEFAULT_TARGET_DURATION), anyInt(),
+ any(SessionConfig.class))).thenReturn(sessionPtr2);
AppHintSession session2 = (AppHintSession) service.getBinderServiceInstance()
- .createHintSession(token, tids2WithIsolated, DEFAULT_TARGET_DURATION);
+ .createHintSessionWithConfig(token, tids2WithIsolated,
+ DEFAULT_TARGET_DURATION, SessionTag.OTHER, new SessionConfig());
assertNotNull(session2);
// trigger clean up through UID state change by making the process background
@@ -608,7 +702,8 @@ public class HintManagerServiceTest {
IBinder token = new Binder();
AppHintSession a = (AppHintSession) service.getBinderServiceInstance()
- .createHintSession(token, SESSION_TIDS_A, DEFAULT_TARGET_DURATION);
+ .createHintSessionWithConfig(token, SESSION_TIDS_A, DEFAULT_TARGET_DURATION,
+ SessionTag.OTHER, new SessionConfig());
a.setMode(0, true);
verify(mNativeWrapperMock, times(1)).halSetMode(anyLong(),
@@ -726,7 +821,8 @@ public class HintManagerServiceTest {
AtomicReference<Boolean> shouldRun) throws Exception {
IBinder token = new Binder();
AppHintSession a = (AppHintSession) service.getBinderServiceInstance()
- .createHintSession(token, SESSION_TIDS_A, DEFAULT_TARGET_DURATION);
+ .createHintSessionWithConfig(token, SESSION_TIDS_A, DEFAULT_TARGET_DURATION,
+ SessionTag.OTHER, new SessionConfig());
// we will start some threads and get their valid TIDs to update
int threadCount = 3;
// the list of TIDs
@@ -793,7 +889,8 @@ public class HintManagerServiceTest {
IBinder token = new Binder();
AppHintSession a = (AppHintSession) service.getBinderServiceInstance()
- .createHintSession(token, SESSION_TIDS_A, DEFAULT_TARGET_DURATION);
+ .createHintSessionWithConfig(token, SESSION_TIDS_A, DEFAULT_TARGET_DURATION,
+ SessionTag.OTHER, new SessionConfig());
a.updateTargetWorkDuration(100L);
a.reportActualWorkDuration2(WORK_DURATIONS_FIVE);
diff --git a/services/tests/uiservicestests/src/com/android/server/notification/NotificationListenersTest.java b/services/tests/uiservicestests/src/com/android/server/notification/NotificationListenersTest.java
index ae3683961d61..983e694a8f1a 100644
--- a/services/tests/uiservicestests/src/com/android/server/notification/NotificationListenersTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/NotificationListenersTest.java
@@ -70,6 +70,7 @@ import android.os.RemoteException;
import android.os.UserHandle;
import android.platform.test.flag.junit.SetFlagsRule;
import android.service.notification.INotificationListener;
+import android.service.notification.IStatusBarNotificationHolder;
import android.service.notification.NotificationListenerFilter;
import android.service.notification.NotificationListenerService;
import android.service.notification.NotificationRankingUpdate;
@@ -90,6 +91,7 @@ import com.google.common.collect.ImmutableList;
import org.junit.Before;
import org.junit.Rule;
import org.junit.Test;
+import org.mockito.ArgumentCaptor;
import org.mockito.ArgumentMatcher;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
@@ -154,6 +156,11 @@ public class NotificationListenersTest extends UiServiceTestCase {
.thenReturn(new ArrayList<>());
mNm.mHandler = mock(NotificationManagerService.WorkerHandler.class);
mNm.mAssistants = mock(NotificationManagerService.NotificationAssistants.class);
+ FieldSetter.setField(mNm,
+ NotificationManagerService.class.getDeclaredField("mListeners"),
+ mListeners);
+ doReturn(android.service.notification.NotificationListenerService.TRIM_FULL)
+ .when(mListeners).getOnNotificationPostedTrim(any());
}
@Test
@@ -827,6 +834,68 @@ public class NotificationListenersTest extends UiServiceTestCase {
verify(mListeners, never()).redactStatusBarNotification(eq(sbn));
}
+ @Test
+ public void testListenerPost_UpdateLifetimeExtended() throws Exception {
+ mSetFlagsRule.enableFlags(android.app.Flags.FLAG_LIFETIME_EXTENSION_REFACTOR);
+
+ // Create original notification, with FLAG_LIFETIME_EXTENDED_BY_DIRECT_REPLY.
+ String pkg = "pkg";
+ int uid = 9;
+ UserHandle userHandle = UserHandle.getUserHandleForUid(uid);
+ NotificationChannel channel = new NotificationChannel("id", "name",
+ NotificationManager.IMPORTANCE_HIGH);
+ Notification.Builder nb = new Notification.Builder(mContext, channel.getId())
+ .setContentTitle("foo")
+ .setSmallIcon(android.R.drawable.sym_def_app_icon)
+ .setFlag(Notification.FLAG_LIFETIME_EXTENDED_BY_DIRECT_REPLY, true);
+ StatusBarNotification sbn = new StatusBarNotification(pkg, pkg, 8, "tag", uid, 0,
+ nb.build(), userHandle, null, 0);
+ NotificationRecord old = new NotificationRecord(mContext, sbn, channel);
+
+ // Creates updated notification (without FLAG_LIFETIME_EXTENDED_BY_DIRECT_REPLY)
+ Notification.Builder nb2 = new Notification.Builder(mContext, channel.getId())
+ .setContentTitle("new title")
+ .setSmallIcon(android.R.drawable.sym_def_app_icon)
+ .setFlag(Notification.FLAG_LIFETIME_EXTENDED_BY_DIRECT_REPLY, false);
+ StatusBarNotification sbn2 = new StatusBarNotification(pkg, pkg, 8, "tag", uid, 0,
+ nb2.build(), userHandle, null, 0);
+ NotificationRecord toPost = new NotificationRecord(mContext, sbn2, channel);
+
+ // Create system ui-like service.
+ ManagedServices.ManagedServiceInfo info = mListeners.new ManagedServiceInfo(
+ null, new ComponentName("a", "a"), sbn2.getUserId(), false, null, 33, 33);
+ info.isSystemUi = true;
+ INotificationListener l1 = mock(INotificationListener.class);
+ info.service = l1;
+ List<ManagedServices.ManagedServiceInfo> services = ImmutableList.of(info);
+ when(mListeners.getServices()).thenReturn(services);
+
+ FieldSetter.setField(mNm,
+ NotificationManagerService.class.getDeclaredField("mHandler"),
+ mock(NotificationManagerService.WorkerHandler.class));
+ doReturn(true).when(mNm).isVisibleToListener(any(), anyInt(), any());
+ doReturn(mock(NotificationRankingUpdate.class)).when(mNm).makeRankingUpdateLocked(info);
+ doReturn(false).when(mNm).isInLockDownMode(anyInt());
+ doNothing().when(mNm).updateUriPermissions(any(), any(), any(), anyInt());
+ doReturn(sbn2).when(mListeners).redactStatusBarNotification(sbn2);
+ doReturn(sbn2).when(mListeners).redactStatusBarNotification(any());
+
+ // The notification change is posted to the service listener.
+ mListeners.notifyPostedLocked(toPost, old);
+
+ // Verify that the post occcurs with the updated notification value.
+ ArgumentCaptor<Runnable> runnableCaptor = ArgumentCaptor.forClass(Runnable.class);
+ verify(mNm.mHandler, times(1)).post(runnableCaptor.capture());
+ runnableCaptor.getValue().run();
+ ArgumentCaptor<IStatusBarNotificationHolder> sbnCaptor =
+ ArgumentCaptor.forClass(IStatusBarNotificationHolder.class);
+ verify(l1, times(1)).onNotificationPosted(sbnCaptor.capture(), any());
+ StatusBarNotification sbnResult = sbnCaptor.getValue().get();
+ assertThat(sbnResult.getNotification()
+ .extras.getCharSequence(Notification.EXTRA_TITLE).toString())
+ .isEqualTo("new title");
+ }
+
/**
* Helper method to test the thread safety of some operations.
*
diff --git a/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java b/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java
index 011f2e39d6f8..aec47146e833 100755
--- a/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java
@@ -106,6 +106,7 @@ import static android.service.notification.NotificationListenerService.Ranking.U
import static android.view.Display.DEFAULT_DISPLAY;
import static android.view.Display.INVALID_DISPLAY;
import static android.view.WindowManager.LayoutParams.TYPE_TOAST;
+
import static com.android.internal.widget.LockPatternUtils.StrongAuthTracker.STRONG_AUTH_REQUIRED_AFTER_USER_LOCKDOWN;
import static com.android.server.am.PendingIntentRecord.FLAG_ACTIVITY_SENDER;
import static com.android.server.am.PendingIntentRecord.FLAG_BROADCAST_SENDER;
@@ -118,11 +119,11 @@ import static com.android.server.notification.NotificationManagerService.TAG;
import static com.android.server.notification.NotificationRecordLogger.NotificationReportedEvent.NOTIFICATION_ADJUSTED;
import static com.android.server.notification.NotificationRecordLogger.NotificationReportedEvent.NOTIFICATION_POSTED;
import static com.android.server.notification.NotificationRecordLogger.NotificationReportedEvent.NOTIFICATION_UPDATED;
+
import static com.google.common.collect.Iterables.getOnlyElement;
import static com.google.common.truth.Truth.assertThat;
import static com.google.common.truth.Truth.assertWithMessage;
-import static java.util.Collections.emptyList;
-import static java.util.Collections.singletonList;
+
import static junit.framework.Assert.assertEquals;
import static junit.framework.Assert.assertFalse;
import static junit.framework.Assert.assertNotNull;
@@ -131,6 +132,7 @@ import static junit.framework.Assert.assertNull;
import static junit.framework.Assert.assertSame;
import static junit.framework.Assert.assertTrue;
import static junit.framework.Assert.fail;
+
import static org.junit.Assert.assertNotEquals;
import static org.junit.Assert.assertThrows;
import static org.mockito.ArgumentMatchers.isNull;
@@ -157,6 +159,9 @@ import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.verifyNoMoreInteractions;
import static org.mockito.Mockito.when;
+import static java.util.Collections.emptyList;
+import static java.util.Collections.singletonList;
+
import android.Manifest;
import android.annotation.Nullable;
import android.annotation.SuppressLint;
@@ -270,8 +275,10 @@ import android.util.Pair;
import android.util.Xml;
import android.view.accessibility.AccessibilityManager;
import android.widget.RemoteViews;
+
import androidx.test.InstrumentationRegistry;
import androidx.test.filters.SmallTest;
+
import com.android.internal.R;
import com.android.internal.config.sysui.SystemUiDeviceConfigFlags;
import com.android.internal.config.sysui.TestableFlagResolver;
@@ -303,10 +310,12 @@ import com.android.server.uri.UriGrantsManagerInternal;
import com.android.server.utils.quota.MultiRateLimiter;
import com.android.server.wm.ActivityTaskManagerInternal;
import com.android.server.wm.WindowManagerInternal;
+
import com.google.android.collect.Lists;
import com.google.common.collect.ImmutableList;
import libcore.junit.util.compat.CoreCompatChangeRule.DisableCompatChanges;
import libcore.junit.util.compat.CoreCompatChangeRule.EnableCompatChanges;
+
import org.junit.After;
import org.junit.Assert;
import org.junit.Before;
@@ -5934,6 +5943,45 @@ public class NotificationManagerServiceTest extends UiServiceTestCase {
}
@Test
+ @EnableFlags(android.app.Flags.FLAG_LIFETIME_EXTENSION_REFACTOR)
+ public void testUpdate_DirectReplyLifetimeExtendedUpdateSucceeds() throws Exception {
+ // Creates a lifetime extended notification.
+ NotificationRecord original = generateNotificationRecord(mTestNotificationChannel);
+ original.getSbn().getNotification().flags |= FLAG_LIFETIME_EXTENDED_BY_DIRECT_REPLY;
+ mService.addNotification(original);
+
+ // Post an update for that notification.
+ StatusBarNotification sbn = new StatusBarNotification(mPkg, mPkg, original.getSbn().getId(),
+ original.getSbn().getTag(), mUid, 0,
+ new Notification.Builder(mContext, mTestNotificationChannel.getId())
+ .setSmallIcon(android.R.drawable.sym_def_app_icon)
+ .setContentTitle("new title").build(),
+ UserHandle.getUserHandleForUid(mUid), null, 0);
+ NotificationRecord update = new NotificationRecord(mContext, sbn, mTestNotificationChannel);
+ mService.addEnqueuedNotification(update);
+
+ NotificationManagerService.PostNotificationRunnable runnable =
+ mService.new PostNotificationRunnable(update.getKey(),
+ update.getSbn().getPackageName(),
+ update.getUid(),
+ mPostNotificationTrackerFactory.newTracker(null));
+ runnable.run();
+ waitForIdle();
+
+ // Checks the update was sent, and that update contains the new title, and does not contain
+ // the lifetime extension flag.
+ ArgumentCaptor<NotificationRecord> captor =
+ ArgumentCaptor.forClass(NotificationRecord.class);
+ verify(mListeners, times(1)).prepareNotifyPostedLocked(captor.capture(), any(),
+ anyBoolean());
+ assertThat(captor.getValue().getNotification().flags
+ & FLAG_LIFETIME_EXTENDED_BY_DIRECT_REPLY).isEqualTo(0);
+ assertThat(captor.getValue()
+ .getNotification().extras.getCharSequence(Notification.EXTRA_TITLE).toString())
+ .isEqualTo("new title");
+ }
+
+ @Test
public void testStats_updatedOnUserExpansion() throws Exception {
NotificationRecord r = generateNotificationRecord(mTestNotificationChannel);
mService.addNotification(r);
@@ -12602,7 +12650,6 @@ public class NotificationManagerServiceTest extends UiServiceTestCase {
// the notifyPostedLocked function is called twice.
verify(mWorkerHandler, times(2)).postDelayed(any(Runnable.class), anyLong());
- //verify(mListeners, times(2)).notifyPostedLocked(any(), any());
}
@Test
diff --git a/services/tests/uiservicestests/src/com/android/server/notification/ZenEnumTest.java b/services/tests/uiservicestests/src/com/android/server/notification/ZenEnumTest.java
index f724510eeb73..8add2f957a07 100644
--- a/services/tests/uiservicestests/src/com/android/server/notification/ZenEnumTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/ZenEnumTest.java
@@ -21,8 +21,8 @@ import static com.google.common.truth.Truth.assertThat;
import android.app.AutomaticZenRule;
import android.provider.Settings;
import android.service.notification.ZenPolicy;
-import android.test.suitebuilder.annotation.SmallTest;
+import androidx.test.filters.SmallTest;
import androidx.test.runner.AndroidJUnit4;
import com.android.os.dnd.ActiveRuleType;
diff --git a/services/tests/wmtests/src/com/android/server/wm/SizeCompatTests.java b/services/tests/wmtests/src/com/android/server/wm/SizeCompatTests.java
index 9697c65dc1ea..000162a9e705 100644
--- a/services/tests/wmtests/src/com/android/server/wm/SizeCompatTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/SizeCompatTests.java
@@ -502,7 +502,6 @@ public class SizeCompatTests extends WindowTestsBase {
final WindowConfiguration translucentWinConf = requestedConfig.windowConfiguration;
translucentWinConf.setActivityType(ACTIVITY_TYPE_STANDARD);
translucentWinConf.setWindowingMode(WINDOWING_MODE_MULTI_WINDOW);
- translucentWinConf.setDisplayWindowingMode(WINDOWING_MODE_MULTI_WINDOW);
translucentWinConf.setAlwaysOnTop(true);
translucentActivity.onRequestedOverrideConfigurationChanged(requestedConfig);
@@ -511,7 +510,6 @@ public class SizeCompatTests extends WindowTestsBase {
// The original override of WindowConfiguration should keep.
assertEquals(ACTIVITY_TYPE_STANDARD, translucentActivity.getActivityType());
assertEquals(WINDOWING_MODE_MULTI_WINDOW, translucentWinConf.getWindowingMode());
- assertEquals(WINDOWING_MODE_MULTI_WINDOW, translucentWinConf.getDisplayWindowingMode());
assertTrue(translucentWinConf.isAlwaysOnTop());
// Unless display is going to be rotated, it should always inherit from parent.
assertEquals(ROTATION_UNDEFINED, translucentWinConf.getDisplayRotation());
@@ -1384,6 +1382,25 @@ public class SizeCompatTests extends WindowTestsBase {
}
@Test
+ @EnableCompatChanges({ActivityInfo.OVERRIDE_ANY_ORIENTATION_TO_USER})
+ public void testShouldNotCreateCompatDisplays_systemFullscreenOverride() {
+ setUpDisplaySizeWithApp(1000, 2500);
+
+ // Make the task root resizable.
+ mActivity.info.resizeMode = RESIZE_MODE_RESIZEABLE;
+
+ // Create an activity on the same task.
+ final ActivityRecord activity = buildActivityRecord(/* supportsSizeChanges= */false,
+ RESIZE_MODE_UNRESIZEABLE, ActivityInfo.SCREEN_ORIENTATION_PORTRAIT);
+
+ // Simulate the user selecting the fullscreen user aspect ratio override
+ spyOn(activity.mLetterboxUiController);
+ doReturn(true).when(activity.mLetterboxUiController)
+ .isSystemOverrideToFullscreenEnabled();
+ assertFalse(activity.shouldCreateCompatDisplayInsets());
+ }
+
+ @Test
@EnableCompatChanges({ActivityInfo.NEVER_SANDBOX_DISPLAY_APIS})
public void testNeverSandboxDisplayApis_configEnabled_sandboxingNotApplied() {
setUpDisplaySizeWithApp(1000, 1200);
diff --git a/services/tests/wmtests/src/com/android/server/wm/TaskFragmentOrganizerControllerTest.java b/services/tests/wmtests/src/com/android/server/wm/TaskFragmentOrganizerControllerTest.java
index 002a3d5a0d53..65a81c419d37 100644
--- a/services/tests/wmtests/src/com/android/server/wm/TaskFragmentOrganizerControllerTest.java
+++ b/services/tests/wmtests/src/com/android/server/wm/TaskFragmentOrganizerControllerTest.java
@@ -1050,6 +1050,8 @@ public class TaskFragmentOrganizerControllerTest extends WindowTestsBase {
// TaskFragment override orientation should be set for a system organizer.
final TaskFragment taskFragment = mWindowOrganizerController.getTaskFragment(fragmentToken);
assertNotNull(taskFragment);
+
+ taskFragment.setVisibleRequested(true);
assertEquals(SCREEN_ORIENTATION_BEHIND, taskFragment.getOverrideOrientation());
}
diff --git a/services/tests/wmtests/src/com/android/server/wm/TaskFragmentTest.java b/services/tests/wmtests/src/com/android/server/wm/TaskFragmentTest.java
index 4837fcbfc262..a90a158e0c2a 100644
--- a/services/tests/wmtests/src/com/android/server/wm/TaskFragmentTest.java
+++ b/services/tests/wmtests/src/com/android/server/wm/TaskFragmentTest.java
@@ -757,6 +757,7 @@ public class TaskFragmentTest extends WindowTestsBase {
final Task task = createTask(mDisplayContent);
final TaskFragment tf = createTaskFragmentWithActivity(task);
final ActivityRecord activity = tf.getTopMostActivity();
+ tf.setVisibleRequested(true);
tf.setOverrideOrientation(SCREEN_ORIENTATION_BEHIND);
// Should report the override orientation
@@ -768,6 +769,26 @@ public class TaskFragmentTest extends WindowTestsBase {
}
@Test
+ public void testGetOrientation_reportOverrideOrientation_whenInvisible() {
+ final Task task = createTask(mDisplayContent);
+ final TaskFragment tf = createTaskFragmentWithActivity(task);
+ final ActivityRecord activity = tf.getTopMostActivity();
+ tf.setVisibleRequested(false);
+ tf.setOverrideOrientation(SCREEN_ORIENTATION_BEHIND);
+
+ // Should report SCREEN_ORIENTATION_UNSPECIFIED for the override orientation when invisible
+ assertEquals(SCREEN_ORIENTATION_UNSPECIFIED, tf.getOverrideOrientation());
+
+ // Should report SCREEN_ORIENTATION_UNSET for the orientation
+ assertEquals(SCREEN_ORIENTATION_UNSET, tf.getOrientation(SCREEN_ORIENTATION_UNSET));
+
+ // Should report SCREEN_ORIENTATION_UNSET even if the activity requests a different
+ // value
+ activity.setRequestedOrientation(SCREEN_ORIENTATION_LANDSCAPE);
+ assertEquals(SCREEN_ORIENTATION_UNSET, tf.getOrientation(SCREEN_ORIENTATION_UNSET));
+ }
+
+ @Test
public void testUpdateImeParentForActivityEmbedding() {
// Setup two activities in ActivityEmbedding.
final Task task = createTask(mDisplayContent);
diff --git a/services/tests/wmtests/src/com/android/server/wm/WindowConfigurationTests.java b/services/tests/wmtests/src/com/android/server/wm/WindowConfigurationTests.java
index 38aac7cfee22..eca51aed334e 100644
--- a/services/tests/wmtests/src/com/android/server/wm/WindowConfigurationTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/WindowConfigurationTests.java
@@ -16,9 +16,7 @@
package com.android.server.wm;
-import static android.app.WindowConfiguration.ACTIVITY_TYPE_ASSISTANT;
import static android.app.WindowConfiguration.ACTIVITY_TYPE_HOME;
-import static android.app.WindowConfiguration.ACTIVITY_TYPE_RECENTS;
import static android.app.WindowConfiguration.ACTIVITY_TYPE_STANDARD;
import static android.app.WindowConfiguration.ROTATION_UNDEFINED;
import static android.app.WindowConfiguration.WINDOWING_MODE_FREEFORM;
@@ -208,43 +206,6 @@ public class WindowConfigurationTests extends WindowTestsBase {
/** Ensure the window always has a caption in Freeform window mode or display mode. */
@Test
- public void testCaptionShownForFreeformWindowingMode() {
- final WindowConfiguration config = new WindowConfiguration();
- config.setActivityType(ACTIVITY_TYPE_STANDARD);
- config.setWindowingMode(WINDOWING_MODE_FREEFORM);
- config.setDisplayWindowingMode(WINDOWING_MODE_FULLSCREEN);
- assertTrue(config.hasWindowDecorCaption());
-
- config.setDisplayWindowingMode(WINDOWING_MODE_FREEFORM);
- assertTrue(config.hasWindowDecorCaption());
-
- config.setWindowingMode(WINDOWING_MODE_FULLSCREEN);
- assertTrue(config.hasWindowDecorCaption());
-
- config.setDisplayWindowingMode(WINDOWING_MODE_FULLSCREEN);
- assertFalse(config.hasWindowDecorCaption());
- }
-
- /** Caption should not show for non-standard activity window. */
- @Test
- public void testCaptionNotShownForNonStandardActivityType() {
- final WindowConfiguration config = new WindowConfiguration();
- config.setActivityType(ACTIVITY_TYPE_HOME);
- config.setWindowingMode(WINDOWING_MODE_FREEFORM);
- config.setDisplayWindowingMode(WINDOWING_MODE_FREEFORM);
- assertFalse(config.hasWindowDecorCaption());
-
- config.setActivityType(ACTIVITY_TYPE_ASSISTANT);
- assertFalse(config.hasWindowDecorCaption());
-
- config.setActivityType(ACTIVITY_TYPE_RECENTS);
- assertFalse(config.hasWindowDecorCaption());
-
- config.setActivityType(ACTIVITY_TYPE_STANDARD);
- assertTrue(config.hasWindowDecorCaption());
- }
-
- @Test
public void testMaskedSetTo() {
final WindowConfiguration config = new WindowConfiguration();
final WindowConfiguration other = new WindowConfiguration();
diff --git a/tests/FsVerityTest/FsVerityTestApp/src/com/android/fsverity/Helper.java b/tests/FsVerityTest/FsVerityTestApp/src/com/android/fsverity/Helper.java
index 2ed4fec4a93c..c52be7c2b0c6 100644
--- a/tests/FsVerityTest/FsVerityTestApp/src/com/android/fsverity/Helper.java
+++ b/tests/FsVerityTest/FsVerityTestApp/src/com/android/fsverity/Helper.java
@@ -27,6 +27,9 @@ import android.util.Log;
import androidx.test.core.app.ApplicationProvider;
import androidx.test.platform.app.InstrumentationRegistry;
+import com.android.compatibility.common.util.AdoptShellPermissionsRule;
+
+import org.junit.Rule;
import org.junit.Test;
import java.io.FileOutputStream;
@@ -46,6 +49,12 @@ public class Helper {
private static final long BLOCK_SIZE = 4096;
+ @Rule
+ public final AdoptShellPermissionsRule mAdoptShellPermissionsRule =
+ new AdoptShellPermissionsRule(
+ InstrumentationRegistry.getInstrumentation().getUiAutomation(),
+ android.Manifest.permission.SETUP_FSVERITY);
+
@Test
public void prepareTest() throws Exception {
Context context = ApplicationProvider.getApplicationContext();