summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--core/api/system-current.txt17
-rw-r--r--core/java/android/app/notification.aconfig13
-rw-r--r--core/java/android/companion/virtual/IVirtualDevice.aidl6
-rw-r--r--core/java/android/companion/virtual/flags/flags.aconfig8
-rw-r--r--core/java/android/companion/virtual/sensor/VirtualSensor.java47
-rw-r--r--core/java/android/companion/virtual/sensor/VirtualSensorAdditionalInfo.aidl19
-rw-r--r--core/java/android/companion/virtual/sensor/VirtualSensorAdditionalInfo.java202
-rw-r--r--core/java/android/companion/virtual/sensor/VirtualSensorConfig.java35
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopDisplayModeController.kt42
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DragResizeInputListener.java20
-rw-r--r--libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopDisplayModeControllerTest.kt234
-rw-r--r--libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/DragResizeInputListenerTest.kt14
-rw-r--r--packages/SettingsLib/MainSwitchPreference/src/com/android/settingslib/widget/MainSwitchBar.java16
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/collection/NotificationEntryTest.java13
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/collection/coordinator/VisualStabilityCoordinatorTest.kt26
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/NotificationLockscreenUserManagerImpl.java6
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/ListEntry.java2
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotifCollection.java7
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotifCollectionCache.kt4
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotificationEntry.java2
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/ShadeListBuilder.java5
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/UseElapsedRealtimeForCreationTime.kt40
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coalescer/EventBatch.java2
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coalescer/GroupCoalescer.java12
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/VisualStabilityCoordinator.java2
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/inflation/OnUserInteractionCallbackImpl.java4
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/listbuilder/pluggable/NotifFilter.java4
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/logging/NotificationLogger.java4
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimController.java11
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/ScrimControllerTest.java2
-rw-r--r--packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/collection/GroupEntryBuilder.java2
-rw-r--r--services/companion/java/com/android/server/companion/virtual/SensorController.java36
-rw-r--r--services/companion/java/com/android/server/companion/virtual/VirtualDeviceImpl.java13
-rw-r--r--services/core/java/com/android/server/sensors/SensorManagerInternal.java18
-rw-r--r--services/core/java/com/android/server/sensors/SensorService.java16
-rw-r--r--services/core/jni/com_android_server_sensor_SensorService.cpp49
-rw-r--r--services/tests/servicestests/src/com/android/server/companion/virtual/SensorControllerTest.java58
-rw-r--r--telephony/java/android/telephony/CellSignalStrengthLte.java2
38 files changed, 928 insertions, 85 deletions
diff --git a/core/api/system-current.txt b/core/api/system-current.txt
index b9d61cd334e3..35720fd17769 100644
--- a/core/api/system-current.txt
+++ b/core/api/system-current.txt
@@ -3643,11 +3643,26 @@ package android.companion.virtual.sensor {
method public int getDeviceId();
method @NonNull public String getName();
method public int getType();
+ method @FlaggedApi("android.companion.virtualdevice.flags.virtual_sensor_additional_info") public void sendAdditionalInfo(@NonNull android.companion.virtual.sensor.VirtualSensorAdditionalInfo);
method public void sendEvent(@NonNull android.companion.virtual.sensor.VirtualSensorEvent);
method public void writeToParcel(@NonNull android.os.Parcel, int);
field @NonNull public static final android.os.Parcelable.Creator<android.companion.virtual.sensor.VirtualSensor> CREATOR;
}
+ @FlaggedApi("android.companion.virtualdevice.flags.virtual_sensor_additional_info") public final class VirtualSensorAdditionalInfo implements android.os.Parcelable {
+ method public int describeContents();
+ method public int getType();
+ method @NonNull public java.util.List<float[]> getValues();
+ method public void writeToParcel(@NonNull android.os.Parcel, int);
+ field @NonNull public static final android.os.Parcelable.Creator<android.companion.virtual.sensor.VirtualSensorAdditionalInfo> CREATOR;
+ }
+
+ public static final class VirtualSensorAdditionalInfo.Builder {
+ ctor public VirtualSensorAdditionalInfo.Builder(int);
+ method @NonNull public android.companion.virtual.sensor.VirtualSensorAdditionalInfo.Builder addValues(@NonNull float[]);
+ method @NonNull public android.companion.virtual.sensor.VirtualSensorAdditionalInfo build();
+ }
+
public interface VirtualSensorCallback {
method public void onConfigurationChanged(@NonNull android.companion.virtual.sensor.VirtualSensor, boolean, @NonNull java.time.Duration, @NonNull java.time.Duration);
}
@@ -3665,6 +3680,7 @@ package android.companion.virtual.sensor {
method public float getResolution();
method public int getType();
method @Nullable public String getVendor();
+ method @FlaggedApi("android.companion.virtualdevice.flags.virtual_sensor_additional_info") public boolean isAdditionalInfoSupported();
method @FlaggedApi("android.companion.virtualdevice.flags.device_aware_display_power") public boolean isWakeUpSensor();
method public void writeToParcel(@NonNull android.os.Parcel, int);
field @NonNull public static final android.os.Parcelable.Creator<android.companion.virtual.sensor.VirtualSensorConfig> CREATOR;
@@ -3673,6 +3689,7 @@ package android.companion.virtual.sensor {
public static final class VirtualSensorConfig.Builder {
ctor public VirtualSensorConfig.Builder(@IntRange(from=1) int, @NonNull String);
method @NonNull public android.companion.virtual.sensor.VirtualSensorConfig build();
+ method @FlaggedApi("android.companion.virtualdevice.flags.virtual_sensor_additional_info") @NonNull public android.companion.virtual.sensor.VirtualSensorConfig.Builder setAdditionalInfoSupported(boolean);
method @NonNull public android.companion.virtual.sensor.VirtualSensorConfig.Builder setDirectChannelTypesSupported(int);
method @NonNull public android.companion.virtual.sensor.VirtualSensorConfig.Builder setHighestDirectReportRateLevel(int);
method @NonNull public android.companion.virtual.sensor.VirtualSensorConfig.Builder setMaxDelay(int);
diff --git a/core/java/android/app/notification.aconfig b/core/java/android/app/notification.aconfig
index 5891bddfbbe6..6f0eafe487af 100644
--- a/core/java/android/app/notification.aconfig
+++ b/core/java/android/app/notification.aconfig
@@ -269,7 +269,7 @@ flag {
namespace: "systemui"
description: "enables metrics when redacting notifications on the lockscreen"
bug: "343631648"
- metadata {
+ metadata {
purpose: PURPOSE_BUGFIX
}
}
@@ -279,7 +279,16 @@ flag {
namespace: "systemui"
description: "enables user expanding the public view of a notification"
bug: "398853084"
- metadata {
+ metadata {
+ purpose: PURPOSE_BUGFIX
+ }
+ }
+flag {
+ name: "notif_entry_creation_time_use_elapsed_realtime"
+ namespace: "systemui"
+ description: "makes the notification entry expect its creation time to be elapsedRealtime, not uptimeMillis"
+ bug: "343631648"
+ metadata {
purpose: PURPOSE_BUGFIX
}
}
diff --git a/core/java/android/companion/virtual/IVirtualDevice.aidl b/core/java/android/companion/virtual/IVirtualDevice.aidl
index f8ac27de1754..db77adeb5a3d 100644
--- a/core/java/android/companion/virtual/IVirtualDevice.aidl
+++ b/core/java/android/companion/virtual/IVirtualDevice.aidl
@@ -24,6 +24,7 @@ import android.companion.virtual.IVirtualDeviceSoundEffectListener;
import android.companion.virtual.audio.IAudioConfigChangedCallback;
import android.companion.virtual.audio.IAudioRoutingCallback;
import android.companion.virtual.sensor.VirtualSensor;
+import android.companion.virtual.sensor.VirtualSensorAdditionalInfo;
import android.companion.virtual.sensor.VirtualSensorConfig;
import android.companion.virtual.sensor.VirtualSensorEvent;
import android.companion.virtual.camera.VirtualCameraConfig;
@@ -251,6 +252,11 @@ interface IVirtualDevice {
boolean sendSensorEvent(IBinder token, in VirtualSensorEvent event);
/**
+ * Sends additional information about the virtual sensor corresponding to the given token.
+ */
+ boolean sendSensorAdditionalInfo(IBinder token, in VirtualSensorAdditionalInfo info);
+
+ /**
* Launches a pending intent on the given display that is owned by this virtual device.
*/
void launchPendingIntent(int displayId, in PendingIntent pendingIntent,
diff --git a/core/java/android/companion/virtual/flags/flags.aconfig b/core/java/android/companion/virtual/flags/flags.aconfig
index 4fb3982c3754..615a6dffdf99 100644
--- a/core/java/android/companion/virtual/flags/flags.aconfig
+++ b/core/java/android/companion/virtual/flags/flags.aconfig
@@ -158,3 +158,11 @@ flag {
bug: "370720522"
is_exported: true
}
+
+flag {
+ name: "virtual_sensor_additional_info"
+ namespace: "virtual_devices"
+ description: "API for injecting SensorAdditionalInfo for VirtualSensor"
+ bug: "393517834"
+ is_exported: true
+}
diff --git a/core/java/android/companion/virtual/sensor/VirtualSensor.java b/core/java/android/companion/virtual/sensor/VirtualSensor.java
index 934a1a8ffcbd..8d4acfcb30d7 100644
--- a/core/java/android/companion/virtual/sensor/VirtualSensor.java
+++ b/core/java/android/companion/virtual/sensor/VirtualSensor.java
@@ -16,12 +16,15 @@
package android.companion.virtual.sensor;
+import android.annotation.FlaggedApi;
import android.annotation.NonNull;
import android.annotation.SuppressLint;
import android.annotation.SystemApi;
import android.annotation.TestApi;
import android.companion.virtual.IVirtualDevice;
+import android.companion.virtualdevice.flags.Flags;
import android.hardware.Sensor;
+import android.hardware.SensorAdditionalInfo;
import android.os.IBinder;
import android.os.Parcel;
import android.os.Parcelable;
@@ -37,20 +40,33 @@ import android.os.RemoteException;
*/
@SystemApi
public final class VirtualSensor implements Parcelable {
+
private final int mHandle;
private final int mType;
private final String mName;
+ private final int mFlags;
private final IVirtualDevice mVirtualDevice;
private final IBinder mToken;
+ // Only one additional info frame set at a time.
+ private final Object mAdditionalInfoLock = new Object();
/**
* @hide
*/
public VirtualSensor(int handle, int type, String name, IVirtualDevice virtualDevice,
IBinder token) {
+ this(handle, type, name, /*flags=*/0, virtualDevice, token);
+ }
+
+ /**
+ * @hide
+ */
+ public VirtualSensor(int handle, int type, String name, int flags, IVirtualDevice virtualDevice,
+ IBinder token) {
mHandle = handle;
mType = type;
mName = name;
+ mFlags = flags;
mVirtualDevice = virtualDevice;
mToken = token;
}
@@ -61,13 +77,14 @@ public final class VirtualSensor implements Parcelable {
@SuppressLint("UnflaggedApi") // @TestApi without associated feature.
@TestApi
public VirtualSensor(int handle, int type, @NonNull String name) {
- this(handle, type, name, /*virtualDevice=*/null, /*token=*/null);
+ this(handle, type, name, /*flags=*/0, /*virtualDevice=*/null, /*token=*/null);
}
private VirtualSensor(Parcel parcel) {
mHandle = parcel.readInt();
mType = parcel.readInt();
mName = parcel.readString8();
+ mFlags = parcel.readInt();
mVirtualDevice = IVirtualDevice.Stub.asInterface(parcel.readStrongBinder());
mToken = parcel.readStrongBinder();
}
@@ -123,6 +140,7 @@ public final class VirtualSensor implements Parcelable {
parcel.writeInt(mHandle);
parcel.writeInt(mType);
parcel.writeString8(mName);
+ parcel.writeInt(mFlags);
parcel.writeStrongBinder(mVirtualDevice.asBinder());
parcel.writeStrongBinder(mToken);
}
@@ -143,6 +161,33 @@ public final class VirtualSensor implements Parcelable {
}
}
+ /**
+ * Send additional information about the sensor to the system.
+ *
+ * @param info the additional sensor information to send.
+ * @throws UnsupportedOperationException if the sensor does not support sending additional info.
+ * @see Sensor#isAdditionalInfoSupported()
+ * @see VirtualSensorConfig.Builder#setAdditionalInfoSupported(boolean)
+ * @see SensorAdditionalInfo
+ * @see VirtualSensorAdditionalInfo
+ */
+ @FlaggedApi(Flags.FLAG_VIRTUAL_SENSOR_ADDITIONAL_INFO)
+ public void sendAdditionalInfo(@NonNull VirtualSensorAdditionalInfo info) {
+ if (!Flags.virtualSensorAdditionalInfo()) {
+ return;
+ }
+ if ((mFlags & VirtualSensorConfig.ADDITIONAL_INFO_MASK) == 0) {
+ throw new UnsupportedOperationException("Sensor additional info not supported.");
+ }
+ try {
+ synchronized (mAdditionalInfoLock) {
+ mVirtualDevice.sendSensorAdditionalInfo(mToken, info);
+ }
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+
@NonNull
public static final Parcelable.Creator<VirtualSensor> CREATOR =
new Parcelable.Creator<VirtualSensor>() {
diff --git a/core/java/android/companion/virtual/sensor/VirtualSensorAdditionalInfo.aidl b/core/java/android/companion/virtual/sensor/VirtualSensorAdditionalInfo.aidl
new file mode 100644
index 000000000000..7267be88ca75
--- /dev/null
+++ b/core/java/android/companion/virtual/sensor/VirtualSensorAdditionalInfo.aidl
@@ -0,0 +1,19 @@
+/*
+ * Copyright (C) 2025 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 android.companion.virtual.sensor;
+
+parcelable VirtualSensorAdditionalInfo; \ No newline at end of file
diff --git a/core/java/android/companion/virtual/sensor/VirtualSensorAdditionalInfo.java b/core/java/android/companion/virtual/sensor/VirtualSensorAdditionalInfo.java
new file mode 100644
index 000000000000..a4fca507b1d5
--- /dev/null
+++ b/core/java/android/companion/virtual/sensor/VirtualSensorAdditionalInfo.java
@@ -0,0 +1,202 @@
+/*
+ * Copyright (C) 2025 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 android.companion.virtual.sensor;
+
+import android.annotation.FlaggedApi;
+import android.annotation.IntDef;
+import android.annotation.NonNull;
+import android.annotation.SystemApi;
+import android.companion.virtualdevice.flags.Flags;
+import android.hardware.SensorAdditionalInfo;
+import android.os.Parcel;
+import android.os.Parcelable;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * An additional information frame for a {@link VirtualSensor}, which is reported through listener
+ * callback {@link android.hardware.SensorEventCallback#onSensorAdditionalInfo}.
+ *
+ * @see SensorAdditionalInfo
+ * @see VirtualSensorConfig.Builder#setAdditionalInfoSupported(boolean)
+ * @hide
+ */
+@FlaggedApi(Flags.FLAG_VIRTUAL_SENSOR_ADDITIONAL_INFO)
+@SystemApi
+public final class VirtualSensorAdditionalInfo implements Parcelable {
+
+ private final int mType;
+ @NonNull
+ private final List<float[]> mValues;
+
+ /** @hide */
+ @IntDef(prefix = "TYPE_", value = {
+ SensorAdditionalInfo.TYPE_UNTRACKED_DELAY,
+ SensorAdditionalInfo.TYPE_INTERNAL_TEMPERATURE,
+ SensorAdditionalInfo.TYPE_VEC3_CALIBRATION,
+ SensorAdditionalInfo.TYPE_SENSOR_PLACEMENT,
+ SensorAdditionalInfo.TYPE_SAMPLING,
+ })
+ @Retention(RetentionPolicy.SOURCE)
+ public @interface Type {}
+
+ private VirtualSensorAdditionalInfo(int type, @NonNull List<float[]> values) {
+ mType = type;
+ mValues = values;
+ }
+
+ private VirtualSensorAdditionalInfo(@NonNull Parcel parcel) {
+ mType = parcel.readInt();
+ final int valuesLength = parcel.readInt();
+ mValues = new ArrayList<>(valuesLength);
+ for (int i = 0; i < valuesLength; ++i) {
+ mValues.add(parcel.createFloatArray());
+ }
+ }
+
+ @Override
+ public void writeToParcel(@NonNull Parcel parcel, int parcelableFlags) {
+ parcel.writeInt(mType);
+ parcel.writeInt(mValues.size());
+ for (int i = 0; i < mValues.size(); ++i) {
+ parcel.writeFloatArray(mValues.get(i));
+ }
+ }
+
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ /**
+ * Returns the type of this information frame.
+ *
+ * @see SensorAdditionalInfo#type
+ */
+ public int getType() {
+ return mType;
+ }
+
+ /**
+ * Returns the float values of this information frame, if any.
+ *
+ * @see SensorAdditionalInfo#floatValues
+ */
+ @NonNull
+ public List<float[]> getValues() {
+ return mValues;
+ }
+
+ /**
+ * Builder for {@link VirtualSensorAdditionalInfo}.
+ */
+ public static final class Builder {
+
+ @VirtualSensorAdditionalInfo.Type
+ private final int mType;
+ @NonNull
+ private final ArrayList<float[]> mValues = new ArrayList<>();
+
+ /**
+ * Creates a new builder.
+ *
+ * @param type type of this additional info frame.
+ * @see SensorAdditionalInfo
+ */
+ public Builder(@VirtualSensorAdditionalInfo.Type int type) {
+ switch (type) {
+ case SensorAdditionalInfo.TYPE_UNTRACKED_DELAY:
+ case SensorAdditionalInfo.TYPE_SAMPLING:
+ case SensorAdditionalInfo.TYPE_INTERNAL_TEMPERATURE:
+ case SensorAdditionalInfo.TYPE_VEC3_CALIBRATION:
+ case SensorAdditionalInfo.TYPE_SENSOR_PLACEMENT:
+ break;
+ default:
+ throw new IllegalArgumentException("Unsupported type " + type);
+ }
+ mType = type;
+ }
+
+ /**
+ * Additional info payload data represented in float values. Depending on the type of
+ * information, this may be null.
+ *
+ * @see SensorAdditionalInfo#floatValues
+ */
+ @NonNull
+ public Builder addValues(@NonNull float[] values) {
+ if (values.length > 14) {
+ throw new IllegalArgumentException("Maximum payload value size is 14.");
+ }
+ if (mValues.isEmpty()) {
+ switch (mType) {
+ case SensorAdditionalInfo.TYPE_UNTRACKED_DELAY:
+ case SensorAdditionalInfo.TYPE_SAMPLING:
+ assertValuesLength(values, 2);
+ break;
+ case SensorAdditionalInfo.TYPE_INTERNAL_TEMPERATURE:
+ assertValuesLength(values, 1);
+ break;
+ case SensorAdditionalInfo.TYPE_VEC3_CALIBRATION:
+ case SensorAdditionalInfo.TYPE_SENSOR_PLACEMENT:
+ assertValuesLength(values, 11);
+ break;
+ }
+ } else if (values.length != mValues.getFirst().length) {
+ throw new IllegalArgumentException("All payload values must have the same length");
+ }
+
+ mValues.add(values);
+ return this;
+ }
+
+ private void assertValuesLength(float[] values, int expected) {
+ if (values.length != expected) {
+ throw new IllegalArgumentException(
+ "Payload values must have size " + expected + " for type " + mType);
+ }
+ }
+
+ /**
+ * Creates a new {@link VirtualSensorAdditionalInfo}.
+ *
+ * @throws IllegalArgumentException if the payload doesn't match the expectation for the
+ * given type, as documented in {@link SensorAdditionalInfo}.
+ */
+ @NonNull
+ public VirtualSensorAdditionalInfo build() {
+ if (mValues.isEmpty()) {
+ throw new IllegalArgumentException("Payload is required");
+ }
+ return new VirtualSensorAdditionalInfo(mType, mValues);
+ }
+ }
+
+ public static final @NonNull Creator<VirtualSensorAdditionalInfo> CREATOR =
+ new Creator<>() {
+ public VirtualSensorAdditionalInfo createFromParcel(Parcel source) {
+ return new VirtualSensorAdditionalInfo(source);
+ }
+
+ public VirtualSensorAdditionalInfo[] newArray(int size) {
+ return new VirtualSensorAdditionalInfo[size];
+ }
+ };
+}
diff --git a/core/java/android/companion/virtual/sensor/VirtualSensorConfig.java b/core/java/android/companion/virtual/sensor/VirtualSensorConfig.java
index 68bc9bce28d2..be8974ec29ad 100644
--- a/core/java/android/companion/virtual/sensor/VirtualSensorConfig.java
+++ b/core/java/android/companion/virtual/sensor/VirtualSensorConfig.java
@@ -59,10 +59,14 @@ public final class VirtualSensorConfig implements Parcelable {
private static final int REPORTING_MODE_MASK = 0xE;
private static final int REPORTING_MODE_SHIFT = 1;
+ // Mask for indication bit of sensor additional information support, bit 6.
+ static final int ADDITIONAL_INFO_MASK = 0x40;
+
// Mask for direct mode highest rate level, bit 7, 8, 9.
private static final int DIRECT_REPORT_MASK = 0x380;
private static final int DIRECT_REPORT_SHIFT = 7;
+
// Mask for supported direct channel, bit 10, 11
private static final int DIRECT_CHANNEL_SHIFT = 10;
@@ -253,6 +257,18 @@ public final class VirtualSensorConfig implements Parcelable {
}
/**
+ * Returns whether the sensor supports additional information.
+ *
+ * @see Builder#setAdditionalInfoSupported(boolean)
+ * @see Sensor#isAdditionalInfoSupported()
+ * @see android.hardware.SensorAdditionalInfo
+ */
+ @FlaggedApi(Flags.FLAG_VIRTUAL_SENSOR_ADDITIONAL_INFO)
+ public boolean isAdditionalInfoSupported() {
+ return (mFlags & ADDITIONAL_INFO_MASK) > 0;
+ }
+
+ /**
* Returns the reporting mode of this sensor.
*
* @see Builder#setReportingMode(int)
@@ -450,6 +466,25 @@ public final class VirtualSensorConfig implements Parcelable {
}
/**
+ * Sets whether this sensor supports sensor additional information.
+ *
+ * @see Sensor#isAdditionalInfoSupported()
+ * @see android.hardware.SensorAdditionalInfo
+ * @see VirtualSensorAdditionalInfo
+ */
+ @FlaggedApi(Flags.FLAG_VIRTUAL_SENSOR_ADDITIONAL_INFO)
+ @NonNull
+ public VirtualSensorConfig.Builder setAdditionalInfoSupported(
+ boolean additionalInfoSupported) {
+ if (additionalInfoSupported) {
+ mFlags |= ADDITIONAL_INFO_MASK;
+ } else {
+ mFlags &= ~ADDITIONAL_INFO_MASK;
+ }
+ return this;
+ }
+
+ /**
* Sets the reporting mode of this sensor.
*
* @throws IllegalArgumentException if the reporting mode is not one of
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopDisplayModeController.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopDisplayModeController.kt
index 6dcc0deb1da1..489e4f0aed01 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopDisplayModeController.kt
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopDisplayModeController.kt
@@ -114,21 +114,36 @@ class DesktopDisplayModeController(
transitions.startTransition(TRANSIT_CHANGE, wct, /* handler= */ null)
}
+ // Do not directly use this method to check the state of desktop-first mode. Check the display
+ // windowing mode instead.
+ private fun canDesktopFirstModeBeEnabledOnDefaultDisplay(): Boolean {
+ if (isDefaultDisplayDesktopEligible()) {
+ if (isExtendedDisplayEnabled() && hasExternalDisplay()) {
+ return true
+ }
+ if (DesktopExperienceFlags.FORM_FACTOR_BASED_DESKTOP_FIRST_SWITCH.isTrue) {
+ if (isInClamshellMode()) {
+ return true
+ }
+ }
+ }
+ return false
+ }
+
@VisibleForTesting
fun getTargetWindowingModeForDefaultDisplay(): Int {
- if (isExtendedDisplayEnabled() && hasExternalDisplay()) {
+ if (canDesktopFirstModeBeEnabledOnDefaultDisplay()) {
return WINDOWING_MODE_FREEFORM
}
- if (DesktopExperienceFlags.FORM_FACTOR_BASED_DESKTOP_FIRST_SWITCH.isTrue) {
- if (isInClamshellMode()) {
- return WINDOWING_MODE_FREEFORM
- }
- return WINDOWING_MODE_FULLSCREEN
- }
- // If form factor-based desktop first switch is disabled, use the default display windowing
- // mode here to keep the freeform mode for some form factors (e.g., FEATURE_PC).
- return windowManager.getWindowingMode(DEFAULT_DISPLAY)
+ return if (DesktopExperienceFlags.FORM_FACTOR_BASED_DESKTOP_FIRST_SWITCH.isTrue) {
+ WINDOWING_MODE_FULLSCREEN
+ } else {
+ // If form factor-based desktop first switch is disabled, use the default display
+ // windowing mode here to keep the freeform mode for some form factors (e.g.,
+ // FEATURE_PC).
+ windowManager.getWindowingMode(DEFAULT_DISPLAY)
+ }
}
private fun isExtendedDisplayEnabled(): Boolean {
@@ -156,6 +171,13 @@ class DesktopDisplayModeController(
private fun isInClamshellMode() = inputManager.isInTabletMode() == InputManager.SWITCH_STATE_OFF
+ private fun isDefaultDisplayDesktopEligible(): Boolean {
+ val display = requireNotNull(displayController.getDisplay(DEFAULT_DISPLAY)) {
+ "Display object of DEFAULT_DISPLAY must be non-null."
+ }
+ return DesktopModeStatus.isDesktopModeSupportedOnDisplay(context, display)
+ }
+
private fun logV(msg: String, vararg arguments: Any?) {
ProtoLog.v(WM_SHELL_DESKTOP_MODE, "%s: $msg", TAG, *arguments)
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DragResizeInputListener.java b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DragResizeInputListener.java
index 7a4a834e9dc2..40737120f364 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DragResizeInputListener.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DragResizeInputListener.java
@@ -23,12 +23,12 @@ import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_TRUSTED_OVERL
import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION;
import static android.view.WindowManager.LayoutParams.TYPE_INPUT_CONSUMER;
+import static com.android.wm.shell.desktopmode.DesktopModeEventLogger.Companion.ResizeTrigger;
import static com.android.wm.shell.protolog.ShellProtoLogGroup.WM_SHELL_DESKTOP_MODE;
import static com.android.wm.shell.windowdecor.DragPositioningCallback.CTRL_TYPE_BOTTOM;
import static com.android.wm.shell.windowdecor.DragPositioningCallback.CTRL_TYPE_LEFT;
import static com.android.wm.shell.windowdecor.DragPositioningCallback.CTRL_TYPE_RIGHT;
import static com.android.wm.shell.windowdecor.DragPositioningCallback.CTRL_TYPE_TOP;
-import static com.android.wm.shell.desktopmode.DesktopModeEventLogger.Companion.ResizeTrigger;
import static com.android.wm.shell.windowdecor.DragResizeWindowGeometry.isEdgeResizePermitted;
import static com.android.wm.shell.windowdecor.DragResizeWindowGeometry.isEventFromTouchscreen;
@@ -127,7 +127,9 @@ class DragResizeInputListener implements AutoCloseable {
Supplier<SurfaceControl.Builder> surfaceControlBuilderSupplier,
Supplier<SurfaceControl.Transaction> surfaceControlTransactionSupplier,
DisplayController displayController,
- DesktopModeEventLogger desktopModeEventLogger) {
+ DesktopModeEventLogger desktopModeEventLogger,
+ InputChannel inputChannel,
+ InputChannel sinkInputChannel) {
mContext = context;
mWindowSession = windowSession;
mBgExecutor = bgExecutor;
@@ -154,9 +156,13 @@ class DragResizeInputListener implements AutoCloseable {
final InputSetUpResult result = setUpInputChannels(mDisplayId, mWindowSession,
mDecorationSurface, mClientToken, mSinkClientToken,
mSurfaceControlBuilderSupplier,
- mSurfaceControlTransactionSupplier);
+ mSurfaceControlTransactionSupplier, inputChannel, sinkInputChannel);
mainExecutor.execute(() -> {
if (mClosed) {
+ result.mInputChannel.dispose();
+ result.mSinkInputChannel.dispose();
+ mSurfaceControlTransactionSupplier.get().remove(
+ result.mInputSinkSurface).apply();
return;
}
mInputSinkSurface = result.mInputSinkSurface;
@@ -208,7 +214,7 @@ class DragResizeInputListener implements AutoCloseable {
new DefaultTaskResizeInputEventReceiverFactory(), taskInfo,
handler, choreographer, displayId, decorationSurface, callback,
surfaceControlBuilderSupplier, surfaceControlTransactionSupplier,
- displayController, desktopModeEventLogger);
+ displayController, desktopModeEventLogger, new InputChannel(), new InputChannel());
}
DragResizeInputListener(
@@ -251,11 +257,11 @@ class DragResizeInputListener implements AutoCloseable {
@NonNull IBinder clientToken,
@NonNull IBinder sinkClientToken,
@NonNull Supplier<SurfaceControl.Builder> surfaceControlBuilderSupplier,
- @NonNull Supplier<SurfaceControl.Transaction> surfaceControlTransactionSupplier) {
+ @NonNull Supplier<SurfaceControl.Transaction> surfaceControlTransactionSupplier,
+ @NonNull InputChannel inputChannel,
+ @NonNull InputChannel sinkInputChannel) {
Trace.beginSection("DragResizeInputListener#setUpInputChannels");
final InputTransferToken inputTransferToken = new InputTransferToken();
- final InputChannel inputChannel = new InputChannel();
- final InputChannel sinkInputChannel = new InputChannel();
try {
windowSession.grantInputChannel(
displayId,
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopDisplayModeControllerTest.kt b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopDisplayModeControllerTest.kt
index 105941079095..da6a67c679ff 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopDisplayModeControllerTest.kt
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopDisplayModeControllerTest.kt
@@ -37,7 +37,6 @@ import android.view.WindowManager.TRANSIT_CHANGE
import android.window.DisplayAreaInfo
import android.window.WindowContainerTransaction
import androidx.test.filters.SmallTest
-import com.android.dx.mockito.inline.extended.ExtendedMockito.doReturn
import com.android.dx.mockito.inline.extended.ExtendedMockito.mockitoSession
import com.android.dx.mockito.inline.extended.ExtendedMockito.never
import com.android.dx.mockito.inline.extended.StaticMockitoSession
@@ -102,6 +101,7 @@ class DesktopDisplayModeControllerTest(
TestRunningTaskInfoBuilder().setWindowingMode(WINDOWING_MODE_FULLSCREEN).build()
private val defaultTDA = DisplayAreaInfo(MockToken().token(), DEFAULT_DISPLAY, 0)
private val wallpaperToken = MockToken().token()
+ private val defaultDisplay = mock<Display>()
private val externalDisplay = mock<Display>()
private lateinit var extendedDisplaySettingsRestoreSession:
@@ -118,7 +118,7 @@ class DesktopDisplayModeControllerTest(
mockitoSession =
mockitoSession()
.strictness(Strictness.LENIENT)
- .spyStatic(DesktopModeStatus::class.java)
+ .mockStatic(DesktopModeStatus::class.java)
.startMocking()
extendedDisplaySettingsRestoreSession =
ExtendedDisplaySettingsRestoreSession(context.contentResolver)
@@ -141,8 +141,15 @@ class DesktopDisplayModeControllerTest(
runningTasks.add(fullscreenTask)
whenever(shellTaskOrganizer.getRunningTasks(anyInt())).thenReturn(ArrayList(runningTasks))
whenever(desktopWallpaperActivityTokenProvider.getToken()).thenReturn(wallpaperToken)
+ whenever(displayController.getDisplay(DEFAULT_DISPLAY)).thenReturn(defaultDisplay)
whenever(displayController.getDisplay(EXTERNAL_DISPLAY_ID)).thenReturn(externalDisplay)
setTabletModeStatus(SwitchState.UNKNOWN)
+ whenever(
+ DesktopModeStatus.isDesktopModeSupportedOnDisplay(
+ context,
+ defaultDisplay
+ )
+ ).thenReturn(true)
}
@After
@@ -210,6 +217,12 @@ class DesktopDisplayModeControllerTest(
}
setTabletModeStatus(tabletModeStatus)
setExtendedMode(param.extendedDisplayEnabled)
+ whenever(
+ DesktopModeStatus.isDesktopModeSupportedOnDisplay(
+ context,
+ defaultDisplay
+ )
+ ).thenReturn(param.isDefaultDisplayDesktopEligible)
assertThat(controller.getTargetWindowingModeForDefaultDisplay())
.isEqualTo(param.expectedWindowingMode)
@@ -228,6 +241,12 @@ class DesktopDisplayModeControllerTest(
}
setTabletModeStatus(param.tabletModeStatus)
setExtendedMode(param.extendedDisplayEnabled)
+ whenever(
+ DesktopModeStatus.isDesktopModeSupportedOnDisplay(
+ context,
+ defaultDisplay
+ )
+ ).thenReturn(param.isDefaultDisplayDesktopEligible)
assertThat(controller.getTargetWindowingModeForDefaultDisplay())
.isEqualTo(param.expectedWindowingMode)
@@ -287,9 +306,12 @@ class DesktopDisplayModeControllerTest(
private fun setExtendedMode(enabled: Boolean) {
if (DisplayFlags.enableDisplayContentModeManagement()) {
- doReturn(enabled).`when` {
- DesktopModeStatus.isDesktopModeSupportedOnDisplay(context, externalDisplay)
- }
+ whenever(
+ DesktopModeStatus.isDesktopModeSupportedOnDisplay(
+ context,
+ externalDisplay
+ )
+ ).thenReturn(enabled)
} else {
Settings.Global.putInt(
context.contentResolver,
@@ -334,54 +356,119 @@ class DesktopDisplayModeControllerTest(
val defaultWindowingMode: Int,
val hasExternalDisplay: Boolean,
val extendedDisplayEnabled: Boolean,
+ val isDefaultDisplayDesktopEligible: Boolean,
val expectedWindowingMode: Int,
) {
- FREEFORM_EXTERNAL_EXTENDED(
+ FREEFORM_EXTERNAL_EXTENDED_NO_PROJECTED(
defaultWindowingMode = WINDOWING_MODE_FREEFORM,
hasExternalDisplay = true,
extendedDisplayEnabled = true,
+ isDefaultDisplayDesktopEligible = true,
expectedWindowingMode = WINDOWING_MODE_FREEFORM,
),
- FULLSCREEN_EXTERNAL_EXTENDED(
+ FULLSCREEN_EXTERNAL_EXTENDED_NO_PROJECTED(
defaultWindowingMode = WINDOWING_MODE_FULLSCREEN,
hasExternalDisplay = true,
extendedDisplayEnabled = true,
+ isDefaultDisplayDesktopEligible = true,
expectedWindowingMode = WINDOWING_MODE_FREEFORM,
),
- FREEFORM_NO_EXTERNAL_EXTENDED(
+ FREEFORM_NO_EXTERNAL_EXTENDED_NO_PROJECTED(
defaultWindowingMode = WINDOWING_MODE_FREEFORM,
hasExternalDisplay = false,
extendedDisplayEnabled = true,
+ isDefaultDisplayDesktopEligible = true,
expectedWindowingMode = WINDOWING_MODE_FREEFORM,
),
- FULLSCREEN_NO_EXTERNAL_EXTENDED(
+ FULLSCREEN_NO_EXTERNAL_EXTENDED_NO_PROJECTED(
defaultWindowingMode = WINDOWING_MODE_FULLSCREEN,
hasExternalDisplay = false,
extendedDisplayEnabled = true,
+ isDefaultDisplayDesktopEligible = true,
expectedWindowingMode = WINDOWING_MODE_FULLSCREEN,
),
- FREEFORM_EXTERNAL_MIRROR(
+ FREEFORM_EXTERNAL_MIRROR_NO_PROJECTED(
defaultWindowingMode = WINDOWING_MODE_FREEFORM,
hasExternalDisplay = true,
extendedDisplayEnabled = false,
+ isDefaultDisplayDesktopEligible = true,
expectedWindowingMode = WINDOWING_MODE_FREEFORM,
),
- FULLSCREEN_EXTERNAL_MIRROR(
+ FULLSCREEN_EXTERNAL_MIRROR_NO_PROJECTED(
defaultWindowingMode = WINDOWING_MODE_FULLSCREEN,
hasExternalDisplay = true,
extendedDisplayEnabled = false,
+ isDefaultDisplayDesktopEligible = true,
expectedWindowingMode = WINDOWING_MODE_FULLSCREEN,
),
- FREEFORM_NO_EXTERNAL_MIRROR(
+ FREEFORM_NO_EXTERNAL_MIRROR_NO_PROJECTED(
defaultWindowingMode = WINDOWING_MODE_FREEFORM,
hasExternalDisplay = false,
extendedDisplayEnabled = false,
+ isDefaultDisplayDesktopEligible = true,
expectedWindowingMode = WINDOWING_MODE_FREEFORM,
),
- FULLSCREEN_NO_EXTERNAL_MIRROR(
+ FULLSCREEN_NO_EXTERNAL_MIRROR_NO_PROJECTED(
defaultWindowingMode = WINDOWING_MODE_FULLSCREEN,
hasExternalDisplay = false,
extendedDisplayEnabled = false,
+ isDefaultDisplayDesktopEligible = true,
+ expectedWindowingMode = WINDOWING_MODE_FULLSCREEN,
+ ),
+ FREEFORM_EXTERNAL_EXTENDED_PROJECTED(
+ defaultWindowingMode = WINDOWING_MODE_FREEFORM,
+ hasExternalDisplay = true,
+ extendedDisplayEnabled = true,
+ isDefaultDisplayDesktopEligible = false,
+ expectedWindowingMode = WINDOWING_MODE_FREEFORM,
+ ),
+ FULLSCREEN_EXTERNAL_EXTENDED_PROJECTED(
+ defaultWindowingMode = WINDOWING_MODE_FULLSCREEN,
+ hasExternalDisplay = true,
+ extendedDisplayEnabled = true,
+ isDefaultDisplayDesktopEligible = false,
+ expectedWindowingMode = WINDOWING_MODE_FULLSCREEN,
+ ),
+ FREEFORM_NO_EXTERNAL_EXTENDED_PROJECTED(
+ defaultWindowingMode = WINDOWING_MODE_FREEFORM,
+ hasExternalDisplay = false,
+ extendedDisplayEnabled = true,
+ isDefaultDisplayDesktopEligible = false,
+ expectedWindowingMode = WINDOWING_MODE_FREEFORM,
+ ),
+ FULLSCREEN_NO_EXTERNAL_EXTENDED_PROJECTED(
+ defaultWindowingMode = WINDOWING_MODE_FULLSCREEN,
+ hasExternalDisplay = false,
+ extendedDisplayEnabled = true,
+ isDefaultDisplayDesktopEligible = false,
+ expectedWindowingMode = WINDOWING_MODE_FULLSCREEN,
+ ),
+ FREEFORM_EXTERNAL_MIRROR_PROJECTED(
+ defaultWindowingMode = WINDOWING_MODE_FREEFORM,
+ hasExternalDisplay = true,
+ extendedDisplayEnabled = false,
+ isDefaultDisplayDesktopEligible = false,
+ expectedWindowingMode = WINDOWING_MODE_FREEFORM,
+ ),
+ FULLSCREEN_EXTERNAL_MIRROR_PROJECTED(
+ defaultWindowingMode = WINDOWING_MODE_FULLSCREEN,
+ hasExternalDisplay = true,
+ extendedDisplayEnabled = false,
+ isDefaultDisplayDesktopEligible = false,
+ expectedWindowingMode = WINDOWING_MODE_FULLSCREEN,
+ ),
+ FREEFORM_NO_EXTERNAL_MIRROR_PROJECTED(
+ defaultWindowingMode = WINDOWING_MODE_FREEFORM,
+ hasExternalDisplay = false,
+ extendedDisplayEnabled = false,
+ isDefaultDisplayDesktopEligible = false,
+ expectedWindowingMode = WINDOWING_MODE_FREEFORM,
+ ),
+ FULLSCREEN_NO_EXTERNAL_MIRROR_PROJECTED(
+ defaultWindowingMode = WINDOWING_MODE_FULLSCREEN,
+ hasExternalDisplay = false,
+ extendedDisplayEnabled = false,
+ isDefaultDisplayDesktopEligible = false,
expectedWindowingMode = WINDOWING_MODE_FULLSCREEN,
),
}
@@ -390,78 +477,175 @@ class DesktopDisplayModeControllerTest(
val hasExternalDisplay: Boolean,
val extendedDisplayEnabled: Boolean,
val tabletModeStatus: SwitchState,
+ val isDefaultDisplayDesktopEligible: Boolean,
val expectedWindowingMode: Int,
) {
- EXTERNAL_EXTENDED_TABLET(
+ EXTERNAL_EXTENDED_TABLET_NO_PROJECTED(
hasExternalDisplay = true,
extendedDisplayEnabled = true,
tabletModeStatus = SwitchState.ON,
+ isDefaultDisplayDesktopEligible = true,
expectedWindowingMode = WINDOWING_MODE_FREEFORM,
),
- NO_EXTERNAL_EXTENDED_TABLET(
+ NO_EXTERNAL_EXTENDED_TABLET_NO_PROJECTED(
hasExternalDisplay = false,
extendedDisplayEnabled = true,
tabletModeStatus = SwitchState.ON,
+ isDefaultDisplayDesktopEligible = true,
expectedWindowingMode = WINDOWING_MODE_FULLSCREEN,
),
- EXTERNAL_MIRROR_TABLET(
+ EXTERNAL_MIRROR_TABLET_NO_PROJECTED(
hasExternalDisplay = true,
extendedDisplayEnabled = false,
tabletModeStatus = SwitchState.ON,
+ isDefaultDisplayDesktopEligible = true,
expectedWindowingMode = WINDOWING_MODE_FULLSCREEN,
),
- NO_EXTERNAL_MIRROR_TABLET(
+ NO_EXTERNAL_MIRROR_TABLET_NO_PROJECTED(
hasExternalDisplay = false,
extendedDisplayEnabled = false,
tabletModeStatus = SwitchState.ON,
+ isDefaultDisplayDesktopEligible = true,
expectedWindowingMode = WINDOWING_MODE_FULLSCREEN,
),
- EXTERNAL_EXTENDED_CLAMSHELL(
+ EXTERNAL_EXTENDED_CLAMSHELL_NO_PROJECTED(
hasExternalDisplay = true,
extendedDisplayEnabled = true,
tabletModeStatus = SwitchState.OFF,
+ isDefaultDisplayDesktopEligible = true,
expectedWindowingMode = WINDOWING_MODE_FREEFORM,
),
- NO_EXTERNAL_EXTENDED_CLAMSHELL(
+ NO_EXTERNAL_EXTENDED_CLAMSHELL_NO_PROJECTED(
hasExternalDisplay = false,
extendedDisplayEnabled = true,
tabletModeStatus = SwitchState.OFF,
+ isDefaultDisplayDesktopEligible = true,
expectedWindowingMode = WINDOWING_MODE_FREEFORM,
),
- EXTERNAL_MIRROR_CLAMSHELL(
+ EXTERNAL_MIRROR_CLAMSHELL_NO_PROJECTED(
hasExternalDisplay = true,
extendedDisplayEnabled = false,
tabletModeStatus = SwitchState.OFF,
+ isDefaultDisplayDesktopEligible = true,
expectedWindowingMode = WINDOWING_MODE_FREEFORM,
),
- NO_EXTERNAL_MIRROR_CLAMSHELL(
+ NO_EXTERNAL_MIRROR_CLAMSHELL_NO_PROJECTED(
hasExternalDisplay = false,
extendedDisplayEnabled = false,
tabletModeStatus = SwitchState.OFF,
+ isDefaultDisplayDesktopEligible = true,
expectedWindowingMode = WINDOWING_MODE_FREEFORM,
),
- EXTERNAL_EXTENDED_UNKNOWN(
+ EXTERNAL_EXTENDED_UNKNOWN_NO_PROJECTED(
hasExternalDisplay = true,
extendedDisplayEnabled = true,
tabletModeStatus = SwitchState.UNKNOWN,
+ isDefaultDisplayDesktopEligible = true,
expectedWindowingMode = WINDOWING_MODE_FREEFORM,
),
- NO_EXTERNAL_EXTENDED_UNKNOWN(
+ NO_EXTERNAL_EXTENDED_UNKNOWN_NO_PROJECTED(
+ hasExternalDisplay = false,
+ extendedDisplayEnabled = true,
+ tabletModeStatus = SwitchState.UNKNOWN,
+ isDefaultDisplayDesktopEligible = true,
+ expectedWindowingMode = WINDOWING_MODE_FULLSCREEN,
+ ),
+ EXTERNAL_MIRROR_UNKNOWN_NO_PROJECTED(
+ hasExternalDisplay = true,
+ extendedDisplayEnabled = false,
+ tabletModeStatus = SwitchState.UNKNOWN,
+ isDefaultDisplayDesktopEligible = true,
+ expectedWindowingMode = WINDOWING_MODE_FULLSCREEN,
+ ),
+ NO_EXTERNAL_MIRROR_UNKNOWN_NO_PROJECTED(
+ hasExternalDisplay = false,
+ extendedDisplayEnabled = false,
+ tabletModeStatus = SwitchState.UNKNOWN,
+ isDefaultDisplayDesktopEligible = true,
+ expectedWindowingMode = WINDOWING_MODE_FULLSCREEN,
+ ),
+ EXTERNAL_EXTENDED_TABLET_PROJECTED(
+ hasExternalDisplay = true,
+ extendedDisplayEnabled = true,
+ tabletModeStatus = SwitchState.ON,
+ isDefaultDisplayDesktopEligible = false,
+ expectedWindowingMode = WINDOWING_MODE_FULLSCREEN,
+ ),
+ NO_EXTERNAL_EXTENDED_TABLET_PROJECTED(
+ hasExternalDisplay = false,
+ extendedDisplayEnabled = true,
+ tabletModeStatus = SwitchState.ON,
+ isDefaultDisplayDesktopEligible = false,
+ expectedWindowingMode = WINDOWING_MODE_FULLSCREEN,
+ ),
+ EXTERNAL_MIRROR_TABLET_PROJECTED(
+ hasExternalDisplay = true,
+ extendedDisplayEnabled = false,
+ tabletModeStatus = SwitchState.ON,
+ isDefaultDisplayDesktopEligible = false,
+ expectedWindowingMode = WINDOWING_MODE_FULLSCREEN,
+ ),
+ NO_EXTERNAL_MIRROR_TABLET_PROJECTED(
+ hasExternalDisplay = false,
+ extendedDisplayEnabled = false,
+ tabletModeStatus = SwitchState.ON,
+ isDefaultDisplayDesktopEligible = false,
+ expectedWindowingMode = WINDOWING_MODE_FULLSCREEN,
+ ),
+ EXTERNAL_EXTENDED_CLAMSHELL_PROJECTED(
+ hasExternalDisplay = true,
+ extendedDisplayEnabled = true,
+ tabletModeStatus = SwitchState.OFF,
+ isDefaultDisplayDesktopEligible = false,
+ expectedWindowingMode = WINDOWING_MODE_FULLSCREEN,
+ ),
+ NO_EXTERNAL_EXTENDED_CLAMSHELL_PROJECTED(
+ hasExternalDisplay = false,
+ extendedDisplayEnabled = true,
+ tabletModeStatus = SwitchState.OFF,
+ isDefaultDisplayDesktopEligible = false,
+ expectedWindowingMode = WINDOWING_MODE_FULLSCREEN,
+ ),
+ EXTERNAL_MIRROR_CLAMSHELL_PROJECTED(
+ hasExternalDisplay = true,
+ extendedDisplayEnabled = false,
+ tabletModeStatus = SwitchState.OFF,
+ isDefaultDisplayDesktopEligible = false,
+ expectedWindowingMode = WINDOWING_MODE_FULLSCREEN,
+ ),
+ NO_EXTERNAL_MIRROR_CLAMSHELL_PROJECTED(
+ hasExternalDisplay = false,
+ extendedDisplayEnabled = false,
+ tabletModeStatus = SwitchState.OFF,
+ isDefaultDisplayDesktopEligible = false,
+ expectedWindowingMode = WINDOWING_MODE_FULLSCREEN,
+ ),
+ EXTERNAL_EXTENDED_UNKNOWN_PROJECTED(
+ hasExternalDisplay = true,
+ extendedDisplayEnabled = true,
+ tabletModeStatus = SwitchState.UNKNOWN,
+ isDefaultDisplayDesktopEligible = false,
+ expectedWindowingMode = WINDOWING_MODE_FULLSCREEN,
+ ),
+ NO_EXTERNAL_EXTENDED_UNKNOWN_PROJECTED(
hasExternalDisplay = false,
extendedDisplayEnabled = true,
tabletModeStatus = SwitchState.UNKNOWN,
+ isDefaultDisplayDesktopEligible = false,
expectedWindowingMode = WINDOWING_MODE_FULLSCREEN,
),
- EXTERNAL_MIRROR_UNKNOWN(
+ EXTERNAL_MIRROR_UNKNOWN_PROJECTED(
hasExternalDisplay = true,
extendedDisplayEnabled = false,
tabletModeStatus = SwitchState.UNKNOWN,
+ isDefaultDisplayDesktopEligible = false,
expectedWindowingMode = WINDOWING_MODE_FULLSCREEN,
),
- NO_EXTERNAL_MIRROR_UNKNOWN(
+ NO_EXTERNAL_MIRROR_UNKNOWN_PROJECTED(
hasExternalDisplay = false,
extendedDisplayEnabled = false,
tabletModeStatus = SwitchState.UNKNOWN,
+ isDefaultDisplayDesktopEligible = false,
expectedWindowingMode = WINDOWING_MODE_FULLSCREEN,
),
}
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/DragResizeInputListenerTest.kt b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/DragResizeInputListenerTest.kt
index 7341e098add5..e23d0ad55b04 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/DragResizeInputListenerTest.kt
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/DragResizeInputListenerTest.kt
@@ -63,6 +63,8 @@ class DragResizeInputListenerTest : ShellTestCase() {
private val testBgExecutor = TestShellExecutor()
private val mockWindowSession = mock<IWindowSession>()
private val mockInputEventReceiver = mock<TaskResizeInputEventReceiver>()
+ private val inputChannel = mock<InputChannel>()
+ private val sinkInputChannel = mock<InputChannel>()
@Test
fun testGrantInputChannelOffMainThread() {
@@ -143,6 +145,16 @@ class DragResizeInputListenerTest : ShellTestCase() {
verify(mockWindowSession).remove(inputListener.mSinkClientToken)
}
+ @Test
+ fun testClose_afterBgSetup_disposesOfInputChannels() {
+ val inputListener = create()
+ testBgExecutor.flushAll()
+ inputListener.close()
+ testMainExecutor.flushAll()
+ verify(inputChannel).dispose()
+ verify(sinkInputChannel).dispose()
+ }
+
private fun verifyNoInputChannelGrantRequests() {
verify(mockWindowSession, never())
.grantInputChannel(
@@ -178,6 +190,8 @@ class DragResizeInputListenerTest : ShellTestCase() {
{ StubTransaction() },
mock<DisplayController>(),
mock<DesktopModeEventLogger>(),
+ inputChannel,
+ sinkInputChannel,
)
private class TestInitializationCallback : Runnable {
diff --git a/packages/SettingsLib/MainSwitchPreference/src/com/android/settingslib/widget/MainSwitchBar.java b/packages/SettingsLib/MainSwitchPreference/src/com/android/settingslib/widget/MainSwitchBar.java
index ddd9d2acdab3..9d4c5c2735fc 100644
--- a/packages/SettingsLib/MainSwitchPreference/src/com/android/settingslib/widget/MainSwitchBar.java
+++ b/packages/SettingsLib/MainSwitchPreference/src/com/android/settingslib/widget/MainSwitchBar.java
@@ -112,6 +112,22 @@ public class MainSwitchBar extends LinearLayout implements OnCheckedChangeListen
if (mSwitch.getVisibility() == VISIBLE) {
mSwitch.setOnCheckedChangeListener(this);
}
+
+ if (attrs != null) {
+ final TypedArray a = context.obtainStyledAttributes(attrs,
+ androidx.preference.R.styleable.Preference, 0 /*defStyleAttr*/,
+ 0 /*defStyleRes*/);
+ final CharSequence title = a.getText(
+ androidx.preference.R.styleable.Preference_android_title);
+ setTitle(title);
+ //TODO(b/369470034): update to next version
+ if (isExpressive && Build.VERSION.SDK_INT >= VERSION_CODES.VANILLA_ICE_CREAM) {
+ CharSequence summary = a.getText(
+ androidx.preference.R.styleable.Preference_android_summary);
+ setSummary(summary);
+ }
+ a.recycle();
+ }
}
@Override
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/collection/NotificationEntryTest.java b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/collection/NotificationEntryTest.java
index 790b2c343a11..bfd700dcc302 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/collection/NotificationEntryTest.java
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/collection/NotificationEntryTest.java
@@ -58,6 +58,7 @@ import com.android.systemui.res.R;
import com.android.systemui.statusbar.RankingBuilder;
import com.android.systemui.statusbar.SbnBuilder;
import com.android.systemui.statusbar.chips.notification.shared.StatusBarNotifChips;
+import com.android.systemui.statusbar.notification.collection.UseElapsedRealtimeForCreationTime;
import com.android.systemui.statusbar.notification.promoted.PromotedNotificationUi;
import com.android.systemui.util.time.FakeSystemClock;
@@ -151,7 +152,8 @@ public class NotificationEntryTest extends SysuiTestCase {
.build();
NotificationEntry entry =
- new NotificationEntry(sbn, ranking, mClock.uptimeMillis());
+ new NotificationEntry(sbn, ranking,
+ UseElapsedRealtimeForCreationTime.getCurrentTime(mClock));
assertFalse(entry.isBlockable());
}
@@ -251,7 +253,8 @@ public class NotificationEntryTest extends SysuiTestCase {
.build();
NotificationEntry entry =
- new NotificationEntry(sbn, ranking, mClock.uptimeMillis());
+ new NotificationEntry(sbn, ranking,
+ UseElapsedRealtimeForCreationTime.getCurrentTime(mClock));
assertEquals(systemGeneratedSmartActions, entry.getSmartActions());
assertEquals(NOTIFICATION_CHANNEL, entry.getChannel());
@@ -365,7 +368,8 @@ public class NotificationEntryTest extends SysuiTestCase {
.setKey(sbn.getKey())
.build();
NotificationEntry entry =
- new NotificationEntry(sbn, ranking, mClock.uptimeMillis());
+ new NotificationEntry(sbn, ranking,
+ UseElapsedRealtimeForCreationTime.getCurrentTime(mClock));
assertFalse(entry.isChannelVisibilityPrivate());
}
@@ -378,7 +382,8 @@ public class NotificationEntryTest extends SysuiTestCase {
.setKey(sbn.getKey())
.build();
NotificationEntry entry =
- new NotificationEntry(sbn, ranking, mClock.uptimeMillis());
+ new NotificationEntry(sbn, ranking,
+ UseElapsedRealtimeForCreationTime.getCurrentTime(mClock));
assertFalse(entry.isChannelVisibilityPrivate());
}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/collection/coordinator/VisualStabilityCoordinatorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/collection/coordinator/VisualStabilityCoordinatorTest.kt
index ef0a4169d98e..d532010f4c55 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/collection/coordinator/VisualStabilityCoordinatorTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/collection/coordinator/VisualStabilityCoordinatorTest.kt
@@ -50,6 +50,7 @@ import com.android.systemui.statusbar.notification.collection.GroupEntry
import com.android.systemui.statusbar.notification.collection.GroupEntryBuilder
import com.android.systemui.statusbar.notification.collection.NotificationEntry
import com.android.systemui.statusbar.notification.collection.NotificationEntryBuilder
+import com.android.systemui.statusbar.notification.collection.UseElapsedRealtimeForCreationTime
import com.android.systemui.statusbar.notification.collection.listbuilder.pluggable.NotifStabilityManager
import com.android.systemui.statusbar.notification.collection.listbuilder.pluggable.Pluggable.PluggableListener
import com.android.systemui.statusbar.notification.collection.notifPipeline
@@ -323,7 +324,10 @@ class VisualStabilityCoordinatorTest(flags: FlagsParameterization) : SysuiTestCa
setPulsing(true)
// WHEN we temporarily allow section changes for this notification entry
- underTest.temporarilyAllowSectionChanges(entry, fakeSystemClock.currentTimeMillis())
+ underTest.temporarilyAllowSectionChanges(
+ entry,
+ UseElapsedRealtimeForCreationTime.getCurrentTime(fakeSystemClock),
+ )
// THEN group changes aren't allowed
assertThat(notifStabilityManager.isGroupChangeAllowed(entry)).isFalse()
@@ -349,7 +353,10 @@ class VisualStabilityCoordinatorTest(flags: FlagsParameterization) : SysuiTestCa
setPulsing(false)
// WHEN we temporarily allow section changes for this notification entry
- underTest.temporarilyAllowSectionChanges(entry, fakeSystemClock.uptimeMillis())
+ underTest.temporarilyAllowSectionChanges(
+ entry,
+ UseElapsedRealtimeForCreationTime.getCurrentTime(fakeSystemClock),
+ )
// THEN the notification list is invalidated
verifyStabilityManagerWasInvalidated(times(1))
@@ -365,7 +372,10 @@ class VisualStabilityCoordinatorTest(flags: FlagsParameterization) : SysuiTestCa
setPulsing(false)
// WHEN we temporarily allow section changes for this notification entry
- underTest.temporarilyAllowSectionChanges(entry, fakeSystemClock.currentTimeMillis())
+ underTest.temporarilyAllowSectionChanges(
+ entry,
+ UseElapsedRealtimeForCreationTime.getCurrentTime(fakeSystemClock),
+ )
// THEN invalidate is not called because this entry was never suppressed from reordering
verifyStabilityManagerWasInvalidated(never())
@@ -382,7 +392,10 @@ class VisualStabilityCoordinatorTest(flags: FlagsParameterization) : SysuiTestCa
assertThat(notifStabilityManager.isSectionChangeAllowed(entry)).isTrue()
// WHEN we temporarily allow section changes for this notification entry
- underTest.temporarilyAllowSectionChanges(entry, fakeSystemClock.currentTimeMillis())
+ underTest.temporarilyAllowSectionChanges(
+ entry,
+ UseElapsedRealtimeForCreationTime.getCurrentTime(fakeSystemClock),
+ )
// THEN invalidate is not called because this entry was never suppressed from
// reordering;
@@ -415,7 +428,10 @@ class VisualStabilityCoordinatorTest(flags: FlagsParameterization) : SysuiTestCa
setPulsing(true)
// WHEN we temporarily allow section changes for this notification entry
- underTest.temporarilyAllowSectionChanges(entry, fakeSystemClock.currentTimeMillis())
+ underTest.temporarilyAllowSectionChanges(
+ entry,
+ UseElapsedRealtimeForCreationTime.getCurrentTime(fakeSystemClock),
+ )
// can now reorder, so invalidates
verifyStabilityManagerWasInvalidated(times(1))
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationLockscreenUserManagerImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationLockscreenUserManagerImpl.java
index 339f898be251..9bf3d5dfe4cf 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationLockscreenUserManagerImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationLockscreenUserManagerImpl.java
@@ -47,7 +47,6 @@ import android.database.ExecutorContentObserver;
import android.net.Uri;
import android.os.Looper;
import android.os.Process;
-import android.os.SystemClock;
import android.os.UserHandle;
import android.os.UserManager;
import android.provider.Settings;
@@ -78,6 +77,7 @@ import com.android.systemui.scene.shared.flag.SceneContainerFlag;
import com.android.systemui.settings.UserTracker;
import com.android.systemui.shared.system.SysUiStatsLog;
import com.android.systemui.statusbar.notification.collection.NotificationEntry;
+import com.android.systemui.statusbar.notification.collection.UseElapsedRealtimeForCreationTime;
import com.android.systemui.statusbar.notification.collection.notifcollection.CommonNotifCollection;
import com.android.systemui.statusbar.notification.collection.render.NotificationVisibilityProvider;
import com.android.systemui.statusbar.notification.row.shared.LockscreenOtpRedaction;
@@ -920,7 +920,9 @@ public class NotificationLockscreenUserManagerImpl implements
// notification's "when" time, or the notification entry creation time
private long getEarliestNotificationTime(NotificationEntry notif) {
long notifWhenWallClock = notif.getSbn().getNotification().getWhen();
- long creationTimeDelta = SystemClock.uptimeMillis() - notif.getCreationTime();
+ long creationTimeDelta = UseElapsedRealtimeForCreationTime.getCurrentTime()
+ - notif.getCreationTime();
+
long creationTimeWallClock = System.currentTimeMillis() - creationTimeDelta;
return Math.min(notifWhenWallClock, creationTimeWallClock);
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/ListEntry.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/ListEntry.java
index caa7abb1aa7a..6b6ac69733cc 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/ListEntry.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/ListEntry.java
@@ -34,7 +34,7 @@ public abstract class ListEntry extends PipelineEntry {
}
/**
- * The SystemClock.uptimeMillis() when this object was created. In general, this means the
+ * The SystemClock.elapsedRealtime() when this object was created. In general, this means the
* moment when NotificationManager notifies our listener about the existence of this entry.
*
* This value will not change if the notification is updated, although it will change if the
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotifCollection.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotifCollection.java
index 9795edf3313c..b7fe39e9c757 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotifCollection.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotifCollection.java
@@ -522,7 +522,7 @@ public class NotifCollection implements Dumpable, PipelineDumpable {
}
private void onNotificationsInitialized() {
- mInitializedTimestamp = mClock.uptimeMillis();
+ mInitializedTimestamp = UseElapsedRealtimeForCreationTime.getCurrentTime(mClock);
}
private void postNotification(
@@ -532,7 +532,8 @@ public class NotifCollection implements Dumpable, PipelineDumpable {
if (entry == null) {
// A new notification!
- entry = new NotificationEntry(sbn, ranking, mClock.uptimeMillis());
+ entry = new NotificationEntry(sbn, ranking,
+ UseElapsedRealtimeForCreationTime.getCurrentTime(mClock));
mEventQueue.add(new InitEntryEvent(entry));
mEventQueue.add(new BindEntryEvent(entry, sbn));
mNotificationSet.put(sbn.getKey(), entry);
@@ -861,7 +862,7 @@ public class NotifCollection implements Dumpable, PipelineDumpable {
// messages from system server.
private void crashIfNotInitializing(RuntimeException exception) {
final boolean isRecentlyInitialized = mInitializedTimestamp == 0
- || mClock.uptimeMillis() - mInitializedTimestamp
+ || UseElapsedRealtimeForCreationTime.getCurrentTime(mClock) - mInitializedTimestamp
< INITIALIZATION_FORGIVENESS_WINDOW;
if (isRecentlyInitialized) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotifCollectionCache.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotifCollectionCache.kt
index 1f8d365cfdad..698fed33a408 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotifCollectionCache.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotifCollectionCache.kt
@@ -89,9 +89,9 @@ class NotifCollectionCache<V>(
return true
}
- // Using uptimeMillis since it's guaranteed to be monotonic, as we don't want a
+ // Using elapsedRealtime since it's guaranteed to be monotonic, as we don't want a
// timezone/clock change to break us
- val now = systemClock.uptimeMillis()
+ val now = UseElapsedRealtimeForCreationTime.getCurrentTime(systemClock)
// Cannot purge the same entry from two threads simultaneously
synchronized(key) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotificationEntry.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotificationEntry.java
index d031d831bf5a..765d444a5c95 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotificationEntry.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotificationEntry.java
@@ -251,7 +251,7 @@ public final class NotificationEntry extends ListEntry {
/**
* @param sbn the StatusBarNotification from system server
* @param ranking also from system server
- * @param creationTime SystemClock.uptimeMillis of when we were created
+ * @param creationTime SystemClock.elapsedRealtime of when we were created
*/
public NotificationEntry(
@NonNull StatusBarNotification sbn,
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/ShadeListBuilder.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/ShadeListBuilder.java
index 780e8f47a7fe..3110db65ca3e 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/ShadeListBuilder.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/ShadeListBuilder.java
@@ -573,7 +573,7 @@ public class ShadeListBuilder implements Dumpable, PipelineDumpable {
List<PipelineEntry> out,
List<NotifFilter> filters) {
Trace.beginSection("ShadeListBuilder.filterNotifs");
- final long now = mSystemClock.uptimeMillis();
+ final long now = UseElapsedRealtimeForCreationTime.getCurrentTime(mSystemClock);
for (PipelineEntry entry : entries) {
if (entry instanceof GroupEntry) {
final GroupEntry groupEntry = (GroupEntry) entry;
@@ -617,7 +617,8 @@ public class ShadeListBuilder implements Dumpable, PipelineDumpable {
GroupEntry group = mGroups.get(topLevelKey);
if (group == null) {
- group = new GroupEntry(topLevelKey, mSystemClock.uptimeMillis());
+ group = new GroupEntry(topLevelKey,
+ UseElapsedRealtimeForCreationTime.getCurrentTime(mSystemClock));
mGroups.put(topLevelKey, group);
}
if (group.getParent() == null) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/UseElapsedRealtimeForCreationTime.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/UseElapsedRealtimeForCreationTime.kt
new file mode 100644
index 000000000000..23f90f3694a8
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/UseElapsedRealtimeForCreationTime.kt
@@ -0,0 +1,40 @@
+/*
+ * Copyright (C) 2025 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.statusbar.notification.collection
+
+import android.app.Flags
+import com.android.systemui.util.time.SystemClock
+
+/** A helper class for replacing uptimeMillis with elapsedRealtime for entry creation times */
+public object UseElapsedRealtimeForCreationTime {
+ @JvmStatic
+ fun getCurrentTime(clock: SystemClock): Long {
+ if (Flags.notifEntryCreationTimeUseElapsedRealtime()) {
+ return clock.elapsedRealtime()
+ }
+ return clock.uptimeMillis()
+ }
+
+ @JvmStatic
+ fun getCurrentTime(): Long {
+ if (Flags.notifEntryCreationTimeUseElapsedRealtime()) {
+ return android.os.SystemClock.elapsedRealtime()
+ }
+ return android.os.SystemClock.uptimeMillis()
+
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coalescer/EventBatch.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coalescer/EventBatch.java
index 2eec68b26347..fb7772e26240 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coalescer/EventBatch.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coalescer/EventBatch.java
@@ -25,7 +25,7 @@ import java.util.List;
* Represents a set of notification post events for a particular notification group.
*/
public class EventBatch {
- /** SystemClock.uptimeMillis() */
+ /** SystemClock.elapsedRealtime() */
final long mCreatedTimestamp;
/** SBN.getGroupKey -- same for all members */
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coalescer/GroupCoalescer.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coalescer/GroupCoalescer.java
index 96b35428b3ce..944e313d795a 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coalescer/GroupCoalescer.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coalescer/GroupCoalescer.java
@@ -34,6 +34,7 @@ import com.android.systemui.statusbar.NotificationListener;
import com.android.systemui.statusbar.NotificationListener.NotificationHandler;
import com.android.systemui.statusbar.notification.collection.PipelineDumpable;
import com.android.systemui.statusbar.notification.collection.PipelineDumper;
+import com.android.systemui.statusbar.notification.collection.UseElapsedRealtimeForCreationTime;
import com.android.systemui.util.concurrency.DelayableExecutor;
import com.android.systemui.util.time.SystemClock;
@@ -182,11 +183,12 @@ public class GroupCoalescer implements Dumpable, PipelineDumpable {
private void maybeEmitBatch(StatusBarNotification sbn) {
final CoalescedEvent event = mCoalescedEvents.get(sbn.getKey());
final EventBatch batch = mBatches.get(sbn.getGroupKey());
+ long now = UseElapsedRealtimeForCreationTime.getCurrentTime(mClock);
if (event != null) {
mLogger.logEarlyEmit(sbn.getKey(), requireNonNull(event.getBatch()).mGroupKey);
emitBatch(requireNonNull(event.getBatch()));
} else if (batch != null
- && mClock.uptimeMillis() - batch.mCreatedTimestamp >= mMaxGroupLingerDuration) {
+ && now - batch.mCreatedTimestamp >= mMaxGroupLingerDuration) {
mLogger.logMaxBatchTimeout(sbn.getKey(), batch.mGroupKey);
emitBatch(batch);
}
@@ -228,7 +230,8 @@ public class GroupCoalescer implements Dumpable, PipelineDumpable {
private EventBatch getOrBuildBatch(final String groupKey) {
EventBatch batch = mBatches.get(groupKey);
if (batch == null) {
- batch = new EventBatch(mClock.uptimeMillis(), groupKey);
+ batch = new EventBatch(UseElapsedRealtimeForCreationTime.getCurrentTime(mClock),
+ groupKey);
mBatches.put(groupKey, batch);
}
return batch;
@@ -268,7 +271,8 @@ public class GroupCoalescer implements Dumpable, PipelineDumpable {
}
events.sort(mEventComparator);
- long batchAge = mClock.uptimeMillis() - batch.mCreatedTimestamp;
+ long batchAge = UseElapsedRealtimeForCreationTime.getCurrentTime(mClock)
+ - batch.mCreatedTimestamp;
mLogger.logEmitBatch(batch.mGroupKey, batch.mMembers.size(), batchAge);
mHandler.onNotificationBatchPosted(events);
@@ -298,7 +302,7 @@ public class GroupCoalescer implements Dumpable, PipelineDumpable {
@Override
public void dump(@NonNull PrintWriter pw, @NonNull String[] args) {
- long now = mClock.uptimeMillis();
+ long now = UseElapsedRealtimeForCreationTime.getCurrentTime(mClock);
int eventCount = 0;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/VisualStabilityCoordinator.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/VisualStabilityCoordinator.java
index 27765635edcb..0466c0359710 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/VisualStabilityCoordinator.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/VisualStabilityCoordinator.java
@@ -501,7 +501,7 @@ public class VisualStabilityCoordinator implements Coordinator, Dumpable {
* notification and we are reordering based on the user's change.
*
* @param entry notification entry that can change sections even if isReorderingAllowed is false
- * @param now current time SystemClock.uptimeMillis
+ * @param now current time SystemClock.elapsedRealtime
*/
public void temporarilyAllowSectionChanges(@NonNull NotificationEntry entry, long now) {
final String entryKey = entry.getKey();
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/inflation/OnUserInteractionCallbackImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/inflation/OnUserInteractionCallbackImpl.java
index 07fa6aeb7900..03b4076ba6fb 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/inflation/OnUserInteractionCallbackImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/inflation/OnUserInteractionCallbackImpl.java
@@ -20,7 +20,6 @@ import static android.service.notification.NotificationStats.DISMISS_SENTIMENT_N
import static com.android.systemui.statusbar.StatusBarState.KEYGUARD;
-import android.os.SystemClock;
import android.service.notification.NotificationStats;
import androidx.annotation.NonNull;
@@ -30,6 +29,7 @@ import com.android.systemui.plugins.statusbar.StatusBarStateController;
import com.android.systemui.statusbar.notification.collection.NotifCollection;
import com.android.systemui.statusbar.notification.collection.NotifCollection.CancellationReason;
import com.android.systemui.statusbar.notification.collection.NotificationEntry;
+import com.android.systemui.statusbar.notification.collection.UseElapsedRealtimeForCreationTime;
import com.android.systemui.statusbar.notification.collection.coordinator.VisualStabilityCoordinator;
import com.android.systemui.statusbar.notification.collection.notifcollection.DismissedByUserStats;
import com.android.systemui.statusbar.notification.collection.render.NotificationVisibilityProvider;
@@ -85,7 +85,7 @@ public class OnUserInteractionCallbackImpl implements OnUserInteractionCallback
public void onImportanceChanged(NotificationEntry entry) {
mVisualStabilityCoordinator.temporarilyAllowSectionChanges(
entry,
- SystemClock.uptimeMillis());
+ UseElapsedRealtimeForCreationTime.getCurrentTime());
}
@NonNull
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/listbuilder/pluggable/NotifFilter.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/listbuilder/pluggable/NotifFilter.java
index 776c7d5eb7f6..389bb3129c8d 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/listbuilder/pluggable/NotifFilter.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/listbuilder/pluggable/NotifFilter.java
@@ -41,8 +41,8 @@ public abstract class NotifFilter extends Pluggable<NotifFilter> {
* this entry will not have any grouping nor sorting information.
* If this filter is registered via {@link NotifPipeline#addFinalizeFilter},
* this entry will have grouping and sorting information.
- * @param now A timestamp in SystemClock.uptimeMillis that represents "now" for the purposes of
- * pipeline execution. This value will be the same for all pluggable calls made
+ * @param now A timestamp in SystemClock.elapsedRealtime that represents "now" for the purposes
+ * of pipeline execution. This value will be the same for all pluggable calls made
* during this pipeline run, giving pluggables a stable concept of "now" to compare
* various entries against.
* @return True if the notif should be removed from the list
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/logging/NotificationLogger.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/logging/NotificationLogger.java
index ec8fbc08de7a..5bdd769dfa03 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/logging/NotificationLogger.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/logging/NotificationLogger.java
@@ -19,7 +19,6 @@ import android.content.Context;
import android.os.Handler;
import android.os.RemoteException;
import android.os.ServiceManager;
-import android.os.SystemClock;
import android.service.notification.NotificationListenerService;
import android.util.ArrayMap;
import android.util.ArraySet;
@@ -44,6 +43,7 @@ import com.android.systemui.statusbar.StatusBarState;
import com.android.systemui.statusbar.notification.collection.NotifLiveDataStore;
import com.android.systemui.statusbar.notification.collection.NotifPipeline;
import com.android.systemui.statusbar.notification.collection.NotificationEntry;
+import com.android.systemui.statusbar.notification.collection.UseElapsedRealtimeForCreationTime;
import com.android.systemui.statusbar.notification.collection.notifcollection.NotifCollectionListener;
import com.android.systemui.statusbar.notification.collection.notifcollection.UpdateSource;
import com.android.systemui.statusbar.notification.collection.render.NotificationVisibilityProvider;
@@ -112,7 +112,7 @@ public class NotificationLogger implements StateListener, CoreStartable,
@Override
public void run() {
- mLastVisibilityReportUptimeMs = SystemClock.uptimeMillis();
+ mLastVisibilityReportUptimeMs = UseElapsedRealtimeForCreationTime.getCurrentTime();
// 1. Loop over active entries:
// A. Keep list of visible notifications.
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimController.java
index 74a42ef3ff7d..f3d72027238f 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimController.java
@@ -29,6 +29,7 @@ import android.animation.Animator;
import android.animation.AnimatorListenerAdapter;
import android.animation.ValueAnimator;
import android.annotation.IntDef;
+import android.content.Context;
import android.graphics.Color;
import android.os.Handler;
import android.util.Log;
@@ -226,6 +227,8 @@ public class ScrimController implements ViewTreeObserver.OnPreDrawListener, Dump
private ScrimState mState = ScrimState.UNINITIALIZED;
+ private Context mContext;
+
private ScrimView mScrimInFront;
private ScrimView mNotificationsScrim;
private ScrimView mScrimBehind;
@@ -365,7 +368,9 @@ public class ScrimController implements ViewTreeObserver.OnPreDrawListener, Dump
@Main CoroutineDispatcher mainDispatcher,
LargeScreenShadeInterpolator largeScreenShadeInterpolator,
BlurConfig blurConfig,
+ @Main Context context,
Lazy<WindowRootViewBlurInteractor> windowRootViewBlurInteractor) {
+ mContext = context;
mScrimStateListener = lightBarController::setScrimState;
mLargeScreenShadeInterpolator = largeScreenShadeInterpolator;
mBlurConfig = blurConfig;
@@ -1627,16 +1632,16 @@ public class ScrimController implements ViewTreeObserver.OnPreDrawListener, Dump
private void updateThemeColors() {
if (mScrimBehind == null) return;
- int background = mScrimBehind.getContext().getColor(
+ int background = mContext.getColor(
com.android.internal.R.color.materialColorSurfaceDim);
- int accent = mScrimBehind.getContext().getColor(
+ int accent = mContext.getColor(
com.android.internal.R.color.materialColorPrimary);
mColors.setMainColor(background);
mColors.setSecondaryColor(accent);
final boolean isBackgroundLight = !ContrastColorUtil.isColorDark(background);
mColors.setSupportsDarkText(isBackgroundLight);
- int surface = mScrimBehind.getContext().getColor(
+ int surface = mContext.getColor(
com.android.internal.R.color.materialColorSurface);
for (ScrimState state : ScrimState.values()) {
state.setSurfaceColor(surface);
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/ScrimControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/ScrimControllerTest.java
index ffb861db182c..063b546cbae9 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/ScrimControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/ScrimControllerTest.java
@@ -296,6 +296,7 @@ public class ScrimControllerTest extends SysuiTestCase {
mKosmos.getTestDispatcher(),
mLinearLargeScreenShadeInterpolator,
new BlurConfig(0.0f, 0.0f),
+ mContext,
mKosmos::getWindowRootViewBlurInteractor);
mScrimController.setScrimVisibleListener(visible -> mScrimVisibility = visible);
mScrimController.attachViews(mScrimBehind, mNotificationsScrim, mScrimInFront);
@@ -1247,6 +1248,7 @@ public class ScrimControllerTest extends SysuiTestCase {
mKosmos.getTestDispatcher(),
mLinearLargeScreenShadeInterpolator,
new BlurConfig(0.0f, 0.0f),
+ mContext,
mKosmos::getWindowRootViewBlurInteractor);
mScrimController.setScrimVisibleListener(visible -> mScrimVisibility = visible);
mScrimController.attachViews(mScrimBehind, mNotificationsScrim, mScrimInFront);
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/collection/GroupEntryBuilder.java b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/collection/GroupEntryBuilder.java
index 4efcada96a14..215df9d59ec9 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/collection/GroupEntryBuilder.java
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/collection/GroupEntryBuilder.java
@@ -58,7 +58,7 @@ public class GroupEntryBuilder {
return this;
}
- /** Sets the creation time. */
+ /** Sets the creation time. Should be SystemClock.elapsedRealtime */
public GroupEntryBuilder setCreationTime(long creationTime) {
mCreationTime = creationTime;
return this;
diff --git a/services/companion/java/com/android/server/companion/virtual/SensorController.java b/services/companion/java/com/android/server/companion/virtual/SensorController.java
index 88b791046c87..6e098d014ee3 100644
--- a/services/companion/java/com/android/server/companion/virtual/SensorController.java
+++ b/services/companion/java/com/android/server/companion/virtual/SensorController.java
@@ -21,15 +21,18 @@ import android.annotation.Nullable;
import android.companion.virtual.IVirtualDevice;
import android.companion.virtual.sensor.IVirtualSensorCallback;
import android.companion.virtual.sensor.VirtualSensor;
+import android.companion.virtual.sensor.VirtualSensorAdditionalInfo;
import android.companion.virtual.sensor.VirtualSensorConfig;
import android.companion.virtual.sensor.VirtualSensorEvent;
import android.content.AttributionSource;
+import android.hardware.SensorAdditionalInfo;
import android.hardware.SensorDirectChannel;
import android.os.Binder;
import android.os.IBinder;
import android.os.ParcelFileDescriptor;
import android.os.RemoteException;
import android.os.SharedMemory;
+import android.os.SystemClock;
import android.util.ArrayMap;
import android.util.Slog;
import android.util.SparseArray;
@@ -140,7 +143,7 @@ public class SensorController {
final IBinder sensorToken =
new Binder("android.hardware.sensor.VirtualSensor:" + config.getName());
VirtualSensor sensor = new VirtualSensor(handle, config.getType(), config.getName(),
- virtualDevice, sensorToken);
+ config.getFlags(), virtualDevice, sensorToken);
synchronized (mLock) {
mSensorDescriptors.put(sensorToken, sensorDescriptor);
mVirtualSensors.put(handle, sensor);
@@ -164,6 +167,37 @@ public class SensorController {
}
}
+ boolean sendSensorAdditionalInfo(@NonNull IBinder token,
+ @NonNull VirtualSensorAdditionalInfo info) {
+ Objects.requireNonNull(token);
+ Objects.requireNonNull(info);
+ synchronized (mLock) {
+ final SensorDescriptor sensorDescriptor = mSensorDescriptors.get(token);
+ long timestamp = SystemClock.elapsedRealtimeNanos();
+ if (sensorDescriptor == null) {
+ throw new IllegalArgumentException("Could not send sensor event for given token");
+ }
+ if (!mSensorManagerInternal.sendSensorAdditionalInfo(
+ sensorDescriptor.getHandle(), SensorAdditionalInfo.TYPE_FRAME_BEGIN,
+ /* serial= */ 0, timestamp++, /* values= */ null)) {
+ return false;
+ }
+ for (int i = 0; i < info.getValues().size(); ++i) {
+ if (!mSensorManagerInternal.sendSensorAdditionalInfo(
+ sensorDescriptor.getHandle(), info.getType(), /* serial= */ i,
+ timestamp++, info.getValues().get(i))) {
+ return false;
+ }
+ }
+ if (!mSensorManagerInternal.sendSensorAdditionalInfo(
+ sensorDescriptor.getHandle(), SensorAdditionalInfo.TYPE_FRAME_END,
+ /* serial= */ 0, timestamp, /* values= */ null)) {
+ return false;
+ }
+ }
+ return true;
+ }
+
@Nullable
VirtualSensor getSensorByHandle(int handle) {
synchronized (mLock) {
diff --git a/services/companion/java/com/android/server/companion/virtual/VirtualDeviceImpl.java b/services/companion/java/com/android/server/companion/virtual/VirtualDeviceImpl.java
index 28efdfc01ee2..0023b6d53837 100644
--- a/services/companion/java/com/android/server/companion/virtual/VirtualDeviceImpl.java
+++ b/services/companion/java/com/android/server/companion/virtual/VirtualDeviceImpl.java
@@ -55,6 +55,7 @@ import android.companion.virtual.audio.IAudioConfigChangedCallback;
import android.companion.virtual.audio.IAudioRoutingCallback;
import android.companion.virtual.camera.VirtualCameraConfig;
import android.companion.virtual.sensor.VirtualSensor;
+import android.companion.virtual.sensor.VirtualSensorAdditionalInfo;
import android.companion.virtual.sensor.VirtualSensorEvent;
import android.companion.virtualdevice.flags.Flags;
import android.compat.annotation.ChangeId;
@@ -1294,6 +1295,18 @@ final class VirtualDeviceImpl extends IVirtualDevice.Stub
}
@Override // Binder call
+ public boolean sendSensorAdditionalInfo(@NonNull IBinder token,
+ @NonNull VirtualSensorAdditionalInfo info) {
+ checkCallerIsDeviceOwner();
+ final long ident = Binder.clearCallingIdentity();
+ try {
+ return mSensorController.sendSensorAdditionalInfo(token, info);
+ } finally {
+ Binder.restoreCallingIdentity(ident);
+ }
+ }
+
+ @Override // Binder call
public void registerIntentInterceptor(IVirtualDeviceIntentInterceptor intentInterceptor,
IntentFilter filter) {
checkCallerIsDeviceOwner();
diff --git a/services/core/java/com/android/server/sensors/SensorManagerInternal.java b/services/core/java/com/android/server/sensors/SensorManagerInternal.java
index 7ff4ade1101c..9636cc6c77a7 100644
--- a/services/core/java/com/android/server/sensors/SensorManagerInternal.java
+++ b/services/core/java/com/android/server/sensors/SensorManagerInternal.java
@@ -17,6 +17,7 @@
package com.android.server.sensors;
import android.annotation.NonNull;
+import android.annotation.Nullable;
import android.hardware.SensorDirectChannel;
import android.os.ParcelFileDescriptor;
@@ -71,7 +72,7 @@ public abstract class SensorManagerInternal {
/**
* Sends an event for the runtime sensor with the given handle to the framework.
*
- * Only relevant for sending runtime sensor events. @see #createRuntimeSensor.
+ * <p>Only relevant for sending runtime sensor events. @see #createRuntimeSensor.</p>
*
* @param handle The sensor handle.
* @param type The type of the sensor.
@@ -83,6 +84,21 @@ public abstract class SensorManagerInternal {
@NonNull float[] values);
/**
+ * Sends an additional info event for the runtime sensor with the given handle to the framework.
+ *
+ * <p>Only relevant for runtime sensors. @see #createRuntimeSensor.</p>
+ *
+ * @param handle The sensor handle.
+ * @param type The type of payload data.
+ * @param serial The sequence number of this frame for this type.
+ * @param timestampNanos Timestamp of the event.
+ * @param values The payload data represented in float values.
+ * @return Whether the event injection was successful.
+ */
+ public abstract boolean sendSensorAdditionalInfo(int handle, int type, int serial,
+ long timestampNanos, @Nullable float[] values);
+
+ /**
* Listener for proximity sensor state changes.
*/
public interface ProximityActiveListener {
diff --git a/services/core/java/com/android/server/sensors/SensorService.java b/services/core/java/com/android/server/sensors/SensorService.java
index 3de191030d71..0d31b22e2020 100644
--- a/services/core/java/com/android/server/sensors/SensorService.java
+++ b/services/core/java/com/android/server/sensors/SensorService.java
@@ -19,6 +19,7 @@ package com.android.server.sensors;
import static com.android.server.sensors.SensorManagerInternal.ProximityActiveListener;
import android.annotation.NonNull;
+import android.annotation.Nullable;
import android.content.Context;
import android.util.ArrayMap;
@@ -62,6 +63,9 @@ public class SensorService extends SystemService {
private static native void unregisterRuntimeSensorNative(long ptr, int handle);
private static native boolean sendRuntimeSensorEventNative(long ptr, int handle, int type,
long timestampNanos, float[] values);
+ private static native boolean sendRuntimeSensorAdditionalInfoNative(long ptr, int handle,
+ int type, int serial, long timestampNanos, float[] values);
+
public SensorService(Context ctx) {
super(ctx);
@@ -129,6 +133,18 @@ public class SensorService extends SystemService {
}
@Override
+ public boolean sendSensorAdditionalInfo(int handle, int type, int serial,
+ long timestampNanos, @Nullable float[] values) {
+ synchronized (mLock) {
+ if (!mRuntimeSensorHandles.contains(handle)) {
+ return false;
+ }
+ return sendRuntimeSensorAdditionalInfoNative(mPtr, handle, type, serial,
+ timestampNanos, values);
+ }
+ }
+
+ @Override
public void addProximityActiveListener(@NonNull Executor executor,
@NonNull ProximityActiveListener listener) {
Objects.requireNonNull(executor, "executor must not be null");
diff --git a/services/core/jni/com_android_server_sensor_SensorService.cpp b/services/core/jni/com_android_server_sensor_SensorService.cpp
index eb729de6afd4..0bee7181c2d5 100644
--- a/services/core/jni/com_android_server_sensor_SensorService.cpp
+++ b/services/core/jni/com_android_server_sensor_SensorService.cpp
@@ -60,6 +60,8 @@ public:
void unregisterRuntimeSensor(jint handle);
jboolean sendRuntimeSensorEvent(JNIEnv* env, jint handle, jint type, jlong timestamp,
jfloatArray values);
+ jboolean sendRuntimeSensorAdditionalInfo(JNIEnv* env, jint handle, jint type, jint serial,
+ jlong timestamp, jfloatArray values);
private:
sp<SensorService> mService;
@@ -172,9 +174,9 @@ jboolean NativeSensorService::sendRuntimeSensorEvent(JNIEnv* env, jint handle, j
sensors_event_t event{
.version = sizeof(sensors_event_t),
- .timestamp = timestamp,
.sensor = handle,
.type = type,
+ .timestamp = timestamp,
};
int valuesLength = env->GetArrayLength(values);
@@ -234,6 +236,42 @@ jboolean NativeSensorService::sendRuntimeSensorEvent(JNIEnv* env, jint handle, j
return err == OK;
}
+jboolean NativeSensorService::sendRuntimeSensorAdditionalInfo(JNIEnv* env, jint handle, jint type,
+ jint serial, jlong timestamp,
+ jfloatArray values) {
+ if (mService == nullptr) {
+ ALOGD("Dropping sendRuntimeSensorAdditionalInfo, sensor service not available.");
+ return false;
+ }
+
+ sensors_event_t event{
+ .version = sizeof(sensors_event_t),
+ .sensor = handle,
+ .type = SENSOR_TYPE_ADDITIONAL_INFO,
+ .timestamp = timestamp,
+ .additional_info =
+ (additional_info_event_t){
+ .type = type,
+ .serial = serial,
+ },
+ };
+
+ if (values != nullptr) {
+ int valuesLength = env->GetArrayLength(values);
+ if (valuesLength > 14) {
+ ALOGD("Dropping sendRuntimeSensorAdditionalInfo, number of values exceeds maximum.");
+ return false;
+ }
+ if (valuesLength > 0) {
+ jfloat* sensorValues = env->GetFloatArrayElements(values, nullptr);
+ memcpy(event.additional_info.data_float, sensorValues, valuesLength * sizeof(float));
+ }
+ }
+
+ status_t err = mService->sendRuntimeSensorEvent(event);
+ return err == OK;
+}
+
NativeSensorService::ProximityActiveListenerDelegate::ProximityActiveListenerDelegate(
JNIEnv* env, jobject listener)
: mListener(env->NewGlobalRef(listener)) {}
@@ -326,6 +364,13 @@ static jboolean sendRuntimeSensorEventNative(JNIEnv* env, jclass, jlong ptr, jin
return service->sendRuntimeSensorEvent(env, handle, type, timestamp, values);
}
+static jboolean sendRuntimeSensorAdditionalInfoNative(JNIEnv* env, jclass, jlong ptr, jint handle,
+ jint type, jint serial, jlong timestamp,
+ jfloatArray values) {
+ auto* service = reinterpret_cast<NativeSensorService*>(ptr);
+ return service->sendRuntimeSensorAdditionalInfo(env, handle, type, serial, timestamp, values);
+}
+
static const JNINativeMethod methods[] = {
{"startSensorServiceNative", "(L" PROXIMITY_ACTIVE_CLASS ";)J",
reinterpret_cast<void*>(startSensorServiceNative)},
@@ -340,6 +385,8 @@ static const JNINativeMethod methods[] = {
reinterpret_cast<void*>(unregisterRuntimeSensorNative)},
{"sendRuntimeSensorEventNative", "(JIIJ[F)Z",
reinterpret_cast<void*>(sendRuntimeSensorEventNative)},
+ {"sendRuntimeSensorAdditionalInfoNative", "(JIIIJ[F)Z",
+ reinterpret_cast<void*>(sendRuntimeSensorAdditionalInfoNative)},
};
int register_android_server_sensor_SensorService(JavaVM* vm, JNIEnv* env) {
diff --git a/services/tests/servicestests/src/com/android/server/companion/virtual/SensorControllerTest.java b/services/tests/servicestests/src/com/android/server/companion/virtual/SensorControllerTest.java
index 67fc564fa778..2e07cd8ae698 100644
--- a/services/tests/servicestests/src/com/android/server/companion/virtual/SensorControllerTest.java
+++ b/services/tests/servicestests/src/com/android/server/companion/virtual/SensorControllerTest.java
@@ -22,18 +22,25 @@ import static org.junit.Assert.assertThrows;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyFloat;
import static org.mockito.ArgumentMatchers.anyInt;
+import static org.mockito.ArgumentMatchers.anyLong;
import static org.mockito.ArgumentMatchers.anyString;
+import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.ArgumentMatchers.isNull;
import static org.mockito.Mockito.clearInvocations;
import static org.mockito.Mockito.doReturn;
+import static org.mockito.Mockito.inOrder;
import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
import android.companion.virtual.IVirtualDevice;
import android.companion.virtual.sensor.IVirtualSensorCallback;
import android.companion.virtual.sensor.VirtualSensor;
+import android.companion.virtual.sensor.VirtualSensorAdditionalInfo;
import android.companion.virtual.sensor.VirtualSensorConfig;
import android.companion.virtual.sensor.VirtualSensorEvent;
import android.content.AttributionSource;
import android.hardware.Sensor;
+import android.hardware.SensorAdditionalInfo;
import android.os.Binder;
import android.os.IBinder;
import android.os.RemoteException;
@@ -49,6 +56,7 @@ import com.google.common.collect.Iterables;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
+import org.mockito.InOrder;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
@@ -65,6 +73,9 @@ public class SensorControllerTest {
private static final int VIRTUAL_SENSOR_TYPE = Sensor.TYPE_ACCELEROMETER;
+ private static final float[] ADDITIONAL_INFO_VALUES_1 = new float[] {1.2f, 3.4f};
+ private static final float[] ADDITIONAL_INFO_VALUES_2 = new float[] {5.6f, 7.8f};
+
@Mock
private SensorManagerInternal mSensorManagerInternalMock;
@Mock
@@ -155,6 +166,53 @@ public class SensorControllerTest {
}
@Test
+ public void sendSensorAdditionalInfo_invalidToken_throwsException() throws Exception {
+ SensorController sensorController = doCreateSensorSuccessfully();
+
+ final VirtualSensorAdditionalInfo info =
+ new VirtualSensorAdditionalInfo.Builder(SensorAdditionalInfo.TYPE_UNTRACKED_DELAY)
+ .addValues(ADDITIONAL_INFO_VALUES_1)
+ .addValues(ADDITIONAL_INFO_VALUES_2)
+ .build();
+ assertThrows(
+ IllegalArgumentException.class,
+ () -> sensorController.sendSensorAdditionalInfo(
+ new Binder("invalidSensorToken"), info));
+ }
+
+ @Test
+ public void sendSensorAdditionalInfo_success() throws Exception {
+ SensorController sensorController = doCreateSensorSuccessfully();
+
+ clearInvocations(mSensorManagerInternalMock);
+ when(mSensorManagerInternalMock.sendSensorAdditionalInfo(
+ anyInt(), anyInt(), anyInt(), anyLong(), any()))
+ .thenReturn(true);
+ IBinder token = Iterables.getOnlyElement(sensorController.getSensorDescriptors().keySet());
+
+ final VirtualSensorAdditionalInfo info =
+ new VirtualSensorAdditionalInfo.Builder(SensorAdditionalInfo.TYPE_UNTRACKED_DELAY)
+ .addValues(ADDITIONAL_INFO_VALUES_1)
+ .addValues(ADDITIONAL_INFO_VALUES_2)
+ .build();
+ sensorController.sendSensorAdditionalInfo(token, info);
+
+ InOrder inOrder = inOrder(mSensorManagerInternalMock);
+ inOrder.verify(mSensorManagerInternalMock).sendSensorAdditionalInfo(
+ eq(SENSOR_HANDLE), eq(SensorAdditionalInfo.TYPE_FRAME_BEGIN),
+ /*serial=*/ eq(0), /* timestamp= */ anyLong(), /*values=*/ isNull());
+ inOrder.verify(mSensorManagerInternalMock).sendSensorAdditionalInfo(
+ eq(SENSOR_HANDLE), eq(SensorAdditionalInfo.TYPE_UNTRACKED_DELAY),
+ /*serial=*/ eq(0), /* timestamp= */ anyLong(), eq(ADDITIONAL_INFO_VALUES_1));
+ inOrder.verify(mSensorManagerInternalMock).sendSensorAdditionalInfo(
+ eq(SENSOR_HANDLE), eq(SensorAdditionalInfo.TYPE_UNTRACKED_DELAY),
+ /*serial=*/ eq(1), /* timestamp= */ anyLong(), eq(ADDITIONAL_INFO_VALUES_2));
+ inOrder.verify(mSensorManagerInternalMock).sendSensorAdditionalInfo(
+ eq(SENSOR_HANDLE), eq(SensorAdditionalInfo.TYPE_FRAME_END),
+ /*serial=*/ eq(0), /* timestamp= */ anyLong(), /*values=*/ isNull());
+ }
+
+ @Test
public void close_unregistersSensors() throws Exception {
SensorController sensorController = doCreateSensorSuccessfully();
diff --git a/telephony/java/android/telephony/CellSignalStrengthLte.java b/telephony/java/android/telephony/CellSignalStrengthLte.java
index f5282639ae6c..6e23edf936c7 100644
--- a/telephony/java/android/telephony/CellSignalStrengthLte.java
+++ b/telephony/java/android/telephony/CellSignalStrengthLte.java
@@ -614,7 +614,7 @@ public final class CellSignalStrengthLte extends CellSignalStrength implements P
/** @hide */
public static int convertRssiAsuToDBm(int rssiAsu) {
- if (rssiAsu == SIGNAL_STRENGTH_LTE_RSSI_ASU_UNKNOWN) {
+ if (rssiAsu == SIGNAL_STRENGTH_LTE_RSSI_ASU_UNKNOWN || rssiAsu == Integer.MAX_VALUE) {
return CellInfo.UNAVAILABLE;
}
if ((rssiAsu < SIGNAL_STRENGTH_LTE_RSSI_VALID_ASU_MIN_VALUE