Merge "Return unique_ptr from readFromParcel" into main
diff --git a/data/etc/aosp_excluded_hardware.xml b/data/etc/aosp_excluded_hardware.xml
index c12f435..013f278 100644
--- a/data/etc/aosp_excluded_hardware.xml
+++ b/data/etc/aosp_excluded_hardware.xml
@@ -18,5 +18,4 @@
<!-- This should be used to exclude this feature from aosp targets. As aosp configurations
may or may not have a valid location provider -->
<unavailable-feature name="android.hardware.location.network" />
- <unavailable-feature name="android.software.device_id_attestation" />
</permissions>
diff --git a/libs/binder/Android.bp b/libs/binder/Android.bp
index fd2b27f..620c23c 100644
--- a/libs/binder/Android.bp
+++ b/libs/binder/Android.bp
@@ -359,6 +359,38 @@
}
cc_library_static {
+ name: "libbinder_rpc_no_blob",
+ vendor_available: true,
+ defaults: [
+ "libbinder_common_defaults",
+ "libbinder_android_defaults",
+ "libbinder_kernel_defaults",
+ ],
+ cflags: [
+ "-DBINDER_DISABLE_BLOB",
+ ],
+ visibility: [
+ ":__subpackages__",
+ ],
+}
+
+cc_library_static {
+ name: "libbinder_rpc_no_native_handle",
+ vendor_available: true,
+ defaults: [
+ "libbinder_common_defaults",
+ "libbinder_android_defaults",
+ "libbinder_kernel_defaults",
+ ],
+ cflags: [
+ "-DBINDER_DISABLE_NATIVE_HANDLE",
+ ],
+ visibility: [
+ ":__subpackages__",
+ ],
+}
+
+cc_library_static {
name: "libbinder_rpc_single_threaded",
defaults: [
"libbinder_common_defaults",
diff --git a/libs/binder/Parcel.cpp b/libs/binder/Parcel.cpp
index b78b0b3..a3ff7d2 100644
--- a/libs/binder/Parcel.cpp
+++ b/libs/binder/Parcel.cpp
@@ -40,7 +40,9 @@
#include <binder/TextOutput.h>
#include <android-base/scopeguard.h>
+#ifndef BINDER_DISABLE_BLOB
#include <cutils/ashmem.h>
+#endif
#include <utils/Flattenable.h>
#include <utils/Log.h>
#include <utils/String16.h>
@@ -1475,6 +1477,7 @@
return writeParcelable(*parcelable);
}
+#ifndef BINDER_DISABLE_NATIVE_HANDLE
status_t Parcel::writeNativeHandle(const native_handle* handle)
{
if (!handle || handle->version != sizeof(native_handle))
@@ -1497,6 +1500,7 @@
err = write(handle->data + handle->numFds, sizeof(int)*handle->numInts);
return err;
}
+#endif
status_t Parcel::writeFileDescriptor(int fd, bool takeOwnership) {
if (auto* rpcFields = maybeRpcFields()) {
@@ -1589,6 +1593,12 @@
status_t Parcel::writeBlob(size_t len, bool mutableCopy, WritableBlob* outBlob)
{
+#ifdef BINDER_DISABLE_BLOB
+ (void)len;
+ (void)mutableCopy;
+ (void)outBlob;
+ return INVALID_OPERATION;
+#else
if (len > INT32_MAX) {
// don't accept size_t values which may have come from an
// inadvertent conversion from a negative int.
@@ -1640,6 +1650,7 @@
}
::close(fd);
return status;
+#endif
}
status_t Parcel::writeDupImmutableBlobFileDescriptor(int fd)
@@ -2271,6 +2282,7 @@
return status.exceptionCode();
}
+#ifndef BINDER_DISABLE_NATIVE_HANDLE
native_handle* Parcel::readNativeHandle() const
{
int numFds, numInts;
@@ -2303,6 +2315,7 @@
}
return h;
}
+#endif
int Parcel::readFileDescriptor() const {
if (const auto* rpcFields = maybeRpcFields()) {
@@ -2423,6 +2436,11 @@
status_t Parcel::readBlob(size_t len, ReadableBlob* outBlob) const
{
+#ifdef BINDER_DISABLE_BLOB
+ (void)len;
+ (void)outBlob;
+ return INVALID_OPERATION;
+#else
int32_t blobType;
status_t status = readInt32(&blobType);
if (status) return status;
@@ -2456,6 +2474,7 @@
outBlob->init(fd, ptr, len, isMutable);
return NO_ERROR;
+#endif
}
status_t Parcel::read(FlattenableHelperInterface& val) const
@@ -3204,6 +3223,7 @@
}
size_t openAshmemSize = 0;
+#ifndef BINDER_DISABLE_BLOB
for (size_t i = 0; i < kernelFields->mObjectsSize; i++) {
const flat_binder_object* flat =
reinterpret_cast<const flat_binder_object*>(mData + kernelFields->mObjects[i]);
@@ -3218,6 +3238,7 @@
}
}
}
+#endif
return openAshmemSize;
}
#endif // BINDER_WITH_KERNEL_IPC
diff --git a/libs/binder/include/binder/Parcel.h b/libs/binder/include/binder/Parcel.h
index b94267c..98d12bb 100644
--- a/libs/binder/include/binder/Parcel.h
+++ b/libs/binder/include/binder/Parcel.h
@@ -26,7 +26,9 @@
#include <vector>
#include <android-base/unique_fd.h>
+#ifndef BINDER_DISABLE_NATIVE_HANDLE
#include <cutils/native_handle.h>
+#endif
#include <utils/Errors.h>
#include <utils/RefBase.h>
#include <utils/String16.h>
@@ -324,11 +326,13 @@
template<typename T>
status_t writeVectorSize(const std::unique_ptr<std::vector<T>>& val) __attribute__((deprecated("use std::optional version instead")));
+#ifndef BINDER_DISABLE_NATIVE_HANDLE
// Place a native_handle into the parcel (the native_handle's file-
// descriptors are dup'ed, so it is safe to delete the native_handle
// when this function returns).
// Doesn't take ownership of the native_handle.
status_t writeNativeHandle(const native_handle* handle);
+#endif
// Place a file descriptor into the parcel. The given fd must remain
// valid for the lifetime of the parcel.
@@ -559,13 +563,14 @@
// response headers rather than doing it by hand.
int32_t readExceptionCode() const;
+#ifndef BINDER_DISABLE_NATIVE_HANDLE
// Retrieve native_handle from the parcel. This returns a copy of the
// parcel's native_handle (the caller takes ownership). The caller
- // must free the native_handle with native_handle_close() and
+ // must free the native_handle with native_handle_close() and
// native_handle_delete().
native_handle* readNativeHandle() const;
+#endif
-
// Retrieve a file descriptor from the parcel. This returns the raw fd
// in the parcel, which you do not own -- use dup() to get your own copy.
int readFileDescriptor() const;
diff --git a/libs/binder/ndk/include_platform/android/binder_manager.h b/libs/binder/ndk/include_platform/android/binder_manager.h
index 89fd7a3..316a79c 100644
--- a/libs/binder/ndk/include_platform/android/binder_manager.h
+++ b/libs/binder/ndk/include_platform/android/binder_manager.h
@@ -120,7 +120,7 @@
/**
* Gets a binder object with this specific instance name. Efficiently waits for the service.
- * If the service is not declared, it will wait indefinitely. Requires the threadpool
+ * If the service is not ever registered, it will wait indefinitely. Requires the threadpool
* to be started in the service.
* This also implicitly calls AIBinder_incStrong (so the caller of this function is responsible
* for calling AIBinder_decStrong).
diff --git a/libs/binder/rust/src/lib.rs b/libs/binder/rust/src/lib.rs
index 8841fe6..ed870b6 100644
--- a/libs/binder/rust/src/lib.rs
+++ b/libs/binder/rust/src/lib.rs
@@ -144,6 +144,7 @@
#[doc(hidden)]
pub mod unstable_api {
pub use crate::binder::AsNative;
+ pub use crate::error::status_result;
pub use crate::proxy::unstable_api::new_spibinder;
pub use crate::sys::AIBinder;
pub use crate::sys::AParcel;
diff --git a/libs/binder/rust/src/parcel.rs b/libs/binder/rust/src/parcel.rs
index 3c615ed..f9f135d 100644
--- a/libs/binder/rust/src/parcel.rs
+++ b/libs/binder/rust/src/parcel.rs
@@ -54,6 +54,10 @@
/// Safety: This type guarantees that it owns the AParcel and that all access to
/// the AParcel happens through the Parcel, so it is ok to send across threads.
+///
+/// It would not be okay to implement Sync, because that would allow you to call
+/// the reading methods from several threads in parallel, which would be a data
+/// race on the cursor position inside the AParcel.
unsafe impl Send for Parcel {}
/// Container for a message (data and object references) that can be sent
diff --git a/libs/binder/tests/binderRpcTest.cpp b/libs/binder/tests/binderRpcTest.cpp
index 5884dbe..1340ea1 100644
--- a/libs/binder/tests/binderRpcTest.cpp
+++ b/libs/binder/tests/binderRpcTest.cpp
@@ -14,7 +14,10 @@
* limitations under the License.
*/
+#ifndef __ANDROID_VENDOR__
+// only used on NDK tests outside of vendor
#include <aidl/IBinderRpcTest.h>
+#endif
#include <android-base/stringprintf.h>
#include <chrono>
diff --git a/libs/binder/tests/parcel_fuzzer/random_parcel.cpp b/libs/binder/tests/parcel_fuzzer/random_parcel.cpp
index f0beed2..f367b41 100644
--- a/libs/binder/tests/parcel_fuzzer/random_parcel.cpp
+++ b/libs/binder/tests/parcel_fuzzer/random_parcel.cpp
@@ -66,6 +66,11 @@
},
// write FD
[&]() {
+ // b/296516864 - Limit number of objects written to a parcel.
+ if (p->objectsCount() > 100) {
+ return;
+ }
+
if (options->extraFds.size() > 0 && provider.ConsumeBool()) {
const base::unique_fd& fd = options->extraFds.at(
provider.ConsumeIntegralInRange<size_t>(0,
@@ -82,7 +87,6 @@
CHECK(OK ==
p->writeFileDescriptor(fds.begin()->release(),
true /*takeOwnership*/));
-
options->extraFds.insert(options->extraFds.end(),
std::make_move_iterator(fds.begin() + 1),
std::make_move_iterator(fds.end()));
@@ -90,6 +94,11 @@
},
// write binder
[&]() {
+ // b/296516864 - Limit number of objects written to a parcel.
+ if (p->objectsCount() > 100) {
+ return;
+ }
+
sp<IBinder> binder;
if (options->extraBinders.size() > 0 && provider.ConsumeBool()) {
binder = options->extraBinders.at(
diff --git a/libs/gui/SurfaceComposerClient.cpp b/libs/gui/SurfaceComposerClient.cpp
index 05e2ddf..a351811 100644
--- a/libs/gui/SurfaceComposerClient.cpp
+++ b/libs/gui/SurfaceComposerClient.cpp
@@ -2604,7 +2604,7 @@
outMode.resolution.height = mode.resolution.height;
outMode.xDpi = mode.xDpi;
outMode.yDpi = mode.yDpi;
- outMode.refreshRate = mode.refreshRate;
+ outMode.peakRefreshRate = mode.peakRefreshRate;
outMode.vsyncRate = mode.vsyncRate;
outMode.appVsyncOffset = mode.appVsyncOffset;
outMode.sfVsyncOffset = mode.sfVsyncOffset;
diff --git a/libs/gui/aidl/android/gui/DisplayMode.aidl b/libs/gui/aidl/android/gui/DisplayMode.aidl
index b057653..f605177 100644
--- a/libs/gui/aidl/android/gui/DisplayMode.aidl
+++ b/libs/gui/aidl/android/gui/DisplayMode.aidl
@@ -30,7 +30,7 @@
int[] supportedHdrTypes;
// Some modes have peak refresh rate lower than the panel vsync rate.
- float refreshRate = 0.0f;
+ float peakRefreshRate = 0.0f;
float vsyncRate = 0.0f;
long appVsyncOffset = 0;
long sfVsyncOffset = 0;
diff --git a/libs/gui/tests/EndToEndNativeInputTest.cpp b/libs/gui/tests/EndToEndNativeInputTest.cpp
index 662e9fe..d4b8dbe 100644
--- a/libs/gui/tests/EndToEndNativeInputTest.cpp
+++ b/libs/gui/tests/EndToEndNativeInputTest.cpp
@@ -394,7 +394,7 @@
// After a new buffer is queued, SurfaceFlinger is notified and will
// latch the new buffer on next vsync. Let's heuristically wait for 3
// vsyncs.
- mBufferPostDelay = static_cast<int32_t>(1e6 / mode.refreshRate) * 3;
+ mBufferPostDelay = static_cast<int32_t>(1e6 / mode.peakRefreshRate) * 3;
}
void TearDown() {
diff --git a/libs/nativedisplay/ADisplay.cpp b/libs/nativedisplay/ADisplay.cpp
index bf0805b..e3be3bc 100644
--- a/libs/nativedisplay/ADisplay.cpp
+++ b/libs/nativedisplay/ADisplay.cpp
@@ -155,7 +155,7 @@
const ui::DisplayMode& mode = modes[j];
modesPerDisplay[i].emplace_back(
DisplayConfigImpl{static_cast<size_t>(mode.id), mode.resolution.getWidth(),
- mode.resolution.getHeight(), mode.refreshRate,
+ mode.resolution.getHeight(), mode.peakRefreshRate,
mode.sfVsyncOffset, mode.appVsyncOffset});
}
}
diff --git a/libs/nativewindow/rust/Android.bp b/libs/nativewindow/rust/Android.bp
index dc1575c..90d0a8e 100644
--- a/libs/nativewindow/rust/Android.bp
+++ b/libs/nativewindow/rust/Android.bp
@@ -19,7 +19,7 @@
}
rust_bindgen {
- name: "libnativewindow_bindgen",
+ name: "libnativewindow_bindgen_internal",
crate_name: "nativewindow_bindgen",
wrapper_src: "sys/nativewindow_bindings.h",
source_stem: "bindings",
@@ -28,13 +28,21 @@
"--bitfield-enum=AHardwareBuffer_UsageFlags",
"--allowlist-file=.*/nativewindow/include/.*\\.h",
+ "--blocklist-type",
+ "AParcel",
+ "--raw-line",
+ "use binder::unstable_api::AParcel;",
"--with-derive-eq",
"--with-derive-partialeq",
],
shared_libs: [
+ "libbinder_ndk",
"libnativewindow",
],
+ rustlibs: [
+ "libbinder_rs",
+ ],
// Currently necessary for host builds
// TODO(b/31559095): bionic on host should define this
@@ -44,12 +52,40 @@
},
},
min_sdk_version: "VanillaIceCream",
+ vendor_available: true,
+}
+
+rust_library {
+ name: "libnativewindow_bindgen",
+ crate_name: "nativewindow_bindgen",
+ srcs: [":libnativewindow_bindgen_internal"],
+ shared_libs: [
+ "libbinder_ndk",
+ "libnativewindow",
+ ],
+ rustlibs: [
+ "libbinder_rs",
+ ],
+ lints: "none",
+ clippy_lints: "none",
+ // Currently necessary for host builds
+ // TODO(b/31559095): bionic on host should define this
+ target: {
+ darwin: {
+ enabled: false,
+ },
+ },
+ min_sdk_version: "VanillaIceCream",
+ vendor_available: true,
}
rust_test {
name: "libnativewindow_bindgen_test",
- srcs: [":libnativewindow_bindgen"],
+ srcs: [":libnativewindow_bindgen_internal"],
crate_name: "nativewindow_bindgen_test",
+ rustlibs: [
+ "libbinder_rs",
+ ],
test_suites: ["general-tests"],
auto_gen_config: true,
clippy_lints: "none",
@@ -60,6 +96,7 @@
name: "libnativewindow_defaults",
srcs: ["src/lib.rs"],
rustlibs: [
+ "libbinder_rs",
"libnativewindow_bindgen",
],
}
@@ -77,6 +114,7 @@
},
},
min_sdk_version: "VanillaIceCream",
+ vendor_available: true,
}
rust_test {
diff --git a/libs/nativewindow/rust/src/lib.rs b/libs/nativewindow/rust/src/lib.rs
index 6eb3bbc..6f86c4a 100644
--- a/libs/nativewindow/rust/src/lib.rs
+++ b/libs/nativewindow/rust/src/lib.rs
@@ -16,13 +16,22 @@
extern crate nativewindow_bindgen as ffi;
-pub use ffi::{AHardwareBuffer, AHardwareBuffer_Format, AHardwareBuffer_UsageFlags};
+pub use ffi::{AHardwareBuffer_Format, AHardwareBuffer_UsageFlags};
+use binder::{
+ binder_impl::{
+ BorrowedParcel, Deserialize, DeserializeArray, DeserializeOption, Serialize,
+ SerializeArray, SerializeOption, NON_NULL_PARCELABLE_FLAG, NULL_PARCELABLE_FLAG,
+ },
+ unstable_api::{status_result, AsNative},
+ StatusCode,
+};
+use ffi::{AHardwareBuffer, AHardwareBuffer_readFromParcel, AHardwareBuffer_writeToParcel};
use std::fmt::{self, Debug, Formatter};
use std::mem::ManuallyDrop;
-use std::ptr::{self, NonNull};
+use std::ptr::{self, null_mut, NonNull};
-/// Wrapper around an opaque C AHardwareBuffer.
+/// Wrapper around an opaque C `AHardwareBuffer`.
#[derive(PartialEq, Eq)]
pub struct HardwareBuffer(NonNull<AHardwareBuffer>);
@@ -120,8 +129,11 @@
/// Available since API level 31.
pub fn id(&self) -> u64 {
let mut out_id = 0;
- // SAFETY: Neither pointers can be null.
- let status = unsafe { ffi::AHardwareBuffer_getId(self.0.as_ref(), &mut out_id) };
+ // SAFETY: The AHardwareBuffer pointer we pass is guaranteed to be non-null and valid
+ // because it must have been allocated by `AHardwareBuffer_allocate`,
+ // `AHardwareBuffer_readFromParcel` or the caller of `from_raw` and we have not yet
+ // released it. The id pointer must be valid because it comes from a reference.
+ let status = unsafe { ffi::AHardwareBuffer_getId(self.0.as_ptr(), &mut out_id) };
assert_eq!(status, 0, "id() failed for AHardwareBuffer with error code: {status}");
out_id
@@ -176,9 +188,10 @@
impl Drop for HardwareBuffer {
fn drop(&mut self) {
- // SAFETY: self.0 will never be null. AHardwareBuffers allocated from within Rust will have
- // a refcount of one, and there is a safety warning on taking an AHardwareBuffer from a raw
- // pointer requiring callers to ensure the refcount is managed appropriately.
+ // SAFETY: The AHardwareBuffer pointer we pass is guaranteed to be non-null and valid
+ // because it must have been allocated by `AHardwareBuffer_allocate`,
+ // `AHardwareBuffer_readFromParcel` or the caller of `from_raw` and we have not yet
+ // released it.
unsafe { ffi::AHardwareBuffer_release(self.0.as_ptr()) }
}
}
@@ -197,6 +210,82 @@
}
}
+impl Serialize for HardwareBuffer {
+ fn serialize(&self, parcel: &mut BorrowedParcel) -> Result<(), StatusCode> {
+ SerializeOption::serialize_option(Some(self), parcel)
+ }
+}
+
+impl SerializeOption for HardwareBuffer {
+ fn serialize_option(
+ this: Option<&Self>,
+ parcel: &mut BorrowedParcel,
+ ) -> Result<(), StatusCode> {
+ if let Some(this) = this {
+ parcel.write(&NON_NULL_PARCELABLE_FLAG)?;
+
+ let status =
+ // SAFETY: The AHardwareBuffer pointer we pass is guaranteed to be non-null and valid
+ // because it must have been allocated by `AHardwareBuffer_allocate`,
+ // `AHardwareBuffer_readFromParcel` or the caller of `from_raw` and we have not yet
+ // released it.
+ unsafe { AHardwareBuffer_writeToParcel(this.0.as_ptr(), parcel.as_native_mut()) };
+ status_result(status)
+ } else {
+ parcel.write(&NULL_PARCELABLE_FLAG)
+ }
+ }
+}
+
+impl Deserialize for HardwareBuffer {
+ type UninitType = Option<Self>;
+
+ fn uninit() -> Option<Self> {
+ None
+ }
+
+ fn from_init(value: Self) -> Option<Self> {
+ Some(value)
+ }
+
+ fn deserialize(parcel: &BorrowedParcel) -> Result<Self, StatusCode> {
+ DeserializeOption::deserialize_option(parcel)
+ .transpose()
+ .unwrap_or(Err(StatusCode::UNEXPECTED_NULL))
+ }
+}
+
+impl DeserializeOption for HardwareBuffer {
+ fn deserialize_option(parcel: &BorrowedParcel) -> Result<Option<Self>, StatusCode> {
+ let present: i32 = parcel.read()?;
+ match present {
+ NULL_PARCELABLE_FLAG => Ok(None),
+ NON_NULL_PARCELABLE_FLAG => {
+ let mut buffer = null_mut();
+
+ let status =
+ // SAFETY: Both pointers must be valid because they are obtained from references.
+ // `AHardwareBuffer_readFromParcel` doesn't store them or do anything else special
+ // with them. If it returns success then it will have allocated a new
+ // `AHardwareBuffer` and incremented the reference count, so we can use it until we
+ // release it.
+ unsafe { AHardwareBuffer_readFromParcel(parcel.as_native(), &mut buffer) };
+
+ status_result(status)?;
+
+ Ok(Some(Self(NonNull::new(buffer).expect(
+ "AHardwareBuffer_readFromParcel returned success but didn't allocate buffer",
+ ))))
+ }
+ _ => Err(StatusCode::BAD_VALUE),
+ }
+ }
+}
+
+impl SerializeArray for HardwareBuffer {}
+
+impl DeserializeArray for HardwareBuffer {}
+
// SAFETY: The underlying *AHardwareBuffers can be moved between threads.
unsafe impl Send for HardwareBuffer {}
diff --git a/libs/nativewindow/rust/sys/nativewindow_bindings.h b/libs/nativewindow/rust/sys/nativewindow_bindings.h
index e652aee..4525a42 100644
--- a/libs/nativewindow/rust/sys/nativewindow_bindings.h
+++ b/libs/nativewindow/rust/sys/nativewindow_bindings.h
@@ -16,5 +16,6 @@
#include <android/data_space.h>
#include <android/hardware_buffer.h>
+#include <android/hardware_buffer_aidl.h>
#include <android/hdr_metadata.h>
#include <android/native_window.h>
diff --git a/libs/renderengine/skia/debug/SkiaMemoryReporter.cpp b/libs/renderengine/skia/debug/SkiaMemoryReporter.cpp
index f24a4f1..5bf6560 100644
--- a/libs/renderengine/skia/debug/SkiaMemoryReporter.cpp
+++ b/libs/renderengine/skia/debug/SkiaMemoryReporter.cpp
@@ -18,7 +18,6 @@
#include "SkiaMemoryReporter.h"
-#include <SkString.h>
#include <android-base/stringprintf.h>
#include <log/log_main.h>
@@ -142,7 +141,7 @@
TraceValue traceValue = convertUnits(result->second);
const char* entry = (traceValue.count > 1) ? "entries" : "entry";
StringAppendF(&log, " %s: %.2f %s (%d %s)\n", categoryItem->first.c_str(),
- traceValue.value, traceValue.units, traceValue.count, entry);
+ traceValue.value, traceValue.units.c_str(), traceValue.count, entry);
}
if (mItemize) {
for (const auto& individualItem : resultsMap) {
@@ -153,7 +152,7 @@
auto result = individualItem.second.find("size");
TraceValue size = convertUnits(result->second);
StringAppendF(&log, " %s: size[%.2f %s]", individualItem.first.c_str(),
- size.value, size.units);
+ size.value, size.units.c_str());
if (!wrappedResources) {
for (const auto& itemValues : individualItem.second) {
if (strcmp("size", itemValues.first) == 0) {
@@ -162,10 +161,10 @@
TraceValue traceValue = convertUnits(itemValues.second);
if (traceValue.value == 0.0f) {
StringAppendF(&log, " %s[%s]", itemValues.first,
- traceValue.units);
+ traceValue.units.c_str());
} else {
StringAppendF(&log, " %s[%.2f %s]", itemValues.first,
- traceValue.value, traceValue.units);
+ traceValue.value, traceValue.units.c_str());
}
}
}
@@ -184,16 +183,16 @@
TraceValue total = convertUnits(mTotalSize);
TraceValue purgeable = convertUnits(mPurgeableSize);
StringAppendF(&log, " %.0f bytes, %.2f %s (%.2f %s is purgeable)\n", mTotalSize.value,
- total.value, total.units, purgeable.value, purgeable.units);
+ total.value, total.units.c_str(), purgeable.value, purgeable.units.c_str());
}
SkiaMemoryReporter::TraceValue SkiaMemoryReporter::convertUnits(const TraceValue& value) {
TraceValue output(value);
- if (SkString("bytes") == SkString(output.units) && output.value >= 1024) {
+ if (SkString("bytes") == output.units && output.value >= 1024) {
output.value = output.value / 1024.0f;
output.units = "KB";
}
- if (SkString("KB") == SkString(output.units) && output.value >= 1024) {
+ if (SkString("KB") == output.units && output.value >= 1024) {
output.value = output.value / 1024.0f;
output.units = "MB";
}
diff --git a/libs/renderengine/skia/debug/SkiaMemoryReporter.h b/libs/renderengine/skia/debug/SkiaMemoryReporter.h
index dbbd65b..da91674 100644
--- a/libs/renderengine/skia/debug/SkiaMemoryReporter.h
+++ b/libs/renderengine/skia/debug/SkiaMemoryReporter.h
@@ -16,6 +16,7 @@
#pragma once
+#include <SkString.h>
#include <SkTraceMemoryDump.h>
#include <string>
@@ -75,7 +76,7 @@
TraceValue(const char* units, uint64_t value) : units(units), value(value), count(1) {}
TraceValue(const TraceValue& v) : units(v.units), value(v.value), count(v.count) {}
- const char* units;
+ SkString units;
float value;
int count;
};
@@ -104,4 +105,4 @@
} /* namespace skia */
} /* namespace renderengine */
-} /* namespace android */
\ No newline at end of file
+} /* namespace android */
diff --git a/libs/sensor/Android.bp b/libs/sensor/Android.bp
index b6b9cc4..d992aa5 100644
--- a/libs/sensor/Android.bp
+++ b/libs/sensor/Android.bp
@@ -49,6 +49,7 @@
"liblog",
"libhardware",
"libpermission",
+ "android.companion.virtual.virtualdevice_aidl-cpp",
],
export_include_dirs: ["include"],
diff --git a/libs/sensor/SensorManager.cpp b/libs/sensor/SensorManager.cpp
index 3ed8c8a..d112a12 100644
--- a/libs/sensor/SensorManager.cpp
+++ b/libs/sensor/SensorManager.cpp
@@ -26,6 +26,8 @@
#include <utils/RefBase.h>
#include <utils/Singleton.h>
+#include <android/companion/virtualnative/IVirtualDeviceManagerNative.h>
+
#include <binder/IBinder.h>
#include <binder/IPermissionController.h>
#include <binder/IServiceManager.h>
@@ -39,6 +41,43 @@
namespace android {
// ----------------------------------------------------------------------------
+namespace {
+
+using ::android::companion::virtualnative::IVirtualDeviceManagerNative;
+
+static constexpr int DEVICE_ID_DEFAULT = 0;
+
+// Returns the deviceId of the device where this uid is observed. If the uid is present on more than
+// one devices, return the default deviceId.
+int getDeviceIdForUid(uid_t uid) {
+ sp<IBinder> binder =
+ defaultServiceManager()->checkService(String16("virtualdevice_native"));
+ if (binder != nullptr) {
+ auto vdm = interface_cast<IVirtualDeviceManagerNative>(binder);
+ std::vector<int> deviceIds;
+ vdm->getDeviceIdsForUid(uid, &deviceIds);
+ // If the UID is associated with multiple virtual devices, use the default device's
+ // sensors as we cannot disambiguate here. This effectively means that the app has
+ // activities on different devices at the same time, so it must handle the device
+ // awareness by itself.
+ if (deviceIds.size() == 1) {
+ const int deviceId = deviceIds.at(0);
+ int devicePolicy = IVirtualDeviceManagerNative::DEVICE_POLICY_DEFAULT;
+ vdm->getDevicePolicy(deviceId,
+ IVirtualDeviceManagerNative::POLICY_TYPE_SENSORS,
+ &devicePolicy);
+ if (devicePolicy == IVirtualDeviceManagerNative::DEVICE_POLICY_CUSTOM) {
+ return deviceId;
+ }
+ }
+ } else {
+ ALOGW("Cannot get virtualdevice_native service");
+ }
+ return DEVICE_ID_DEFAULT;
+}
+
+} // namespace
+
Mutex SensorManager::sLock;
std::map<String16, SensorManager*> SensorManager::sPackageInstances;
@@ -53,6 +92,7 @@
sensorManager = iterator->second;
} else {
String16 opPackageName = packageName;
+ const uid_t uid = IPCThreadState::self()->getCallingUid();
// It is possible that the calling code has no access to the package name.
// In this case we will get the packages for the calling UID and pick the
@@ -63,7 +103,6 @@
if (opPackageName.size() <= 0) {
sp<IBinder> binder = defaultServiceManager()->getService(String16("permission"));
if (binder != nullptr) {
- const uid_t uid = IPCThreadState::self()->getCallingUid();
Vector<String16> packages;
interface_cast<IPermissionController>(binder)->getPackagesForUid(uid, packages);
if (!packages.isEmpty()) {
@@ -76,7 +115,10 @@
}
}
- sensorManager = new SensorManager(opPackageName);
+ // Check if the calling UID is observed on a virtual device. If so, provide that device's
+ // sensors by default instead of the default device's sensors.
+ const int deviceId = getDeviceIdForUid(uid);
+ sensorManager = new SensorManager(opPackageName, deviceId);
// If we had no package name, we looked it up from the UID and the sensor
// manager instance we created should also be mapped to the empty package
@@ -102,8 +144,9 @@
}
}
-SensorManager::SensorManager(const String16& opPackageName)
- : mSensorList(nullptr), mOpPackageName(opPackageName), mDirectConnectionHandle(1) {
+SensorManager::SensorManager(const String16& opPackageName, int deviceId)
+ : mSensorList(nullptr), mOpPackageName(opPackageName), mDeviceId(deviceId),
+ mDirectConnectionHandle(1) {
Mutex::Autolock _l(mLock);
assertStateLocked();
}
@@ -174,7 +217,12 @@
mDeathObserver = new DeathObserver(*const_cast<SensorManager *>(this));
IInterface::asBinder(mSensorServer)->linkToDeath(mDeathObserver);
- mSensors = mSensorServer->getSensorList(mOpPackageName);
+ if (mDeviceId == DEVICE_ID_DEFAULT) {
+ mSensors = mSensorServer->getSensorList(mOpPackageName);
+ } else {
+ mSensors = mSensorServer->getRuntimeSensorList(mOpPackageName, mDeviceId);
+ }
+
size_t count = mSensors.size();
// If count is 0, mSensorList will be non-null. This is old
// existing behavior and callers expect this.
diff --git a/libs/sensor/include/sensor/SensorManager.h b/libs/sensor/include/sensor/SensorManager.h
index 64b4501..e67fac7 100644
--- a/libs/sensor/include/sensor/SensorManager.h
+++ b/libs/sensor/include/sensor/SensorManager.h
@@ -79,7 +79,7 @@
void sensorManagerDied();
static status_t waitForSensorService(sp<ISensorServer> *server);
- explicit SensorManager(const String16& opPackageName);
+ explicit SensorManager(const String16& opPackageName, int deviceId);
status_t assertStateLocked();
private:
@@ -94,6 +94,7 @@
Vector<Sensor> mDynamicSensors;
sp<IBinder::DeathRecipient> mDeathObserver;
const String16 mOpPackageName;
+ const int mDeviceId;
std::unordered_map<int, sp<ISensorEventConnection>> mDirectConnection;
int32_t mDirectConnectionHandle;
};
diff --git a/libs/ui/include/ui/DisplayMode.h b/libs/ui/include/ui/DisplayMode.h
index a469c78..ddb9bbd 100644
--- a/libs/ui/include/ui/DisplayMode.h
+++ b/libs/ui/include/ui/DisplayMode.h
@@ -38,7 +38,7 @@
std::vector<ui::Hdr> supportedHdrTypes;
// Some modes have peak refresh rate lower than the panel vsync rate.
- float refreshRate = 0.f;
+ float peakRefreshRate = 0.f;
float vsyncRate = 0.f;
nsecs_t appVsyncOffset = 0;
nsecs_t sfVsyncOffset = 0;
diff --git a/services/sensorservice/aidl/fuzzer/Android.bp b/services/sensorservice/aidl/fuzzer/Android.bp
index 6870d4e..f6f104e 100644
--- a/services/sensorservice/aidl/fuzzer/Android.bp
+++ b/services/sensorservice/aidl/fuzzer/Android.bp
@@ -17,6 +17,7 @@
static_libs: [
"libsensorserviceaidl",
"libpermission",
+ "android.companion.virtual.virtualdevice_aidl-cpp",
"android.frameworks.sensorservice-V1-ndk",
"android.hardware.sensors-V1-convert",
"android.hardware.sensors-V2-ndk",
diff --git a/services/surfaceflinger/CompositionEngine/tests/MockHWComposer.h b/services/surfaceflinger/CompositionEngine/tests/MockHWComposer.h
index 892bb8f..6807c8e 100644
--- a/services/surfaceflinger/CompositionEngine/tests/MockHWComposer.h
+++ b/services/surfaceflinger/CompositionEngine/tests/MockHWComposer.h
@@ -147,6 +147,8 @@
MOCK_METHOD(const aidl::android::hardware::graphics::composer3::OverlayProperties&,
getOverlaySupport, (), (const, override));
MOCK_METHOD(status_t, setRefreshRateChangedCallbackDebugEnabled, (PhysicalDisplayId, bool));
+ MOCK_METHOD(status_t, notifyExpectedPresentIfRequired,
+ (PhysicalDisplayId, nsecs_t, int32_t, int32_t));
};
} // namespace mock
diff --git a/services/surfaceflinger/DisplayHardware/AidlComposerHal.cpp b/services/surfaceflinger/DisplayHardware/AidlComposerHal.cpp
index 1f409c6..1643ad0 100644
--- a/services/surfaceflinger/DisplayHardware/AidlComposerHal.cpp
+++ b/services/surfaceflinger/DisplayHardware/AidlComposerHal.cpp
@@ -1450,6 +1450,20 @@
return Error::NONE;
}
+Error AidlComposer::notifyExpectedPresent(Display displayId, nsecs_t expectedPresentTime,
+ int32_t frameIntervalNs) {
+ const auto status =
+ mAidlComposerClient->notifyExpectedPresent(translate<int64_t>(displayId),
+ ClockMonotonicTimestamp{expectedPresentTime},
+ frameIntervalNs);
+
+ if (!status.isOk()) {
+ ALOGE("notifyExpectedPresent failed %s", status.getDescription().c_str());
+ return static_cast<Error>(status.getServiceSpecificError());
+ }
+ return Error::NONE;
+}
+
Error AidlComposer::getClientTargetProperty(
Display display, ClientTargetPropertyWithBrightness* outClientTargetProperty) {
Error error = Error::NONE;
diff --git a/services/surfaceflinger/DisplayHardware/AidlComposerHal.h b/services/surfaceflinger/DisplayHardware/AidlComposerHal.h
index b1b57a4..7693a80 100644
--- a/services/surfaceflinger/DisplayHardware/AidlComposerHal.h
+++ b/services/surfaceflinger/DisplayHardware/AidlComposerHal.h
@@ -240,6 +240,8 @@
Error getHdrConversionCapabilities(std::vector<HdrConversionCapability>*) override;
Error setHdrConversionStrategy(HdrConversionStrategy, Hdr*) override;
Error setRefreshRateChangedCallbackDebugEnabled(Display, bool) override;
+ Error notifyExpectedPresent(Display, nsecs_t expectedPresentTime,
+ int32_t frameIntervalNs) override;
private:
// Many public functions above simply write a command into the command
diff --git a/services/surfaceflinger/DisplayHardware/ComposerHal.h b/services/surfaceflinger/DisplayHardware/ComposerHal.h
index e942587..6704d88 100644
--- a/services/surfaceflinger/DisplayHardware/ComposerHal.h
+++ b/services/surfaceflinger/DisplayHardware/ComposerHal.h
@@ -298,6 +298,8 @@
virtual Error setHdrConversionStrategy(
::aidl::android::hardware::graphics::common::HdrConversionStrategy, Hdr*) = 0;
virtual Error setRefreshRateChangedCallbackDebugEnabled(Display, bool) = 0;
+ virtual Error notifyExpectedPresent(Display, nsecs_t expectedPresentTime,
+ int32_t frameIntervalNs) = 0;
};
} // namespace Hwc2
diff --git a/services/surfaceflinger/DisplayHardware/HWComposer.cpp b/services/surfaceflinger/DisplayHardware/HWComposer.cpp
index 812621f..fb6089d 100644
--- a/services/surfaceflinger/DisplayHardware/HWComposer.cpp
+++ b/services/surfaceflinger/DisplayHardware/HWComposer.cpp
@@ -31,6 +31,7 @@
#include <compositionengine/OutputLayer.h>
#include <compositionengine/impl/OutputLayerCompositionState.h>
#include <ftl/concat.h>
+#include <gui/TraceUtils.h>
#include <log/log.h>
#include <ui/DebugUtils.h>
#include <ui/GraphicBuffer.h>
@@ -484,6 +485,7 @@
}();
displayData.validateWasSkipped = false;
+ displayData.lastExpectedPresentTimestamp = expectedPresentTime;
if (canSkipValidate) {
sp<Fence> outPresentFence;
uint32_t state = UINT32_MAX;
@@ -876,6 +878,30 @@
return NO_ERROR;
}
+status_t HWComposer::notifyExpectedPresentIfRequired(PhysicalDisplayId displayId,
+ nsecs_t expectedPresentTime,
+ int32_t frameIntervalNs, int32_t timeoutNs) {
+ RETURN_IF_INVALID_DISPLAY(displayId, BAD_INDEX);
+
+ auto& displayData = mDisplayData[displayId];
+ if (expectedPresentTime >= displayData.lastExpectedPresentTimestamp &&
+ expectedPresentTime < displayData.lastExpectedPresentTimestamp + timeoutNs) {
+ return NO_ERROR;
+ }
+
+ displayData.lastExpectedPresentTimestamp = expectedPresentTime;
+ ATRACE_FORMAT("%s ExpectedPresentTime %" PRId64 " frameIntervalNs %d", __func__,
+ expectedPresentTime, frameIntervalNs);
+ const auto error = mComposer->notifyExpectedPresent(displayData.hwcDisplay->getId(),
+ expectedPresentTime, frameIntervalNs);
+
+ if (error != hal::Error::NONE) {
+ ALOGE("Error in notifyExpectedPresent call %s", to_string(error).c_str());
+ return INVALID_OPERATION;
+ }
+ return NO_ERROR;
+}
+
status_t HWComposer::getDisplayDecorationSupport(
PhysicalDisplayId displayId,
std::optional<aidl::android::hardware::graphics::common::DisplayDecorationSupport>*
diff --git a/services/surfaceflinger/DisplayHardware/HWComposer.h b/services/surfaceflinger/DisplayHardware/HWComposer.h
index b4d3d28..726a8ea 100644
--- a/services/surfaceflinger/DisplayHardware/HWComposer.h
+++ b/services/surfaceflinger/DisplayHardware/HWComposer.h
@@ -60,6 +60,7 @@
struct DisplayedFrameStats;
class GraphicBuffer;
class TestableSurfaceFlinger;
+struct HWComposerTest;
struct CompositionInfo;
namespace Hwc2 {
@@ -300,6 +301,9 @@
aidl::android::hardware::graphics::common::HdrConversionStrategy,
aidl::android::hardware::graphics::common::Hdr*) = 0;
virtual status_t setRefreshRateChangedCallbackDebugEnabled(PhysicalDisplayId, bool enabled) = 0;
+ virtual status_t notifyExpectedPresentIfRequired(PhysicalDisplayId, nsecs_t expectedPresentTime,
+ int32_t frameIntervalNs,
+ int32_t timeoutNs) = 0;
};
static inline bool operator==(const android::HWComposer::DeviceRequestedChanges& lhs,
@@ -458,6 +462,8 @@
aidl::android::hardware::graphics::common::HdrConversionStrategy,
aidl::android::hardware::graphics::common::Hdr*) override;
status_t setRefreshRateChangedCallbackDebugEnabled(PhysicalDisplayId, bool enabled) override;
+ status_t notifyExpectedPresentIfRequired(PhysicalDisplayId, nsecs_t expectedPresentTime,
+ int32_t frameIntervalNs, int32_t timeoutNs) override;
// for debugging ----------------------------------------------------------
void dump(std::string& out) const override;
@@ -483,6 +489,7 @@
private:
// For unit tests
friend TestableSurfaceFlinger;
+ friend HWComposerTest;
struct DisplayData {
std::unique_ptr<HWC2::Display> hwcDisplay;
@@ -490,6 +497,8 @@
sp<Fence> lastPresentFence = Fence::NO_FENCE; // signals when the last set op retires
nsecs_t lastPresentTimestamp = 0;
+ nsecs_t lastExpectedPresentTimestamp = 0;
+
std::unordered_map<HWC2::Layer*, sp<Fence>> releaseFences;
bool validateWasSkipped;
diff --git a/services/surfaceflinger/DisplayHardware/HidlComposerHal.cpp b/services/surfaceflinger/DisplayHardware/HidlComposerHal.cpp
index 70d48de..c13e568 100644
--- a/services/surfaceflinger/DisplayHardware/HidlComposerHal.cpp
+++ b/services/surfaceflinger/DisplayHardware/HidlComposerHal.cpp
@@ -1378,6 +1378,10 @@
return Error::UNSUPPORTED;
}
+Error HidlComposer::notifyExpectedPresent(Display, nsecs_t, int32_t) {
+ return Error::UNSUPPORTED;
+}
+
Error HidlComposer::getClientTargetProperty(
Display display, ClientTargetPropertyWithBrightness* outClientTargetProperty) {
IComposerClient::ClientTargetProperty property;
diff --git a/services/surfaceflinger/DisplayHardware/HidlComposerHal.h b/services/surfaceflinger/DisplayHardware/HidlComposerHal.h
index 26d2222..1004ddd 100644
--- a/services/surfaceflinger/DisplayHardware/HidlComposerHal.h
+++ b/services/surfaceflinger/DisplayHardware/HidlComposerHal.h
@@ -348,6 +348,7 @@
Error setHdrConversionStrategy(aidl::android::hardware::graphics::common::HdrConversionStrategy,
Hdr*) override;
Error setRefreshRateChangedCallbackDebugEnabled(Display, bool) override;
+ Error notifyExpectedPresent(Display, nsecs_t, int32_t) override;
private:
class CommandWriter : public CommandWriterBase {
diff --git a/services/surfaceflinger/FrontEnd/LayerSnapshot.h b/services/surfaceflinger/FrontEnd/LayerSnapshot.h
index 80a51ea..4fd6495 100644
--- a/services/surfaceflinger/FrontEnd/LayerSnapshot.h
+++ b/services/surfaceflinger/FrontEnd/LayerSnapshot.h
@@ -84,6 +84,7 @@
bool isTrustedOverlay;
gui::GameMode gameMode;
scheduler::LayerInfo::FrameRate frameRate;
+ scheduler::LayerInfo::FrameRate inheritedFrameRate;
scheduler::LayerInfo::FrameRateSelectionStrategy frameRateSelectionStrategy;
scheduler::FrameRateCompatibility defaultFrameRateCompatibility =
scheduler::FrameRateCompatibility::Default;
@@ -122,6 +123,8 @@
ReachableByRelativeParent
};
Reachablilty reachablilty;
+ // True when the surfaceDamage is recognized as a small area update.
+ bool isSmallDirty = false;
static bool isOpaqueFormat(PixelFormat format);
static bool isTransformValid(const ui::Transform& t);
diff --git a/services/surfaceflinger/FrontEnd/LayerSnapshotBuilder.cpp b/services/surfaceflinger/FrontEnd/LayerSnapshotBuilder.cpp
index 4db2b66..2a0857d 100644
--- a/services/surfaceflinger/FrontEnd/LayerSnapshotBuilder.cpp
+++ b/services/surfaceflinger/FrontEnd/LayerSnapshotBuilder.cpp
@@ -815,9 +815,14 @@
RequestedLayerState::Changes::Hierarchy)) {
bool shouldOverrideChildren = parentSnapshot.frameRateSelectionStrategy ==
scheduler::LayerInfo::FrameRateSelectionStrategy::OverrideChildren;
- snapshot.frameRate = !requested.requestedFrameRate.isValid() || shouldOverrideChildren
- ? parentSnapshot.frameRate
- : requested.requestedFrameRate;
+ if (!requested.requestedFrameRate.isValid() || shouldOverrideChildren) {
+ snapshot.inheritedFrameRate = parentSnapshot.inheritedFrameRate;
+ } else {
+ snapshot.inheritedFrameRate = requested.requestedFrameRate;
+ }
+ // Set the framerate as the inherited frame rate and allow children to override it if
+ // needed.
+ snapshot.frameRate = snapshot.inheritedFrameRate;
snapshot.changes |= RequestedLayerState::Changes::FrameRate;
}
diff --git a/services/surfaceflinger/FrontEnd/RequestedLayerState.cpp b/services/surfaceflinger/FrontEnd/RequestedLayerState.cpp
index ce845d9..0e49b75 100644
--- a/services/surfaceflinger/FrontEnd/RequestedLayerState.cpp
+++ b/services/surfaceflinger/FrontEnd/RequestedLayerState.cpp
@@ -209,7 +209,7 @@
(barrierFrameNumber > bufferData->frameNumber))) {
ALOGE("Out of order buffers detected for %s producedId=%d frameNumber=%" PRIu64
" -> producedId=%d frameNumber=%" PRIu64,
- getDebugString().c_str(), bufferData->producerId, bufferData->frameNumber,
+ getDebugString().c_str(), barrierProducerId, barrierFrameNumber,
bufferData->producerId, frameNumber);
TransactionTraceWriter::getInstance().invoke("out_of_order_buffers_",
/*overwrite=*/false);
@@ -401,6 +401,7 @@
if (!handleAlive) debug << " !handle";
if (z != 0) debug << " z=" << z;
if (layerStack.id != 0) debug << " layerStack=" << layerStack.id;
+ debug << "}";
return debug.str();
}
diff --git a/services/surfaceflinger/Layer.cpp b/services/surfaceflinger/Layer.cpp
index 73b1ca8..700baa2 100644
--- a/services/surfaceflinger/Layer.cpp
+++ b/services/surfaceflinger/Layer.cpp
@@ -2932,11 +2932,6 @@
}
}
- // Prevent tracing the same release multiple times.
- if (mPreviousFrameNumber != mPreviousReleasedFrameNumber) {
- mPreviousReleasedFrameNumber = mPreviousFrameNumber;
- }
-
if (ch != nullptr) {
ch->previousReleaseCallbackId = mPreviousReleaseCallbackId;
ch->previousReleaseFences.emplace_back(std::move(futureFenceResult));
@@ -3369,7 +3364,7 @@
mDrawingState.surfaceDamageRegion = surfaceDamage;
mDrawingState.modified = true;
setTransactionFlags(eTransactionNeeded);
- setIsSmallDirty();
+ setIsSmallDirty(surfaceDamage, getTransform());
return true;
}
@@ -4416,7 +4411,9 @@
mLastLatchTime = latchTime;
}
-void Layer::setIsSmallDirty() {
+void Layer::setIsSmallDirty(const Region& damageRegion,
+ const ui::Transform& layerToDisplayTransform) {
+ mSmallDirty = false;
if (!mFlinger->mScheduler->supportSmallDirtyDetection()) {
return;
}
@@ -4425,17 +4422,26 @@
mWindowType != WindowInfo::Type::BASE_APPLICATION) {
return;
}
- Rect bounds = mDrawingState.surfaceDamageRegion.getBounds();
+
+ Rect bounds = damageRegion.getBounds();
if (!bounds.isValid()) {
return;
}
+ // Transform to screen space.
+ bounds = layerToDisplayTransform.transform(bounds);
+
// If the damage region is a small dirty, this could give the hint for the layer history that
// it could suppress the heuristic rate when calculating.
mSmallDirty = mFlinger->mScheduler->isSmallDirtyArea(mOwnerAppId,
bounds.getWidth() * bounds.getHeight());
}
+void Layer::setIsSmallDirty(frontend::LayerSnapshot* snapshot) {
+ setIsSmallDirty(snapshot->surfaceDamage, snapshot->localTransform);
+ snapshot->isSmallDirty = mSmallDirty;
+}
+
} // namespace android
#if defined(__gl_h_)
diff --git a/services/surfaceflinger/Layer.h b/services/surfaceflinger/Layer.h
index f67da2a..dd91adc 100644
--- a/services/surfaceflinger/Layer.h
+++ b/services/surfaceflinger/Layer.h
@@ -937,7 +937,8 @@
const sp<SurfaceFlinger> mFlinger;
// Check if the damage region is a small dirty.
- void setIsSmallDirty();
+ void setIsSmallDirty(const Region& damageRegion, const ui::Transform& layerToDisplayTransform);
+ void setIsSmallDirty(frontend::LayerSnapshot* snapshot);
protected:
// For unit tests
@@ -1235,9 +1236,6 @@
ReleaseCallbackId mPreviousReleaseCallbackId = ReleaseCallbackId::INVALID_ID;
sp<IBinder> mPreviousReleaseBufferEndpoint;
- uint64_t mPreviousReleasedFrameNumber = 0;
-
- uint64_t mPreviousBarrierFrameNumber = 0;
bool mReleasePreviousBuffer = false;
diff --git a/services/surfaceflinger/Scheduler/VSyncDispatchTimerQueue.cpp b/services/surfaceflinger/Scheduler/VSyncDispatchTimerQueue.cpp
index c4c9fa5..f467670 100644
--- a/services/surfaceflinger/Scheduler/VSyncDispatchTimerQueue.cpp
+++ b/services/surfaceflinger/Scheduler/VSyncDispatchTimerQueue.cpp
@@ -40,16 +40,18 @@
namespace {
-nsecs_t getExpectedCallbackTime(nsecs_t nextVsyncTime,
+nsecs_t getExpectedCallbackTime(nsecs_t now, nsecs_t nextVsyncTime,
const VSyncDispatch::ScheduleTiming& timing) {
- return nextVsyncTime - timing.readyDuration - timing.workDuration;
+ const auto expectedCallbackTime = nextVsyncTime - timing.readyDuration - timing.workDuration;
+ const auto baseTime = flags::dont_skip_on_early() ? now : expectedCallbackTime;
+ return std::max(baseTime, expectedCallbackTime);
}
nsecs_t getExpectedCallbackTime(VSyncTracker& tracker, nsecs_t now,
const VSyncDispatch::ScheduleTiming& timing) {
const auto nextVsyncTime = tracker.nextAnticipatedVSyncTimeFrom(
std::max(timing.earliestVsync, now + timing.workDuration + timing.readyDuration));
- return getExpectedCallbackTime(nextVsyncTime, timing);
+ return getExpectedCallbackTime(now, nextVsyncTime, timing);
}
} // namespace
@@ -105,11 +107,11 @@
mArmedInfo && ((nextWakeupTime > (mArmedInfo->mActualWakeupTime + mMinVsyncDistance)));
if (flags::dont_skip_on_early()) {
if (wouldSkipAVsyncTarget || wouldSkipAWakeup) {
- return getExpectedCallbackTime(mArmedInfo->mActualVsyncTime, timing);
+ return getExpectedCallbackTime(now, mArmedInfo->mActualVsyncTime, timing);
}
} else {
if (wouldSkipAVsyncTarget && wouldSkipAWakeup) {
- return getExpectedCallbackTime(nextVsyncTime, timing);
+ return getExpectedCallbackTime(now, nextVsyncTime, timing);
}
}
@@ -119,7 +121,7 @@
auto const nextReadyTime = nextVsyncTime - timing.readyDuration;
mScheduleTiming = timing;
mArmedInfo = {nextWakeupTime, nextVsyncTime, nextReadyTime};
- return getExpectedCallbackTime(nextVsyncTime, timing);
+ return getExpectedCallbackTime(now, nextVsyncTime, timing);
}
void VSyncDispatchTimerQueueEntry::addPendingWorkloadUpdate(VSyncDispatch::ScheduleTiming timing) {
diff --git a/services/surfaceflinger/SurfaceFlinger.cpp b/services/surfaceflinger/SurfaceFlinger.cpp
index b13c0e8..fc51721 100644
--- a/services/surfaceflinger/SurfaceFlinger.cpp
+++ b/services/surfaceflinger/SurfaceFlinger.cpp
@@ -266,7 +266,7 @@
bool isAbove4k30(const ui::DisplayMode& outMode) {
using fps_approx_ops::operator>;
- Fps refreshRate = Fps::fromValue(outMode.refreshRate);
+ Fps refreshRate = Fps::fromValue(outMode.peakRefreshRate);
return outMode.resolution.getWidth() >= FOUR_K_WIDTH &&
outMode.resolution.getHeight() >= FOUR_K_HEIGHT && refreshRate > 30_Hz;
}
@@ -1046,11 +1046,11 @@
outMode.yDpi = yDpi;
const auto peakFps = mode->getPeakFps();
- outMode.refreshRate = peakFps.getValue();
+ outMode.peakRefreshRate = peakFps.getValue();
outMode.vsyncRate = mode->getVsyncRate().getValue();
- const auto vsyncConfigSet =
- mVsyncConfiguration->getConfigsForRefreshRate(Fps::fromValue(outMode.refreshRate));
+ const auto vsyncConfigSet = mVsyncConfiguration->getConfigsForRefreshRate(
+ Fps::fromValue(outMode.peakRefreshRate));
outMode.appVsyncOffset = vsyncConfigSet.late.appOffset;
outMode.sfVsyncOffset = vsyncConfigSet.late.sfOffset;
outMode.group = mode->getGroup();
@@ -1232,7 +1232,7 @@
}
status_t SurfaceFlinger::setActiveModeFromBackdoor(const sp<display::DisplayToken>& displayToken,
- DisplayModeId modeId) {
+ DisplayModeId modeId, Fps minFps, Fps maxFps) {
ATRACE_CALL();
if (!displayToken) {
@@ -1265,13 +1265,15 @@
}
const Fps fps = *fpsOpt;
+ const FpsRange physical = {fps, fps};
+ const FpsRange render = {minFps.isValid() ? minFps : fps, maxFps.isValid() ? maxFps : fps};
+ const FpsRanges ranges = {physical, render};
// Keep the old switching type.
const bool allowGroupSwitching =
display->refreshRateSelector().getCurrentPolicy().allowGroupSwitching;
- const scheduler::RefreshRateSelector::DisplayManagerPolicy policy{modeId,
- {fps, fps},
+ const scheduler::RefreshRateSelector::DisplayManagerPolicy policy{modeId, ranges, ranges,
allowGroupSwitching};
return setDesiredDisplayModeSpecsInternal(display, policy);
@@ -2211,8 +2213,29 @@
continue;
}
- if (!snapshot->changes.any(Changes::FrameRate | Changes::Buffer | Changes::Animation) &&
- (snapshot->clientChanges & layer_state_t::eDefaultFrameRateCompatibilityChanged) == 0) {
+ const bool updateSmallDirty = mScheduler->supportSmallDirtyDetection() &&
+ ((snapshot->clientChanges & layer_state_t::eSurfaceDamageRegionChanged) ||
+ snapshot->changes.any(Changes::Geometry));
+
+ const bool hasChanges =
+ snapshot->changes.any(Changes::FrameRate | Changes::Buffer | Changes::Animation) ||
+ (snapshot->clientChanges & layer_state_t::eDefaultFrameRateCompatibilityChanged) !=
+ 0;
+
+ if (!updateSmallDirty && !hasChanges) {
+ continue;
+ }
+
+ auto it = mLegacyLayers.find(snapshot->sequence);
+ LOG_ALWAYS_FATAL_IF(it == mLegacyLayers.end(), "Couldn't find layer object for %s",
+ snapshot->getDebugString().c_str());
+
+ if (updateSmallDirty) {
+ // Update small dirty flag while surface damage region or geometry changed
+ it->second->setIsSmallDirty(snapshot.get());
+ }
+
+ if (!hasChanges) {
continue;
}
@@ -2222,12 +2245,9 @@
.transform = snapshot->geomLayerTransform,
.setFrameRateVote = snapshot->frameRate,
.frameRateSelectionPriority = snapshot->frameRateSelectionPriority,
+ .isSmallDirty = snapshot->isSmallDirty,
};
- auto it = mLegacyLayers.find(snapshot->sequence);
- LOG_ALWAYS_FATAL_IF(it == mLegacyLayers.end(), "Couldnt find layer object for %s",
- snapshot->getDebugString().c_str());
-
if (snapshot->clientChanges & layer_state_t::eDefaultFrameRateCompatibilityChanged) {
mScheduler->setDefaultFrameRateCompatibility(snapshot->sequence,
snapshot->defaultFrameRateCompatibility);
@@ -4031,7 +4051,7 @@
if (sysprop::use_content_detection_for_refresh_rate(false)) {
features |= Feature::kContentDetection;
- if (flags::vrr_small_dirty_detection()) {
+ if (flags::enable_small_area_detection()) {
features |= Feature::kSmallDirtyContentDetection;
}
}
@@ -6932,6 +6952,12 @@
return NO_ERROR;
}
case 1035: {
+ // Parameters:
+ // - (required) i32 mode id.
+ // - (optional) i64 display id. Using default display if not provided.
+ // - (optional) f min render rate. Using mode's fps is not provided.
+ // - (optional) f max render rate. Using mode's fps is not provided.
+
const int modeId = data.readInt32();
const auto display = [&]() -> sp<IBinder> {
@@ -6948,8 +6974,21 @@
return nullptr;
}();
+ const auto getFps = [&] {
+ float value;
+ if (data.readFloat(&value) == NO_ERROR) {
+ return Fps::fromValue(value);
+ }
+
+ return Fps();
+ };
+
+ const auto minFps = getFps();
+ const auto maxFps = getFps();
+
mDebugDisplayModeSetByBackdoor = false;
- const status_t result = setActiveModeFromBackdoor(display, DisplayModeId{modeId});
+ const status_t result =
+ setActiveModeFromBackdoor(display, DisplayModeId{modeId}, minFps, maxFps);
mDebugDisplayModeSetByBackdoor = result == NO_ERROR;
return result;
}
@@ -8492,15 +8531,6 @@
void SurfaceFlinger::onActiveDisplaySizeChanged(const DisplayDevice& activeDisplay) {
mScheduler->onActiveDisplayAreaChanged(activeDisplay.getWidth() * activeDisplay.getHeight());
getRenderEngine().onActiveDisplaySizeChanged(activeDisplay.getSize());
-
- // Notify layers to update small dirty flag.
- if (mScheduler->supportSmallDirtyDetection()) {
- mCurrentState.traverse([&](Layer* layer) {
- if (layer->getLayerStack() == activeDisplay.getLayerStack()) {
- layer->setIsSmallDirty();
- }
- });
- }
}
sp<DisplayDevice> SurfaceFlinger::getActivatableDisplay() const {
@@ -9191,7 +9221,7 @@
outMode.resolution.height = mode.resolution.height;
outMode.xDpi = mode.xDpi;
outMode.yDpi = mode.yDpi;
- outMode.refreshRate = mode.refreshRate;
+ outMode.peakRefreshRate = mode.peakRefreshRate;
outMode.vsyncRate = mode.vsyncRate;
outMode.appVsyncOffset = mode.appVsyncOffset;
outMode.sfVsyncOffset = mode.sfVsyncOffset;
diff --git a/services/surfaceflinger/SurfaceFlinger.h b/services/surfaceflinger/SurfaceFlinger.h
index 7500b96..96b67b8 100644
--- a/services/surfaceflinger/SurfaceFlinger.h
+++ b/services/surfaceflinger/SurfaceFlinger.h
@@ -684,7 +684,8 @@
void setDesiredActiveMode(display::DisplayModeRequest&&, bool force = false)
REQUIRES(mStateLock);
- status_t setActiveModeFromBackdoor(const sp<display::DisplayToken>&, DisplayModeId);
+ status_t setActiveModeFromBackdoor(const sp<display::DisplayToken>&, DisplayModeId, Fps minFps,
+ Fps maxFps);
void initiateDisplayModeChanges() REQUIRES(mStateLock, kMainThreadContext);
void finalizeDisplayModeChange(DisplayDevice&) REQUIRES(mStateLock, kMainThreadContext);
diff --git a/services/surfaceflinger/SurfaceFlingerProperties.cpp b/services/surfaceflinger/SurfaceFlingerProperties.cpp
index 66c8f33..a765078 100644
--- a/services/surfaceflinger/SurfaceFlingerProperties.cpp
+++ b/services/surfaceflinger/SurfaceFlingerProperties.cpp
@@ -371,5 +371,9 @@
return SurfaceFlingerProperties::clear_slots_with_set_layer_buffer().value_or(defaultValue);
}
+int32_t game_default_frame_rate_override(int32_t defaultValue) {
+ return SurfaceFlingerProperties::game_default_frame_rate_override().value_or(defaultValue);
+}
+
} // namespace sysprop
} // namespace android
diff --git a/services/surfaceflinger/SurfaceFlingerProperties.h b/services/surfaceflinger/SurfaceFlingerProperties.h
index a080420..65ebe2a 100644
--- a/services/surfaceflinger/SurfaceFlingerProperties.h
+++ b/services/surfaceflinger/SurfaceFlingerProperties.h
@@ -101,6 +101,8 @@
bool clear_slots_with_set_layer_buffer(bool defaultValue);
+int32_t game_default_frame_rate_override(int32_t defaultValue);
+
} // namespace sysprop
} // namespace android
#endif // SURFACEFLINGERPROPERTIES_H_
diff --git a/services/surfaceflinger/Tracing/tools/main.cpp b/services/surfaceflinger/Tracing/tools/main.cpp
index 698ef06..18022b1 100644
--- a/services/surfaceflinger/Tracing/tools/main.cpp
+++ b/services/surfaceflinger/Tracing/tools/main.cpp
@@ -64,8 +64,17 @@
if (!LayerTraceGenerator().generate(transactionTraceFile, traceFlags, layerTracing,
generateLastEntryOnly)) {
- std::cout << "Error: Failed to generate layers trace " << outputLayersTracePath;
+ std::cout << "Error: Failed to generate layers trace " << outputLayersTracePath << "\n";
return -1;
}
+
+ // Set output file permissions (-rw-r--r--)
+ outStream.close();
+ const mode_t mode = S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH;
+ if (chmod(outputLayersTracePath, mode) != 0) {
+ std::cout << "Error: Failed to set permissions of " << outputLayersTracePath << "\n";
+ return -1;
+ }
+
return 0;
}
diff --git a/services/surfaceflinger/fuzzer/surfaceflinger_frametracer_fuzzer.cpp b/services/surfaceflinger/fuzzer/surfaceflinger_frametracer_fuzzer.cpp
index a22a778..8978971 100644
--- a/services/surfaceflinger/fuzzer/surfaceflinger_frametracer_fuzzer.cpp
+++ b/services/surfaceflinger/fuzzer/surfaceflinger_frametracer_fuzzer.cpp
@@ -23,12 +23,19 @@
using namespace google::protobuf;
-constexpr size_t kMaxStringSize = 100;
+constexpr size_t kMaxStringSize = 256;
constexpr size_t kMinLayerIds = 1;
constexpr size_t kMaxLayerIds = 10;
+constexpr int32_t kMinRange = 0;
constexpr int32_t kConfigDuration = 500;
constexpr int32_t kBufferSize = 1024;
constexpr int32_t kTimeOffset = 100000;
+constexpr perfetto::BackendType backendTypes[] = {
+ perfetto::kUnspecifiedBackend,
+ perfetto::kInProcessBackend,
+ perfetto::kSystemBackend,
+ perfetto::kCustomBackend,
+};
class FrameTracerFuzzer {
public:
@@ -47,24 +54,25 @@
void process();
private:
- std::unique_ptr<perfetto::TracingSession> getTracingSessionForTest();
void traceTimestamp();
- std::vector<int32_t> generateLayerIds(size_t numLayerIds);
void traceTimestamp(std::vector<int32_t> layerIds, size_t numLayerIds);
void traceFence(std::vector<int32_t> layerIds, size_t numLayerIds);
+ std::unique_ptr<perfetto::TracingSession> getTracingSessionForTest();
std::unique_ptr<android::FrameTracer> mFrameTracer = nullptr;
- FuzzedDataProvider mFdp;
+ std::vector<int32_t> generateLayerIds(size_t numLayerIds);
android::FenceToFenceTimeMap mFenceFactory;
+ FuzzedDataProvider mFdp;
};
std::unique_ptr<perfetto::TracingSession> FrameTracerFuzzer::getTracingSessionForTest() {
perfetto::TraceConfig cfg;
- cfg.set_duration_ms(kConfigDuration);
- cfg.add_buffers()->set_size_kb(kBufferSize);
+ cfg.set_duration_ms(mFdp.ConsumeIntegralInRange<int32_t>(kMinRange, kConfigDuration));
+ cfg.add_buffers()->set_size_kb(mFdp.ConsumeIntegralInRange<int32_t>(kMinRange, kBufferSize));
auto* dsCfg = cfg.add_data_sources()->mutable_config();
dsCfg->set_name(android::FrameTracer::kFrameTracerDataSource);
- auto tracingSession = perfetto::Tracing::NewTrace(perfetto::kInProcessBackend);
+ auto tracingSession =
+ perfetto::Tracing::NewTrace(mFdp.PickValueInArray<perfetto::BackendType>(backendTypes));
tracingSession->Setup(cfg);
return tracingSession;
}
@@ -78,17 +86,23 @@
}
void FrameTracerFuzzer::traceTimestamp(std::vector<int32_t> layerIds, size_t numLayerIds) {
- int32_t layerId = layerIds.at(mFdp.ConsumeIntegralInRange<size_t>(0, numLayerIds - 1));
+ uint32_t layerId = layerIds.at(mFdp.ConsumeIntegralInRange<size_t>(0, numLayerIds - 1));
+ android::FrameTracer::FrameEvent::BufferEventType type = static_cast<
+ android::FrameTracer::FrameEvent::BufferEventType>(
+ mFdp.ConsumeIntegralInRange<uint32_t>(android::FrameTracer::FrameEvent::UNSPECIFIED,
+ android::FrameTracer::FrameEvent::CANCEL));
mFrameTracer->traceTimestamp(layerId, mFdp.ConsumeIntegral<uint64_t>() /*bufferID*/,
mFdp.ConsumeIntegral<uint64_t>() /*frameNumber*/,
- mFdp.ConsumeIntegral<nsecs_t>() /*timestamp*/,
- android::FrameTracer::FrameEvent::UNSPECIFIED,
+ mFdp.ConsumeIntegral<nsecs_t>() /*timestamp*/, type,
mFdp.ConsumeIntegral<nsecs_t>() /*duration*/);
}
void FrameTracerFuzzer::traceFence(std::vector<int32_t> layerIds, size_t numLayerIds) {
- const nsecs_t signalTime = systemTime();
- const nsecs_t startTime = signalTime + kTimeOffset;
+ const nsecs_t signalTime =
+ mFdp.ConsumeBool() ? android::Fence::SIGNAL_TIME_PENDING : systemTime();
+ const nsecs_t startTime = (signalTime == android::Fence::SIGNAL_TIME_PENDING)
+ ? signalTime - kTimeOffset
+ : signalTime + kTimeOffset;
auto fence = mFenceFactory.createFenceTimeForTest(android::Fence::NO_FENCE);
mFenceFactory.signalAllForTest(android::Fence::NO_FENCE, signalTime);
int32_t layerId = layerIds.at(mFdp.ConsumeIntegralInRange<size_t>(0, numLayerIds - 1));
@@ -98,26 +112,30 @@
}
void FrameTracerFuzzer::process() {
- mFrameTracer->registerDataSource();
+ std::vector<int32_t> layerIds =
+ generateLayerIds(mFdp.ConsumeIntegralInRange<size_t>(kMinLayerIds, kMaxLayerIds));
- auto tracingSession = getTracingSessionForTest();
- tracingSession->StartBlocking();
-
- size_t numLayerIds = mFdp.ConsumeIntegralInRange<size_t>(kMinLayerIds, kMaxLayerIds);
- std::vector<int32_t> layerIds = generateLayerIds(numLayerIds);
-
- for (auto it = layerIds.begin(); it != layerIds.end(); ++it) {
- mFrameTracer->traceNewLayer(*it /*layerId*/,
- mFdp.ConsumeRandomLengthString(kMaxStringSize) /*layerName*/);
+ while (mFdp.remaining_bytes()) {
+ auto invokeFrametracerAPI = mFdp.PickValueInArray<const std::function<void()>>({
+ [&]() { mFrameTracer->registerDataSource(); },
+ [&]() {
+ auto tracingSession = getTracingSessionForTest();
+ tracingSession->StartBlocking();
+ },
+ [&]() { traceTimestamp(layerIds, layerIds.size()); },
+ [&]() { traceFence(layerIds, layerIds.size()); },
+ [&]() {
+ for (auto it = layerIds.begin(); it != layerIds.end(); ++it) {
+ mFrameTracer->traceNewLayer(*it /*layerId*/,
+ mFdp.ConsumeRandomLengthString(
+ kMaxStringSize) /*layerName*/);
+ }
+ },
+ [&]() { mFenceFactory.signalAllForTest(android::Fence::NO_FENCE, systemTime()); },
+ });
+ invokeFrametracerAPI();
}
- traceTimestamp(layerIds, numLayerIds);
- traceFence(layerIds, numLayerIds);
-
- mFenceFactory.signalAllForTest(android::Fence::NO_FENCE, systemTime());
-
- tracingSession->StopBlocking();
-
for (auto it = layerIds.begin(); it != layerIds.end(); ++it) {
mFrameTracer->onDestroy(*it);
}
diff --git a/services/surfaceflinger/surfaceflinger_flags.aconfig b/services/surfaceflinger/surfaceflinger_flags.aconfig
index 5a277bd..19d194f 100644
--- a/services/surfaceflinger/surfaceflinger_flags.aconfig
+++ b/services/surfaceflinger/surfaceflinger_flags.aconfig
@@ -32,14 +32,6 @@
}
flag {
- name: "vrr_small_dirty_detection"
- namespace: "core_graphics"
- description: "Controls small dirty detection for VRR"
- bug: "283055450"
- is_fixed_read_only: true
-}
-
-flag {
name: "dont_skip_on_early"
namespace: "core_graphics"
description: "This flag is guarding the behaviour where SurfaceFlinger is trying to opportunistically present a frame when the configuration change from late to early"
@@ -53,3 +45,11 @@
bug: "259132483"
is_fixed_read_only: true
}
+
+flag {
+ name: "enable_small_area_detection"
+ namespace: "core_graphics"
+ description: "Feature flag for SmallAreaDetection"
+ bug: "283055450"
+ is_fixed_read_only: true
+}
\ No newline at end of file
diff --git a/services/surfaceflinger/sysprop/SurfaceFlingerProperties.sysprop b/services/surfaceflinger/sysprop/SurfaceFlingerProperties.sysprop
index be29be4..0ad5ac9 100644
--- a/services/surfaceflinger/sysprop/SurfaceFlingerProperties.sysprop
+++ b/services/surfaceflinger/sysprop/SurfaceFlingerProperties.sysprop
@@ -495,3 +495,15 @@
access: Readonly
prop_name: "ro.surface_flinger.clear_slots_with_set_layer_buffer"
}
+
+# Controls the default frame rate override of game applications. Ideally, game applications set
+# desired frame rate via setFrameRate() API. However, to cover the scenario when the game didn't
+# have a set frame rate, we introduce the default frame rate. The priority of this override is the
+# lowest among setFrameRate() and game intervention override.
+prop {
+ api_name: "game_default_frame_rate_override"
+ type: Integer
+ scope: Public
+ access: Readonly
+ prop_name: "ro.surface_flinger.game_default_frame_rate_override"
+}
diff --git a/services/surfaceflinger/sysprop/api/SurfaceFlingerProperties-current.txt b/services/surfaceflinger/sysprop/api/SurfaceFlingerProperties-current.txt
index ba88acc..0017300 100644
--- a/services/surfaceflinger/sysprop/api/SurfaceFlingerProperties-current.txt
+++ b/services/surfaceflinger/sysprop/api/SurfaceFlingerProperties-current.txt
@@ -65,6 +65,11 @@
prop_name: "ro.surface_flinger.force_hwc_copy_for_virtual_displays"
}
prop {
+ api_name: "game_default_frame_rate_override"
+ type: Integer
+ prop_name: "ro.surface_flinger.game_default_frame_rate_override"
+ }
+ prop {
api_name: "has_HDR_display"
prop_name: "ro.surface_flinger.has_HDR_display"
}
diff --git a/services/surfaceflinger/tests/DisplayConfigs_test.cpp b/services/surfaceflinger/tests/DisplayConfigs_test.cpp
index 4be961b..0a951d4 100644
--- a/services/surfaceflinger/tests/DisplayConfigs_test.cpp
+++ b/services/surfaceflinger/tests/DisplayConfigs_test.cpp
@@ -75,8 +75,8 @@
setSpecs.allowGroupSwitching = false;
for (size_t i = 0; i < modes.size(); i++) {
setSpecs.defaultMode = modes[i].id;
- setSpecs.primaryRanges.physical.min = modes[i].refreshRate;
- setSpecs.primaryRanges.physical.max = modes[i].refreshRate;
+ setSpecs.primaryRanges.physical.min = modes[i].peakRefreshRate;
+ setSpecs.primaryRanges.physical.max = modes[i].peakRefreshRate;
setSpecs.primaryRanges.render = setSpecs.primaryRanges.physical;
setSpecs.appRequestRanges = setSpecs.primaryRanges;
res = SurfaceComposerClient::setDesiredDisplayModeSpecs(mDisplayToken, setSpecs);
diff --git a/services/surfaceflinger/tests/LayerTransactionTest.h b/services/surfaceflinger/tests/LayerTransactionTest.h
index 2bdb8a4..9269e7c 100644
--- a/services/surfaceflinger/tests/LayerTransactionTest.h
+++ b/services/surfaceflinger/tests/LayerTransactionTest.h
@@ -299,7 +299,7 @@
// After a new buffer is queued, SurfaceFlinger is notified and will
// latch the new buffer on next vsync. Let's heuristically wait for 3
// vsyncs.
- mBufferPostDelay = static_cast<int32_t>(1e6 / mode.refreshRate) * 3;
+ mBufferPostDelay = static_cast<int32_t>(1e6 / mode.peakRefreshRate) * 3;
mBlackBgSurface =
createSurface(mClient, "BaseSurface", 0 /* buffer width */, 0 /* buffer height */,
diff --git a/services/surfaceflinger/tests/unittests/HWComposerTest.cpp b/services/surfaceflinger/tests/unittests/HWComposerTest.cpp
index 8a45f17..4f545a9 100644
--- a/services/surfaceflinger/tests/unittests/HWComposerTest.cpp
+++ b/services/surfaceflinger/tests/unittests/HWComposerTest.cpp
@@ -32,6 +32,7 @@
#include <gui/LayerMetadata.h>
#include <log/log.h>
+#include <chrono>
#include "DisplayHardware/DisplayMode.h"
#include "DisplayHardware/HWComposer.h"
@@ -44,11 +45,11 @@
#pragma clang diagnostic pop // ignored "-Wconversion"
namespace android {
-namespace {
namespace V2_1 = hardware::graphics::composer::V2_1;
namespace V2_4 = hardware::graphics::composer::V2_4;
namespace aidl = aidl::android::hardware::graphics::composer3;
+using namespace std::chrono_literals;
using Hwc2::Config;
@@ -77,6 +78,12 @@
EXPECT_CALL(*mHal, setVsyncEnabled(hwcDisplayId, Hwc2::IComposerClient::Vsync::DISABLE));
EXPECT_CALL(*mHal, onHotplugConnect(hwcDisplayId));
}
+
+ void setDisplayData(HalDisplayId displayId, nsecs_t lastExpectedPresentTimestamp) {
+ ASSERT_TRUE(mHwc.mDisplayData.find(displayId) != mHwc.mDisplayData.end());
+ auto& displayData = mHwc.mDisplayData.at(displayId);
+ displayData.lastExpectedPresentTimestamp = lastExpectedPresentTimestamp;
+ }
};
TEST_F(HWComposerTest, isHeadless) {
@@ -227,12 +234,19 @@
constexpr int32_t kHeight = 720;
constexpr int32_t kConfigGroup = 1;
constexpr int32_t kVsyncPeriod = 16666667;
- hal::DisplayConfiguration displayConfiguration;
- displayConfiguration.configId = kConfigId;
- displayConfiguration.configGroup = kConfigGroup;
- displayConfiguration.height = kHeight;
- displayConfiguration.width = kWidth;
- displayConfiguration.vsyncPeriod = kVsyncPeriod;
+ const hal::VrrConfig vrrConfig =
+ hal::VrrConfig{.minFrameIntervalNs = static_cast<Fps>(120_Hz).getPeriodNsecs(),
+ .notifyExpectedPresentConfig = hal::VrrConfig::
+ NotifyExpectedPresentConfig{.notifyExpectedPresentHeadsUpNs =
+ ms2ns(30),
+ .notifyExpectedPresentTimeoutNs =
+ ms2ns(30)}};
+ hal::DisplayConfiguration displayConfiguration{.configId = kConfigId,
+ .width = kWidth,
+ .height = kHeight,
+ .configGroup = kConfigGroup,
+ .vsyncPeriod = kVsyncPeriod,
+ .vrrConfig = vrrConfig};
EXPECT_CALL(*mHal, getDisplayConfigurations(kHwcDisplayId, _, _))
.WillOnce(DoAll(SetArgPointee<2>(std::vector<hal::DisplayConfiguration>{
@@ -247,6 +261,7 @@
EXPECT_EQ(modes.front().height, kHeight);
EXPECT_EQ(modes.front().configGroup, kConfigGroup);
EXPECT_EQ(modes.front().vsyncPeriod, kVsyncPeriod);
+ EXPECT_EQ(modes.front().vrrConfig, vrrConfig);
EXPECT_EQ(modes.front().dpiX, -1);
EXPECT_EQ(modes.front().dpiY, -1);
@@ -266,6 +281,7 @@
EXPECT_EQ(modes.front().height, kHeight);
EXPECT_EQ(modes.front().configGroup, kConfigGroup);
EXPECT_EQ(modes.front().vsyncPeriod, kVsyncPeriod);
+ EXPECT_EQ(modes.front().vrrConfig, vrrConfig);
EXPECT_EQ(modes.front().dpiX, kDpi);
EXPECT_EQ(modes.front().dpiY, kDpi);
}
@@ -299,6 +315,55 @@
EXPECT_FALSE(displayIdOpt);
}
+TEST_F(HWComposerTest, notifyExpectedPresentTimeout) {
+ constexpr hal::HWDisplayId kHwcDisplayId = 2;
+ expectHotplugConnect(kHwcDisplayId);
+ const auto info = mHwc.onHotplug(kHwcDisplayId, hal::Connection::CONNECTED);
+ ASSERT_TRUE(info);
+
+ auto expectedPresentTime = systemTime() + ms2ns(10);
+ const int32_t frameIntervalNs = static_cast<Fps>(60_Hz).getPeriodNsecs();
+ static constexpr nsecs_t kTimeoutNs = ms2ns(30);
+
+ ASSERT_NO_FATAL_FAILURE(setDisplayData(info->id, /* lastExpectedPresentTimestamp= */ 0));
+
+ {
+ // Very first ExpectedPresent after idle, no previous timestamp
+ EXPECT_CALL(*mHal,
+ notifyExpectedPresent(kHwcDisplayId, expectedPresentTime, frameIntervalNs))
+ .WillOnce(Return(HalError::NONE));
+ mHwc.notifyExpectedPresentIfRequired(info->id, expectedPresentTime, frameIntervalNs,
+ kTimeoutNs);
+ }
+ {
+ // ExpectedPresent is after the timeoutNs
+ expectedPresentTime += ms2ns(50);
+ EXPECT_CALL(*mHal,
+ notifyExpectedPresent(kHwcDisplayId, expectedPresentTime, frameIntervalNs))
+ .WillOnce(Return(HalError::NONE));
+ mHwc.notifyExpectedPresentIfRequired(info->id, expectedPresentTime, frameIntervalNs,
+ kTimeoutNs);
+ }
+ {
+ // ExpectedPresent is after the last reported ExpectedPresent.
+ expectedPresentTime += ms2ns(10);
+ EXPECT_CALL(*mHal, notifyExpectedPresent(kHwcDisplayId, _, _)).Times(0);
+ mHwc.notifyExpectedPresentIfRequired(info->id, expectedPresentTime, frameIntervalNs,
+ kTimeoutNs);
+ }
+ {
+ // ExpectedPresent is before the last reported ExpectedPresent but after the timeoutNs,
+ // representing we changed our decision and want to present earlier than previously
+ // reported.
+ expectedPresentTime -= ms2ns(20);
+ EXPECT_CALL(*mHal,
+ notifyExpectedPresent(kHwcDisplayId, expectedPresentTime, frameIntervalNs))
+ .WillOnce(Return(HalError::NONE));
+ mHwc.notifyExpectedPresentIfRequired(info->id, expectedPresentTime, frameIntervalNs,
+ kTimeoutNs);
+ }
+}
+
struct MockHWC2ComposerCallback final : StrictMock<HWC2::ComposerCallback> {
MOCK_METHOD2(onComposerHalHotplug, void(hal::HWDisplayId, hal::Connection));
MOCK_METHOD1(onComposerHalRefresh, void(hal::HWDisplayId));
@@ -423,5 +488,4 @@
EXPECT_EQ(hal::Error::UNSUPPORTED, result);
}
-} // namespace
} // namespace android
diff --git a/services/surfaceflinger/tests/unittests/LayerHierarchyTest.h b/services/surfaceflinger/tests/unittests/LayerHierarchyTest.h
index 7f3171f..48f8923 100644
--- a/services/surfaceflinger/tests/unittests/LayerHierarchyTest.h
+++ b/services/surfaceflinger/tests/unittests/LayerHierarchyTest.h
@@ -421,6 +421,17 @@
mLifecycleManager.applyTransactions(transactions);
}
+ void setDamageRegion(uint32_t id, const Region& damageRegion) {
+ std::vector<TransactionState> transactions;
+ transactions.emplace_back();
+ transactions.back().states.push_back({});
+
+ transactions.back().states.front().state.what = layer_state_t::eSurfaceDamageRegionChanged;
+ transactions.back().states.front().layerId = id;
+ transactions.back().states.front().state.surfaceDamageRegion = damageRegion;
+ mLifecycleManager.applyTransactions(transactions);
+ }
+
void setDataspace(uint32_t id, ui::Dataspace dataspace) {
std::vector<TransactionState> transactions;
transactions.emplace_back();
@@ -432,6 +443,19 @@
mLifecycleManager.applyTransactions(transactions);
}
+ void setMatrix(uint32_t id, float dsdx, float dtdx, float dtdy, float dsdy) {
+ layer_state_t::matrix22_t matrix{dsdx, dtdx, dtdy, dsdy};
+
+ std::vector<TransactionState> transactions;
+ transactions.emplace_back();
+ transactions.back().states.push_back({});
+
+ transactions.back().states.front().state.what = layer_state_t::eMatrixChanged;
+ transactions.back().states.front().layerId = id;
+ transactions.back().states.front().state.matrix = matrix;
+ mLifecycleManager.applyTransactions(transactions);
+ }
+
void setShadowRadius(uint32_t id, float shadowRadius) {
std::vector<TransactionState> transactions;
transactions.emplace_back();
diff --git a/services/surfaceflinger/tests/unittests/LayerHistoryIntegrationTest.cpp b/services/surfaceflinger/tests/unittests/LayerHistoryIntegrationTest.cpp
index a462082..631adf1 100644
--- a/services/surfaceflinger/tests/unittests/LayerHistoryIntegrationTest.cpp
+++ b/services/surfaceflinger/tests/unittests/LayerHistoryIntegrationTest.cpp
@@ -866,6 +866,104 @@
}
}
+class SmallAreaDetectionTest : public LayerHistoryIntegrationTest {
+protected:
+ static constexpr int32_t DISPLAY_WIDTH = 100;
+ static constexpr int32_t DISPLAY_HEIGHT = 100;
+
+ static constexpr int32_t kAppId1 = 10100;
+ static constexpr int32_t kAppId2 = 10101;
+
+ static constexpr float kThreshold1 = 0.05f;
+ static constexpr float kThreshold2 = 0.07f;
+
+ SmallAreaDetectionTest() : LayerHistoryIntegrationTest() {
+ std::vector<std::pair<int32_t, float>> mappings;
+ mappings.reserve(2);
+ mappings.push_back(std::make_pair(kAppId1, kThreshold1));
+ mappings.push_back(std::make_pair(kAppId2, kThreshold2));
+
+ mFlinger.enableNewFrontEnd();
+
+ mScheduler->onActiveDisplayAreaChanged(DISPLAY_WIDTH * DISPLAY_HEIGHT);
+ mScheduler->updateSmallAreaDetection(mappings);
+ }
+
+ auto createLegacyAndFrontedEndLayer(uint32_t sequence) {
+ std::string layerName = "test layer:" + std::to_string(sequence);
+
+ LayerCreationArgs args = LayerCreationArgs{mFlinger.flinger(),
+ nullptr,
+ layerName,
+ 0,
+ {},
+ std::make_optional<uint32_t>(sequence)};
+ args.ownerUid = kAppId1;
+ args.metadata.setInt32(gui::METADATA_WINDOW_TYPE, 2); // APPLICATION
+ const auto layer = sp<Layer>::make(args);
+ mFlinger.injectLegacyLayer(layer);
+ createRootLayer(sequence);
+ return layer;
+ }
+};
+
+TEST_F(SmallAreaDetectionTest, SmallDirtyLayer) {
+ auto layer = createLegacyAndFrontedEndLayer(1);
+
+ nsecs_t time = systemTime();
+
+ EXPECT_EQ(1u, layerCount());
+ EXPECT_EQ(0u, activeLayerCount());
+ EXPECT_EQ(0, frequentLayerCount(time));
+
+ uint32_t sequence = static_cast<uint32_t>(layer->sequence);
+ setBuffer(sequence);
+ setDamageRegion(sequence, Region(Rect(10, 10)));
+ updateLayerSnapshotsAndLayerHistory(time);
+
+ ASSERT_EQ(true, mFlinger.mutableLayerSnapshotBuilder().getSnapshot(1)->isSmallDirty);
+}
+
+TEST_F(SmallAreaDetectionTest, NotSmallDirtyLayer) {
+ auto layer = createLegacyAndFrontedEndLayer(1);
+
+ nsecs_t time = systemTime();
+
+ EXPECT_EQ(1u, layerCount());
+ EXPECT_EQ(0u, activeLayerCount());
+ EXPECT_EQ(0, frequentLayerCount(time));
+
+ uint32_t sequence = static_cast<uint32_t>(layer->sequence);
+ setBuffer(sequence);
+ setDamageRegion(sequence, Region(Rect(50, 50)));
+ updateLayerSnapshotsAndLayerHistory(time);
+
+ ASSERT_EQ(false, mFlinger.mutableLayerSnapshotBuilder().getSnapshot(1)->isSmallDirty);
+}
+
+TEST_F(SmallAreaDetectionTest, smallDirtyLayerWithMatrix) {
+ auto layer = createLegacyAndFrontedEndLayer(1);
+
+ nsecs_t time = systemTime();
+
+ EXPECT_EQ(1u, layerCount());
+ EXPECT_EQ(0u, activeLayerCount());
+ EXPECT_EQ(0, frequentLayerCount(time));
+
+ // Original damage region is a small dirty.
+ uint32_t sequence = static_cast<uint32_t>(layer->sequence);
+ setBuffer(sequence);
+ setDamageRegion(sequence, Region(Rect(20, 20)));
+ updateLayerSnapshotsAndLayerHistory(time);
+ ASSERT_EQ(true, mFlinger.mutableLayerSnapshotBuilder().getSnapshot(1)->isSmallDirty);
+
+ setMatrix(sequence, 2.0f, 0, 0, 2.0f);
+ updateLayerSnapshotsAndLayerHistory(time);
+
+ // Verify if the small dirty is scaled.
+ ASSERT_EQ(false, mFlinger.mutableLayerSnapshotBuilder().getSnapshot(1)->isSmallDirty);
+}
+
INSTANTIATE_TEST_CASE_P(LeapYearTests, LayerHistoryIntegrationTestParameterized,
::testing::Values(1s, 2s, 3s, 4s, 5s));
diff --git a/services/surfaceflinger/tests/unittests/LayerSnapshotTest.cpp b/services/surfaceflinger/tests/unittests/LayerSnapshotTest.cpp
index fc4bb22..e784eb7 100644
--- a/services/surfaceflinger/tests/unittests/LayerSnapshotTest.cpp
+++ b/services/surfaceflinger/tests/unittests/LayerSnapshotTest.cpp
@@ -282,17 +282,7 @@
// │ └── 13
// └── 2
- std::vector<TransactionState> transactions;
- transactions.emplace_back();
- transactions.back().states.push_back({});
- transactions.back().states.front().state.what = layer_state_t::eFrameRateChanged;
- transactions.back().states.front().state.frameRate = 90.0;
- transactions.back().states.front().state.frameRateCompatibility =
- ANATIVEWINDOW_FRAME_RATE_EXACT;
- transactions.back().states.front().state.changeFrameRateStrategy =
- ANATIVEWINDOW_CHANGE_FRAME_RATE_ALWAYS;
- transactions.back().states.front().layerId = 11;
- mLifecycleManager.applyTransactions(transactions);
+ setFrameRate(11, 90.0, ANATIVEWINDOW_FRAME_RATE_EXACT, ANATIVEWINDOW_CHANGE_FRAME_RATE_ALWAYS);
UPDATE_AND_VERIFY(mSnapshotBuilder, STARTING_ZORDER);
EXPECT_EQ(getSnapshot(11)->frameRate.vote.rate.getIntValue(), 90);
@@ -303,6 +293,42 @@
EXPECT_EQ(getSnapshot(1)->frameRate.vote.type, scheduler::FrameRateCompatibility::NoVote);
}
+TEST_F(LayerSnapshotTest, NoLayerVoteForParentWithChildVotesDoesNotAffectSiblings) {
+ // ROOT
+ // ├── 1 (verify layer has no vote)
+ // │ ├── 11 (frame rate set)
+ // │ │ └── 111
+ // │ ├── 12 (frame rate set)
+ // │ │ ├── 121
+ // │ │ └── 122
+ // │ │ └── 1221
+ // │ └── 13 (verify layer has default vote)
+ // └── 2
+
+ setFrameRate(11, 90.0, ANATIVEWINDOW_FRAME_RATE_EXACT, ANATIVEWINDOW_CHANGE_FRAME_RATE_ALWAYS);
+ setFrameRate(12, 45.0, ANATIVEWINDOW_FRAME_RATE_EXACT, ANATIVEWINDOW_CHANGE_FRAME_RATE_ALWAYS);
+
+ UPDATE_AND_VERIFY(mSnapshotBuilder, STARTING_ZORDER);
+
+ EXPECT_EQ(getSnapshot(11)->frameRate.vote.rate.getIntValue(), 90);
+ EXPECT_EQ(getSnapshot(11)->frameRate.vote.type, scheduler::FrameRateCompatibility::Exact);
+ EXPECT_EQ(getSnapshot(111)->frameRate.vote.rate.getIntValue(), 90);
+ EXPECT_EQ(getSnapshot(111)->frameRate.vote.type, scheduler::FrameRateCompatibility::Exact);
+ EXPECT_EQ(getSnapshot(12)->frameRate.vote.rate.getIntValue(), 45);
+ EXPECT_EQ(getSnapshot(12)->frameRate.vote.type, scheduler::FrameRateCompatibility::Exact);
+ EXPECT_EQ(getSnapshot(121)->frameRate.vote.rate.getIntValue(), 45);
+ EXPECT_EQ(getSnapshot(121)->frameRate.vote.type, scheduler::FrameRateCompatibility::Exact);
+ EXPECT_EQ(getSnapshot(1221)->frameRate.vote.rate.getIntValue(), 45);
+ EXPECT_EQ(getSnapshot(1221)->frameRate.vote.type, scheduler::FrameRateCompatibility::Exact);
+
+ EXPECT_EQ(getSnapshot(1)->frameRate.vote.rate.getIntValue(), 0);
+ EXPECT_EQ(getSnapshot(1)->frameRate.vote.type, scheduler::FrameRateCompatibility::NoVote);
+ EXPECT_EQ(getSnapshot(13)->frameRate.vote.rate.getIntValue(), 0);
+ EXPECT_EQ(getSnapshot(13)->frameRate.vote.type, scheduler::FrameRateCompatibility::Default);
+ EXPECT_EQ(getSnapshot(2)->frameRate.vote.rate.getIntValue(), 0);
+ EXPECT_EQ(getSnapshot(2)->frameRate.vote.type, scheduler::FrameRateCompatibility::Default);
+}
+
TEST_F(LayerSnapshotTest, CanCropTouchableRegion) {
// ROOT
// ├── 1
diff --git a/services/surfaceflinger/tests/unittests/TestableScheduler.h b/services/surfaceflinger/tests/unittests/TestableScheduler.h
index 014d07c..3d1c900 100644
--- a/services/surfaceflinger/tests/unittests/TestableScheduler.h
+++ b/services/surfaceflinger/tests/unittests/TestableScheduler.h
@@ -44,7 +44,10 @@
TestableScheduler(std::unique_ptr<VsyncController> controller,
std::shared_ptr<VSyncTracker> tracker, RefreshRateSelectorPtr selectorPtr,
sp<VsyncModulator> modulatorPtr, ISchedulerCallback& callback)
- : Scheduler(*this, callback, Feature::kContentDetection, std::move(modulatorPtr)) {
+ : Scheduler(*this, callback,
+ (FeatureFlags)Feature::kContentDetection |
+ Feature::kSmallDirtyContentDetection,
+ std::move(modulatorPtr)) {
const auto displayId = selectorPtr->getActiveMode().modePtr->getPhysicalDisplayId();
registerDisplay(displayId, std::move(selectorPtr), std::move(controller),
std::move(tracker));
diff --git a/services/surfaceflinger/tests/unittests/TestableSurfaceFlinger.h b/services/surfaceflinger/tests/unittests/TestableSurfaceFlinger.h
index 3b39420..8f1982d 100644
--- a/services/surfaceflinger/tests/unittests/TestableSurfaceFlinger.h
+++ b/services/surfaceflinger/tests/unittests/TestableSurfaceFlinger.h
@@ -670,6 +670,11 @@
return mFlinger->initTransactionTraceWriter();
}
+ void enableNewFrontEnd() {
+ mFlinger->mLayerLifecycleManagerEnabled = true;
+ mFlinger->mLegacyFrontEndEnabled = false;
+ }
+
~TestableSurfaceFlinger() {
// All these pointer and container clears help ensure that GMock does
// not report a leaked object, since the SurfaceFlinger instance may
diff --git a/services/surfaceflinger/tests/unittests/VSyncDispatchTimerQueueTest.cpp b/services/surfaceflinger/tests/unittests/VSyncDispatchTimerQueueTest.cpp
index b8fdce1..1dc5498 100644
--- a/services/surfaceflinger/tests/unittests/VSyncDispatchTimerQueueTest.cpp
+++ b/services/surfaceflinger/tests/unittests/VSyncDispatchTimerQueueTest.cpp
@@ -759,6 +759,8 @@
// b/1450138150
TEST_F(VSyncDispatchTimerQueueTest, doesNotMoveCallbackBackwardsAndSkipAScheduledTargetVSync) {
+ SET_FLAG_FOR_TEST(flags::dont_skip_on_early, false);
+
EXPECT_CALL(mMockClock, alarmAt(_, 500));
CountingCallback cb(mDispatch);
auto result =
@@ -772,6 +774,29 @@
{.workDuration = 800, .readyDuration = 0, .earliestVsync = 1000});
EXPECT_TRUE(result.has_value());
EXPECT_EQ(1200, *result);
+
+ advanceToNextCallback();
+ ASSERT_THAT(cb.mCalls.size(), Eq(1));
+}
+
+// b/1450138150
+TEST_F(VSyncDispatchTimerQueueTest, movesCallbackBackwardsAndSkipAScheduledTargetVSync) {
+ SET_FLAG_FOR_TEST(flags::dont_skip_on_early, true);
+
+ EXPECT_CALL(mMockClock, alarmAt(_, 500));
+ CountingCallback cb(mDispatch);
+ auto result =
+ mDispatch->schedule(cb,
+ {.workDuration = 500, .readyDuration = 0, .earliestVsync = 1000});
+ EXPECT_TRUE(result.has_value());
+ EXPECT_EQ(500, *result);
+ mMockClock.advanceBy(400);
+
+ result = mDispatch->schedule(cb,
+ {.workDuration = 800, .readyDuration = 0, .earliestVsync = 1000});
+ EXPECT_TRUE(result.has_value());
+ EXPECT_EQ(400, *result);
+
advanceToNextCallback();
ASSERT_THAT(cb.mCalls.size(), Eq(1));
}
@@ -826,6 +851,8 @@
}
TEST_F(VSyncDispatchTimerQueueTest, scheduleUpdatesDoesNotAffectSchedulingState) {
+ SET_FLAG_FOR_TEST(flags::dont_skip_on_early, false);
+
EXPECT_CALL(mMockClock, alarmAt(_, 600));
CountingCallback cb(mDispatch);
@@ -843,6 +870,26 @@
advanceToNextCallback();
}
+TEST_F(VSyncDispatchTimerQueueTest, scheduleUpdatesDoesAffectSchedulingState) {
+ SET_FLAG_FOR_TEST(flags::dont_skip_on_early, true);
+
+ EXPECT_CALL(mMockClock, alarmAt(_, 600));
+
+ CountingCallback cb(mDispatch);
+ auto result =
+ mDispatch->schedule(cb,
+ {.workDuration = 400, .readyDuration = 0, .earliestVsync = 1000});
+ EXPECT_TRUE(result.has_value());
+ EXPECT_EQ(600, *result);
+
+ result = mDispatch->schedule(cb,
+ {.workDuration = 1400, .readyDuration = 0, .earliestVsync = 1000});
+ EXPECT_TRUE(result.has_value());
+ EXPECT_EQ(0, *result);
+
+ advanceToNextCallback();
+}
+
TEST_F(VSyncDispatchTimerQueueTest, helperMove) {
EXPECT_CALL(mMockClock, alarmAt(_, 500)).Times(1);
EXPECT_CALL(mMockClock, alarmCancel()).Times(1);
@@ -1045,6 +1092,8 @@
}
TEST_F(VSyncDispatchTimerQueueTest, updatesVsyncTimeForCloseWakeupTime) {
+ SET_FLAG_FOR_TEST(flags::dont_skip_on_early, false);
+
Sequence seq;
EXPECT_CALL(mMockClock, alarmAt(_, 600)).InSequence(seq);
@@ -1065,6 +1114,29 @@
EXPECT_THAT(cb.mReadyTime[0], Eq(2000));
}
+TEST_F(VSyncDispatchTimerQueueTest, doesNotUpdatesVsyncTimeForCloseWakeupTime) {
+ SET_FLAG_FOR_TEST(flags::dont_skip_on_early, true);
+
+ Sequence seq;
+ EXPECT_CALL(mMockClock, alarmAt(_, 600)).InSequence(seq);
+
+ CountingCallback cb(mDispatch);
+
+ mDispatch->schedule(cb, {.workDuration = 400, .readyDuration = 0, .earliestVsync = 1000});
+ mDispatch->schedule(cb, {.workDuration = 1400, .readyDuration = 0, .earliestVsync = 1000});
+
+ advanceToNextCallback();
+
+ advanceToNextCallback();
+
+ ASSERT_THAT(cb.mCalls.size(), Eq(1));
+ EXPECT_THAT(cb.mCalls[0], Eq(1000));
+ ASSERT_THAT(cb.mWakeupTime.size(), Eq(1));
+ EXPECT_THAT(cb.mWakeupTime[0], Eq(600));
+ ASSERT_THAT(cb.mReadyTime.size(), Eq(1));
+ EXPECT_THAT(cb.mReadyTime[0], Eq(1000));
+}
+
TEST_F(VSyncDispatchTimerQueueTest, skipAVsyc) {
SET_FLAG_FOR_TEST(flags::dont_skip_on_early, false);
@@ -1101,7 +1173,7 @@
result = mDispatch->schedule(cb,
{.workDuration = 800, .readyDuration = 0, .earliestVsync = 1000});
EXPECT_TRUE(result.has_value());
- EXPECT_EQ(200, *result);
+ EXPECT_EQ(300, *result);
advanceToNextCallback();
ASSERT_THAT(cb.mCalls.size(), Eq(1));
diff --git a/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockComposer.h b/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockComposer.h
index 95004a4..0b07745 100644
--- a/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockComposer.h
+++ b/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockComposer.h
@@ -178,6 +178,7 @@
MOCK_METHOD1(onHotplugConnect, void(Display));
MOCK_METHOD1(onHotplugDisconnect, void(Display));
MOCK_METHOD(Error, setRefreshRateChangedCallbackDebugEnabled, (Display, bool));
+ MOCK_METHOD(Error, notifyExpectedPresent, (Display, nsecs_t, int32_t));
};
} // namespace Hwc2::mock