Merge "Revert "Add new ANativeWindow_setFrameRateParams API"" into main
diff --git a/Android.bp b/Android.bp
index 13954ef..a689b2d 100644
--- a/Android.bp
+++ b/Android.bp
@@ -48,6 +48,7 @@
export_include_dirs: [
"include/",
],
+ product_available: true,
}
ndk_headers {
diff --git a/cmds/dumpstate/dumpstate.cpp b/cmds/dumpstate/dumpstate.cpp
index d24edc4..4758607 100644
--- a/cmds/dumpstate/dumpstate.cpp
+++ b/cmds/dumpstate/dumpstate.cpp
@@ -1841,6 +1841,11 @@
RunCommand("DUMP VENDOR RIL LOGS", {"vril-dump"}, options.Build());
}
+ /* Dump USB information */
+ RunCommand("typec_connector_class", {"typec_connector_class"},
+ CommandOptions::WithTimeout(10).AsRootIfAvailable().Build());
+ RunCommand("lsusb", {"lsusb"}, CommandOptions::WithTimeout(10).AsRootIfAvailable().Build());
+
printf("========================================================\n");
printf("== Android Framework Services\n");
printf("========================================================\n");
@@ -4651,7 +4656,7 @@
void Dumpstate::TakeScreenshot(const std::string& path) {
const std::string& real_path = path.empty() ? screenshot_path_ : path;
int status =
- RunCommand("", {"/system/bin/screencap", "-p", real_path},
+ RunCommand("", {"screencap", "-p", real_path},
CommandOptions::WithTimeout(10).Always().DropRoot().RedirectStderr().Build());
if (status == 0) {
MYLOGD("Screenshot saved on %s\n", real_path.c_str());
diff --git a/data/etc/android.hardware.bluetooth_le.channel_sounding.xml b/data/etc/android.hardware.bluetooth_le.channel_sounding.xml
new file mode 100644
index 0000000..f0ee5d9
--- /dev/null
+++ b/data/etc/android.hardware.bluetooth_le.channel_sounding.xml
@@ -0,0 +1,19 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2024 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<!-- Adds the feature indicating support for the BLE Channel sounding -->
+<permissions>
+ <feature name="android.hardware.bluetooth_le.channel_sounding" />
+</permissions>
diff --git a/data/etc/android.hardware.vulkan.version-1_4.xml b/data/etc/android.hardware.vulkan.version-1_4.xml
new file mode 100644
index 0000000..010f6da
--- /dev/null
+++ b/data/etc/android.hardware.vulkan.version-1_4.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright 2024 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+
+<!-- This is the standard feature indicating that the device has a Vulkan
+ driver that supports API version 1.4 (0x00404000) -->
+<permissions>
+ <feature name="android.hardware.vulkan.version" version="4210688" />
+</permissions>
diff --git a/data/etc/android.hardware.xr.input.controller.xml b/data/etc/android.hardware.xr.input.controller.xml
new file mode 100644
index 0000000..1fb8b41
--- /dev/null
+++ b/data/etc/android.hardware.xr.input.controller.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2024 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+
+<!-- This is the standard feature to indicate the device supports
+ input from XR controllers. -->
+<permissions>
+ <feature name="android.hardware.xr.input.controller" />
+</permissions>
diff --git a/data/etc/android.hardware.xr.input.eye_tracking.xml b/data/etc/android.hardware.xr.input.eye_tracking.xml
new file mode 100644
index 0000000..8c6c2ed
--- /dev/null
+++ b/data/etc/android.hardware.xr.input.eye_tracking.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2024 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+
+<!-- This is the standard feature to indicate the device supports
+ input from an XR user's eyes. -->
+<permissions>
+ <feature name="android.hardware.xr.input.eye_tracking" />
+</permissions>
diff --git a/data/etc/android.hardware.xr.input.hand_tracking.xml b/data/etc/android.hardware.xr.input.hand_tracking.xml
new file mode 100644
index 0000000..6de3bee
--- /dev/null
+++ b/data/etc/android.hardware.xr.input.hand_tracking.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2024 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+
+<!-- This is the standard feature to indicate the device supports
+ input from an XR user's hands. -->
+<permissions>
+ <feature name="android.hardware.xr.input.hand_tracking" />
+</permissions>
diff --git a/data/etc/android.software.xr.api.openxr-1_0.xml b/data/etc/android.software.xr.api.openxr-1_0.xml
new file mode 100644
index 0000000..71c4a94
--- /dev/null
+++ b/data/etc/android.software.xr.api.openxr-1_0.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2024 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+
+<!-- This is the standard feature to indicate the device has a runtime
+ that supports OpenXR 1.0 (0x00010000). -->
+<permissions>
+ <feature name="android.software.xr.api.openxr" version="65536" />
+</permissions>
diff --git a/data/etc/android.software.xr.api.openxr-1_1.xml b/data/etc/android.software.xr.api.openxr-1_1.xml
new file mode 100644
index 0000000..45c1065
--- /dev/null
+++ b/data/etc/android.software.xr.api.openxr-1_1.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2024 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+
+<!-- This is the standard feature to indicate the device has a runtime
+ that supports OpenXR 1.1 (0x00010001). -->
+<permissions>
+ <feature name="android.software.xr.api.openxr" version="65537" />
+</permissions>
diff --git a/data/etc/android.software.xr.api.openxr-1_2.xml b/data/etc/android.software.xr.api.openxr-1_2.xml
new file mode 100644
index 0000000..ba11b8d
--- /dev/null
+++ b/data/etc/android.software.xr.api.openxr-1_2.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2024 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+
+<!-- This is the standard feature to indicate the device has a runtime
+ that supports OpenXR 1.2 (0x00010002). -->
+<permissions>
+ <feature name="android.software.xr.api.openxr" version="65538" />
+</permissions>
diff --git a/data/etc/android.software.xr.api.spatial-1.xml b/data/etc/android.software.xr.api.spatial-1.xml
new file mode 100644
index 0000000..ce425aa
--- /dev/null
+++ b/data/etc/android.software.xr.api.spatial-1.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2024 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+
+<!-- This is the standard feature to indicate the device has a runtime
+ that supports Android XR Spatial APIs, version 1. -->
+<permissions>
+ <feature name="android.software.xr.api.spatial" version="1" />
+</permissions>
diff --git a/data/etc/go_handheld_core_hardware.xml b/data/etc/go_handheld_core_hardware.xml
index 8df7fdb..a092842 100644
--- a/data/etc/go_handheld_core_hardware.xml
+++ b/data/etc/go_handheld_core_hardware.xml
@@ -51,6 +51,9 @@
<!-- Feature to specify if the device supports adding device admins. -->
<feature name="android.software.device_admin" />
+ <!-- Feature to specify if the device support managed users. -->
+ <feature name="android.software.managed_users" />
+
<!-- Devices with all optimizations required to support VR Mode and
pass all CDD requirements for this feature may include
android.hardware.vr.high_performance -->
diff --git a/include/android/OWNERS b/include/android/OWNERS
index fad8c1b..5b014d0 100644
--- a/include/android/OWNERS
+++ b/include/android/OWNERS
@@ -3,3 +3,10 @@
# Window manager
per-file surface_control_input_receiver.h = file:platform/frameworks/base:/services/core/java/com/android/server/wm/OWNERS
per-file input_transfer_token.h = file:platform/frameworks/base:/services/core/java/com/android/server/wm/OWNERS
+
+# CoGS
+per-file *luts* = file:platform/frameworks/base:/graphics/java/android/graphics/OWNERS
+
+# ADPF
+per-file performance_hint.h = file:platform/frameworks/base:/ADPF_OWNERS
+per-file thermal.h = file:platform/frameworks/base:/ADPF_OWNERS
diff --git a/include/android/choreographer.h b/include/android/choreographer.h
index bec3283..2622a01 100644
--- a/include/android/choreographer.h
+++ b/include/android/choreographer.h
@@ -318,7 +318,7 @@
/**
* Gets the token used by the platform to identify the frame timeline at the given \c index.
- * q
+ *
* Available since API level 33.
*
* \param index index of a frame timeline, in \f( [0, FrameTimelinesLength) \f). See
diff --git a/include/android/display_luts.h b/include/android/display_luts.h
new file mode 100644
index 0000000..08dfb12
--- /dev/null
+++ b/include/android/display_luts.h
@@ -0,0 +1,165 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/**
+ * @addtogroup NativeActivity Native Activity
+ * @{
+ */
+
+/**
+ * @file display_luts.h
+ */
+#pragma once
+
+#include <inttypes.h>
+
+__BEGIN_DECLS
+
+/**
+ * The dimension of the lut
+ */
+enum ADisplayLuts_Dimension : int32_t {
+ ADISPLAYLUTS_ONE_DIMENSION = 1,
+ ADISPLAYLUTS_THREE_DIMENSION = 3,
+};
+typedef enum ADisplayLuts_Dimension ADisplayLuts_Dimension;
+
+/**
+ * The sampling key used by the lut
+ */
+enum ADisplayLuts_SamplingKey : int32_t {
+ ADISPLAYLUTS_SAMPLINGKEY_RGB = 0,
+ ADISPLAYLUTS_SAMPLINGKEY_MAX_RGB = 1,
+};
+typedef enum ADisplayLuts_SamplingKey ADisplayLuts_SamplingKey;
+
+/**
+ * Used to get and set display luts entry
+ */
+typedef struct ADisplayLutsEntry ADisplayLutsEntry;
+
+/**
+ * Used to get and set display luts
+ */
+typedef struct ADisplayLuts ADisplayLuts;
+
+/**
+ * Creates a \a ADisplayLutsEntry entry.
+ *
+ * You are responsible for mamanging the memory of the returned object.
+ * Always call \a ADisplayLutsEntry_destroy to release it after use.
+ *
+ * Functions like \a ADisplayLuts_set create their own copies of entries,
+ * therefore they don't take the ownership of the instance created by
+ * \a ADisplayLutsEntry_create.
+ *
+ * @param buffer The lut raw buffer. The function creates a copy of it and does not need to
+ * outlive the life of the ADisplayLutsEntry.
+ * @param length The length of lut raw buffer
+ * @param dimension The dimension of the lut. see \a ADisplayLuts_Dimension
+ * @param key The sampling key used by the lut. see \a ADisplayLuts_SamplingKey
+ * @return a new \a ADisplayLutsEntry instance.
+ */
+ADisplayLutsEntry* _Nonnull ADisplayLutsEntry_createEntry(float* _Nonnull buffer,
+ int32_t length, int32_t dimension, int32_t key) __INTRODUCED_IN(36);
+
+/**
+ * Destroy the \a ADisplayLutsEntry instance.
+ *
+ * @param entry The entry to be destroyed
+ */
+void ADisplayLutsEntry_destroy(ADisplayLutsEntry* _Nullable entry) __INTRODUCED_IN(36);
+
+/**
+ * Gets the dimension of the entry.
+ *
+ * The function is only valid for the lifetime of the `entry`.
+ *
+ * @param entry The entry to query
+ * @return the dimension of the lut
+ */
+ADisplayLuts_Dimension ADisplayLutsEntry_getDimension(const ADisplayLutsEntry* _Nonnull entry)
+ __INTRODUCED_IN(36);
+
+/**
+ * Gets the size for each dimension of the entry.
+ *
+ * The function is only valid for the lifetime of the `entry`.
+ *
+ * @param entry The entry to query
+ * @return the size of each dimension of the lut
+ */
+int32_t ADisplayLutsEntry_getSize(const ADisplayLutsEntry* _Nonnull entry)
+ __INTRODUCED_IN(36);
+
+/**
+ * Gets the sampling key used by the entry.
+ *
+ * The function is only valid for the lifetime of the `entry`.
+ *
+ * @param entry The entry to query
+ * @return the sampling key used by the lut
+ */
+ADisplayLuts_SamplingKey ADisplayLutsEntry_getSamplingKey(const ADisplayLutsEntry* _Nonnull entry)
+ __INTRODUCED_IN(36);
+
+/**
+ * Gets the lut buffer of the entry.
+ *
+ * The function is only valid for the lifetime of the `entry`.
+ *
+ * @param entry The entry to query
+ * @return a pointer to the raw lut buffer
+ */
+const float* _Nonnull ADisplayLutsEntry_getBuffer(const ADisplayLutsEntry* _Nonnull entry)
+ __INTRODUCED_IN(36);
+
+/**
+ * Creates a \a ADisplayLuts instance.
+ *
+ * You are responsible for mamanging the memory of the returned object.
+ * Always call \a ADisplayLuts_destroy to release it after use. E.g., after calling
+ * the function \a ASurfaceTransaction_setLuts.
+ *
+ * @return a new \a ADisplayLuts instance
+ */
+ADisplayLuts* _Nonnull ADisplayLuts_create() __INTRODUCED_IN(36);
+
+/**
+ * Sets Luts in order to be applied.
+ *
+ * The function accepts a single 1D Lut, or a single 3D Lut or both 1D and 3D Lut in order.
+ * And the function will replace any previously set lut(s).
+ * If you want to clear the previously set lut(s), set `entries` to be nullptr,
+ * and `numEntries` will be internally ignored.
+ *
+ * @param luts the pointer of the \a ADisplayLuts instance
+ * @param entries the pointer of the array of lut entries to be applied
+ * @param numEntries the number of lut entries. The value should be either 1 or 2.
+ */
+void ADisplayLuts_setEntries(ADisplayLuts* _Nonnull luts,
+ ADisplayLutsEntry* _Nullable *_Nullable entries, int32_t numEntries) __INTRODUCED_IN(36);
+
+/**
+ * Deletes the \a ADisplayLuts instance.
+ *
+ * @param luts The luts to be destroyed
+ */
+void ADisplayLuts_destroy(ADisplayLuts* _Nullable luts) __INTRODUCED_IN(36);
+
+__END_DECLS
+
+/** @} */
\ No newline at end of file
diff --git a/include/android/keycodes.h b/include/android/keycodes.h
index 79cdbca..495e0bd 100644
--- a/include/android/keycodes.h
+++ b/include/android/keycodes.h
@@ -843,6 +843,44 @@
AKEYCODE_EMOJI_PICKER = 317,
/** Take Screenshot */
AKEYCODE_SCREENSHOT = 318,
+ /** To start dictate to an input field */
+ AKEYCODE_DICTATE = 319,
+ /** AC New */
+ AKEYCODE_NEW = 320,
+ /** AC Close */
+ AKEYCODE_CLOSE = 321,
+ /** To toggle 'Do Not Disturb' mode */
+ AKEYCODE_DO_NOT_DISTURB = 322,
+ /** To Print */
+ AKEYCODE_PRINT = 323,
+ /** To Lock the screen */
+ AKEYCODE_LOCK = 324,
+ /** To toggle fullscreen mode (on the current application) */
+ AKEYCODE_FULLSCREEN = 325,
+ /** F13 key */
+ AKEYCODE_F13 = 326,
+ /** F14 key */
+ AKEYCODE_F14 = 327,
+ /** F15 key */
+ AKEYCODE_F15 = 328,
+ /** F16 key */
+ AKEYCODE_F16 = 329,
+ /** F17 key */
+ AKEYCODE_F17 = 330,
+ /** F18 key */
+ AKEYCODE_F18 = 331,
+ /** F19 key */
+ AKEYCODE_F19 = 332,
+ /** F20 key */
+ AKEYCODE_F20 = 333,
+ /** F21 key */
+ AKEYCODE_F21 = 334,
+ /** F22 key */
+ AKEYCODE_F22 = 335,
+ /** F23 key */
+ AKEYCODE_F23 = 336,
+ /** F24 key */
+ AKEYCODE_F24 = 337,
// NOTE: If you add a new keycode here you must also add it to several other files.
// Refer to frameworks/base/core/java/android/view/KeyEvent.java for the full list.
diff --git a/include/android/looper.h b/include/android/looper.h
index d80a366..8cf1396 100644
--- a/include/android/looper.h
+++ b/include/android/looper.h
@@ -90,20 +90,23 @@
ALOOPER_POLL_WAKE = -1,
/**
- * Result from ALooper_pollOnce() and ALooper_pollAll():
- * One or more callbacks were executed.
+ * Result from ALooper_pollOnce():
+ * One or more callbacks were executed. The poll may also have been
+ * explicitly woken by ALooper_wake().
*/
ALOOPER_POLL_CALLBACK = -2,
/**
* Result from ALooper_pollOnce() and ALooper_pollAll():
- * The timeout expired.
+ * The timeout expired. The poll may also have been explicitly woken by
+ * ALooper_wake().
*/
ALOOPER_POLL_TIMEOUT = -3,
/**
* Result from ALooper_pollOnce() and ALooper_pollAll():
- * An error occurred.
+ * An error occurred. The poll may also have been explicitly woken by
+ * ALooper_wake(()).
*/
ALOOPER_POLL_ERROR = -4,
};
@@ -182,10 +185,13 @@
* If the timeout is zero, returns immediately without blocking.
* If the timeout is negative, waits indefinitely until an event appears.
*
+ * **All return values may also imply ALOOPER_POLL_WAKE.** If you call this in a
+ * loop, you must treat all return values as if they also indicated
+ * ALOOPER_POLL_WAKE.
+ *
* Returns ALOOPER_POLL_WAKE if the poll was awoken using ALooper_wake() before
* the timeout expired and no callbacks were invoked and no other file
- * descriptors were ready. **All return values may also imply
- * ALOOPER_POLL_WAKE.**
+ * descriptors were ready.
*
* Returns ALOOPER_POLL_CALLBACK if one or more callbacks were invoked. The poll
* may also have been explicitly woken by ALooper_wake.
@@ -214,9 +220,9 @@
* data has been consumed or a file descriptor is available with no callback.
* This function will never return ALOOPER_POLL_CALLBACK.
*
- * This API cannot be used safely, but a safe alternative exists (see below). As
- * such, new builds will not be able to call this API and must migrate to the
- * safe API. Binary compatibility is preserved to support already-compiled apps.
+ * This API will not reliably respond to ALooper_wake. As such, this API is
+ * hidden and callers should migrate to ALooper_pollOnce. Binary compatibility
+ * is preserved to support already-compiled apps.
*
* \bug ALooper_pollAll will not wake in response to ALooper_wake calls if it
* also handles another event at the same time.
@@ -235,6 +241,8 @@
*
* This method can be called on any thread.
* This method returns immediately.
+ *
+ * \bug ALooper_pollAll will not reliably wake in response to ALooper_wake.
*/
void ALooper_wake(ALooper* looper);
diff --git a/include/android/performance_hint.h b/include/android/performance_hint.h
index 3f32a5a..ca86c27 100644
--- a/include/android/performance_hint.h
+++ b/include/android/performance_hint.h
@@ -19,9 +19,23 @@
*
* APerformanceHint allows apps to create performance hint sessions for groups
* of threads, and provide hints to the system about the workload of those threads,
- * to help the system more accurately allocate power for them. It is the NDK
+ * to help the system more accurately allocate resources for them. It is the NDK
* counterpart to the Java PerformanceHintManager SDK API.
*
+ * This API is intended for periodic workloads, such as frame production. Clients are
+ * expected to create an instance of APerformanceHintManager, create a session with
+ * that, and then set a target duration for the session. Then, they can report the actual
+ * work duration at the end of each cycle to inform the framework about how long those
+ * workloads are taking. The framework will then compare the actual durations to the target
+ * duration and attempt to help the client reach a steady state under the target.
+ *
+ * Unlike reportActualWorkDuration, the "notify..." hints are intended to be sent in
+ * advance of large changes in the workload, to prevent them from going over the target
+ * when there is a sudden, unforseen change. Their effects are intended to last for only
+ * one cycle, after which reportActualWorkDuration will have a chance to catch up.
+ * These hints should be used judiciously, only in cases where the workload is changing
+ * substantially. To enforce that, they are tracked using a per-app rate limiter to avoid
+ * excessive hinting and encourage clients to be mindful about when to send them.
* @{
*/
@@ -35,6 +49,7 @@
#define ANDROID_NATIVE_PERFORMANCE_HINT_H
#include <sys/cdefs.h>
+#include <jni.h>
/******************************************************************
*
@@ -52,7 +67,6 @@
* - DO NOT CHANGE THE LAYOUT OR SIZE OF STRUCTURES
*/
-#include <android/api-level.h>
#include <stdbool.h>
#include <stdint.h>
#include <unistd.h>
@@ -62,6 +76,8 @@
struct APerformanceHintManager;
struct APerformanceHintSession;
struct AWorkDuration;
+struct ANativeWindow;
+struct ASurfaceControl;
/**
* {@link AWorkDuration} is an opaque type that represents the breakdown of the
@@ -97,9 +113,24 @@
typedef struct APerformanceHintManager APerformanceHintManager;
/**
+ * An opaque type representing a handle to a performance hint session creation configuration.
+ * It is consumed by {@link APerformanceHint_createSessionUsingConfig}.
+ *
+ * A session creation config encapsulates the required information for a session.
+ * Additionally, the caller can set various settings for the session,
+ * to be passed during creation, streamlining the session setup process.
+ *
+ * The caller may reuse this object and modify the settings in it
+ * to create additional sessions.
+ *
+ */
+typedef struct ASessionCreationConfig ASessionCreationConfig;
+
+/**
* An opaque type representing a handle to a performance hint session.
* A session can only be acquired from a {@link APerformanceHintManager}
- * with {@link APerformanceHint_createSession}. It must be
+ * with {@link APerformanceHint_createSession}
+ * or {@link APerformanceHint_createSessionUsingConfig}. It must be
* freed with {@link APerformanceHint_closeSession} after use.
*
* A Session represents a group of threads with an inter-related workload such that hints for
@@ -121,6 +152,9 @@
*/
typedef struct APerformanceHintSession APerformanceHintSession;
+typedef struct ANativeWindow ANativeWindow;
+typedef struct ASurfaceControl ASurfaceControl;
+
/**
* Acquire an instance of the performance hint manager.
*
@@ -139,7 +173,7 @@
* @param size The size of the list of threadIds.
* @param initialTargetWorkDurationNanos The target duration in nanoseconds for the new session.
* This must be positive if using the work duration API, or 0 otherwise.
- * @return APerformanceHintManager instance on success, nullptr on failure.
+ * @return APerformanceHintSession pointer on success, nullptr on failure.
*/
APerformanceHintSession* _Nullable APerformanceHint_createSession(
APerformanceHintManager* _Nonnull manager,
@@ -147,6 +181,20 @@
int64_t initialTargetWorkDurationNanos) __INTRODUCED_IN(__ANDROID_API_T__);
/**
+ * Creates a session for the given set of threads that are graphics pipeline threads
+ * and set their initial target work duration.
+ *
+ * @param manager The performance hint manager instance.
+ * @param config The configuration struct containing required information
+ * to create a session.
+ * @return APerformanceHintSession pointer on success, nullptr on failure.
+ */
+APerformanceHintSession* _Nullable APerformanceHint_createSessionUsingConfig(
+ APerformanceHintManager* _Nonnull manager,
+ ASessionCreationConfig* _Nonnull config)
+ __INTRODUCED_IN(36);
+
+/**
* Get preferred update rate information for this device.
*
* @param manager The performance hint manager instance.
@@ -156,6 +204,15 @@
APerformanceHintManager* _Nonnull manager) __INTRODUCED_IN(__ANDROID_API_T__);
/**
+ * Get maximum number of graphics pipieline threads per-app for this device.
+ *
+ * @param manager The performance hint manager instance.
+ * @return the maximum number of graphics pipeline threads supported by device.
+ */
+ int APerformanceHint_getMaxGraphicsPipelineThreadsCount(
+ APerformanceHintManager* _Nonnull manager) __INTRODUCED_IN(36);
+
+/**
* Updates this session's target duration for each cycle of work.
*
* @param session The performance hint session instance to update.
@@ -189,6 +246,9 @@
* Release the performance hint manager pointer acquired via
* {@link APerformanceHint_createSession}.
*
+ * This cannot be used to close a Java PerformanceHintManager.Session, as its
+ * lifecycle is tied to the object in the SDK.
+ *
* @param session The performance hint session instance to release.
*/
void APerformanceHint_closeSession(
@@ -251,16 +311,97 @@
AWorkDuration* _Nonnull workDuration) __INTRODUCED_IN(__ANDROID_API_V__);
/**
+ * Informs the framework of an upcoming increase in the workload of a graphics pipeline
+ * bound to this session. The user can specify whether the increase is expected to be
+ * on the CPU, GPU, or both.
+ *
+ * Sending hints for both CPU and GPU counts as two separate hints for the purposes of the
+ * rate limiter.
+ *
+ * @param cpu Indicates if the workload increase is expected to affect the CPU.
+ * @param gpu Indicates if the workload increase is expected to affect the GPU.
+ * @param debugName A required string used to identify this specific hint during
+ * tracing. This debug string will only be held for the duration of the
+ * method, and can be safely discarded after.
+ *
+ * @return 0 on success.
+ * EINVAL if no hints were requested.
+ * EBUSY if the hint was rate limited.
+ * EPIPE if communication with the system service has failed.
+ * ENOTSUP if the hint is not supported.
+ */
+int APerformanceHint_notifyWorkloadIncrease(
+ APerformanceHintSession* _Nonnull session,
+ bool cpu, bool gpu, const char* _Nonnull debugName) __INTRODUCED_IN(36);
+
+/**
+ * Informs the framework of an upcoming reset in the workload of a graphics pipeline
+ * bound to this session, or the imminent start of a new workload. The user can specify
+ * whether the reset is expected to affect the CPU, GPU, or both.
+ *
+ * Sending hints for both CPU and GPU counts as two separate hints for the purposes of the
+ * this load tracking.
+ *
+ * @param cpu Indicates if the workload reset is expected to affect the CPU.
+ * @param gpu Indicates if the workload reset is expected to affect the GPU.
+ * @param debugName A required string used to identify this specific hint during
+ * tracing. This debug string will only be held for the duration of the
+ * method, and can be safely discarded after.
+ *
+ * @return 0 on success.
+ * EINVAL if no hints were requested.
+ * EBUSY if the hint was rate limited.
+ * EPIPE if communication with the system service has failed.
+ * ENOTSUP if the hint is not supported.
+ */
+int APerformanceHint_notifyWorkloadReset(
+ APerformanceHintSession* _Nonnull session,
+ bool cpu, bool gpu, const char* _Nonnull debugName) __INTRODUCED_IN(36);
+
+/**
+ * Associates a session with any {@link ASurfaceControl} or {@link ANativeWindow}
+ * instances managed by this session.
+ *
+ * This method is primarily intended for sessions that manage the timing of an entire
+ * graphics pipeline end-to-end, such as those using the
+ * {@link ASessionCreationConfig_setGraphicsPipeline} API. However, any session directly
+ * or indirectly managing a graphics pipeline should still associate themselves with
+ * directly relevant ASurfaceControl or ANativeWindow instances for better optimization.
+ *
+ * To see any benefit from this method, the client must make sure they are updating the framerate
+ * of attached surfaces using methods such as {@link ANativeWindow_setFrameRate}, or by updating
+ * any associated ASurfaceControls with transactions that have {ASurfaceTransaction_setFrameRate}.
+ *
+ * @param session The {@link APerformanceHintSession} instance to update.
+ * @param nativeWindows A pointer to a list of ANativeWindows associated with this session.
+ * nullptr can be passed to indicate there are no associated ANativeWindows.
+ * @param nativeWindowsSize The number of ANativeWindows in the list.
+ * @param surfaceControls A pointer to a list of ASurfaceControls associated with this session.
+ * nullptr can be passed to indicate there are no associated ASurfaceControls.
+ * @param surfaceControlsSize The number of ASurfaceControls in the list.
+ *
+ * @return 0 on success.
+ * EPIPE if communication has failed.
+ * ENOTSUP if unsupported.
+ * EINVAL if invalid or empty arguments passed.
+ */
+
+int APerformanceHint_setNativeSurfaces(APerformanceHintSession* _Nonnull session,
+ ANativeWindow* _Nonnull* _Nullable nativeWindows, int nativeWindowsSize,
+ ASurfaceControl* _Nonnull* _Nullable surfaceControls, int surfaceControlsSize)
+ __INTRODUCED_IN(36);
+
+/**
* Creates a new AWorkDuration. When the client finishes using {@link AWorkDuration}, it should
* call {@link AWorkDuration_release()} to destroy {@link AWorkDuration} and release all resources
* associated with it.
*
- * @return AWorkDuration on success and nullptr otherwise.
+ * @return AWorkDuration pointer.
*/
AWorkDuration* _Nonnull AWorkDuration_create() __INTRODUCED_IN(__ANDROID_API_V__);
/**
- * Destroys {@link AWorkDuration} and free all resources associated to it.
+ * Destroys a {@link AWorkDuration} and frees all resources associated with it.
*
* @param aWorkDuration The {@link AWorkDuration} created by calling {@link AWorkDuration_create()}
*/
@@ -309,6 +450,171 @@
void AWorkDuration_setActualGpuDurationNanos(AWorkDuration* _Nonnull aWorkDuration,
int64_t actualGpuDurationNanos) __INTRODUCED_IN(__ANDROID_API_V__);
+/**
+ * Return the APerformanceHintSession wrapped by a Java PerformanceHintManager.Session object.
+ *
+ * The Java session maintains ownership over the wrapped native session, so it cannot be
+ * closed using {@link APerformanceHint_closeSession}.
+ *
+ * @param env The Java environment where the PerformanceHintManager.Session lives.
+ * @param sessionObj The Java Session to unwrap.
+ *
+ * @return A pointer to the APerformanceHintManager that backs the Java Session.
+ */
+APerformanceHintSession* _Nonnull APerformanceHint_borrowSessionFromJava(
+ JNIEnv* _Nonnull env, jobject _Nonnull sessionObj) __INTRODUCED_IN(36);
+
+/*
+ * Creates a new ASessionCreationConfig.
+ *
+ * When the client finishes using {@link ASessionCreationConfig}, it should
+ * call {@link ASessionCreationConfig_release()} to destroy
+ * {@link ASessionCreationConfig} and release all resources
+ * associated with it.
+ *
+ * @return ASessionCreationConfig pointer.
+ */
+ASessionCreationConfig* _Nonnull ASessionCreationConfig_create()
+ __INTRODUCED_IN(36);
+
+
+/**
+ * Destroys a {@link ASessionCreationConfig} and frees all
+ * resources associated with it.
+ *
+ * @param config The {@link ASessionCreationConfig}
+ * created by calling {@link ASessionCreationConfig_create()}.
+ */
+void ASessionCreationConfig_release(
+ ASessionCreationConfig* _Nonnull config) __INTRODUCED_IN(36);
+
+/**
+ * Sets the tids to be associated with the session to be created.
+ *
+ * @param config The {@link ASessionCreationConfig}
+ * created by calling {@link ASessionCreationConfig_create()}
+ * @param tids The list of tids to be associated with this session. They must be part of
+ * this process' thread group.
+ * @param size The size of the list of tids.
+ *
+ * @return 0 on success.
+ * EINVAL if invalid array pointer or the value of size
+ */
+int ASessionCreationConfig_setTids(
+ ASessionCreationConfig* _Nonnull config,
+ const pid_t* _Nonnull tids, size_t size) __INTRODUCED_IN(36);
+
+/**
+ * Sets the initial target work duration in nanoseconds for the session to be created.
+ *
+ * @param config The {@link ASessionCreationConfig}
+ * created by calling {@link ASessionCreationConfig_create()}.
+ * @param targetWorkDurationNanos The parameter to specify a target duration
+ * in nanoseconds for the new session; this value must be positive to use
+ * the work duration API.
+ *
+ * @return 0 on success.
+ * ENOTSUP if unsupported
+ * EINVAL if invalid value
+ */
+int ASessionCreationConfig_setTargetWorkDurationNanos(
+ ASessionCreationConfig* _Nonnull config,
+ int64_t targetWorkDurationNanos) __INTRODUCED_IN(36);
+
+/**
+ * Sets whether power efficiency mode will be enabled for the session.
+ * This tells the session that these threads can be
+ * safely scheduled to prefer power efficiency over performance.
+ *
+ * @param config The {@link ASessionCreationConfig}
+ * created by calling {@link ASessionCreationConfig_create()}.
+ * @param enabled Whether power efficiency mode will be enabled.
+ *
+ * @return 0 on success.
+ * ENOTSUP if unsupported
+ * EINVAL if invalid pointer to creation config
+ */
+int ASessionCreationConfig_setPreferPowerEfficiency(
+ ASessionCreationConfig* _Nonnull config, bool enabled) __INTRODUCED_IN(36);
+
+/**
+ * Sessions setting this hint are expected to time the critical path of
+ * graphics pipeline from end to end, with the total work duration
+ * representing the time from the start of frame production until the
+ * buffer is fully finished drawing.
+ *
+ * It should include any threads on the critical path of that pipeline,
+ * up to a limit accessible from {@link getMaxGraphicsPipelineThreadsCount()}.
+ *
+ * @param config The {@link ASessionCreationConfig}
+ * created by calling {@link ASessionCreationConfig_create()}.
+ * @param enabled Whether this session manages a graphics pipeline's critical path.
+ *
+ * @return 0 on success.
+ * ENOTSUP if unsupported
+ * EINVAL if invalid pointer to creation config or maximum threads for graphics
+ pipeline is reached.
+ */
+int ASessionCreationConfig_setGraphicsPipeline(
+ ASessionCreationConfig* _Nonnull config, bool enabled) __INTRODUCED_IN(36);
+
+/**
+ * Associates a session with any {@link ASurfaceControl} or {@link ANativeWindow}
+ * instances managed by this session. See {@link APerformanceHint_setNativeSurfaces}
+ * for more details.
+ *
+ * @param config The {@link ASessionCreationConfig}
+ * created by calling {@link ASessionCreationConfig_create()}.
+ * @param nativeWindows A pointer to a list of ANativeWindows associated with this session.
+ * nullptr can be passed to indicate there are no associated ANativeWindows.
+ * @param nativeWindowsSize The number of ANativeWindows in the list.
+ * @param surfaceControls A pointer to a list of ASurfaceControls associated with this session.
+ * nullptr can be passed to indicate there are no associated ASurfaceControls.
+ * @param surfaceControlsSize The number of ASurfaceControls in the list.
+ *
+ * @return 0 on success.
+ * ENOTSUP if unsupported.
+ * EINVAL if invalid or empty arguments passed.
+ */
+int ASessionCreationConfig_setNativeSurfaces(
+ ASessionCreationConfig* _Nonnull config,
+ ANativeWindow* _Nonnull* _Nullable nativeWindows, int nativeWindowsSize,
+ ASurfaceControl* _Nonnull* _Nullable surfaceControls, int surfaceControlsSize)
+ __INTRODUCED_IN(36);
+
+/**
+ * Enable automatic timing mode for sessions using the GRAPHICS_PIPELINE API with an attached
+ * surface. In this mode, sessions do not need to report actual durations and only need
+ * to keep their thread list up-to-date, set a native surface, call
+ * {@link ASessionCreationConfig_setGraphicsPipeline()} to signal that the session is in
+ * "graphics pipeline" mode, and then set whether automatic timing is desired for the
+ * CPU, GPU, or both, using this method.
+ *
+ * It is still be beneficial to set an accurate target time, as this may help determine
+ * timing information for some workloads where there is less information available from
+ * the framework, such as games. Additionally, reported CPU durations will be ignored
+ * while automatic CPU timing is enabled, and similarly GPU durations will be ignored
+ * when automatic GPU timing is enabled. When both are enabled, the entire
+ * reportActualWorkDuration call will be ignored, and the session will be managed
+ * completely automatically.
+ *
+ * This mode will not work unless the client makes sure they are updating the framerate
+ * of attached surfaces with methods such as {@link ANativeWindow_setFrameRate}, or updating
+ * any associated ASurfaceControls with transactions that have {ASurfaceTransaction_setFrameRate}.
+ *
+ * @param config The {@link ASessionCreationConfig}
+ * created by calling {@link ASessionCreationConfig_create()}.
+ * @param cpu Whether to enable automatic timing for the CPU for this session.
+ * @param gpu Whether to enable automatic timing for the GPU for this session.
+ *
+ * @return 0 on success.
+ * ENOTSUP if unsupported.
+ */
+int ASessionCreationConfig_setUseAutoTiming(
+ ASessionCreationConfig* _Nonnull config,
+ bool cpu, bool gpu)
+ __INTRODUCED_IN(36);
+
__END_DECLS
#endif // ANDROID_NATIVE_PERFORMANCE_HINT_H
diff --git a/include/android/surface_control.h b/include/android/surface_control.h
index 8d61e77..9554015 100644
--- a/include/android/surface_control.h
+++ b/include/android/surface_control.h
@@ -28,6 +28,7 @@
#include <sys/cdefs.h>
+#include <android/display_luts.h>
#include <android/choreographer.h>
#include <android/data_space.h>
#include <android/hardware_buffer.h>
@@ -713,6 +714,23 @@
__INTRODUCED_IN(__ANDROID_API_V__);
/**
+ * Sets the Lut(s) to be applied for the layer.
+ *
+ * The function makes a deep copy of the provided `luts`.
+ * Any modifications made to the `luts` object after calling this function
+ * will not affect the Lut(s) applied to the layer.
+ *
+ * @param surface_control The layer where Lut(s) is being applied
+ * @param luts The Lut(s) to be applied
+ *
+ * Available since API level 36.
+ */
+void ASurfaceTransaction_setLuts(ASurfaceTransaction* _Nonnull transaction,
+ ASurfaceControl* _Nonnull surface_control,
+ const struct ADisplayLuts* _Nullable luts)
+ __INTRODUCED_IN(36);
+
+/**
* Same as ASurfaceTransaction_setFrameRateWithChangeStrategy(transaction, surface_control,
* frameRate, compatibility, ANATIVEWINDOW_CHANGE_FRAME_RATE_ONLY_IF_SEAMLESS).
*
@@ -763,6 +781,69 @@
__INTRODUCED_IN(31);
/**
+ * Sets the intended frame rate for the given \a surface_control.
+ *
+ * On devices that are capable of running the display at different frame rates,
+ * the system may choose a display refresh rate to better match this surface's frame
+ * rate. Usage of this API won't introduce frame rate throttling, or affect other
+ * aspects of the application's frame production pipeline. However, because the system
+ * may change the display refresh rate, calls to this function may result in changes
+ * to Choreographer callback timings, and changes to the time interval at which the
+ * system releases buffers back to the application.
+ *
+ * You can register for changes in the refresh rate using
+ * \a AChoreographer_registerRefreshRateCallback.
+ *
+ * See ASurfaceTransaction_clearFrameRate().
+ *
+ * Available since API level 36.
+ *
+ * \param desiredMinRate The desired minimum frame rate (inclusive) for the surface, specifying that
+ * the surface prefers the device render rate to be at least `desiredMinRate`.
+ *
+ * <p>Set `desiredMinRate` = `desiredMaxRate` to indicate the surface prefers an exact frame rate.
+ *
+ * <p>Set `desiredMinRate` = 0 to indicate the surface has no preference
+ * and any frame rate is acceptable.
+ *
+ * <p>The value should be greater than or equal to 0.
+ *
+ * \param desiredMaxRate The desired maximum frame rate (inclusive) for the surface, specifying that
+ * the surface prefers the device render rate to be at most `desiredMaxRate`.
+ *
+ * <p>Set `desiredMaxRate` = `desiredMinRate` to indicate the surface prefers an exact frame rate.
+ *
+ * <p>Set `desiredMaxRate` = positive infinity to indicate the surface has no preference
+ * and any frame rate is acceptable.
+ *
+ * <p>The value should be greater than or equal to `desiredMinRate`.
+ *
+ * \param fixedSourceRate The "fixed source" frame rate of the surface if the content has an
+ * inherently fixed frame rate, e.g. a video that has a specific frame rate.
+ *
+ * <p>When the frame rate chosen for the surface is the `fixedSourceRate` or a
+ * multiple, the surface can render without frame pulldown, for optimal smoothness. For
+ * example, a 30 fps video (`fixedSourceRate`=30) renders just as smoothly on 30 fps,
+ * 60 fps, 90 fps, 120 fps, and so on.
+ *
+ * <p>Setting the fixed source rate can also be used together with a desired
+ * frame rate min and max via setting `desiredMinRate` and `desiredMaxRate`. This still
+ * means the surface's content has a fixed frame rate of `fixedSourceRate`, but additionally
+ * specifies the preference to be in the range [`desiredMinRate`, `desiredMaxRate`]. For example, an
+ * app might want to specify there is 30 fps video (`fixedSourceRate`=30) as well as a smooth
+ * animation on the same surface which looks good when drawing within a frame rate range such as
+ * [`desiredMinRate`, `desiredMaxRate`] = [60,120].
+ *
+ * \param changeFrameRateStrategy Whether display refresh rate transitions caused by this surface
+ * should be seamless. A seamless transition is one that doesn't have any visual interruptions, such
+ * as a black screen for a second or two.
+ */
+void ASurfaceTransaction_setFrameRateParams(
+ ASurfaceTransaction* _Nonnull transaction, ASurfaceControl* _Nonnull surface_control,
+ float desiredMinRate, float desiredMaxRate, float fixedSourceRate,
+ ANativeWindow_ChangeFrameRateStrategy changeFrameRateStrategy) __INTRODUCED_IN(36);
+
+/**
* Clears the frame rate which is set for \a surface_control.
*
* This is equivalent to calling
diff --git a/include/audiomanager/AudioManager.h b/include/audiomanager/AudioManager.h
index 917d9a7..203623d 100644
--- a/include/audiomanager/AudioManager.h
+++ b/include/audiomanager/AudioManager.h
@@ -22,6 +22,7 @@
// must be kept in sync with definitions in AudioPlaybackConfiguration.java
#define PLAYER_PIID_INVALID -1
+// TODO (b/309532236) remove manual IAudioManager impl in favor of AIDL.
typedef enum {
PLAYER_TYPE_SLES_AUDIOPLAYER_BUFFERQUEUE = 11,
PLAYER_TYPE_SLES_AUDIOPLAYER_URI_FD = 12,
@@ -60,6 +61,7 @@
PLAYER_MUTE_CLIENT_VOLUME = (1 << 4),
PLAYER_MUTE_VOLUME_SHAPER = (1 << 5),
PLAYER_MUTE_PORT_VOLUME = (1 << 6),
+ PLAYER_MUTE_OP_AUDIO_CONTROL = (1 << 7),
};
struct mute_state_t {
@@ -77,6 +79,8 @@
bool muteFromVolumeShaper = false;
/** Flag used when volume is muted by port volume. */
bool muteFromPortVolume = false;
+ /** Flag used when volume is muted by audio control op. */
+ bool muteFromOpAudioControl = false;
explicit operator int() const
{
@@ -87,6 +91,7 @@
result |= muteFromClientVolume * PLAYER_MUTE_CLIENT_VOLUME;
result |= muteFromVolumeShaper * PLAYER_MUTE_VOLUME_SHAPER;
result |= muteFromPortVolume * PLAYER_MUTE_PORT_VOLUME;
+ result |= muteFromOpAudioControl * PLAYER_MUTE_OP_AUDIO_CONTROL;
return result;
}
diff --git a/include/audiomanager/IAudioManager.h b/include/audiomanager/IAudioManager.h
index 0b7e16b..a35a145 100644
--- a/include/audiomanager/IAudioManager.h
+++ b/include/audiomanager/IAudioManager.h
@@ -56,7 +56,7 @@
/*oneway*/ virtual status_t playerAttributes(audio_unique_id_t piid, audio_usage_t usage,
audio_content_type_t content)= 0;
/*oneway*/ virtual status_t playerEvent(audio_unique_id_t piid, player_state_t event,
- audio_port_handle_t eventId) = 0;
+ const std::vector<audio_port_handle_t>& eventIds) = 0;
/*oneway*/ virtual status_t releasePlayer(audio_unique_id_t piid) = 0;
virtual audio_unique_id_t trackRecorder(const sp<IBinder>& recorder) = 0;
/*oneway*/ virtual status_t recorderEvent(audio_unique_id_t riid, recorder_state_t event) = 0;
diff --git a/include/ftl/flags.h b/include/ftl/flags.h
index dbe3148..a2a22eb 100644
--- a/include/ftl/flags.h
+++ b/include/ftl/flags.h
@@ -22,6 +22,7 @@
#include <bitset>
#include <cstdint>
#include <iterator>
+#include <initializer_list>
#include <string>
#include <type_traits>
@@ -40,6 +41,7 @@
public:
constexpr Flags(F f) : mFlags(static_cast<U>(f)) {}
+ constexpr Flags(std::initializer_list<F> fs) : mFlags(combine(fs)) {}
constexpr Flags() : mFlags(0) {}
constexpr Flags(const Flags<F>& f) : mFlags(f.mFlags) {}
@@ -197,6 +199,14 @@
private:
U mFlags;
+ static constexpr U combine(std::initializer_list<F> fs) {
+ U result = 0;
+ for (const F f : fs) {
+ result |= static_cast<U>(f);
+ }
+ return result;
+ }
+
static void appendFlag(std::string& str, const std::string_view& flag, bool& first) {
if (first) {
first = false;
diff --git a/include/input/CoordinateFilter.h b/include/input/CoordinateFilter.h
new file mode 100644
index 0000000..8f2e605
--- /dev/null
+++ b/include/input/CoordinateFilter.h
@@ -0,0 +1,54 @@
+/**
+ * Copyright 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include <chrono>
+
+#include <input/Input.h>
+#include <input/OneEuroFilter.h>
+
+namespace android {
+
+/**
+ * Pair of OneEuroFilters that independently filter X and Y coordinates. Both filters share the same
+ * constructor's parameters. The minimum cutoff frequency is the base cutoff frequency, that is, the
+ * resulting cutoff frequency in the absence of signal's speed. Likewise, beta is a scaling factor
+ * of the signal's speed that sets how much the signal's speed contributes to the resulting cutoff
+ * frequency. The adaptive cutoff frequency criterion is f_c = f_c_min + β|̇x_filtered|
+ */
+class CoordinateFilter {
+public:
+ explicit CoordinateFilter(float minCutoffFreq, float beta);
+
+ /**
+ * Filters in place only the AXIS_X and AXIS_Y fields from coords. Each call to filter must
+ * provide a timestamp strictly greater than the timestamp of the previous call. The first time
+ * this method is invoked no filtering takes place. Subsequent calls do overwrite `coords` with
+ * filtered data.
+ *
+ * @param timestamp The timestamps at which to filter. It must be greater than the one passed in
+ * the previous call.
+ * @param coords Coordinates to be overwritten by the corresponding filtered coordinates.
+ */
+ void filter(std::chrono::nanoseconds timestamp, PointerCoords& coords);
+
+private:
+ OneEuroFilter mXFilter;
+ OneEuroFilter mYFilter;
+};
+
+} // namespace android
\ No newline at end of file
diff --git a/include/input/Input.h b/include/input/Input.h
index a8684bd..2cabd56 100644
--- a/include/input/Input.h
+++ b/include/input/Input.h
@@ -294,6 +294,8 @@
NONE = AINPUT_KEYBOARD_TYPE_NONE,
NON_ALPHABETIC = AINPUT_KEYBOARD_TYPE_NON_ALPHABETIC,
ALPHABETIC = AINPUT_KEYBOARD_TYPE_ALPHABETIC,
+ ftl_first = NONE,
+ ftl_last = ALPHABETIC,
};
bool isStylusToolType(ToolType toolType);
@@ -994,6 +996,15 @@
std::vector<PointerProperties> mPointerProperties;
std::vector<nsecs_t> mSampleEventTimes;
std::vector<PointerCoords> mSamplePointerCoords;
+
+private:
+ /**
+ * Create a human-readable string representation of the event's data for debugging purposes.
+ *
+ * Unlike operator<<, this method does not assume that the event data is valid or consistent, or
+ * call any accessor methods that might themselves call safeDump in the case of invalid data.
+ */
+ std::string safeDump() const;
};
std::ostream& operator<<(std::ostream& out, const MotionEvent& event);
diff --git a/include/input/InputConsumerNoResampling.h b/include/input/InputConsumerNoResampling.h
index 228347d..70d00d1 100644
--- a/include/input/InputConsumerNoResampling.h
+++ b/include/input/InputConsumerNoResampling.h
@@ -141,7 +141,7 @@
}
private:
- std::function<int(int events)> mCallback;
+ const std::function<int(int events)> mCallback;
};
sp<LooperEventCallback> mCallback;
/**
@@ -211,16 +211,17 @@
* `consumeBatchedInputEvents`.
*/
std::map<DeviceId, std::queue<InputMessage>> mBatches;
+
/**
- * Creates a MotionEvent by consuming samples from the provided queue. If one message has
- * eventTime > adjustedFrameTime, all subsequent messages in the queue will be skipped. It is
- * assumed that messages are queued in chronological order. In other words, only events that
- * occurred prior to the adjustedFrameTime will be consumed.
- * @param requestedFrameTime the time up to which to consume events.
- * @param messages the queue of messages to consume from
+ * Creates a MotionEvent by consuming samples from the provided queue. Consumes all messages
+ * with eventTime <= requestedFrameTime - resampleLatency, where `resampleLatency` is latency
+ * introduced by the resampler. Assumes that messages are queued in chronological order.
+ * @param requestedFrameTime The time up to which consume messages, as given by the inequality
+ * above. If std::nullopt, everything in messages will be consumed.
+ * @param messages the queue of messages to consume from.
*/
std::pair<std::unique_ptr<MotionEvent>, std::optional<uint32_t>> createBatchedMotionEvent(
- const nsecs_t requestedFrameTime, std::queue<InputMessage>& messages);
+ const std::optional<nsecs_t> requestedFrameTime, std::queue<InputMessage>& messages);
/**
* Consumes the batched input events, optionally allowing the caller to specify a device id
diff --git a/include/input/InputDevice.h b/include/input/InputDevice.h
index 6a248ef..ea1e4ae 100644
--- a/include/input/InputDevice.h
+++ b/include/input/InputDevice.h
@@ -111,12 +111,12 @@
};
enum class InputDeviceSensorAccuracy : int32_t {
- ACCURACY_NONE = 0,
- ACCURACY_LOW = 1,
- ACCURACY_MEDIUM = 2,
- ACCURACY_HIGH = 3,
+ NONE = 0,
+ LOW = 1,
+ MEDIUM = 2,
+ HIGH = 3,
- ftl_last = ACCURACY_HIGH,
+ ftl_last = HIGH,
};
enum class InputDeviceSensorReportingMode : int32_t {
@@ -131,8 +131,9 @@
PLAYER_ID = 1,
KEYBOARD_BACKLIGHT = 2,
KEYBOARD_MIC_MUTE = 3,
+ KEYBOARD_VOLUME_MUTE = 4,
- ftl_last = KEYBOARD_MIC_MUTE
+ ftl_last = KEYBOARD_VOLUME_MUTE
};
enum class InputDeviceLightCapability : uint32_t {
diff --git a/include/input/OneEuroFilter.h b/include/input/OneEuroFilter.h
new file mode 100644
index 0000000..bdd82b2
--- /dev/null
+++ b/include/input/OneEuroFilter.h
@@ -0,0 +1,101 @@
+/**
+ * Copyright 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include <chrono>
+#include <optional>
+
+#include <input/Input.h>
+
+namespace android {
+
+/**
+ * Low pass filter with adaptive low pass frequency based on the signal's speed. The signal's cutoff
+ * frequency is determined by f_c = f_c_min + β|̇x_filtered|. Refer to
+ * https://dl.acm.org/doi/10.1145/2207676.2208639 for details on how the filter works and how to
+ * tune it.
+ */
+class OneEuroFilter {
+public:
+ /**
+ * Default cutoff frequency of the filtered signal's speed. 1.0 Hz is the value in the filter's
+ * paper.
+ */
+ static constexpr float kDefaultSpeedCutoffFreq = 1.0;
+
+ OneEuroFilter() = delete;
+
+ explicit OneEuroFilter(float minCutoffFreq, float beta,
+ float speedCutoffFreq = kDefaultSpeedCutoffFreq);
+
+ OneEuroFilter(const OneEuroFilter&) = delete;
+ OneEuroFilter& operator=(const OneEuroFilter&) = delete;
+ OneEuroFilter(OneEuroFilter&&) = delete;
+ OneEuroFilter& operator=(OneEuroFilter&&) = delete;
+
+ /**
+ * Returns the filtered value of rawPosition. Each call to filter must provide a timestamp
+ * strictly greater than the timestamp of the previous call. The first time the method is
+ * called, it returns the value of rawPosition. Any subsequent calls provide a filtered value.
+ *
+ * @param timestamp The timestamp at which to filter. It must be strictly greater than the one
+ * provided in the previous call.
+ * @param rawPosition Position to be filtered.
+ */
+ float filter(std::chrono::nanoseconds timestamp, float rawPosition);
+
+private:
+ /**
+ * Minimum cutoff frequency. This is the constant term in the adaptive cutoff frequency
+ * criterion. Units are Hertz.
+ */
+ const float mMinCutoffFreq;
+
+ /**
+ * Slope of the cutoff frequency criterion. This is the term scaling the absolute value of the
+ * filtered signal's speed. Units are 1 / position.
+ */
+ const float mBeta;
+
+ /**
+ * Cutoff frequency of the signal's speed. This is the cutoff frequency applied to the filtering
+ * of the signal's speed. Units are Hertz.
+ */
+ const float mSpeedCutoffFreq;
+
+ /**
+ * The timestamp from the previous call.
+ */
+ std::optional<std::chrono::nanoseconds> mPrevTimestamp;
+
+ /**
+ * The raw position from the previous call.
+ */
+ std::optional<float> mPrevRawPosition;
+
+ /**
+ * The filtered velocity from the previous call. Units are position per nanosecond.
+ */
+ std::optional<float> mPrevFilteredVelocity;
+
+ /**
+ * The filtered position from the previous call.
+ */
+ std::optional<float> mPrevFilteredPosition;
+};
+
+} // namespace android
diff --git a/include/input/Resampler.h b/include/input/Resampler.h
index 6d95ca7..1550977 100644
--- a/include/input/Resampler.h
+++ b/include/input/Resampler.h
@@ -19,11 +19,13 @@
#include <array>
#include <chrono>
#include <iterator>
+#include <map>
#include <optional>
#include <vector>
#include <android-base/logging.h>
#include <ftl/mixins.h>
+#include <input/CoordinateFilter.h>
#include <input/Input.h>
#include <input/InputTransport.h>
#include <input/RingBuffer.h>
@@ -293,4 +295,43 @@
inline static void addSampleToMotionEvent(const Sample& sample, MotionEvent& motionEvent);
};
+/**
+ * Resampler that first applies the LegacyResampler resampling algorithm, then independently filters
+ * the X and Y coordinates with a pair of One Euro filters.
+ */
+class FilteredLegacyResampler final : public Resampler {
+public:
+ /**
+ * Creates a resampler, using the given minCutoffFreq and beta to instantiate its One Euro
+ * filters.
+ */
+ explicit FilteredLegacyResampler(float minCutoffFreq, float beta);
+
+ void resampleMotionEvent(std::chrono::nanoseconds requestedFrameTime, MotionEvent& motionEvent,
+ const InputMessage* futureMessage) override;
+
+ std::chrono::nanoseconds getResampleLatency() const override;
+
+private:
+ LegacyResampler mResampler;
+
+ /**
+ * Minimum cutoff frequency of the value's low pass filter. Refer to OneEuroFilter class for a
+ * more detailed explanation.
+ */
+ const float mMinCutoffFreq;
+
+ /**
+ * Scaling factor of the adaptive cutoff frequency criterion. Refer to OneEuroFilter class for a
+ * more detailed explanation.
+ */
+ const float mBeta;
+
+ /*
+ * Note: an associative array with constant insertion and lookup times would be more efficient.
+ * When this was implemented, there was no container with these properties.
+ */
+ std::map<int32_t /*pointerId*/, CoordinateFilter> mFilteredPointers;
+};
+
} // namespace android
diff --git a/include/powermanager/OWNERS b/include/powermanager/OWNERS
new file mode 100644
index 0000000..9f40e27
--- /dev/null
+++ b/include/powermanager/OWNERS
@@ -0,0 +1 @@
+file:platform/frameworks/base:/ADPF_OWNERS
\ No newline at end of file
diff --git a/include/powermanager/PowerHalController.h b/include/powermanager/PowerHalController.h
index 7e0bd5b..f4f4d5e 100644
--- a/include/powermanager/PowerHalController.h
+++ b/include/powermanager/PowerHalController.h
@@ -72,6 +72,7 @@
virtual HalResult<aidl::android::hardware::power::ChannelConfig> getSessionChannel(
int tgid, int uid) override;
virtual HalResult<void> closeSessionChannel(int tgid, int uid) override;
+ virtual HalResult<aidl::android::hardware::power::SupportInfo> getSupportInfo() override;
private:
std::mutex mConnectedHalMutex;
diff --git a/include/powermanager/PowerHalWrapper.h b/include/powermanager/PowerHalWrapper.h
index 6e347a9..4290182 100644
--- a/include/powermanager/PowerHalWrapper.h
+++ b/include/powermanager/PowerHalWrapper.h
@@ -63,6 +63,7 @@
virtual HalResult<aidl::android::hardware::power::ChannelConfig> getSessionChannel(int tgid,
int uid) = 0;
virtual HalResult<void> closeSessionChannel(int tgid, int uid) = 0;
+ virtual HalResult<aidl::android::hardware::power::SupportInfo> getSupportInfo() = 0;
};
// Empty Power HAL wrapper that ignores all api calls.
@@ -85,6 +86,7 @@
HalResult<aidl::android::hardware::power::ChannelConfig> getSessionChannel(int tgid,
int uid) override;
HalResult<void> closeSessionChannel(int tgid, int uid) override;
+ HalResult<aidl::android::hardware::power::SupportInfo> getSupportInfo() override;
protected:
virtual const char* getUnsupportedMessage();
@@ -170,6 +172,7 @@
HalResult<aidl::android::hardware::power::ChannelConfig> getSessionChannel(int tgid,
int uid) override;
HalResult<void> closeSessionChannel(int tgid, int uid) override;
+ HalResult<aidl::android::hardware::power::SupportInfo> getSupportInfo() override;
protected:
const char* getUnsupportedMessage() override;
diff --git a/include/private/OWNERS b/include/private/OWNERS
new file mode 100644
index 0000000..37da96d
--- /dev/null
+++ b/include/private/OWNERS
@@ -0,0 +1,3 @@
+# ADPF
+per-file thermal_private.h = file:platform/frameworks/base:/ADPF_OWNERS
+per-file performance_hint_private.h = file:platform/frameworks/base:/ADPF_OWNERS
diff --git a/include/private/display_luts_private.h b/include/private/display_luts_private.h
new file mode 100644
index 0000000..240e1f9
--- /dev/null
+++ b/include/private/display_luts_private.h
@@ -0,0 +1,65 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include <stdint.h>
+#include <vector>
+#include <utils/RefBase.h>
+
+using namespace android;
+
+__BEGIN_DECLS
+
+struct ADisplayLutsEntry_buffer {
+ std::vector<float> data;
+};
+
+struct ADisplayLutsEntry_properties {
+ int32_t dimension;
+ int32_t size;
+ int32_t samplingKey;
+};
+
+struct ADisplayLutsEntry: public RefBase {
+ struct ADisplayLutsEntry_buffer buffer;
+ struct ADisplayLutsEntry_properties properties;
+ ADisplayLutsEntry() {}
+
+ // copy constructor
+ ADisplayLutsEntry(const ADisplayLutsEntry& other) :
+ buffer(other.buffer),
+ properties(other.properties) {}
+
+ // copy operator
+ ADisplayLutsEntry& operator=(const ADisplayLutsEntry& other) {
+ if (this != &other) { // Protect against self-assignment
+ buffer = other.buffer;
+ properties = other.properties;
+ }
+ return *this;
+ }
+};
+
+struct ADisplayLuts: public RefBase {
+ int32_t totalBufferSize;
+ std::vector<int32_t> offsets;
+ std::vector<sp<ADisplayLutsEntry>> entries;
+
+ ADisplayLuts() : totalBufferSize(0) {}
+};
+
+__END_DECLS
\ No newline at end of file
diff --git a/include/private/performance_hint_private.h b/include/private/performance_hint_private.h
index b7308c2..f150fb1 100644
--- a/include/private/performance_hint_private.h
+++ b/include/private/performance_hint_private.h
@@ -108,12 +108,46 @@
APerformanceHintSession* APerformanceHint_createSessionInternal(APerformanceHintManager* manager,
const int32_t* threadIds, size_t size,
int64_t initialTargetWorkDurationNanos, SessionTag tag);
+/**
+ * Creates a session using ASessionCreationConfig
+ */
+APerformanceHintSession* APerformanceHint_createSessionUsingConfigInternal(
+ APerformanceHintManager* manager, ASessionCreationConfig* sessionCreationConfig,
+ SessionTag tag);
+
+/**
+ * Creates a session from the Java SDK implementation
+ */
+APerformanceHintSession* APerformanceHint_createSessionFromJava(APerformanceHintManager* manager,
+ const int32_t* threadIds, size_t size,
+ int64_t initialTargetWorkDurationNanos);
+
+/**
+ * Special method for Java SDK implementation to kill sessions
+ */
+void APerformanceHint_closeSessionFromJava(APerformanceHintSession* session);
/**
* Forces FMQ to be enabled or disabled, for testing only.
*/
void APerformanceHint_setUseFMQForTesting(bool enabled);
+/**
+ * Get the rate limiter properties for testing.
+ */
+void APerformanceHint_getRateLimiterPropertiesForTesting(
+ int32_t* maxLoadHintsPerInterval, int64_t* loadHintInterval);
+
+/*
+ * Forces the "new load hint" flag to be disabled for testing.
+ */
+void APerformanceHint_setUseNewLoadHintBehaviorForTesting(bool newBehavior);
+
+/*
+ * Forces the graphics pipeline flag to be enabled or disabled, for testing only.
+ */
+void APerformanceHint_setUseGraphicsPipelineForTesting(bool enabled);
+
__END_DECLS
#endif // ANDROID_PRIVATE_NATIVE_PERFORMANCE_HINT_PRIVATE_H
diff --git a/libs/battery/LongArrayMultiStateCounter.cpp b/libs/battery/LongArrayMultiStateCounter.cpp
index 125cfaf..334d84b 100644
--- a/libs/battery/LongArrayMultiStateCounter.cpp
+++ b/libs/battery/LongArrayMultiStateCounter.cpp
@@ -21,59 +21,138 @@
namespace android {
namespace battery {
+Uint64ArrayRW::Uint64ArrayRW(const Uint64Array ©) : Uint64Array(copy.size()) {
+ if (mSize != 0 && copy.data() != nullptr) {
+ mData = new uint64_t[mSize];
+ memcpy(mData, copy.data(), mSize * sizeof(uint64_t));
+ } else {
+ mData = nullptr;
+ }
+}
+
+uint64_t *Uint64ArrayRW::dataRW() {
+ if (mData == nullptr) {
+ mData = new uint64_t[mSize];
+ memset(mData, 0, mSize * sizeof(uint64_t));
+ }
+ return mData;
+}
+
+Uint64ArrayRW &Uint64ArrayRW::operator=(const Uint64Array &t) {
+ if (t.size() != mSize) {
+ delete[] mData;
+ mSize = t.size();
+ mData = nullptr;
+ }
+ if (mSize != 0) {
+ if (t.data() != nullptr) {
+ if (mData == nullptr) {
+ mData = new uint64_t[mSize];
+ }
+ memcpy(mData, t.data(), mSize * sizeof(uint64_t));
+ } else {
+ delete[] mData;
+ mData = nullptr;
+ }
+ }
+ return *this;
+}
+
+std::ostream &operator<<(std::ostream &os, const Uint64Array &v) {
+ os << "{";
+ const uint64_t *data = v.data();
+ if (data != nullptr) {
+ bool first = true;
+ for (size_t i = 0; i < v.size(); i++) {
+ if (!first) {
+ os << ", ";
+ }
+ os << data[i];
+ first = false;
+ }
+ }
+ os << "}";
+ return os;
+}
+
+// Convenience constructor for tests
+Uint64ArrayRW::Uint64ArrayRW(std::initializer_list<uint64_t> init) : Uint64Array(init.size()) {
+ mData = new uint64_t[mSize];
+ memcpy(mData, init.begin(), mSize * sizeof(uint64_t));
+}
+
+// Used in tests only.
+bool Uint64Array::operator==(const Uint64Array &other) const {
+ if (size() != other.size()) {
+ return false;
+ }
+ const uint64_t* thisData = data();
+ const uint64_t* thatData = other.data();
+ for (size_t i = 0; i < mSize; i++) {
+ const uint64_t v1 = thisData != nullptr ? thisData[i] : 0;
+ const uint64_t v2 = thatData != nullptr ? thatData[i] : 0;
+ if (v1 != v2) {
+ return false;
+ }
+ }
+ return true;
+}
+
template <>
-bool LongArrayMultiStateCounter::delta(const std::vector<uint64_t>& previousValue,
- const std::vector<uint64_t>& newValue,
- std::vector<uint64_t>* outValue) const {
+void LongArrayMultiStateCounter::add(Uint64ArrayRW *value1, const Uint64Array &value2,
+ const uint64_t numerator, const uint64_t denominator) const {
+ const uint64_t* data2 = value2.data();
+ if (data2 == nullptr) {
+ return;
+ }
+
+ uint64_t* data1 = value1->dataRW();
+ size_t size = value2.size();
+ if (numerator != denominator) {
+ for (size_t i = 0; i < size; i++) {
+ // The caller ensures that denominator != 0
+ data1[i] += data2[i] * numerator / denominator;
+ }
+ } else {
+ for (size_t i = 0; i < size; i++) {
+ data1[i] += data2[i];
+ }
+ }
+}
+
+template<>
+bool LongArrayMultiStateCounter::delta(const Uint64ArrayRW &previousValue,
+ const Uint64Array &newValue, Uint64ArrayRW *outValue) const {
size_t size = previousValue.size();
if (newValue.size() != size) {
- ALOGE("Incorrect array size: %d, should be %d", (int)newValue.size(), (int)size);
+ ALOGE("Incorrect array size: %d, should be %d", (int) newValue.size(), (int) size);
+ return false;
+ }
+ if (outValue->size() != size) {
+ ALOGE("Incorrect outValue size: %d, should be %d", (int) outValue->size(), (int) size);
return false;
}
bool is_delta_valid = true;
- for (int i = size - 1; i >= 0; i--) {
- if (newValue[i] >= previousValue[i]) {
- (*outValue)[i] = newValue[i] - previousValue[i];
- } else {
- (*outValue)[i] = 0;
+ const uint64_t *prevData = previousValue.data();
+ const uint64_t *newData = newValue.data();
+ uint64_t *outData = outValue->dataRW();
+ for (size_t i = 0; i < size; i++) {
+ if (prevData == nullptr) {
+ if (newData == nullptr) {
+ outData[i] = 0;
+ } else {
+ outData[i] = newData[i];
+ }
+ } else if (newData == nullptr || newData[i] < prevData[i]) {
+ outData[i] = 0;
is_delta_valid = false;
+ } else {
+ outData[i] = newData[i] - prevData[i];
}
}
return is_delta_valid;
}
-template <>
-void LongArrayMultiStateCounter::add(std::vector<uint64_t>* value1,
- const std::vector<uint64_t>& value2, const uint64_t numerator,
- const uint64_t denominator) const {
- if (numerator != denominator) {
- for (int i = value2.size() - 1; i >= 0; i--) {
- // The caller ensures that denominator != 0
- (*value1)[i] += value2[i] * numerator / denominator;
- }
- } else {
- for (int i = value2.size() - 1; i >= 0; i--) {
- (*value1)[i] += value2[i];
- }
- }
-}
-
-template <>
-std::string LongArrayMultiStateCounter::valueToString(const std::vector<uint64_t>& v) const {
- std::stringstream s;
- s << "{";
- bool first = true;
- for (uint64_t n : v) {
- if (!first) {
- s << ", ";
- }
- s << n;
- first = false;
- }
- s << "}";
- return s.str();
-}
-
} // namespace battery
} // namespace android
diff --git a/libs/battery/LongArrayMultiStateCounter.h b/libs/battery/LongArrayMultiStateCounter.h
index f3439f6..e00c968 100644
--- a/libs/battery/LongArrayMultiStateCounter.h
+++ b/libs/battery/LongArrayMultiStateCounter.h
@@ -23,7 +23,66 @@
namespace android {
namespace battery {
-typedef MultiStateCounter<std::vector<uint64_t>> LongArrayMultiStateCounter;
+/**
+ * Wrapper for an array of uint64's.
+ */
+class Uint64Array {
+ protected:
+ size_t mSize;
+
+ public:
+ Uint64Array() : Uint64Array(0) {}
+
+ Uint64Array(size_t size) : mSize(size) {}
+
+ virtual ~Uint64Array() {}
+
+ size_t size() const { return mSize; }
+
+ /**
+ * Returns the wrapped array.
+ *
+ * Nullable! Null should be interpreted the same as an array of zeros
+ */
+ virtual const uint64_t *data() const { return nullptr; }
+
+ friend std::ostream &operator<<(std::ostream &os, const Uint64Array &v);
+
+ // Test API
+ bool operator==(const Uint64Array &other) const;
+};
+
+/**
+ * Mutable version of Uint64Array.
+ */
+class Uint64ArrayRW: public Uint64Array {
+ uint64_t* mData;
+
+public:
+ Uint64ArrayRW() : Uint64ArrayRW(0) {}
+
+ Uint64ArrayRW(size_t size) : Uint64Array(size), mData(nullptr) {}
+
+ Uint64ArrayRW(const Uint64Array ©);
+
+ // Need an explicit copy constructor. In the initialization context C++ does not understand that
+ // a Uint64ArrayRW is a Uint64Array.
+ Uint64ArrayRW(const Uint64ArrayRW ©) : Uint64ArrayRW((const Uint64Array &) copy) {}
+
+ // Test API
+ Uint64ArrayRW(std::initializer_list<uint64_t> init);
+
+ ~Uint64ArrayRW() override { delete[] mData; }
+
+ const uint64_t *data() const override { return mData; }
+
+ // NonNull. Will initialize the wrapped array if it is null.
+ uint64_t *dataRW();
+
+ Uint64ArrayRW &operator=(const Uint64Array &t);
+};
+
+typedef MultiStateCounter<Uint64ArrayRW, Uint64Array> LongArrayMultiStateCounter;
} // namespace battery
} // namespace android
diff --git a/libs/battery/LongArrayMultiStateCounterTest.cpp b/libs/battery/LongArrayMultiStateCounterTest.cpp
index e4e6b2a..1c74e3f 100644
--- a/libs/battery/LongArrayMultiStateCounterTest.cpp
+++ b/libs/battery/LongArrayMultiStateCounterTest.cpp
@@ -24,25 +24,25 @@
class LongArrayMultiStateCounterTest : public testing::Test {};
TEST_F(LongArrayMultiStateCounterTest, stateChange) {
- LongArrayMultiStateCounter testCounter(2, std::vector<uint64_t>(4));
- testCounter.updateValue(std::vector<uint64_t>({0, 0, 0, 0}), 1000);
+ LongArrayMultiStateCounter testCounter(2, Uint64Array(4));
+ testCounter.updateValue(Uint64ArrayRW({0, 0, 0, 0}), 1000);
testCounter.setState(0, 1000);
testCounter.setState(1, 2000);
- testCounter.updateValue(std::vector<uint64_t>({100, 200, 300, 400}), 3000);
+ testCounter.updateValue(Uint64ArrayRW({100, 200, 300, 400}), 3000);
// Time was split in half between the two states, so the counts will be split 50:50 too
- EXPECT_EQ(std::vector<uint64_t>({50, 100, 150, 200}), testCounter.getCount(0));
- EXPECT_EQ(std::vector<uint64_t>({50, 100, 150, 200}), testCounter.getCount(1));
+ EXPECT_EQ(Uint64ArrayRW({50, 100, 150, 200}), testCounter.getCount(0));
+ EXPECT_EQ(Uint64ArrayRW({50, 100, 150, 200}), testCounter.getCount(1));
}
TEST_F(LongArrayMultiStateCounterTest, accumulation) {
- LongArrayMultiStateCounter testCounter(2, std::vector<uint64_t>(4));
- testCounter.updateValue(std::vector<uint64_t>({0, 0, 0, 0}), 1000);
+ LongArrayMultiStateCounter testCounter(2, Uint64Array(4));
+ testCounter.updateValue(Uint64ArrayRW({0, 0, 0, 0}), 1000);
testCounter.setState(0, 1000);
testCounter.setState(1, 2000);
- testCounter.updateValue(std::vector<uint64_t>({100, 200, 300, 400}), 3000);
+ testCounter.updateValue(Uint64ArrayRW({100, 200, 300, 400}), 3000);
testCounter.setState(0, 4000);
- testCounter.updateValue(std::vector<uint64_t>({200, 300, 400, 500}), 8000);
+ testCounter.updateValue(Uint64ArrayRW({200, 300, 400, 500}), 8000);
// The first delta is split 50:50:
// 0: {50, 100, 150, 200}
@@ -50,16 +50,16 @@
// The second delta is split 4:1
// 0: {80, 80, 80, 80}
// 1: {20, 20, 20, 20}
- EXPECT_EQ(std::vector<uint64_t>({130, 180, 230, 280}), testCounter.getCount(0));
- EXPECT_EQ(std::vector<uint64_t>({70, 120, 170, 220}), testCounter.getCount(1));
+ EXPECT_EQ(Uint64ArrayRW({130, 180, 230, 280}), testCounter.getCount(0));
+ EXPECT_EQ(Uint64ArrayRW({70, 120, 170, 220}), testCounter.getCount(1));
}
TEST_F(LongArrayMultiStateCounterTest, toString) {
- LongArrayMultiStateCounter testCounter(2, std::vector<uint64_t>(4));
- testCounter.updateValue(std::vector<uint64_t>({0, 0, 0, 0}), 1000);
+ LongArrayMultiStateCounter testCounter(2, Uint64Array(4));
+ testCounter.updateValue(Uint64ArrayRW({0, 0, 0, 0}), 1000);
testCounter.setState(0, 1000);
testCounter.setState(1, 2000);
- testCounter.updateValue(std::vector<uint64_t>({100, 200, 300, 400}), 3000);
+ testCounter.updateValue(Uint64ArrayRW({100, 200, 300, 400}), 3000);
EXPECT_STREQ("[0: {50, 100, 150, 200}, 1: {50, 100, 150, 200}] updated: 3000 currentState: 1",
testCounter.toString().c_str());
diff --git a/libs/battery/MultiStateCounter.h b/libs/battery/MultiStateCounter.h
index 04b7186..fadc4ff 100644
--- a/libs/battery/MultiStateCounter.h
+++ b/libs/battery/MultiStateCounter.h
@@ -35,12 +35,12 @@
typedef uint16_t state_t;
-template <class T>
+template <class T, class V>
class MultiStateCounter {
- uint16_t stateCount;
+ const uint16_t stateCount;
+ const V emptyValue;
state_t currentState;
time_t lastStateChangeTimestamp;
- T emptyValue;
T lastValue;
time_t lastUpdateTimestamp;
T deltaValue;
@@ -54,7 +54,7 @@
State* states;
public:
- MultiStateCounter(uint16_t stateCount, const T& emptyValue);
+ MultiStateCounter(uint16_t stateCount, const V& emptyValue);
virtual ~MultiStateCounter();
@@ -66,35 +66,35 @@
* Copies the current state and accumulated times-in-state from the source. Resets
* the accumulated value.
*/
- void copyStatesFrom(const MultiStateCounter<T>& source);
+ void copyStatesFrom(const MultiStateCounter<T, V> &source);
- void setValue(state_t state, const T& value);
+ void setValue(state_t state, const V& value);
/**
* Updates the value by distributing the delta from the previously set value
* among states according to their respective time-in-state.
* Returns the delta from the previously set value.
*/
- const T& updateValue(const T& value, time_t timestamp);
+ const V& updateValue(const V& value, time_t timestamp);
/**
* Updates the value by distributing the specified increment among states according
* to their respective time-in-state.
*/
- void incrementValue(const T& increment, time_t timestamp);
+ void incrementValue(const V& increment, time_t timestamp);
/**
* Adds the specified increment to the value for the current state, without affecting
* the last updated value or timestamp. Ignores partial time-in-state: the entirety of
* the increment is given to the current state.
*/
- void addValue(const T& increment);
+ void addValue(const V& increment);
void reset();
uint16_t getStateCount();
- const T& getCount(state_t state);
+ const V& getCount(state_t state);
std::string toString();
@@ -104,27 +104,25 @@
* Returns true iff the combination of previousValue and newValue is valid
* (newValue >= prevValue)
*/
- bool delta(const T& previousValue, const T& newValue, T* outValue) const;
+ bool delta(const T& previousValue, const V& newValue, T* outValue) const;
/**
* Adds value2 to value1 and stores the result in value1. Denominator is
* guaranteed to be non-zero.
*/
- void add(T* value1, const T& value2, const uint64_t numerator,
+ void add(T* value1, const V& value2, const uint64_t numerator,
const uint64_t denominator) const;
-
- std::string valueToString(const T& value) const;
};
// ---------------------- MultiStateCounter Implementation -------------------------
// Since MultiStateCounter is a template, the implementation must be inlined.
-template <class T>
-MultiStateCounter<T>::MultiStateCounter(uint16_t stateCount, const T& emptyValue)
+template <class T, class V>
+MultiStateCounter<T, V>::MultiStateCounter(uint16_t stateCount, const V& emptyValue)
: stateCount(stateCount),
+ emptyValue(emptyValue),
currentState(0),
lastStateChangeTimestamp(-1),
- emptyValue(emptyValue),
lastValue(emptyValue),
lastUpdateTimestamp(-1),
deltaValue(emptyValue),
@@ -136,13 +134,13 @@
}
}
-template <class T>
-MultiStateCounter<T>::~MultiStateCounter() {
+template <class T, class V>
+MultiStateCounter<T, V>::~MultiStateCounter() {
delete[] states;
};
-template <class T>
-void MultiStateCounter<T>::setEnabled(bool enabled, time_t timestamp) {
+template <class T, class V>
+void MultiStateCounter<T, V>::setEnabled(bool enabled, time_t timestamp) {
if (enabled == isEnabled) {
return;
}
@@ -167,8 +165,8 @@
}
}
-template <class T>
-void MultiStateCounter<T>::setState(state_t state, time_t timestamp) {
+template <class T, class V>
+void MultiStateCounter<T, V>::setState(state_t state, time_t timestamp) {
if (isEnabled && lastStateChangeTimestamp >= 0 && lastUpdateTimestamp >= 0) {
// If the update arrived out-of-order, just push back the timestamp to
// avoid having the situation where timeInStateSinceUpdate > timeSinceUpdate
@@ -198,8 +196,8 @@
lastStateChangeTimestamp = timestamp;
}
-template <class T>
-void MultiStateCounter<T>::copyStatesFrom(const MultiStateCounter<T>& source) {
+template <class T, class V>
+void MultiStateCounter<T, V>::copyStatesFrom(const MultiStateCounter<T, V>& source) {
if (stateCount != source.stateCount) {
ALOGE("State count mismatch: %u vs. %u\n", stateCount, source.stateCount);
return;
@@ -214,14 +212,14 @@
lastUpdateTimestamp = source.lastUpdateTimestamp;
}
-template <class T>
-void MultiStateCounter<T>::setValue(state_t state, const T& value) {
+template <class T, class V>
+void MultiStateCounter<T, V>::setValue(state_t state, const V& value) {
states[state].counter = value;
}
-template <class T>
-const T& MultiStateCounter<T>::updateValue(const T& value, time_t timestamp) {
- T* returnValue = &emptyValue;
+template <class T, class V>
+const V& MultiStateCounter<T, V>::updateValue(const V& value, time_t timestamp) {
+ const V* returnValue = &emptyValue;
// If the counter is disabled, we ignore the update, except when the counter got disabled after
// the previous update, in which case we still need to pick up the residual delta.
@@ -250,8 +248,8 @@
}
} else {
std::stringstream str;
- str << "updateValue is called with a value " << valueToString(value)
- << ", which is lower than the previous value " << valueToString(lastValue)
+ str << "updateValue is called with a value " << value
+ << ", which is lower than the previous value " << lastValue
<< "\n";
ALOGE("%s", str.str().c_str());
@@ -276,23 +274,25 @@
return *returnValue;
}
-template <class T>
-void MultiStateCounter<T>::incrementValue(const T& increment, time_t timestamp) {
+template <class T, class V>
+void MultiStateCounter<T, V>::incrementValue(const V& increment, time_t timestamp) {
+// T newValue;
+// newValue = lastValue; // Copy assignment, not initialization.
T newValue = lastValue;
add(&newValue, increment, 1 /* numerator */, 1 /* denominator */);
updateValue(newValue, timestamp);
}
-template <class T>
-void MultiStateCounter<T>::addValue(const T& value) {
+template <class T, class V>
+void MultiStateCounter<T, V>::addValue(const V& value) {
if (!isEnabled) {
return;
}
add(&states[currentState].counter, value, 1 /* numerator */, 1 /* denominator */);
}
-template <class T>
-void MultiStateCounter<T>::reset() {
+template <class T, class V>
+void MultiStateCounter<T, V>::reset() {
lastStateChangeTimestamp = -1;
lastUpdateTimestamp = -1;
for (int i = 0; i < stateCount; i++) {
@@ -301,25 +301,26 @@
}
}
-template <class T>
-uint16_t MultiStateCounter<T>::getStateCount() {
+template <class T, class V>
+uint16_t MultiStateCounter<T, V>::getStateCount() {
return stateCount;
}
-template <class T>
-const T& MultiStateCounter<T>::getCount(state_t state) {
+template <class T, class V>
+const V& MultiStateCounter<T, V>::getCount(state_t state) {
return states[state].counter;
}
-template <class T>
-std::string MultiStateCounter<T>::toString() {
+template <class T, class V>
+std::string MultiStateCounter<T, V>::toString() {
std::stringstream str;
+// str << "LAST VALUE: " << valueToString(lastValue);
str << "[";
for (int i = 0; i < stateCount; i++) {
if (i != 0) {
str << ", ";
}
- str << i << ": " << valueToString(states[i].counter);
+ str << i << ": " << states[i].counter;
if (states[i].timeInStateSinceUpdate > 0) {
str << " timeInStateSinceUpdate: " << states[i].timeInStateSinceUpdate;
}
diff --git a/libs/battery/MultiStateCounterTest.cpp b/libs/battery/MultiStateCounterTest.cpp
index a51a38a..589b7fe 100644
--- a/libs/battery/MultiStateCounterTest.cpp
+++ b/libs/battery/MultiStateCounterTest.cpp
@@ -21,7 +21,7 @@
namespace android {
namespace battery {
-typedef MultiStateCounter<double> DoubleMultiStateCounter;
+typedef MultiStateCounter<double, double> DoubleMultiStateCounter;
template <>
bool DoubleMultiStateCounter::delta(const double& previousValue, const double& newValue,
@@ -41,11 +41,6 @@
}
}
-template <>
-std::string DoubleMultiStateCounter::valueToString(const double& v) const {
- return std::to_string(v);
-}
-
class MultiStateCounterTest : public testing::Test {};
TEST_F(MultiStateCounterTest, constructor) {
diff --git a/libs/binder/Android.bp b/libs/binder/Android.bp
index 6903cb5..0a61178 100644
--- a/libs/binder/Android.bp
+++ b/libs/binder/Android.bp
@@ -829,6 +829,7 @@
backend: {
rust: {
apex_available: [
+ "//apex_available:platform",
"com.android.virt",
],
enabled: true,
@@ -871,13 +872,16 @@
symbol_file: "libbinder_rpc_unstable.map.txt",
},
+ header_abi_checker: {
+ enabled: false,
+ },
+
// This library is intentionally limited to these targets, and it will be removed later.
// Do not expand the visibility.
visibility: [
":__subpackages__",
"//packages/modules/Virtualization:__subpackages__",
"//device/google/cuttlefish/shared/minidroid:__subpackages__",
- "//system/software_defined_vehicle:__subpackages__",
"//visibility:any_system_partition",
],
}
diff --git a/libs/binder/BackendUnifiedServiceManager.cpp b/libs/binder/BackendUnifiedServiceManager.cpp
index f7b9f05..d32eecd 100644
--- a/libs/binder/BackendUnifiedServiceManager.cpp
+++ b/libs/binder/BackendUnifiedServiceManager.cpp
@@ -105,7 +105,8 @@
};
bool BinderCacheWithInvalidation::isClientSideCachingEnabled(const std::string& serviceName) {
- if (ProcessState::self()->getThreadPoolMaxTotalThreadCount() <= 0) {
+ sp<ProcessState> self = ProcessState::selfOrNull();
+ if (!self || self->getThreadPoolMaxTotalThreadCount() <= 0) {
ALOGW("Thread Pool max thread count is 0. Cannot cache binder as linkToDeath cannot be "
"implemented. serviceName: %s",
serviceName.c_str());
@@ -172,10 +173,6 @@
mCacheForGetService = std::make_shared<BinderCacheWithInvalidation>();
}
-sp<AidlServiceManager> BackendUnifiedServiceManager::getImpl() {
- return mTheRealServiceManager;
-}
-
Status BackendUnifiedServiceManager::getService(const ::std::string& name,
sp<IBinder>* _aidl_return) {
os::Service service;
diff --git a/libs/binder/BackendUnifiedServiceManager.h b/libs/binder/BackendUnifiedServiceManager.h
index feb8470..abc0eda 100644
--- a/libs/binder/BackendUnifiedServiceManager.h
+++ b/libs/binder/BackendUnifiedServiceManager.h
@@ -121,7 +121,6 @@
public:
explicit BackendUnifiedServiceManager(const sp<os::IServiceManager>& impl);
- sp<os::IServiceManager> getImpl();
binder::Status getService(const ::std::string& name, sp<IBinder>* _aidl_return) override;
binder::Status getService2(const ::std::string& name, os::Service* out) override;
binder::Status checkService(const ::std::string& name, os::Service* out) override;
diff --git a/libs/binder/IServiceManager.cpp b/libs/binder/IServiceManager.cpp
index 32388db..39d8c24 100644
--- a/libs/binder/IServiceManager.cpp
+++ b/libs/binder/IServiceManager.cpp
@@ -561,8 +561,9 @@
sp<IBinder> svc = checkService(name);
if (svc != nullptr) return svc;
+ sp<ProcessState> self = ProcessState::selfOrNull();
const bool isVendorService =
- strcmp(ProcessState::self()->getDriverName().c_str(), "/dev/vndbinder") == 0;
+ self && strcmp(self->getDriverName().c_str(), "/dev/vndbinder") == 0;
constexpr auto timeout = 5s;
const auto startTime = std::chrono::steady_clock::now();
// Vendor code can't access system properties
@@ -579,7 +580,7 @@
const useconds_t sleepTime = gSystemBootCompleted ? 1000 : 100;
ALOGI("Waiting for service '%s' on '%s'...", String8(name).c_str(),
- ProcessState::self()->getDriverName().c_str());
+ self ? self->getDriverName().c_str() : "RPC accessors only");
int n = 0;
while (std::chrono::steady_clock::now() - startTime < timeout) {
@@ -661,7 +662,8 @@
if (Status status = realGetService(name, &out); !status.isOk()) {
ALOGW("Failed to getService in waitForService for %s: %s", name.c_str(),
status.toString8().c_str());
- if (0 == ProcessState::self()->getThreadPoolMaxTotalThreadCount()) {
+ sp<ProcessState> self = ProcessState::selfOrNull();
+ if (self && 0 == self->getThreadPoolMaxTotalThreadCount()) {
ALOGW("Got service, but may be racey because we could not wait efficiently for it. "
"Threadpool has 0 guaranteed threads. "
"Is the threadpool configured properly? "
@@ -695,9 +697,10 @@
if (waiter->mBinder != nullptr) return waiter->mBinder;
}
+ sp<ProcessState> self = ProcessState::selfOrNull();
ALOGW("Waited one second for %s (is service started? Number of threads started in the "
"threadpool: %zu. Are binder threads started and available?)",
- name.c_str(), ProcessState::self()->getThreadPoolMaxTotalThreadCount());
+ name.c_str(), self ? self->getThreadPoolMaxTotalThreadCount() : 0);
// Handle race condition for lazy services. Here is what can happen:
// - the service dies (not processed by init yet).
diff --git a/libs/binder/Parcel.cpp b/libs/binder/Parcel.cpp
index 96d821e..a5f416f 100644
--- a/libs/binder/Parcel.cpp
+++ b/libs/binder/Parcel.cpp
@@ -2223,9 +2223,7 @@
const char* eos = reinterpret_cast<const char*>(memchr(str, 0, avail));
if (eos) {
const size_t len = eos - str;
- mDataPos += pad_size(len+1);
- ALOGV("readCString Setting data pos of %p to %zu", this, mDataPos);
- return str;
+ return static_cast<const char*>(readInplace(len + 1));
}
}
return nullptr;
diff --git a/libs/binder/TEST_MAPPING b/libs/binder/TEST_MAPPING
index ab44957..99a9c91 100644
--- a/libs/binder/TEST_MAPPING
+++ b/libs/binder/TEST_MAPPING
@@ -67,15 +67,7 @@
"name": "fuzz_service_test"
},
{
- "name": "CtsOsTestCases",
- "options": [
- {
- "include-filter": "android.os.cts.BinderTest"
- },
- {
- "include-filter": "android.os.cts.ParcelTest"
- }
- ]
+ "name": "CtsOsTestCases_ParcelAndBinderTests"
},
{
"name": "libbinder_rs-internal_test"
diff --git a/libs/binder/include/binder/Parcel.h b/libs/binder/include/binder/Parcel.h
index ceab20a..0c7366e 100644
--- a/libs/binder/include/binder/Parcel.h
+++ b/libs/binder/include/binder/Parcel.h
@@ -178,7 +178,8 @@
LIBBINDER_EXPORTED status_t writeUint64(uint64_t val);
LIBBINDER_EXPORTED status_t writeFloat(float val);
LIBBINDER_EXPORTED status_t writeDouble(double val);
- LIBBINDER_EXPORTED status_t writeCString(const char* str);
+ LIBBINDER_EXPORTED status_t writeCString(const char* str)
+ __attribute__((deprecated("use AIDL, writeString* instead")));
LIBBINDER_EXPORTED status_t writeString8(const String8& str);
LIBBINDER_EXPORTED status_t writeString8(const char* str, size_t len);
LIBBINDER_EXPORTED status_t writeString16(const String16& str);
@@ -434,7 +435,8 @@
LIBBINDER_EXPORTED status_t readUtf8FromUtf16(std::unique_ptr<std::string>* str) const
__attribute__((deprecated("use std::optional version instead")));
- LIBBINDER_EXPORTED const char* readCString() const;
+ LIBBINDER_EXPORTED const char* readCString() const
+ __attribute__((deprecated("use AIDL, use readString*")));
LIBBINDER_EXPORTED String8 readString8() const;
LIBBINDER_EXPORTED status_t readString8(String8* pArg) const;
LIBBINDER_EXPORTED const char* readString8Inplace(size_t* outLen) const;
diff --git a/libs/binder/include_rpc_unstable/binder_rpc_unstable.hpp b/libs/binder/include_rpc_unstable/binder_rpc_unstable.hpp
index 392ebb5..48c0ea6 100644
--- a/libs/binder/include_rpc_unstable/binder_rpc_unstable.hpp
+++ b/libs/binder/include_rpc_unstable/binder_rpc_unstable.hpp
@@ -37,8 +37,12 @@
// Set `cid` to VMADDR_CID_LOCAL to only bind to the local vsock interface.
// Returns an opaque handle to the running server instance, or null if the server
// could not be started.
+// Set |port| to VMADDR_PORT_ANY to pick an available ephemeral port.
+// |assignedPort| will be set to the assigned port number if it is not null.
+// This will be the provided |port|, or the chosen available ephemeral port when
+// |port| is VMADDR_PORT_ANY.
[[nodiscard]] ARpcServer* ARpcServer_newVsock(AIBinder* service, unsigned int cid,
- unsigned int port);
+ unsigned int port, unsigned int* assignedPort);
// Starts a Unix domain RPC server with an open raw socket file descriptor
// and a given root IBinder object.
diff --git a/libs/binder/libbinder_rpc_unstable.cpp b/libs/binder/libbinder_rpc_unstable.cpp
index 21537fc..a84a0c6 100644
--- a/libs/binder/libbinder_rpc_unstable.cpp
+++ b/libs/binder/libbinder_rpc_unstable.cpp
@@ -81,7 +81,8 @@
extern "C" {
#ifndef __TRUSTY__
-ARpcServer* ARpcServer_newVsock(AIBinder* service, unsigned int cid, unsigned int port) {
+ARpcServer* ARpcServer_newVsock(AIBinder* service, unsigned int cid, unsigned int port,
+ unsigned int* assignedPort) {
auto server = RpcServer::make();
unsigned int bindCid = VMADDR_CID_ANY; // bind to the remote interface
@@ -90,7 +91,7 @@
cid = VMADDR_CID_ANY; // no need for a connection filter
}
- if (status_t status = server->setupVsockServer(bindCid, port); status != OK) {
+ if (status_t status = server->setupVsockServer(bindCid, port, assignedPort); status != OK) {
ALOGE("Failed to set up vsock server with port %u error: %s", port,
statusToString(status).c_str());
return nullptr;
diff --git a/libs/binder/ndk/Android.bp b/libs/binder/ndk/Android.bp
index a7423b3..5710bbf 100644
--- a/libs/binder/ndk/Android.bp
+++ b/libs/binder/ndk/Android.bp
@@ -82,7 +82,6 @@
llndk: {
symbol_file: "libbinder_ndk.map.txt",
- export_llndk_headers: ["libvendorsupport_llndk_headers"],
},
cflags: [
@@ -110,11 +109,9 @@
],
header_libs: [
- "libvendorsupport_llndk_headers",
"jni_headers",
],
export_header_lib_headers: [
- "libvendorsupport_llndk_headers",
"jni_headers",
],
diff --git a/libs/binder/ndk/binder_rpc.cpp b/libs/binder/ndk/binder_rpc.cpp
index 886eb4b..bb5989d 100644
--- a/libs/binder/ndk/binder_rpc.cpp
+++ b/libs/binder/ndk/binder_rpc.cpp
@@ -104,19 +104,23 @@
};
ABinderRpc_AccessorProvider* ABinderRpc_registerAccessorProvider(
- ABinderRpc_AccessorProvider_getAccessorCallback provider, const char** instances,
- size_t numInstances, void* data,
+ ABinderRpc_AccessorProvider_getAccessorCallback provider,
+ const char* const* const instances, size_t numInstances, void* data,
ABinderRpc_AccessorProviderUserData_deleteCallback onDelete) {
- if (provider == nullptr) {
- ALOGE("Null provider passed to ABinderRpc_registerAccessorProvider");
- return nullptr;
- }
if (data && onDelete == nullptr) {
ALOGE("If a non-null data ptr is passed to ABinderRpc_registerAccessorProvider, then a "
"ABinderRpc_AccessorProviderUserData_deleteCallback must also be passed to delete "
"the data object once the ABinderRpc_AccessorProvider is removed.");
return nullptr;
}
+ // call the onDelete when the last reference of this goes away (when the
+ // last reference to the generate std::function goes away).
+ std::shared_ptr<OnDeleteProviderHolder> onDeleteHolder =
+ std::make_shared<OnDeleteProviderHolder>(data, onDelete);
+ if (provider == nullptr) {
+ ALOGE("Null provider passed to ABinderRpc_registerAccessorProvider");
+ return nullptr;
+ }
if (numInstances == 0 || instances == nullptr) {
ALOGE("No instances passed to ABinderRpc_registerAccessorProvider. numInstances: %zu",
numInstances);
@@ -126,10 +130,6 @@
for (size_t i = 0; i < numInstances; i++) {
instanceStrings.emplace(instances[i]);
}
- // call the onDelete when the last reference of this goes away (when the
- // last reference to the generate std::function goes away).
- std::shared_ptr<OnDeleteProviderHolder> onDeleteHolder =
- std::make_shared<OnDeleteProviderHolder>(data, onDelete);
android::RpcAccessorProvider generate = [provider,
onDeleteHolder](const String16& name) -> sp<IBinder> {
ABinderRpc_Accessor* accessor = provider(String8(name).c_str(), onDeleteHolder->mData);
diff --git a/libs/binder/ndk/include_cpp/android/binder_interface_utils.h b/libs/binder/ndk/include_cpp/android/binder_interface_utils.h
index 0ad110e..c6518d8 100644
--- a/libs/binder/ndk/include_cpp/android/binder_interface_utils.h
+++ b/libs/binder/ndk/include_cpp/android/binder_interface_utils.h
@@ -30,16 +30,14 @@
#include <android/binder_auto_utils.h>
#include <android/binder_ibinder.h>
-#if defined(__ANDROID_VENDOR_API__)
-#include <android/llndk-versioning.h>
-#elif !defined(API_LEVEL_AT_LEAST)
#if defined(__BIONIC__)
-#define API_LEVEL_AT_LEAST(sdk_api_level, vendor_api_level) \
- (__builtin_available(android sdk_api_level, *))
+#define API_LEVEL_AT_LEAST(sdk_api_level) __builtin_available(android sdk_api_level, *)
+#elif defined(TRUSTY_USERSPACE)
+// TODO(b/349936395): set to true for Trusty
+#define API_LEVEL_AT_LEAST(sdk_api_level) (false)
#else
-#define API_LEVEL_AT_LEAST(sdk_api_level, vendor_api_level) (true)
+#define API_LEVEL_AT_LEAST(sdk_api_level) (true)
#endif // __BIONIC__
-#endif // __ANDROID_VENDOR_API__
#if __has_include(<android/binder_shell.h>)
#include <android/binder_shell.h>
@@ -298,9 +296,8 @@
#endif
#if defined(__ANDROID_UNAVAILABLE_SYMBOLS_ARE_WEAK__) || __ANDROID_API__ >= 36
- if API_LEVEL_AT_LEAST (36, 202504) {
- if (codeToFunction != nullptr &&
- (&AIBinder_Class_setTransactionCodeToFunctionNameMap != nullptr)) {
+ if (API_LEVEL_AT_LEAST(36)) {
+ if (codeToFunction != nullptr) {
AIBinder_Class_setTransactionCodeToFunctionNameMap(clazz, codeToFunction,
functionCount);
}
diff --git a/libs/binder/ndk/include_cpp/android/persistable_bundle_aidl.h b/libs/binder/ndk/include_cpp/android/persistable_bundle_aidl.h
index 83976b3..f3f3c38 100644
--- a/libs/binder/ndk/include_cpp/android/persistable_bundle_aidl.h
+++ b/libs/binder/ndk/include_cpp/android/persistable_bundle_aidl.h
@@ -22,17 +22,14 @@
#include <set>
#include <sstream>
-// Include llndk-versioning.h only for non-system build as it is not available for NDK headers.
-#if defined(__ANDROID_VENDOR_API__)
-#include <android/llndk-versioning.h>
-#elif !defined(API_LEVEL_AT_LEAST)
#if defined(__BIONIC__)
-#define API_LEVEL_AT_LEAST(sdk_api_level, vendor_api_level) \
- (__builtin_available(android sdk_api_level, *))
+#define API_LEVEL_AT_LEAST(sdk_api_level) __builtin_available(android sdk_api_level, *)
+#elif defined(TRUSTY_USERSPACE)
+// TODO(b/349936395): set to true for Trusty
+#define API_LEVEL_AT_LEAST(sdk_api_level) (false)
#else
-#define API_LEVEL_AT_LEAST(sdk_api_level, vendor_api_level) (true)
+#define API_LEVEL_AT_LEAST(sdk_api_level) (true)
#endif // __BIONIC__
-#endif // __ANDROID_VENDOR_API__
namespace aidl::android::os {
@@ -44,7 +41,7 @@
class PersistableBundle {
public:
PersistableBundle() noexcept {
- if API_LEVEL_AT_LEAST(__ANDROID_API_V__, 202404) {
+ if (API_LEVEL_AT_LEAST(__ANDROID_API_V__)) {
mPBundle = APersistableBundle_new();
}
}
@@ -54,13 +51,13 @@
PersistableBundle(PersistableBundle&& other) noexcept : mPBundle(other.release()) {}
// duplicates, does not take ownership of the APersistableBundle*
PersistableBundle(const PersistableBundle& other) {
- if API_LEVEL_AT_LEAST(__ANDROID_API_V__, 202404) {
+ if (API_LEVEL_AT_LEAST(__ANDROID_API_V__)) {
mPBundle = APersistableBundle_dup(other.mPBundle);
}
}
// duplicates, does not take ownership of the APersistableBundle*
PersistableBundle& operator=(const PersistableBundle& other) {
- if API_LEVEL_AT_LEAST(__ANDROID_API_V__, 202404) {
+ if (API_LEVEL_AT_LEAST(__ANDROID_API_V__)) {
mPBundle = APersistableBundle_dup(other.mPBundle);
}
return *this;
@@ -70,7 +67,7 @@
binder_status_t readFromParcel(const AParcel* _Nonnull parcel) {
reset();
- if API_LEVEL_AT_LEAST(__ANDROID_API_V__, 202404) {
+ if (API_LEVEL_AT_LEAST(__ANDROID_API_V__)) {
return APersistableBundle_readFromParcel(parcel, &mPBundle);
} else {
return STATUS_INVALID_OPERATION;
@@ -81,7 +78,7 @@
if (!mPBundle) {
return STATUS_BAD_VALUE;
}
- if API_LEVEL_AT_LEAST(__ANDROID_API_V__, 202404) {
+ if (API_LEVEL_AT_LEAST(__ANDROID_API_V__)) {
return APersistableBundle_writeToParcel(mPBundle, parcel);
} else {
return STATUS_INVALID_OPERATION;
@@ -96,7 +93,7 @@
*/
void reset(APersistableBundle* _Nullable pBundle = nullptr) noexcept {
if (mPBundle) {
- if API_LEVEL_AT_LEAST(__ANDROID_API_V__, 202404) {
+ if (API_LEVEL_AT_LEAST(__ANDROID_API_V__)) {
APersistableBundle_delete(mPBundle);
}
mPBundle = nullptr;
@@ -109,7 +106,7 @@
* what should be used to check for equality.
*/
bool deepEquals(const PersistableBundle& rhs) const {
- if API_LEVEL_AT_LEAST(__ANDROID_API_V__, 202404) {
+ if (API_LEVEL_AT_LEAST(__ANDROID_API_V__)) {
return APersistableBundle_isEqual(get(), rhs.get());
} else {
return false;
@@ -148,7 +145,7 @@
inline std::string toString() const {
if (!mPBundle) {
return "<PersistableBundle: null>";
- } else if API_LEVEL_AT_LEAST(__ANDROID_API_V__, 202404) {
+ } else if (API_LEVEL_AT_LEAST(__ANDROID_API_V__)) {
std::ostringstream os;
os << "<PersistableBundle: ";
os << "size: " << std::to_string(APersistableBundle_size(mPBundle));
@@ -159,7 +156,7 @@
}
int32_t size() const {
- if API_LEVEL_AT_LEAST(__ANDROID_API_V__, 202404) {
+ if (API_LEVEL_AT_LEAST(__ANDROID_API_V__)) {
return APersistableBundle_size(mPBundle);
} else {
return 0;
@@ -167,7 +164,7 @@
}
int32_t erase(const std::string& key) {
- if API_LEVEL_AT_LEAST(__ANDROID_API_V__, 202404) {
+ if (API_LEVEL_AT_LEAST(__ANDROID_API_V__)) {
return APersistableBundle_erase(mPBundle, key.c_str());
} else {
return 0;
@@ -175,37 +172,37 @@
}
void putBoolean(const std::string& key, bool val) {
- if API_LEVEL_AT_LEAST(__ANDROID_API_V__, 202404) {
+ if (API_LEVEL_AT_LEAST(__ANDROID_API_V__)) {
APersistableBundle_putBoolean(mPBundle, key.c_str(), val);
}
}
void putInt(const std::string& key, int32_t val) {
- if API_LEVEL_AT_LEAST(__ANDROID_API_V__, 202404) {
+ if (API_LEVEL_AT_LEAST(__ANDROID_API_V__)) {
APersistableBundle_putInt(mPBundle, key.c_str(), val);
}
}
void putLong(const std::string& key, int64_t val) {
- if API_LEVEL_AT_LEAST(__ANDROID_API_V__, 202404) {
+ if (API_LEVEL_AT_LEAST(__ANDROID_API_V__)) {
APersistableBundle_putLong(mPBundle, key.c_str(), val);
}
}
void putDouble(const std::string& key, double val) {
- if API_LEVEL_AT_LEAST(__ANDROID_API_V__, 202404) {
+ if (API_LEVEL_AT_LEAST(__ANDROID_API_V__)) {
APersistableBundle_putDouble(mPBundle, key.c_str(), val);
}
}
void putString(const std::string& key, const std::string& val) {
- if API_LEVEL_AT_LEAST(__ANDROID_API_V__, 202404) {
+ if (API_LEVEL_AT_LEAST(__ANDROID_API_V__)) {
APersistableBundle_putString(mPBundle, key.c_str(), val.c_str());
}
}
void putBooleanVector(const std::string& key, const std::vector<bool>& vec) {
- if API_LEVEL_AT_LEAST(__ANDROID_API_V__, 202404) {
+ if (API_LEVEL_AT_LEAST(__ANDROID_API_V__)) {
// std::vector<bool> has no ::data().
int32_t num = vec.size();
if (num > 0) {
@@ -222,7 +219,7 @@
}
void putIntVector(const std::string& key, const std::vector<int32_t>& vec) {
- if API_LEVEL_AT_LEAST(__ANDROID_API_V__, 202404) {
+ if (API_LEVEL_AT_LEAST(__ANDROID_API_V__)) {
int32_t num = vec.size();
if (num > 0) {
APersistableBundle_putIntVector(mPBundle, key.c_str(), vec.data(), num);
@@ -230,7 +227,7 @@
}
}
void putLongVector(const std::string& key, const std::vector<int64_t>& vec) {
- if API_LEVEL_AT_LEAST(__ANDROID_API_V__, 202404) {
+ if (API_LEVEL_AT_LEAST(__ANDROID_API_V__)) {
int32_t num = vec.size();
if (num > 0) {
APersistableBundle_putLongVector(mPBundle, key.c_str(), vec.data(), num);
@@ -238,7 +235,7 @@
}
}
void putDoubleVector(const std::string& key, const std::vector<double>& vec) {
- if API_LEVEL_AT_LEAST(__ANDROID_API_V__, 202404) {
+ if (API_LEVEL_AT_LEAST(__ANDROID_API_V__)) {
int32_t num = vec.size();
if (num > 0) {
APersistableBundle_putDoubleVector(mPBundle, key.c_str(), vec.data(), num);
@@ -246,7 +243,7 @@
}
}
void putStringVector(const std::string& key, const std::vector<std::string>& vec) {
- if API_LEVEL_AT_LEAST(__ANDROID_API_V__, 202404) {
+ if (API_LEVEL_AT_LEAST(__ANDROID_API_V__)) {
int32_t num = vec.size();
if (num > 0) {
char** inVec = (char**)malloc(num * sizeof(char*));
@@ -261,13 +258,13 @@
}
}
void putPersistableBundle(const std::string& key, const PersistableBundle& pBundle) {
- if API_LEVEL_AT_LEAST(__ANDROID_API_V__, 202404) {
+ if (API_LEVEL_AT_LEAST(__ANDROID_API_V__)) {
APersistableBundle_putPersistableBundle(mPBundle, key.c_str(), pBundle.mPBundle);
}
}
bool getBoolean(const std::string& key, bool* _Nonnull val) const {
- if API_LEVEL_AT_LEAST(__ANDROID_API_V__, 202404) {
+ if (API_LEVEL_AT_LEAST(__ANDROID_API_V__)) {
return APersistableBundle_getBoolean(mPBundle, key.c_str(), val);
} else {
return false;
@@ -275,7 +272,7 @@
}
bool getInt(const std::string& key, int32_t* _Nonnull val) const {
- if API_LEVEL_AT_LEAST(__ANDROID_API_V__, 202404) {
+ if (API_LEVEL_AT_LEAST(__ANDROID_API_V__)) {
return APersistableBundle_getInt(mPBundle, key.c_str(), val);
} else {
return false;
@@ -283,7 +280,7 @@
}
bool getLong(const std::string& key, int64_t* _Nonnull val) const {
- if API_LEVEL_AT_LEAST(__ANDROID_API_V__, 202404) {
+ if (API_LEVEL_AT_LEAST(__ANDROID_API_V__)) {
return APersistableBundle_getLong(mPBundle, key.c_str(), val);
} else {
return false;
@@ -291,7 +288,7 @@
}
bool getDouble(const std::string& key, double* _Nonnull val) const {
- if API_LEVEL_AT_LEAST(__ANDROID_API_V__, 202404) {
+ if (API_LEVEL_AT_LEAST(__ANDROID_API_V__)) {
return APersistableBundle_getDouble(mPBundle, key.c_str(), val);
} else {
return false;
@@ -303,7 +300,7 @@
}
bool getString(const std::string& key, std::string* _Nonnull val) const {
- if API_LEVEL_AT_LEAST(__ANDROID_API_V__, 202404) {
+ if (API_LEVEL_AT_LEAST(__ANDROID_API_V__)) {
char* outString = nullptr;
bool ret = APersistableBundle_getString(mPBundle, key.c_str(), &outString,
&stringAllocator, nullptr);
@@ -321,7 +318,7 @@
const char* _Nonnull, T* _Nullable, int32_t),
const APersistableBundle* _Nonnull pBundle, const char* _Nonnull key,
std::vector<T>* _Nonnull vec) const {
- if API_LEVEL_AT_LEAST(__ANDROID_API_V__, 202404) {
+ if (API_LEVEL_AT_LEAST(__ANDROID_API_V__)) {
int32_t bytes = 0;
// call first with nullptr to get required size in bytes
bytes = getVec(pBundle, key, nullptr, 0);
@@ -343,28 +340,28 @@
}
bool getBooleanVector(const std::string& key, std::vector<bool>* _Nonnull vec) const {
- if API_LEVEL_AT_LEAST(__ANDROID_API_V__, 202404) {
+ if (API_LEVEL_AT_LEAST(__ANDROID_API_V__)) {
return getVecInternal<bool>(&APersistableBundle_getBooleanVector, mPBundle, key.c_str(),
vec);
}
return false;
}
bool getIntVector(const std::string& key, std::vector<int32_t>* _Nonnull vec) const {
- if API_LEVEL_AT_LEAST(__ANDROID_API_V__, 202404) {
+ if (API_LEVEL_AT_LEAST(__ANDROID_API_V__)) {
return getVecInternal<int32_t>(&APersistableBundle_getIntVector, mPBundle, key.c_str(),
vec);
}
return false;
}
bool getLongVector(const std::string& key, std::vector<int64_t>* _Nonnull vec) const {
- if API_LEVEL_AT_LEAST(__ANDROID_API_V__, 202404) {
+ if (API_LEVEL_AT_LEAST(__ANDROID_API_V__)) {
return getVecInternal<int64_t>(&APersistableBundle_getLongVector, mPBundle, key.c_str(),
vec);
}
return false;
}
bool getDoubleVector(const std::string& key, std::vector<double>* _Nonnull vec) const {
- if API_LEVEL_AT_LEAST(__ANDROID_API_V__, 202404) {
+ if (API_LEVEL_AT_LEAST(__ANDROID_API_V__)) {
return getVecInternal<double>(&APersistableBundle_getDoubleVector, mPBundle,
key.c_str(), vec);
}
@@ -389,7 +386,7 @@
}
bool getStringVector(const std::string& key, std::vector<std::string>* _Nonnull vec) const {
- if API_LEVEL_AT_LEAST(__ANDROID_API_V__, 202404) {
+ if (API_LEVEL_AT_LEAST(__ANDROID_API_V__)) {
int32_t bytes = APersistableBundle_getStringVector(mPBundle, key.c_str(), nullptr, 0,
&stringAllocator, nullptr);
if (bytes > 0) {
@@ -406,7 +403,7 @@
}
bool getPersistableBundle(const std::string& key, PersistableBundle* _Nonnull val) const {
- if API_LEVEL_AT_LEAST(__ANDROID_API_V__, 202404) {
+ if (API_LEVEL_AT_LEAST(__ANDROID_API_V__)) {
APersistableBundle* bundle = nullptr;
bool ret = APersistableBundle_getPersistableBundle(mPBundle, key.c_str(), &bundle);
if (ret) {
@@ -438,77 +435,77 @@
}
std::set<std::string> getBooleanKeys() const {
- if API_LEVEL_AT_LEAST(__ANDROID_API_V__, 202404) {
+ if (API_LEVEL_AT_LEAST(__ANDROID_API_V__)) {
return getKeys(&APersistableBundle_getBooleanKeys, mPBundle);
} else {
return {};
}
}
std::set<std::string> getIntKeys() const {
- if API_LEVEL_AT_LEAST(__ANDROID_API_V__, 202404) {
+ if (API_LEVEL_AT_LEAST(__ANDROID_API_V__)) {
return getKeys(&APersistableBundle_getIntKeys, mPBundle);
} else {
return {};
}
}
std::set<std::string> getLongKeys() const {
- if API_LEVEL_AT_LEAST(__ANDROID_API_V__, 202404) {
+ if (API_LEVEL_AT_LEAST(__ANDROID_API_V__)) {
return getKeys(&APersistableBundle_getLongKeys, mPBundle);
} else {
return {};
}
}
std::set<std::string> getDoubleKeys() const {
- if API_LEVEL_AT_LEAST(__ANDROID_API_V__, 202404) {
+ if (API_LEVEL_AT_LEAST(__ANDROID_API_V__)) {
return getKeys(&APersistableBundle_getDoubleKeys, mPBundle);
} else {
return {};
}
}
std::set<std::string> getStringKeys() const {
- if API_LEVEL_AT_LEAST(__ANDROID_API_V__, 202404) {
+ if (API_LEVEL_AT_LEAST(__ANDROID_API_V__)) {
return getKeys(&APersistableBundle_getStringKeys, mPBundle);
} else {
return {};
}
}
std::set<std::string> getBooleanVectorKeys() const {
- if API_LEVEL_AT_LEAST(__ANDROID_API_V__, 202404) {
+ if (API_LEVEL_AT_LEAST(__ANDROID_API_V__)) {
return getKeys(&APersistableBundle_getBooleanVectorKeys, mPBundle);
} else {
return {};
}
}
std::set<std::string> getIntVectorKeys() const {
- if API_LEVEL_AT_LEAST(__ANDROID_API_V__, 202404) {
+ if (API_LEVEL_AT_LEAST(__ANDROID_API_V__)) {
return getKeys(&APersistableBundle_getIntVectorKeys, mPBundle);
} else {
return {};
}
}
std::set<std::string> getLongVectorKeys() const {
- if API_LEVEL_AT_LEAST(__ANDROID_API_V__, 202404) {
+ if (API_LEVEL_AT_LEAST(__ANDROID_API_V__)) {
return getKeys(&APersistableBundle_getLongVectorKeys, mPBundle);
} else {
return {};
}
}
std::set<std::string> getDoubleVectorKeys() const {
- if API_LEVEL_AT_LEAST(__ANDROID_API_V__, 202404) {
+ if (API_LEVEL_AT_LEAST(__ANDROID_API_V__)) {
return getKeys(&APersistableBundle_getDoubleVectorKeys, mPBundle);
} else {
return {};
}
}
std::set<std::string> getStringVectorKeys() const {
- if API_LEVEL_AT_LEAST(__ANDROID_API_V__, 202404) {
+ if (API_LEVEL_AT_LEAST(__ANDROID_API_V__)) {
return getKeys(&APersistableBundle_getStringVectorKeys, mPBundle);
} else {
return {};
}
}
std::set<std::string> getPersistableBundleKeys() const {
- if API_LEVEL_AT_LEAST(__ANDROID_API_V__, 202404) {
+ if (API_LEVEL_AT_LEAST(__ANDROID_API_V__)) {
return getKeys(&APersistableBundle_getPersistableBundleKeys, mPBundle);
} else {
return {};
diff --git a/libs/binder/ndk/include_platform/android/binder_rpc.h b/libs/binder/ndk/include_platform/android/binder_rpc.h
index 66667d3..1318889 100644
--- a/libs/binder/ndk/include_platform/android/binder_rpc.h
+++ b/libs/binder/ndk/include_platform/android/binder_rpc.h
@@ -139,13 +139,16 @@
* registered. In the error case of duplicate instances, if data was
* provided with a ABinderRpc_AccessorProviderUserData_deleteCallback,
* the callback will be called to delete the data.
+ * If nullptr is returned, ABinderRpc_AccessorProviderUserData_deleteCallback
+ * will be called on data immediately.
* Otherwise returns a pointer to the ABinderRpc_AccessorProvider that
* can be used to remove with ABinderRpc_unregisterAccessorProvider.
*/
ABinderRpc_AccessorProvider* _Nullable ABinderRpc_registerAccessorProvider(
ABinderRpc_AccessorProvider_getAccessorCallback _Nonnull provider,
- const char* _Nullable* _Nonnull instances, size_t numInstances, void* _Nullable data,
- ABinderRpc_AccessorProviderUserData_deleteCallback _Nullable onDelete) __INTRODUCED_IN(36);
+ const char* _Nullable const* const _Nonnull instances, size_t numInstances,
+ void* _Nullable data, ABinderRpc_AccessorProviderUserData_deleteCallback _Nullable onDelete)
+ __INTRODUCED_IN(36);
/**
* Remove an ABinderRpc_AccessorProvider from libbinder. This will remove references
diff --git a/libs/binder/ndk/libbinder_ndk.map.txt b/libs/binder/ndk/libbinder_ndk.map.txt
index 4d691f8..a637165 100644
--- a/libs/binder/ndk/libbinder_ndk.map.txt
+++ b/libs/binder/ndk/libbinder_ndk.map.txt
@@ -164,96 +164,53 @@
LIBBINDER_NDK35 { # introduced=VanillaIceCream
global:
APersistableBundle_readFromParcel;
- APersistableBundle_readFromParcel; # llndk=202404
APersistableBundle_writeToParcel;
- APersistableBundle_writeToParcel; # llndk=202404
APersistableBundle_new;
- APersistableBundle_new; # llndk=202404
APersistableBundle_dup;
- APersistableBundle_dup; # llndk=202404
APersistableBundle_delete;
- APersistableBundle_delete; # llndk=202404
APersistableBundle_isEqual;
- APersistableBundle_isEqual; # llndk=202404
APersistableBundle_size;
- APersistableBundle_size; # llndk=202404
APersistableBundle_erase;
- APersistableBundle_erase; # llndk=202404
APersistableBundle_putBoolean;
- APersistableBundle_putBoolean; # llndk=202404
APersistableBundle_putInt;
- APersistableBundle_putInt; # llndk=202404
APersistableBundle_putLong;
- APersistableBundle_putLong; # llndk=202404
APersistableBundle_putDouble;
- APersistableBundle_putDouble; # llndk=202404
APersistableBundle_putString;
- APersistableBundle_putString; # llndk=202404
APersistableBundle_putBooleanVector;
- APersistableBundle_putBooleanVector; # llndk=202404
APersistableBundle_putIntVector;
- APersistableBundle_putIntVector; # llndk=202404
APersistableBundle_putLongVector;
- APersistableBundle_putLongVector; # llndk=202404
APersistableBundle_putDoubleVector;
- APersistableBundle_putDoubleVector; # llndk=202404
APersistableBundle_putStringVector;
- APersistableBundle_putStringVector; # llndk=202404
APersistableBundle_putPersistableBundle;
- APersistableBundle_putPersistableBundle; # llndk=202404
APersistableBundle_getBoolean;
- APersistableBundle_getBoolean; # llndk=202404
APersistableBundle_getInt;
- APersistableBundle_getInt; # llndk=202404
APersistableBundle_getLong;
- APersistableBundle_getLong; # llndk=202404
APersistableBundle_getDouble;
- APersistableBundle_getDouble; # llndk=202404
APersistableBundle_getString;
- APersistableBundle_getString; # llndk=202404
APersistableBundle_getBooleanVector;
- APersistableBundle_getBooleanVector; # llndk=202404
APersistableBundle_getIntVector;
- APersistableBundle_getIntVector; # llndk=202404
APersistableBundle_getLongVector;
- APersistableBundle_getLongVector; # llndk=202404
APersistableBundle_getDoubleVector;
- APersistableBundle_getDoubleVector; # llndk=202404
APersistableBundle_getStringVector;
- APersistableBundle_getStringVector; # llndk=202404
APersistableBundle_getPersistableBundle;
- APersistableBundle_getPersistableBundle; # llndk=202404
APersistableBundle_getBooleanKeys;
- APersistableBundle_getBooleanKeys; # llndk=202404
APersistableBundle_getIntKeys;
- APersistableBundle_getIntKeys; # llndk=202404
APersistableBundle_getLongKeys;
- APersistableBundle_getLongKeys; # llndk=202404
APersistableBundle_getDoubleKeys;
- APersistableBundle_getDoubleKeys; # llndk=202404
APersistableBundle_getStringKeys;
- APersistableBundle_getStringKeys; # llndk=202404
APersistableBundle_getBooleanVectorKeys;
- APersistableBundle_getBooleanVectorKeys; # llndk=202404
APersistableBundle_getIntVectorKeys;
- APersistableBundle_getIntVectorKeys; # llndk=202404
APersistableBundle_getLongVectorKeys;
- APersistableBundle_getLongVectorKeys; # llndk=202404
APersistableBundle_getDoubleVectorKeys;
- APersistableBundle_getDoubleVectorKeys; # llndk=202404
APersistableBundle_getStringVectorKeys;
- APersistableBundle_getStringVectorKeys; # llndk=202404
APersistableBundle_getPersistableBundleKeys;
- APersistableBundle_getPersistableBundleKeys; # llndk=202404
- AServiceManager_openDeclaredPassthroughHal; # systemapi llndk=202404
+ AServiceManager_openDeclaredPassthroughHal; # systemapi llndk
};
LIBBINDER_NDK36 { # introduced=36
global:
AIBinder_Class_setTransactionCodeToFunctionNameMap;
- AIBinder_Class_setTransactionCodeToFunctionNameMap; # llndk=202504
AIBinder_Class_getFunctionName;
- AIBinder_Class_getFunctionName; # llndk=202504
ABinderRpc_registerAccessorProvider; # systemapi
ABinderRpc_unregisterAccessorProvider; # systemapi
ABinderRpc_Accessor_new; # systemapi
diff --git a/libs/binder/rust/Android.bp b/libs/binder/rust/Android.bp
index 020ebcc..8404a48 100644
--- a/libs/binder/rust/Android.bp
+++ b/libs/binder/rust/Android.bp
@@ -139,6 +139,9 @@
"--raw-line",
"use libc::sockaddr;",
],
+ cflags: [
+ "-DANDROID_PLATFORM",
+ ],
shared_libs: [
"libbinder_ndk",
],
@@ -179,6 +182,9 @@
// rustified
"libbinder_ndk_bindgen_flags.txt",
],
+ cflags: [
+ "-DANDROID_PLATFORM",
+ ],
shared_libs: [
"libbinder_ndk_on_trusty_mock",
"libc++",
diff --git a/libs/binder/rust/Cargo.toml b/libs/binder/rust/Cargo.toml
new file mode 100644
index 0000000..e5738c5
--- /dev/null
+++ b/libs/binder/rust/Cargo.toml
@@ -0,0 +1,15 @@
+[package]
+name = "android-binder"
+version = "0.1.0"
+edition = "2021"
+description = "Safe bindings to Android Binder, restricted to the NDK"
+license = "Apache-2.0"
+
+[dependencies]
+binder-ndk-sys = { package = "android-binder-ndk-sys", version = "0.1", path = "./sys" }
+downcast-rs = "1.2.1"
+libc = "0.2.159"
+
+[lints.rust.unexpected_cfgs]
+level = "warn"
+check-cfg = ["cfg(android_vendor)", "cfg(android_ndk)", "cfg(android_vndk)", "cfg(trusty)"]
diff --git a/libs/binder/rust/build.rs b/libs/binder/rust/build.rs
new file mode 100644
index 0000000..f3e6b53
--- /dev/null
+++ b/libs/binder/rust/build.rs
@@ -0,0 +1,4 @@
+fn main() {
+ // Anything with cargo is NDK only. If you want to access anything else, use Soong.
+ println!("cargo::rustc-cfg=android_ndk");
+}
diff --git a/libs/binder/rust/rpcbinder/Android.bp b/libs/binder/rust/rpcbinder/Android.bp
index 174fe8a..4036551 100644
--- a/libs/binder/rust/rpcbinder/Android.bp
+++ b/libs/binder/rust/rpcbinder/Android.bp
@@ -27,7 +27,6 @@
visibility: [
"//device/google/cuttlefish/shared/minidroid/sample",
"//packages/modules/Virtualization:__subpackages__",
- "//system/software_defined_vehicle:__subpackages__",
],
apex_available: [
"//apex_available:platform",
diff --git a/libs/binder/rust/rpcbinder/src/server/android.rs b/libs/binder/rust/rpcbinder/src/server/android.rs
index 2ab3447..74ce315 100644
--- a/libs/binder/rust/rpcbinder/src/server/android.rs
+++ b/libs/binder/rust/rpcbinder/src/server/android.rs
@@ -18,7 +18,7 @@
use binder::{unstable_api::AsNative, SpIBinder};
use binder_rpc_unstable_bindgen::ARpcServer;
use foreign_types::{foreign_type, ForeignType, ForeignTypeRef};
-use std::ffi::CString;
+use std::ffi::{c_uint, CString};
use std::io::{Error, ErrorKind};
use std::os::unix::io::{IntoRawFd, OwnedFd};
@@ -42,18 +42,29 @@
/// Creates a binder RPC server, serving the supplied binder service implementation on the given
/// vsock port. Only connections from the given CID are accepted.
///
- // Set `cid` to libc::VMADDR_CID_ANY to accept connections from any client.
- // Set `cid` to libc::VMADDR_CID_LOCAL to only bind to the local vsock interface.
- pub fn new_vsock(mut service: SpIBinder, cid: u32, port: u32) -> Result<RpcServer, Error> {
+ /// Set `cid` to [`libc::VMADDR_CID_ANY`] to accept connections from any client.
+ /// Set `cid` to [`libc::VMADDR_CID_LOCAL`] to only bind to the local vsock interface.
+ /// Set `port` to [`libc::VMADDR_PORT_ANY`] to pick an ephemeral port.
+ /// The assigned port is returned with RpcServer.
+ pub fn new_vsock(
+ mut service: SpIBinder,
+ cid: u32,
+ port: u32,
+ ) -> Result<(RpcServer, u32 /* assigned_port */), Error> {
let service = service.as_native_mut();
+ let mut assigned_port: c_uint = 0;
// SAFETY: Service ownership is transferring to the server and won't be valid afterward.
// Plus the binder objects are threadsafe.
- unsafe {
+ let server = unsafe {
Self::checked_from_ptr(binder_rpc_unstable_bindgen::ARpcServer_newVsock(
- service, cid, port,
- ))
- }
+ service,
+ cid,
+ port,
+ &mut assigned_port,
+ ))?
+ };
+ Ok((server, assigned_port as _))
}
/// Creates a binder RPC server, serving the supplied binder service implementation on the given
diff --git a/libs/binder/rust/src/binder.rs b/libs/binder/rust/src/binder.rs
index 23026e5..8c0501b 100644
--- a/libs/binder/rust/src/binder.rs
+++ b/libs/binder/rust/src/binder.rs
@@ -207,8 +207,10 @@
/// Corresponds to TF_ONE_WAY -- an asynchronous call.
pub const FLAG_ONEWAY: TransactionFlags = sys::FLAG_ONEWAY;
/// Corresponds to TF_CLEAR_BUF -- clear transaction buffers after call is made.
+#[cfg(not(android_ndk))]
pub const FLAG_CLEAR_BUF: TransactionFlags = sys::FLAG_CLEAR_BUF;
/// Set to the vendor flag if we are building for the VNDK, 0 otherwise
+#[cfg(not(android_ndk))]
pub const FLAG_PRIVATE_LOCAL: TransactionFlags = sys::FLAG_PRIVATE_LOCAL;
/// Internal interface of binder local or remote objects for making
@@ -221,7 +223,7 @@
fn is_binder_alive(&self) -> bool;
/// Indicate that the service intends to receive caller security contexts.
- #[cfg(not(android_vndk))]
+ #[cfg(not(any(android_vndk, android_ndk)))]
fn set_requesting_sid(&mut self, enable: bool);
/// Dump this object to the given file handle
@@ -346,7 +348,6 @@
panic!("Expected non-null class pointer from AIBinder_Class_define!");
}
sys::AIBinder_Class_setOnDump(class, Some(I::on_dump));
- sys::AIBinder_Class_setHandleShellCommand(class, None);
class
};
InterfaceClass(ptr)
@@ -714,7 +715,7 @@
pub struct BinderFeatures {
/// Indicates that the service intends to receive caller security contexts. This must be true
/// for `ThreadState::with_calling_sid` to work.
- #[cfg(not(android_vndk))]
+ #[cfg(not(any(android_vndk, android_ndk)))]
pub set_requesting_sid: bool,
// Ensure that clients include a ..BinderFeatures::default() to preserve backwards compatibility
// when new fields are added. #[non_exhaustive] doesn't work because it prevents struct
@@ -916,8 +917,12 @@
impl $native {
/// Create a new binder service.
pub fn new_binder<T: $interface + Sync + Send + 'static>(inner: T, features: $crate::BinderFeatures) -> $crate::Strong<dyn $interface> {
+ #[cfg(not(android_ndk))]
let mut binder = $crate::binder_impl::Binder::new_with_stability($native(Box::new(inner)), $stability);
- #[cfg(not(android_vndk))]
+ #[cfg(android_ndk)]
+ let mut binder = $crate::binder_impl::Binder::new($native(Box::new(inner)));
+
+ #[cfg(not(any(android_vndk, android_ndk)))]
$crate::binder_impl::IBinderInternal::set_requesting_sid(&mut binder, features.set_requesting_sid);
$crate::Strong::new(Box::new(binder))
}
diff --git a/libs/binder/rust/src/lib.rs b/libs/binder/rust/src/lib.rs
index f7f3f35..1b24b0a 100644
--- a/libs/binder/rust/src/lib.rs
+++ b/libs/binder/rust/src/lib.rs
@@ -100,11 +100,11 @@
mod native;
mod parcel;
mod proxy;
-#[cfg(not(trusty))]
+#[cfg(not(any(trusty, android_ndk)))]
mod service;
-#[cfg(not(trusty))]
+#[cfg(not(any(trusty, android_ndk)))]
mod state;
-#[cfg(not(any(android_vendor, android_vndk)))]
+#[cfg(not(any(android_vendor, android_ndk, android_vndk)))]
mod system_only;
use binder_ndk_sys as sys;
@@ -114,16 +114,19 @@
pub use error::{ExceptionCode, IntoBinderResult, Status, StatusCode};
pub use parcel::{ParcelFileDescriptor, Parcelable, ParcelableHolder};
pub use proxy::{DeathRecipient, SpIBinder, WpIBinder};
-#[cfg(not(trusty))]
+#[cfg(not(any(trusty, android_ndk)))]
pub use service::{
add_service, check_interface, check_service, force_lazy_services_persist,
- get_declared_instances, get_interface, get_service, is_declared, is_handling_transaction,
- register_lazy_service, wait_for_interface, wait_for_service, LazyServiceGuard,
+ get_declared_instances, is_declared, is_handling_transaction, register_lazy_service,
+ wait_for_interface, wait_for_service, LazyServiceGuard,
};
-#[cfg(not(trusty))]
+#[cfg(not(any(trusty, android_ndk)))]
+#[allow(deprecated)]
+pub use service::{get_interface, get_service};
+#[cfg(not(any(trusty, android_ndk)))]
pub use state::{ProcessState, ThreadState};
-#[cfg(not(any(android_vendor, android_vndk)))]
-pub use system_only::{delegate_accessor, Accessor, ConnectionInfo};
+#[cfg(not(any(android_vendor, android_vndk, android_ndk)))]
+pub use system_only::{delegate_accessor, Accessor, AccessorProvider, ConnectionInfo};
/// Binder result containing a [`Status`] on error.
pub type Result<T> = std::result::Result<T, Status>;
@@ -134,9 +137,10 @@
pub use crate::binder::{
IBinderInternal, InterfaceClass, LocalStabilityType, Remotable, Stability, StabilityType,
ToAsyncInterface, ToSyncInterface, TransactionCode, TransactionFlags, VintfStabilityType,
- FIRST_CALL_TRANSACTION, FLAG_CLEAR_BUF, FLAG_ONEWAY, FLAG_PRIVATE_LOCAL,
- LAST_CALL_TRANSACTION,
+ FIRST_CALL_TRANSACTION, FLAG_ONEWAY, LAST_CALL_TRANSACTION,
};
+ #[cfg(not(android_ndk))]
+ pub use crate::binder::{FLAG_CLEAR_BUF, FLAG_PRIVATE_LOCAL};
pub use crate::binder_async::BinderAsyncRuntime;
pub use crate::error::status_t;
pub use crate::native::Binder;
diff --git a/libs/binder/rust/src/native.rs b/libs/binder/rust/src/native.rs
index c87cc94..9e1cfd6 100644
--- a/libs/binder/rust/src/native.rs
+++ b/libs/binder/rust/src/native.rs
@@ -14,9 +14,9 @@
* limitations under the License.
*/
-use crate::binder::{
- AsNative, Interface, InterfaceClassMethods, Remotable, Stability, TransactionCode,
-};
+#[cfg(not(android_ndk))]
+use crate::binder::Stability;
+use crate::binder::{AsNative, Interface, InterfaceClassMethods, Remotable, TransactionCode};
use crate::error::{status_result, status_t, Result, StatusCode};
use crate::parcel::{BorrowedParcel, Serialize};
use crate::proxy::SpIBinder;
@@ -76,14 +76,32 @@
/// This moves the `rust_object` into an owned [`Box`] and Binder will
/// manage its lifetime.
pub fn new(rust_object: T) -> Binder<T> {
- Self::new_with_stability(rust_object, Stability::default())
+ #[cfg(not(android_ndk))]
+ {
+ Self::new_with_stability(rust_object, Stability::default())
+ }
+ #[cfg(android_ndk)]
+ {
+ Self::new_unmarked(rust_object)
+ }
}
/// Create a new Binder remotable object with the given stability
///
/// This moves the `rust_object` into an owned [`Box`] and Binder will
/// manage its lifetime.
+ #[cfg(not(android_ndk))]
pub fn new_with_stability(rust_object: T, stability: Stability) -> Binder<T> {
+ let mut binder = Self::new_unmarked(rust_object);
+ binder.mark_stability(stability);
+ binder
+ }
+
+ /// Creates a new Binder remotable object with unset stability
+ ///
+ /// This is internal because normally we want to set the stability explicitly,
+ /// however for the NDK variant we cannot mark the stability.
+ fn new_unmarked(rust_object: T) -> Binder<T> {
let class = T::get_class();
let rust_object = Box::into_raw(Box::new(rust_object));
// Safety: `AIBinder_new` expects a valid class pointer (which we
@@ -93,9 +111,7 @@
// decremented via `AIBinder_decStrong` when the reference lifetime
// ends.
let ibinder = unsafe { sys::AIBinder_new(class.into(), rust_object as *mut c_void) };
- let mut binder = Binder { ibinder, rust_object };
- binder.mark_stability(stability);
- binder
+ Binder { ibinder, rust_object }
}
/// Set the extension of a binder interface. This allows a downstream
@@ -189,6 +205,7 @@
}
/// Mark this binder object with the given stability guarantee
+ #[cfg(not(android_ndk))]
fn mark_stability(&mut self, stability: Stability) {
match stability {
Stability::Local => self.mark_local_stability(),
@@ -215,7 +232,7 @@
/// Mark this binder object with local stability, which is vendor if we are
/// building for android_vendor and system otherwise.
- #[cfg(not(android_vendor))]
+ #[cfg(not(any(android_vendor, android_ndk)))]
fn mark_local_stability(&mut self) {
// Safety: Self always contains a valid `AIBinder` pointer, so we can
// always call this C API safely.
diff --git a/libs/binder/rust/src/parcel.rs b/libs/binder/rust/src/parcel.rs
index 3bfc425..485b0bd 100644
--- a/libs/binder/rust/src/parcel.rs
+++ b/libs/binder/rust/src/parcel.rs
@@ -197,6 +197,7 @@
// Data serialization methods
impl<'a> BorrowedParcel<'a> {
/// Data written to parcelable is zero'd before being deleted or reallocated.
+ #[cfg(not(android_ndk))]
pub fn mark_sensitive(&mut self) {
// Safety: guaranteed to have a parcel object, and this method never fails
unsafe { sys::AParcel_markSensitive(self.as_native()) }
@@ -342,6 +343,7 @@
impl Parcel {
/// Data written to parcelable is zero'd before being deleted or reallocated.
+ #[cfg(not(android_ndk))]
pub fn mark_sensitive(&mut self) {
self.borrowed().mark_sensitive()
}
diff --git a/libs/binder/rust/src/proxy.rs b/libs/binder/rust/src/proxy.rs
index 04f1517..593d12c 100644
--- a/libs/binder/rust/src/proxy.rs
+++ b/libs/binder/rust/src/proxy.rs
@@ -298,7 +298,7 @@
unsafe { sys::AIBinder_isAlive(self.as_native()) }
}
- #[cfg(not(android_vndk))]
+ #[cfg(not(any(android_vndk, android_ndk)))]
fn set_requesting_sid(&mut self, enable: bool) {
// Safety: `SpIBinder` guarantees that `self` always contains a valid
// pointer to an `AIBinder`.
diff --git a/libs/binder/rust/src/service.rs b/libs/binder/rust/src/service.rs
index 29dd8e1..f4fdcf5 100644
--- a/libs/binder/rust/src/service.rs
+++ b/libs/binder/rust/src/service.rs
@@ -176,6 +176,7 @@
/// seconds if it doesn't yet exist.
#[deprecated = "this polls 5s, use wait_for_interface or check_interface"]
pub fn get_interface<T: FromIBinder + ?Sized>(name: &str) -> Result<Strong<T>> {
+ #[allow(deprecated)]
interface_cast(get_service(name))
}
diff --git a/libs/binder/rust/src/system_only.rs b/libs/binder/rust/src/system_only.rs
index 9833cbe..1a58d6b 100644
--- a/libs/binder/rust/src/system_only.rs
+++ b/libs/binder/rust/src/system_only.rs
@@ -91,6 +91,32 @@
Accessor { accessor }
}
+ /// Creates a new Accessor instance based on an existing Accessor's binder.
+ /// This is useful when the Accessor instance is hosted in another process
+ /// that has the permissions to create the socket connection FD.
+ ///
+ /// The `instance` argument must match the instance that the original Accessor
+ /// is responsible for.
+ /// `instance` must not contain null bytes and is used to create a CString to
+ /// pass through FFI.
+ /// The `binder` argument must be a valid binder from an Accessor
+ pub fn from_binder(instance: &str, binder: SpIBinder) -> Option<Accessor> {
+ let inst = CString::new(instance).unwrap();
+
+ // Safety: All `SpIBinder` objects (the `binder` argument) hold a valid pointer
+ // to an `AIBinder` that is guaranteed to remain valid for the lifetime of the
+ // SpIBinder. `ABinderRpc_Accessor_fromBinder` creates a new pointer to that binder
+ // that it is responsible for.
+ // The `inst` argument is a new CString that will copied by
+ // `ABinderRpc_Accessor_fromBinder` and not modified.
+ let accessor =
+ unsafe { sys::ABinderRpc_Accessor_fromBinder(inst.as_ptr(), binder.as_raw()) };
+ if accessor.is_null() {
+ return None;
+ }
+ Some(Accessor { accessor })
+ }
+
/// Get the underlying binder for this Accessor for when it needs to be either
/// registered with service manager or sent to another process.
pub fn as_binder(&self) -> Option<SpIBinder> {
@@ -100,17 +126,36 @@
unsafe { SpIBinder::from_raw(sys::ABinderRpc_Accessor_asBinder(self.accessor)) }
}
+ /// Release the underlying ABinderRpc_Accessor pointer for use with the ndk API
+ /// This gives up ownership of the ABinderRpc_Accessor and it is the responsibility of
+ /// the caller to delete it with ABinderRpc_Accessor_delete
+ ///
+ /// # Safety
+ ///
+ /// - The returned `ABinderRpc_Accessor` pointer is now owned by the caller, who must
+ /// call `ABinderRpc_Accessor_delete` to delete the object.
+ /// - This `Accessor` object is now useless after `release` so it can be dropped.
+ unsafe fn release(mut self) -> *mut sys::ABinderRpc_Accessor {
+ if self.accessor.is_null() {
+ log::error!("Attempting to release an Accessor that was already released");
+ return ptr::null_mut();
+ }
+ let ptr = self.accessor;
+ self.accessor = ptr::null_mut();
+ ptr
+ }
+
/// Callback invoked from C++ when the connection info is needed.
///
/// # Safety
///
- /// The `instance` parameter must be a non-null pointer to a valid C string for
- /// CStr::from_ptr. The memory must contain a valid null terminator at the end of
- /// the string within isize::MAX from the pointer. The memory must not be mutated for
- /// the duration of this function call and must be valid for reads from the pointer
- /// to the null terminator.
- /// The `cookie` parameter must be the cookie for an `Arc<F>` and
- /// the caller must hold a ref-count to it.
+ /// - The `instance` parameter must be a non-null pointer to a valid C string for
+ /// CStr::from_ptr. The memory must contain a valid null terminator at the end of
+ /// the string within isize::MAX from the pointer. The memory must not be mutated for
+ /// the duration of this function call and must be valid for reads from the pointer
+ /// to the null terminator.
+ /// - The `cookie` parameter must be the cookie for an `Arc<F>` and
+ /// the caller must hold a ref-count to it.
unsafe extern "C" fn connection_info<F>(
instance: *const c_char,
cookie: *mut c_void,
@@ -172,8 +217,8 @@
///
/// # Safety
///
- /// The `cookie` parameter must be the cookie for an `Arc<F>` and
- /// the owner must give up a ref-count to it.
+ /// - The `cookie` parameter must be the cookie for an `Arc<F>` and
+ /// the owner must give up a ref-count to it.
unsafe extern "C" fn cookie_decr_refcount<F>(cookie: *mut c_void)
where
F: Fn(&str) -> Option<ConnectionInfo> + Send + Sync + 'static,
@@ -185,6 +230,10 @@
impl Drop for Accessor {
fn drop(&mut self) {
+ if self.accessor.is_null() {
+ // This Accessor was already released.
+ return;
+ }
// Safety: `self.accessor` is always a valid, owned
// `ABinderRpc_Accessor` pointer returned by
// `ABinderRpc_Accessor_new` when `self` was created. This delete
@@ -218,3 +267,140 @@
// point, so can be safely passed to `SpIBinder::from_raw`.
Ok(unsafe { SpIBinder::from_raw(delegator).expect("Expected valid binder at this point") })
}
+
+/// Rust wrapper around ABinderRpc_AccessorProvider objects for RPC binder service management.
+///
+/// Dropping the `AccessorProvider` will drop/unregister the underlying object.
+#[derive(Debug)]
+pub struct AccessorProvider {
+ accessor_provider: *mut sys::ABinderRpc_AccessorProvider,
+}
+
+/// Safety: A `AccessorProvider` is a wrapper around `ABinderRpc_AccessorProvider` which is
+/// `Sync` and `Send`. As
+/// `ABinderRpc_AccessorProvider` is threadsafe, this structure is too.
+/// The Fn owned the AccessorProvider has `Sync` and `Send` properties
+unsafe impl Send for AccessorProvider {}
+
+/// Safety: A `AccessorProvider` is a wrapper around `ABinderRpc_AccessorProvider` which is
+/// `Sync` and `Send`. As `ABinderRpc_AccessorProvider` is threadsafe, this structure is too.
+/// The Fn owned the AccessorProvider has `Sync` and `Send` properties
+unsafe impl Sync for AccessorProvider {}
+
+impl AccessorProvider {
+ /// Create a new `AccessorProvider` that will give libbinder `Accessors` in order to
+ /// connect to binder services over sockets.
+ ///
+ /// `instances` is a list of all instances that this `AccessorProvider` is responsible for.
+ /// It is declaring these instances as available to this process and will return
+ /// `Accessor` objects for them when libbinder calls the `provider` callback.
+ /// `provider` is the callback that libbinder will call when a service is being requested.
+ /// The callback takes a `&str` argument representing the service that is being requested.
+ /// See the `ABinderRpc_AccessorProvider_getAccessorCallback` for the C++ equivalent.
+ pub fn new<F>(instances: &[String], provider: F) -> Option<AccessorProvider>
+ where
+ F: Fn(&str) -> Option<Accessor> + Send + Sync + 'static,
+ {
+ let callback: *mut c_void = Arc::into_raw(Arc::new(provider)) as *mut c_void;
+ let c_str_instances: Vec<CString> =
+ instances.iter().map(|s| CString::new(s.as_bytes()).unwrap()).collect();
+ let mut c_instances: Vec<*const c_char> =
+ c_str_instances.iter().map(|s| s.as_ptr()).collect();
+ let num_instances: usize = c_instances.len();
+ // Safety:
+ // - The function pointer for the first argument is a valid `get_accessor` callback.
+ // - This call returns an owned `ABinderRpc_AccessorProvider` pointer which
+ // must be destroyed via `ABinderRpc_unregisterAccessorProvider` when no longer
+ // needed.
+ // - When the underlying ABinderRpc_AccessorProvider is deleted, it will call
+ // the `cookie_decr_refcount` callback on the `callback` pointer to release its
+ // strong ref.
+ // - The `c_instances` vector is not modified by the function
+ let accessor_provider = unsafe {
+ sys::ABinderRpc_registerAccessorProvider(
+ Some(Self::get_accessor::<F>),
+ c_instances.as_mut_ptr(),
+ num_instances,
+ callback,
+ Some(Self::accessor_cookie_decr_refcount::<F>),
+ )
+ };
+
+ if accessor_provider.is_null() {
+ return None;
+ }
+ Some(AccessorProvider { accessor_provider })
+ }
+
+ /// Callback invoked from C++ when an Accessor is needed.
+ ///
+ /// # Safety
+ ///
+ /// - libbinder guarantees the `instance` argument is a valid C string if it's not null.
+ /// - The `cookie` pointer is same pointer that we pass to ABinderRpc_registerAccessorProvider
+ /// in AccessorProvider.new() which is the closure that we will delete with
+ /// self.accessor_cookie_decr_refcount when unregistering the AccessorProvider.
+ unsafe extern "C" fn get_accessor<F>(
+ instance: *const c_char,
+ cookie: *mut c_void,
+ ) -> *mut binder_ndk_sys::ABinderRpc_Accessor
+ where
+ F: Fn(&str) -> Option<Accessor> + Send + Sync + 'static,
+ {
+ if cookie.is_null() || instance.is_null() {
+ log::error!("Cookie({cookie:p}) or instance({instance:p}) is null!");
+ return ptr::null_mut();
+ }
+ // Safety: The caller promises that `cookie` is for an Arc<F>.
+ let callback = unsafe { (cookie as *const F).as_ref().unwrap() };
+
+ let inst = {
+ // Safety: The caller in libbinder_ndk will have already verified this is a valid
+ // C string
+ match unsafe { CStr::from_ptr(instance) }.to_str() {
+ Ok(s) => s,
+ Err(err) => {
+ log::error!("Failed to get a valid C string! {err:?}");
+ return ptr::null_mut();
+ }
+ }
+ };
+
+ match callback(inst) {
+ Some(a) => {
+ // Safety: This is giving up ownership of this ABinderRpc_Accessor
+ // to the caller of this function (libbinder) and it is responsible
+ // for deleting it.
+ unsafe { a.release() }
+ }
+ None => ptr::null_mut(),
+ }
+ }
+
+ /// Callback that decrements the ref-count.
+ /// This is invoked from C++ when the provider is unregistered.
+ ///
+ /// # Safety
+ ///
+ /// - The `cookie` parameter must be the cookie for an `Arc<F>` and
+ /// the owner must give up a ref-count to it.
+ unsafe extern "C" fn accessor_cookie_decr_refcount<F>(cookie: *mut c_void)
+ where
+ F: Fn(&str) -> Option<Accessor> + Send + Sync + 'static,
+ {
+ // Safety: The caller promises that `cookie` is for an Arc<F>.
+ unsafe { Arc::decrement_strong_count(cookie as *const F) };
+ }
+}
+
+impl Drop for AccessorProvider {
+ fn drop(&mut self) {
+ // Safety: `self.accessor_provider` is always a valid, owned
+ // `ABinderRpc_AccessorProvider` pointer returned by
+ // `ABinderRpc_registerAccessorProvider` when `self` was created. This delete
+ // method can only be called once when `self` is dropped.
+ unsafe {
+ sys::ABinderRpc_unregisterAccessorProvider(self.accessor_provider);
+ }
+ }
+}
diff --git a/libs/binder/rust/sys/BinderBindings.hpp b/libs/binder/rust/sys/BinderBindings.hpp
index bd666fe..557f0e8 100644
--- a/libs/binder/rust/sys/BinderBindings.hpp
+++ b/libs/binder/rust/sys/BinderBindings.hpp
@@ -15,15 +15,19 @@
*/
#include <android/binder_ibinder.h>
+#include <android/binder_parcel.h>
+#include <android/binder_status.h>
+
+/* Platform only */
+#if defined(ANDROID_PLATFORM) || defined(__ANDROID_VENDOR__)
#include <android/binder_ibinder_platform.h>
#include <android/binder_manager.h>
-#include <android/binder_parcel.h>
#include <android/binder_parcel_platform.h>
#include <android/binder_process.h>
#include <android/binder_rpc.h>
#include <android/binder_shell.h>
#include <android/binder_stability.h>
-#include <android/binder_status.h>
+#endif
namespace android {
@@ -81,8 +85,10 @@
enum {
FLAG_ONEWAY = FLAG_ONEWAY,
+#if defined(ANDROID_PLATFORM) || defined(__ANDROID_VENDOR__)
FLAG_CLEAR_BUF = FLAG_CLEAR_BUF,
FLAG_PRIVATE_LOCAL = FLAG_PRIVATE_LOCAL,
+#endif
};
} // namespace consts
diff --git a/libs/binder/rust/sys/Cargo.toml b/libs/binder/rust/sys/Cargo.toml
new file mode 100644
index 0000000..ad8e9c2
--- /dev/null
+++ b/libs/binder/rust/sys/Cargo.toml
@@ -0,0 +1,14 @@
+[package]
+name = "android-binder-ndk-sys"
+version = "0.1.0"
+edition = "2021"
+description = "Bindgen bindings to android binder, restricted to the NDK"
+license = "Apache-2.0"
+
+[dependencies]
+
+[lib]
+path = "lib.rs"
+
+[build-dependencies]
+bindgen = "0.70.1"
diff --git a/libs/binder/rust/sys/build.rs b/libs/binder/rust/sys/build.rs
new file mode 100644
index 0000000..cb9c65b
--- /dev/null
+++ b/libs/binder/rust/sys/build.rs
@@ -0,0 +1,59 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+use std::env;
+use std::path::PathBuf;
+
+fn main() {
+ let ndk_home = PathBuf::from(env::var("ANDROID_NDK_HOME").unwrap());
+ let toolchain = ndk_home.join("toolchains/llvm/prebuilt/linux-x86_64/");
+ let sysroot = toolchain.join("sysroot");
+ let bindings = bindgen::Builder::default()
+ .clang_arg(format!("--sysroot={}", sysroot.display()))
+ // TODO figure out what the "standard" #define is and use that instead
+ .header("BinderBindings.hpp")
+ .parse_callbacks(Box::new(bindgen::CargoCallbacks::new()))
+ // Keep in sync with libbinder_ndk_bindgen_flags.txt
+ .default_enum_style(bindgen::EnumVariation::Rust { non_exhaustive: true })
+ .constified_enum("android::c_interface::consts::.*")
+ .allowlist_type("android::c_interface::.*")
+ .allowlist_type("AStatus")
+ .allowlist_type("AIBinder_Class")
+ .allowlist_type("AIBinder")
+ .allowlist_type("AIBinder_Weak")
+ .allowlist_type("AIBinder_DeathRecipient")
+ .allowlist_type("AParcel")
+ .allowlist_type("binder_status_t")
+ .blocklist_function("vprintf")
+ .blocklist_function("strtold")
+ .blocklist_function("_vtlog")
+ .blocklist_function("vscanf")
+ .blocklist_function("vfprintf_worker")
+ .blocklist_function("vsprintf")
+ .blocklist_function("vsnprintf")
+ .blocklist_function("vsnprintf_filtered")
+ .blocklist_function("vfscanf")
+ .blocklist_function("vsscanf")
+ .blocklist_function("vdprintf")
+ .blocklist_function("vasprintf")
+ .blocklist_function("strtold_l")
+ .allowlist_function(".*")
+ .generate()
+ .expect("Couldn't generate bindings");
+ let out_path = PathBuf::from(env::var("OUT_DIR").unwrap());
+ bindings.write_to_file(out_path.join("bindings.rs")).expect("Couldn't write bindings.");
+ println!("cargo::rustc-link-lib=binder_ndk");
+}
diff --git a/libs/binder/rust/sys/lib.rs b/libs/binder/rust/sys/lib.rs
index 5352473..349e5a9 100644
--- a/libs/binder/rust/sys/lib.rs
+++ b/libs/binder/rust/sys/lib.rs
@@ -20,6 +20,7 @@
use std::fmt;
#[cfg(not(target_os = "trusty"))]
+#[allow(bad_style)]
mod bindings {
include!(concat!(env!("OUT_DIR"), "/bindings.rs"));
}
diff --git a/libs/binder/rust/tests/integration.rs b/libs/binder/rust/tests/integration.rs
index 489fa0a..da4f128 100644
--- a/libs/binder/rust/tests/integration.rs
+++ b/libs/binder/rust/tests/integration.rs
@@ -384,8 +384,8 @@
use std::time::Duration;
use binder::{
- Accessor, BinderFeatures, DeathRecipient, FromIBinder, IBinder, Interface, SpIBinder,
- StatusCode, Strong,
+ Accessor, AccessorProvider, BinderFeatures, DeathRecipient, FromIBinder, IBinder,
+ Interface, SpIBinder, StatusCode, Strong,
};
// Import from impl API for testing only, should not be necessary as long as
// you are using AIDL.
@@ -982,6 +982,90 @@
assert_eq!(delegator_binder, Err(StatusCode::NAME_NOT_FOUND));
}
+ #[test]
+ fn test_accessor_provider_simple() {
+ let instances: Vec<String> = vec!["foo.service".to_owned(), "foo.other_service".to_owned()];
+ let accessor = AccessorProvider::new(&instances, move |_inst: &str| None);
+ assert!(accessor.is_some());
+ }
+
+ #[test]
+ fn test_accessor_provider_no_instance() {
+ let instances: Vec<String> = vec![];
+ let accessor = AccessorProvider::new(&instances, move |_inst: &str| None);
+ assert!(accessor.is_none());
+ }
+
+ #[test]
+ fn test_accessor_provider_double_register() {
+ let instances: Vec<String> = vec!["foo.service".to_owned(), "foo.other_service".to_owned()];
+ let accessor = AccessorProvider::new(&instances, move |_inst: &str| None);
+ assert!(accessor.is_some());
+ let accessor2 = AccessorProvider::new(&instances, move |_inst: &str| None);
+ assert!(accessor2.is_none());
+ }
+
+ #[test]
+ fn test_accessor_provider_register_drop_register() {
+ let instances: Vec<String> = vec!["foo.service".to_owned(), "foo.other_service".to_owned()];
+ {
+ let accessor = AccessorProvider::new(&instances, move |_inst: &str| None);
+ assert!(accessor.is_some());
+ // accessor drops and unregisters the provider
+ }
+ {
+ let accessor = AccessorProvider::new(&instances, move |_inst: &str| None);
+ assert!(accessor.is_some());
+ }
+ }
+
+ #[test]
+ fn test_accessor_provider_callback_destruction() {
+ let deleted: Arc<AtomicBool> = Arc::new(AtomicBool::new(false));
+ let instances: Vec<String> = vec!["foo.service".to_owned(), "foo.other_service".to_owned()];
+ {
+ let accessor: Option<AccessorProvider>;
+ {
+ let helper = ToBeDeleted { deleted: deleted.clone() };
+ accessor = AccessorProvider::new(&instances, move |_inst: &str| {
+ let _ = &helper;
+ None
+ });
+ }
+ assert!(accessor.is_some());
+ assert!(!deleted.load(Ordering::Relaxed));
+ }
+ assert!(deleted.load(Ordering::Relaxed));
+ }
+
+ #[test]
+ fn test_accessor_from_accessor_binder() {
+ let get_connection_info = move |_instance: &str| None;
+ let accessor = Accessor::new("foo.service", get_connection_info);
+ let accessor2 =
+ Accessor::from_binder("foo.service", accessor.as_binder().unwrap()).unwrap();
+ assert_eq!(accessor.as_binder(), accessor2.as_binder());
+ }
+
+ #[test]
+ fn test_accessor_from_non_accessor_binder() {
+ let service_name = "rust_test_ibinder";
+ let _process = ScopedServiceProcess::new(service_name);
+ let binder = binder::get_service(service_name).unwrap();
+ assert!(binder.is_binder_alive());
+
+ let accessor = Accessor::from_binder("rust_test_ibinder", binder);
+ assert!(accessor.is_none());
+ }
+
+ #[test]
+ fn test_accessor_from_wrong_accessor_binder() {
+ let get_connection_info = move |_instance: &str| None;
+ let accessor = Accessor::new("foo.service", get_connection_info);
+ let accessor2 = Accessor::from_binder("NOT.foo.service", accessor.as_binder().unwrap());
+ assert!(accessor2.is_none());
+ }
+
#[tokio::test]
async fn reassociate_rust_binder_async() {
let service_name = "testing_service";
diff --git a/libs/binder/tests/binderParcelUnitTest.cpp b/libs/binder/tests/binderParcelUnitTest.cpp
index 32a70e5..6259d9d 100644
--- a/libs/binder/tests/binderParcelUnitTest.cpp
+++ b/libs/binder/tests/binderParcelUnitTest.cpp
@@ -33,6 +33,38 @@
using android::binder::Status;
using android::binder::unique_fd;
+static void checkCString(const char* str) {
+ for (size_t i = 0; i < 3; i++) {
+ Parcel p;
+
+ for (size_t j = 0; j < i; j++) p.writeInt32(3);
+
+ p.writeCString(str);
+ int32_t pos = p.dataPosition();
+
+ p.setDataPosition(0);
+
+ for (size_t j = 0; j < i; j++) p.readInt32();
+ const char* str2 = p.readCString();
+
+ ASSERT_EQ(std::string(str), str2);
+ ASSERT_EQ(pos, p.dataPosition());
+ }
+}
+
+TEST(Parcel, TestReadCString) {
+ // we should remove the *CString APIs, but testing them until
+ // they are deleted.
+ checkCString("");
+ checkCString("a");
+ checkCString("\n");
+ checkCString("32");
+ checkCString("321");
+ checkCString("3210");
+ checkCString("3210b");
+ checkCString("123434");
+}
+
TEST(Parcel, NonNullTerminatedString8) {
String8 kTestString = String8("test-is-good");
diff --git a/libs/binder/tests/binderRpcTest.cpp b/libs/binder/tests/binderRpcTest.cpp
index 506fc71..da5a8e3 100644
--- a/libs/binder/tests/binderRpcTest.cpp
+++ b/libs/binder/tests/binderRpcTest.cpp
@@ -1400,6 +1400,26 @@
EXPECT_TRUE(isDeleted);
}
+TEST_F(BinderARpcNdk, ARpcProviderDeleteOnError) {
+ bool isDeleted = false;
+ AccessorProviderData* data = new AccessorProviderData{{}, 0, &isDeleted};
+
+ ABinderRpc_AccessorProvider* provider =
+ ABinderRpc_registerAccessorProvider(getAccessor, kARpcSupportedServices, 0, data,
+ accessorProviderDataOnDelete);
+
+ ASSERT_EQ(provider, nullptr);
+ EXPECT_TRUE(isDeleted);
+}
+
+TEST_F(BinderARpcNdk, ARpcProvideOnErrorNoDeleteCbNoCrash) {
+ ABinderRpc_AccessorProvider* provider =
+ ABinderRpc_registerAccessorProvider(getAccessor, kARpcSupportedServices, 0, nullptr,
+ nullptr);
+
+ ASSERT_EQ(provider, nullptr);
+}
+
TEST_F(BinderARpcNdk, ARpcProviderDuplicateInstance) {
const char* instance = "some.instance.name.IFoo/default";
const uint32_t numInstances = 2;
diff --git a/libs/binder/tests/binderUtilsHostTest.cpp b/libs/binder/tests/binderUtilsHostTest.cpp
index 6301c74..a62ad96 100644
--- a/libs/binder/tests/binderUtilsHostTest.cpp
+++ b/libs/binder/tests/binderUtilsHostTest.cpp
@@ -56,7 +56,7 @@
});
auto elapsedMs = millisSince(start);
EXPECT_GE(elapsedMs, 1000);
- EXPECT_LT(elapsedMs, 2000);
+ EXPECT_LT(elapsedMs, 3000); // b/377571547: higher to reduce flake
ASSERT_TRUE(result.has_value());
EXPECT_EQ(std::nullopt, result->exitCode);
@@ -65,7 +65,7 @@
// ~CommandResult() called, child process is killed.
// Assert that the second sleep does not finish.
- EXPECT_LT(millisSince(start), 2000);
+ EXPECT_LT(millisSince(start), 3000);
}
TEST(UtilsHost, ExecuteLongRunning2) {
diff --git a/libs/binder/tests/parcel_fuzzer/test_fuzzer/Android.bp b/libs/binder/tests/parcel_fuzzer/test_fuzzer/Android.bp
index 690c39a..4008c56 100644
--- a/libs/binder/tests/parcel_fuzzer/test_fuzzer/Android.bp
+++ b/libs/binder/tests/parcel_fuzzer/test_fuzzer/Android.bp
@@ -59,6 +59,11 @@
darwin: {
enabled: false,
},
+ host: {
+ data_libs: [
+ "libc++",
+ ],
+ },
},
test_suites: ["general-tests"],
}
diff --git a/libs/binder/tests/parcel_fuzzer/test_fuzzer/run_fuzz_service_test.sh b/libs/binder/tests/parcel_fuzzer/test_fuzzer/run_fuzz_service_test.sh
index 5d68fe1..b623c5f 100755
--- a/libs/binder/tests/parcel_fuzzer/test_fuzzer/run_fuzz_service_test.sh
+++ b/libs/binder/tests/parcel_fuzzer/test_fuzzer/run_fuzz_service_test.sh
@@ -39,6 +39,7 @@
else
echo -e "${color_failed}Failed: Unable to find successful fuzzing output from test_service_fuzzer_should_crash"
echo "${color_reset}"
+ cat "$FUZZER_OUT"
exit 1
fi
done
diff --git a/libs/binder/trusty/RpcServerTrusty.cpp b/libs/binder/trusty/RpcServerTrusty.cpp
index 17919c2..0580046 100644
--- a/libs/binder/trusty/RpcServerTrusty.cpp
+++ b/libs/binder/trusty/RpcServerTrusty.cpp
@@ -151,8 +151,10 @@
int RpcServerTrusty::handleMessageInternal(void* ctx) {
auto* channelContext = reinterpret_cast<ChannelContext*>(ctx);
- LOG_ALWAYS_FATAL_IF(channelContext == nullptr,
- "bad state: message received on uninitialized channel");
+ if (channelContext == nullptr) {
+ LOG_RPC_DETAIL("bad state: message received on uninitialized channel");
+ return ERR_BAD_STATE;
+ }
auto& session = channelContext->session;
auto& connection = channelContext->connection;
diff --git a/libs/ftl/flags_test.cpp b/libs/ftl/flags_test.cpp
index 1279d11..bb43e8d 100644
--- a/libs/ftl/flags_test.cpp
+++ b/libs/ftl/flags_test.cpp
@@ -17,7 +17,7 @@
#include <ftl/flags.h>
#include <gtest/gtest.h>
-#include <type_traits>
+#include <initializer_list>
namespace android::test {
@@ -59,6 +59,18 @@
ASSERT_FALSE(flags.all(TestFlags::ONE | TestFlags::TWO | TestFlags::THREE));
}
+TEST(Flags, ImplicitConstructionAndAssignmentFromInitializerList) {
+ Flags<TestFlags> flags = {TestFlags::ONE, TestFlags::THREE};
+ ASSERT_TRUE(flags.test(TestFlags::ONE));
+ ASSERT_FALSE(flags.test(TestFlags::TWO));
+ ASSERT_TRUE(flags.test(TestFlags::THREE));
+
+ flags = {};
+ ASSERT_FALSE(flags.test(TestFlags::ONE));
+ ASSERT_FALSE(flags.test(TestFlags::TWO));
+ ASSERT_FALSE(flags.test(TestFlags::THREE));
+}
+
TEST(Flags, DefaultConstructor_hasNoFlagsSet) {
Flags<TestFlags> flags;
ASSERT_FALSE(flags.any(TestFlags::ONE | TestFlags::TWO | TestFlags::THREE));
diff --git a/libs/gralloc/types/Android.bp b/libs/gralloc/types/Android.bp
index 8dabc2c..f9f304a 100644
--- a/libs/gralloc/types/Android.bp
+++ b/libs/gralloc/types/Android.bp
@@ -56,7 +56,7 @@
],
export_shared_lib_headers: [
- "android.hardware.graphics.common-V5-ndk",
+ "android.hardware.graphics.common-V6-ndk",
"android.hardware.graphics.mapper@4.0",
"libhidlbase",
],
diff --git a/libs/graphicsenv/GraphicsEnv.cpp b/libs/graphicsenv/GraphicsEnv.cpp
index d1a5663..a8d5fe7 100644
--- a/libs/graphicsenv/GraphicsEnv.cpp
+++ b/libs/graphicsenv/GraphicsEnv.cpp
@@ -92,7 +92,7 @@
"android.hardware.common-V2-ndk.so:"
"android.hardware.common.fmq-V1-ndk.so:"
"android.hardware.graphics.allocator-V2-ndk.so:"
- "android.hardware.graphics.common-V5-ndk.so:"
+ "android.hardware.graphics.common-V6-ndk.so:"
"android.hardware.graphics.common@1.0.so:"
"android.hardware.graphics.common@1.1.so:"
"android.hardware.graphics.common@1.2.so:"
diff --git a/libs/gui/Android.bp b/libs/gui/Android.bp
index 1243b21..052b519 100644
--- a/libs/gui/Android.bp
+++ b/libs/gui/Android.bp
@@ -264,6 +264,7 @@
"DisplayEventDispatcher.cpp",
"DisplayEventReceiver.cpp",
"FenceMonitor.cpp",
+ "Flags.cpp",
"GLConsumer.cpp",
"IConsumerListener.cpp",
"IGraphicBufferConsumer.cpp",
@@ -274,6 +275,7 @@
"LayerMetadata.cpp",
"LayerStatePermissions.cpp",
"LayerState.cpp",
+ "DisplayLuts.cpp",
"OccupancyTracker.cpp",
"StreamSplitter.cpp",
"ScreenCaptureResults.cpp",
@@ -341,6 +343,10 @@
"libgui_aidl_headers",
],
+ static_libs: [
+ "libsurfaceflingerflags",
+ ],
+
afdo: true,
lto: {
diff --git a/libs/gui/BLASTBufferQueue.cpp b/libs/gui/BLASTBufferQueue.cpp
index 49f4cba..7aee903 100644
--- a/libs/gui/BLASTBufferQueue.cpp
+++ b/libs/gui/BLASTBufferQueue.cpp
@@ -52,19 +52,18 @@
namespace {
#if COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(BUFFER_RELEASE_CHANNEL)
-// RAII wrapper to defer arbitrary work until the Deferred instance is deleted.
-template <class F>
-class Deferred {
+template <class Mutex>
+class UnlockGuard {
public:
- explicit Deferred(F f) : mF{std::move(f)} {}
+ explicit UnlockGuard(Mutex& lock) : mLock{lock} { mLock.unlock(); }
- ~Deferred() { mF(); }
+ ~UnlockGuard() { mLock.lock(); }
- Deferred(const Deferred&) = delete;
- Deferred& operator=(const Deferred&) = delete;
+ UnlockGuard(const UnlockGuard&) = delete;
+ UnlockGuard& operator=(const UnlockGuard&) = delete;
private:
- F mF;
+ Mutex& mLock;
};
#endif
@@ -271,9 +270,6 @@
void BLASTBufferQueue::onFirstRef() {
// safe default, most producers are expected to override this
mProducer->setMaxDequeuedBufferCount(2);
-#if COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(BUFFER_RELEASE_CHANNEL)
- mBufferReleaseThread.emplace(sp<BLASTBufferQueue>::fromExisting(this));
-#endif
}
void BLASTBufferQueue::update(const sp<SurfaceControl>& surface, uint32_t width, uint32_t height,
@@ -290,18 +286,23 @@
if (surfaceControlChanged && mSurfaceControl != nullptr) {
BQA_LOGD("Updating SurfaceControl without recreating BBQ");
}
- bool applyTransaction = false;
// Always update the native object even though they might have the same layer handle, so we can
// get the updated transform hint from WM.
mSurfaceControl = surface;
SurfaceComposerClient::Transaction t;
+ bool applyTransaction = false;
if (surfaceControlChanged) {
+#if COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(BUFFER_RELEASE_CHANNEL)
+ updateBufferReleaseProducer();
+#endif
t.setFlags(mSurfaceControl, layer_state_t::eEnableBackpressure,
layer_state_t::eEnableBackpressure);
-#if COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(BUFFER_RELEASE_CHANNEL)
- t.setBufferReleaseChannel(mSurfaceControl, mBufferReleaseProducer);
-#endif
+ // Migrate the picture profile handle to the new surface control.
+ if (com_android_graphics_libgui_flags_apply_picture_profiles() &&
+ mPictureProfileHandle.has_value()) {
+ t.setPictureProfileHandle(mSurfaceControl, *mPictureProfileHandle);
+ }
applyTransaction = true;
}
mTransformHint = mSurfaceControl->getTransformHint();
@@ -325,7 +326,7 @@
}
if (applyTransaction) {
// All transactions on our apply token are one-way. See comment on mAppliedLastTransaction
- t.setApplyToken(mApplyToken).apply(false, true);
+ t.setApplyToken(mApplyToken).apply(false /* synchronous */, true /* oneWay */);
}
}
@@ -419,7 +420,6 @@
stat.latchTime,
stat.frameEventStats.dequeueReadyTime);
}
-#if !COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(BUFFER_RELEASE_CHANNEL)
auto currFrameNumber = stat.frameEventStats.frameNumber;
std::vector<ReleaseCallbackId> staleReleases;
for (const auto& [key, value]: mSubmitted) {
@@ -435,7 +435,6 @@
stat.currentMaxAcquiredBufferCount,
true /* fakeRelease */);
}
-#endif
} else {
BQA_LOGE("Failed to find matching SurfaceControl in transactionCallback");
}
@@ -469,6 +468,9 @@
return;
}
bbq->releaseBufferCallback(id, releaseFence, currentMaxAcquiredBufferCount);
+#if COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(BUFFER_RELEASE_CHANNEL)
+ bbq->drainBufferReleaseConsumer();
+#endif
};
}
@@ -535,8 +537,6 @@
const sp<Fence>& releaseFence) {
auto it = mSubmitted.find(callbackId);
if (it == mSubmitted.end()) {
- BQA_LOGE("ERROR: releaseBufferCallback without corresponding submitted buffer %s",
- callbackId.to_string().c_str());
return;
}
mNumAcquired--;
@@ -646,12 +646,7 @@
bufferItem.mGraphicBuffer->getHeight(), bufferItem.mTransform,
bufferItem.mScalingMode, crop);
-#if COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(BUFFER_RELEASE_CHANNEL)
- ReleaseBufferCallback releaseBufferCallback =
- applyTransaction ? nullptr : makeReleaseBufferCallbackThunk();
-#else
auto releaseBufferCallback = makeReleaseBufferCallbackThunk();
-#endif
sp<Fence> fence = bufferItem.mFence ? new Fence(bufferItem.mFence->dup()) : Fence::NO_FENCE;
nsecs_t dequeueTime = -1;
@@ -689,6 +684,17 @@
if (!bufferItem.mIsAutoTimestamp) {
t->setDesiredPresentTime(bufferItem.mTimestamp);
}
+ if (com_android_graphics_libgui_flags_apply_picture_profiles() &&
+ bufferItem.mPictureProfileHandle.has_value()) {
+ t->setPictureProfileHandle(mSurfaceControl, *bufferItem.mPictureProfileHandle);
+ // The current picture profile must be maintained in case the BBQ gets its
+ // SurfaceControl switched out.
+ mPictureProfileHandle = bufferItem.mPictureProfileHandle;
+ // Clear out the picture profile if the requestor has asked for it to be cleared
+ if (mPictureProfileHandle == PictureProfileHandle::NONE) {
+ mPictureProfileHandle = std::nullopt;
+ }
+ }
// Drop stale frame timeline infos
while (!mPendingFrameTimelines.empty() &&
@@ -1230,12 +1236,7 @@
// we want to ignore it. This must be done before unlocking the BufferQueue lock to ensure
// we don't miss an interrupt.
bbq->mBufferReleaseReader->clearInterrupts();
- bbq->mThreadsBlockingOnDequeue++;
- bufferQueueLock.unlock();
- Deferred cleanup{[&]() {
- bufferQueueLock.lock();
- bbq->mThreadsBlockingOnDequeue--;
- }};
+ UnlockGuard unlockGuard{bufferQueueLock};
ATRACE_FORMAT("waiting for free buffer");
ReleaseCallbackId id;
@@ -1345,6 +1346,35 @@
#if COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(BUFFER_RELEASE_CHANNEL)
+void BLASTBufferQueue::updateBufferReleaseProducer() {
+ // SELinux policy may prevent this process from sending the BufferReleaseChannel's file
+ // descriptor to SurfaceFlinger, causing the entire transaction to be dropped. We send this
+ // transaction independently of any other updates to ensure those updates aren't lost.
+ SurfaceComposerClient::Transaction t;
+ status_t status = t.setApplyToken(mApplyToken)
+ .setBufferReleaseChannel(mSurfaceControl, mBufferReleaseProducer)
+ .apply(false /* synchronous */, true /* oneWay */);
+ if (status != OK) {
+ ALOGW("[%s] %s - failed to set buffer release channel on %s", mName.c_str(),
+ statusToString(status).c_str(), mSurfaceControl->getName().c_str());
+ }
+}
+
+void BLASTBufferQueue::drainBufferReleaseConsumer() {
+ ATRACE_CALL();
+ while (true) {
+ ReleaseCallbackId id;
+ sp<Fence> fence;
+ uint32_t maxAcquiredBufferCount;
+ status_t status =
+ mBufferReleaseConsumer->readReleaseFence(id, fence, maxAcquiredBufferCount);
+ if (status != OK) {
+ return;
+ }
+ releaseBufferCallback(id, fence, maxAcquiredBufferCount);
+ }
+}
+
BLASTBufferQueue::BufferReleaseReader::BufferReleaseReader(BLASTBufferQueue& bbq) : mBbq{bbq} {
mEpollFd = android::base::unique_fd{epoll_create1(EPOLL_CLOEXEC)};
LOG_ALWAYS_FATAL_IF(!mEpollFd.ok(),
@@ -1438,95 +1468,6 @@
}
}
-BLASTBufferQueue::BufferReleaseThread::BufferReleaseThread(const sp<BLASTBufferQueue>& bbq) {
- android::base::unique_fd epollFd{epoll_create1(EPOLL_CLOEXEC)};
- LOG_ALWAYS_FATAL_IF(!epollFd.ok(),
- "Failed to create buffer release background thread epoll file descriptor. "
- "errno=%d message='%s'",
- errno, strerror(errno));
-
- epoll_event registerEndpointFd{};
- registerEndpointFd.events = EPOLLIN;
- registerEndpointFd.data.fd = bbq->mBufferReleaseConsumer->getFd();
- status_t status = epoll_ctl(epollFd.get(), EPOLL_CTL_ADD, bbq->mBufferReleaseConsumer->getFd(),
- ®isterEndpointFd);
- LOG_ALWAYS_FATAL_IF(status == -1,
- "Failed to register background thread buffer release consumer file "
- "descriptor with epoll. errno=%d message='%s'",
- errno, strerror(errno));
-
- // EventFd is used to break the background thread's loop.
- android::base::unique_fd eventFd{eventfd(0, EFD_CLOEXEC | EFD_NONBLOCK)};
- LOG_ALWAYS_FATAL_IF(!eventFd.ok(),
- "Failed to create background thread buffer release event file descriptor. "
- "errno=%d message='%s'",
- errno, strerror(errno));
-
- epoll_event registerEventFd{};
- registerEventFd.events = EPOLLIN;
- registerEventFd.data.fd = eventFd.get();
- status = epoll_ctl(epollFd.get(), EPOLL_CTL_ADD, eventFd.get(), ®isterEventFd);
- LOG_ALWAYS_FATAL_IF(status == -1,
- "Failed to register background thread event file descriptor with epoll. "
- "errno=%d message='%s'",
- errno, strerror(errno));
-
- mEventFd = eventFd.get();
-
- std::thread([epollFd = std::move(epollFd), eventFd = std::move(eventFd),
- weakBbq = wp<BLASTBufferQueue>(bbq)]() {
- pthread_setname_np(pthread_self(), "BufferReleaseThread");
- while (true) {
- epoll_event event{};
- int eventCount;
- do {
- eventCount = epoll_wait(epollFd.get(), &event, 1 /*maxevents*/, -1 /*timeout*/);
- } while (eventCount == -1 && errno != EINTR);
-
- if (eventCount == -1) {
- ALOGE("epoll_wait error while waiting for buffer release in background thread. "
- "errno=%d message='%s'",
- errno, strerror(errno));
- continue;
- }
-
- // EventFd is used to join this thread.
- if (event.data.fd == eventFd.get()) {
- return;
- }
-
- sp<BLASTBufferQueue> bbq = weakBbq.promote();
- if (!bbq) {
- return;
- }
-
- // If there are threads blocking on dequeue, give those threads priority for handling
- // the release.
- if (bbq->mThreadsBlockingOnDequeue > 0) {
- std::this_thread::sleep_for(0ms);
- continue;
- }
-
- ReleaseCallbackId id;
- sp<Fence> fence;
- uint32_t maxAcquiredBufferCount;
- status_t status = bbq->mBufferReleaseConsumer->readReleaseFence(id, fence,
- maxAcquiredBufferCount);
- if (status != OK) {
- ALOGE("failed to read from buffer release consumer in background thread. errno=%d "
- "message='%s'",
- errno, strerror(errno));
- continue;
- }
- bbq->releaseBufferCallback(id, fence, maxAcquiredBufferCount);
- }
- }).detach();
-}
-
-BLASTBufferQueue::BufferReleaseThread::~BufferReleaseThread() {
- eventfd_write(mEventFd, 1);
-}
-
#endif
} // namespace android
diff --git a/libs/gui/BufferItem.cpp b/libs/gui/BufferItem.cpp
index 5beba02..3b2d337 100644
--- a/libs/gui/BufferItem.cpp
+++ b/libs/gui/BufferItem.cpp
@@ -38,26 +38,25 @@
return static_cast<T>(static_cast<uint64_t>(hi)<<32 | lo);
}
-BufferItem::BufferItem() :
- mGraphicBuffer(nullptr),
- mFence(nullptr),
- mCrop(Rect::INVALID_RECT),
- mTransform(0),
- mScalingMode(NATIVE_WINDOW_SCALING_MODE_FREEZE),
- mTimestamp(0),
- mIsAutoTimestamp(false),
- mDataSpace(HAL_DATASPACE_UNKNOWN),
- mFrameNumber(0),
- mSlot(INVALID_BUFFER_SLOT),
- mIsDroppable(false),
- mAcquireCalled(false),
- mTransformToDisplayInverse(false),
- mSurfaceDamage(),
- mAutoRefresh(false),
- mQueuedBuffer(true),
- mIsStale(false),
- mApi(0) {
-}
+BufferItem::BufferItem()
+ : mGraphicBuffer(nullptr),
+ mFence(nullptr),
+ mCrop(Rect::INVALID_RECT),
+ mTransform(0),
+ mScalingMode(NATIVE_WINDOW_SCALING_MODE_FREEZE),
+ mTimestamp(0),
+ mIsAutoTimestamp(false),
+ mDataSpace(HAL_DATASPACE_UNKNOWN),
+ mFrameNumber(0),
+ mSlot(INVALID_BUFFER_SLOT),
+ mIsDroppable(false),
+ mAcquireCalled(false),
+ mTransformToDisplayInverse(false),
+ mSurfaceDamage(),
+ mAutoRefresh(false),
+ mQueuedBuffer(true),
+ mIsStale(false),
+ mApi(0) {}
BufferItem::~BufferItem() {}
@@ -76,6 +75,11 @@
addAligned(size, high32(mTimestamp));
addAligned(size, mIsAutoTimestamp);
addAligned(size, mDataSpace);
+#if COM_ANDROID_GRAPHICS_LIBUI_FLAGS_APPLY_PICTURE_PROFILES
+ addAligned(size, mPictureProfileHandle.has_value());
+ addAligned(size, low32(PictureProfileHandle::NONE.getId()));
+ addAligned(size, high32(PictureProfileHandle::NONE.getId()));
+#endif // COM_ANDROID_GRAPHICS_LIBUI_FLAGS_APPLY_PICTURE_PROFILES
addAligned(size, low32(mFrameNumber));
addAligned(size, high32(mFrameNumber));
addAligned(size, mSlot);
@@ -170,6 +174,16 @@
writeAligned(buffer, size, high32(mTimestamp));
writeAligned(buffer, size, mIsAutoTimestamp);
writeAligned(buffer, size, mDataSpace);
+#if COM_ANDROID_GRAPHICS_LIBUI_FLAGS_APPLY_PICTURE_PROFILES
+ writeAligned(buffer, size, mPictureProfileHandle.has_value());
+ if (mPictureProfileHandle.has_value()) {
+ writeAligned(buffer, size, low32(mPictureProfileHandle->getId()));
+ writeAligned(buffer, size, high32(mPictureProfileHandle->getId()));
+ } else {
+ writeAligned(buffer, size, low32(PictureProfileHandle::NONE.getId()));
+ writeAligned(buffer, size, high32(PictureProfileHandle::NONE.getId()));
+ }
+#endif // COM_ANDROID_GRAPHICS_LIBUI_FLAGS_APPLY_PICTURE_PROFILES
writeAligned(buffer, size, low32(mFrameNumber));
writeAligned(buffer, size, high32(mFrameNumber));
writeAligned(buffer, size, mSlot);
@@ -231,6 +245,7 @@
uint32_t timestampLo = 0, timestampHi = 0;
uint32_t frameNumberLo = 0, frameNumberHi = 0;
+ int32_t pictureProfileIdLo = 0, pictureProfileIdHi = 0;
readAligned(buffer, size, mCrop);
readAligned(buffer, size, mTransform);
@@ -240,6 +255,16 @@
mTimestamp = to64<int64_t>(timestampLo, timestampHi);
readAligned(buffer, size, mIsAutoTimestamp);
readAligned(buffer, size, mDataSpace);
+#if COM_ANDROID_GRAPHICS_LIBUI_FLAGS_APPLY_PICTURE_PROFILES
+ bool hasPictureProfileHandle;
+ readAligned(buffer, size, hasPictureProfileHandle);
+ readAligned(buffer, size, pictureProfileIdLo);
+ readAligned(buffer, size, pictureProfileIdHi);
+ mPictureProfileHandle = hasPictureProfileHandle
+ ? std::optional(PictureProfileHandle(
+ to64<PictureProfileId>(pictureProfileIdLo, pictureProfileIdHi)))
+ : std::nullopt;
+#endif // COM_ANDROID_GRAPHICS_LIBUI_FLAGS_APPLY_PICTURE_PROFILES
readAligned(buffer, size, frameNumberLo);
readAligned(buffer, size, frameNumberHi);
mFrameNumber = to64<uint64_t>(frameNumberLo, frameNumberHi);
diff --git a/libs/gui/BufferItemConsumer.cpp b/libs/gui/BufferItemConsumer.cpp
index bfe3d6e..8566419 100644
--- a/libs/gui/BufferItemConsumer.cpp
+++ b/libs/gui/BufferItemConsumer.cpp
@@ -140,7 +140,7 @@
BI_LOGE("Failed to addReleaseFenceLocked");
}
- err = releaseBufferLocked(slotIndex, buffer, EGL_NO_DISPLAY, EGL_NO_SYNC_KHR);
+ err = releaseBufferLocked(slotIndex, buffer);
if (err != OK && err != IGraphicBufferConsumer::STALE_BUFFER_SLOT) {
BI_LOGE("Failed to release buffer: %s (%d)",
strerror(-err), err);
diff --git a/libs/gui/BufferQueueConsumer.cpp b/libs/gui/BufferQueueConsumer.cpp
index d0607bf..9855b5b 100644
--- a/libs/gui/BufferQueueConsumer.cpp
+++ b/libs/gui/BufferQueueConsumer.cpp
@@ -28,6 +28,10 @@
#define VALIDATE_CONSISTENCY()
#endif
+#define EGL_EGLEXT_PROTOTYPES
+#include <EGL/egl.h>
+#include <EGL/eglext.h>
+
#include <gui/BufferItem.h>
#include <gui/BufferQueueConsumer.h>
#include <gui/BufferQueueCore.h>
@@ -486,6 +490,27 @@
return BAD_VALUE;
}
+#if COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(BQ_GL_FENCE_CLEANUP)
+ if (eglFence != EGL_NO_SYNC_KHR) {
+ // Most platforms will be using native fences, so it's unlikely that we'll ever have to
+ // process an eglFence. Ideally we can remove this code eventually. In the mean time, do our
+ // best to wait for it so the buffer stays valid, otherwise return an error to the caller.
+ //
+ // EGL_SYNC_FLUSH_COMMANDS_BIT_KHR so that we don't wait forever on a fence that hasn't
+ // shown up on the GPU yet.
+ EGLint result = eglClientWaitSyncKHR(eglDisplay, eglFence, EGL_SYNC_FLUSH_COMMANDS_BIT_KHR,
+ 1000000000);
+ if (result == EGL_FALSE) {
+ BQ_LOGE("releaseBuffer: error %#x waiting for fence", eglGetError());
+ return UNKNOWN_ERROR;
+ } else if (result == EGL_TIMEOUT_EXPIRED_KHR) {
+ BQ_LOGE("releaseBuffer: timeout waiting for fence");
+ return UNKNOWN_ERROR;
+ }
+ eglDestroySyncKHR(eglDisplay, eglFence);
+ }
+#endif
+
sp<IProducerListener> listener;
{ // Autolock scope
std::lock_guard<std::mutex> lock(mCore->mMutex);
@@ -507,8 +532,10 @@
return BAD_VALUE;
}
+#if !COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(BQ_GL_FENCE_CLEANUP)
mSlots[slot].mEglDisplay = eglDisplay;
mSlots[slot].mEglFence = eglFence;
+#endif
mSlots[slot].mFence = releaseFence;
mSlots[slot].mBufferState.release();
diff --git a/libs/gui/BufferQueueCore.cpp b/libs/gui/BufferQueueCore.cpp
index d52cf70..5a09399 100644
--- a/libs/gui/BufferQueueCore.cpp
+++ b/libs/gui/BufferQueueCore.cpp
@@ -262,14 +262,16 @@
mSlots[slot].mFrameNumber = 0;
mSlots[slot].mAcquireCalled = false;
mSlots[slot].mNeedsReallocation = true;
+ mSlots[slot].mFence = Fence::NO_FENCE;
+#if !COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(BQ_GL_FENCE_CLEANUP)
// Destroy fence as BufferQueue now takes ownership
if (mSlots[slot].mEglFence != EGL_NO_SYNC_KHR) {
eglDestroySyncKHR(mSlots[slot].mEglDisplay, mSlots[slot].mEglFence);
mSlots[slot].mEglFence = EGL_NO_SYNC_KHR;
}
- mSlots[slot].mFence = Fence::NO_FENCE;
mSlots[slot].mEglDisplay = EGL_NO_DISPLAY;
+#endif
if (mLastQueuedSlot == slot) {
mLastQueuedSlot = INVALID_BUFFER_SLOT;
diff --git a/libs/gui/BufferQueueProducer.cpp b/libs/gui/BufferQueueProducer.cpp
index 473a374..2e7cef0 100644
--- a/libs/gui/BufferQueueProducer.cpp
+++ b/libs/gui/BufferQueueProducer.cpp
@@ -451,8 +451,10 @@
}
status_t returnFlags = NO_ERROR;
+#if !COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(BQ_GL_FENCE_CLEANUP)
EGLDisplay eglDisplay = EGL_NO_DISPLAY;
EGLSyncKHR eglFence = EGL_NO_SYNC_KHR;
+#endif
bool attachedByConsumer = false;
sp<IConsumerListener> listener;
@@ -569,8 +571,10 @@
mSlots[found].mAcquireCalled = false;
mSlots[found].mGraphicBuffer = nullptr;
mSlots[found].mRequestBufferCalled = false;
+#if !COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(BQ_GL_FENCE_CLEANUP)
mSlots[found].mEglDisplay = EGL_NO_DISPLAY;
mSlots[found].mEglFence = EGL_NO_SYNC_KHR;
+#endif
mSlots[found].mFence = Fence::NO_FENCE;
mCore->mBufferAge = 0;
mCore->mIsAllocating = true;
@@ -595,14 +599,18 @@
found, buffer->width, buffer->height, buffer->format);
}
+#if !COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(BQ_GL_FENCE_CLEANUP)
eglDisplay = mSlots[found].mEglDisplay;
eglFence = mSlots[found].mEglFence;
+#endif
// Don't return a fence in shared buffer mode, except for the first
// frame.
*outFence = (mCore->mSharedBufferMode &&
mCore->mSharedBufferSlot == found) ?
Fence::NO_FENCE : mSlots[found].mFence;
+#if !COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(BQ_GL_FENCE_CLEANUP)
mSlots[found].mEglFence = EGL_NO_SYNC_KHR;
+#endif
mSlots[found].mFence = Fence::NO_FENCE;
// If shared buffer mode has just been enabled, cache the slot of the
@@ -691,6 +699,7 @@
returnFlags |= BUFFER_NEEDS_REALLOCATION;
}
+#if !COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(BQ_GL_FENCE_CLEANUP)
if (eglFence != EGL_NO_SYNC_KHR) {
EGLint result = eglClientWaitSyncKHR(eglDisplay, eglFence, 0,
1000000000);
@@ -705,6 +714,7 @@
}
eglDestroySyncKHR(eglDisplay, eglFence);
}
+#endif
BQ_LOGV("dequeueBuffer: returning slot=%d/%" PRIu64 " buf=%p flags=%#x",
*outSlot,
@@ -908,7 +918,9 @@
mSlots[*outSlot].mGraphicBuffer = buffer;
mSlots[*outSlot].mBufferState.attachProducer();
+#if !COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(BQ_GL_FENCE_CLEANUP)
mSlots[*outSlot].mEglFence = EGL_NO_SYNC_KHR;
+#endif
mSlots[*outSlot].mFence = Fence::NO_FENCE;
mSlots[*outSlot].mRequestBufferCalled = true;
mSlots[*outSlot].mAcquireCalled = false;
@@ -938,6 +950,8 @@
&getFrameTimestamps);
const Region& surfaceDamage = input.getSurfaceDamage();
const HdrMetadata& hdrMetadata = input.getHdrMetadata();
+ const std::optional<PictureProfileHandle>& pictureProfileHandle =
+ input.getPictureProfileHandle();
if (acquireFence == nullptr) {
BQ_LOGE("queueBuffer: fence is NULL");
@@ -1044,6 +1058,7 @@
item.mIsAutoTimestamp = isAutoTimestamp;
item.mDataSpace = dataSpace;
item.mHdrMetadata = hdrMetadata;
+ item.mPictureProfileHandle = pictureProfileHandle;
item.mFrameNumber = currentFrameNumber;
item.mSlot = slot;
item.mFence = acquireFence;
diff --git a/libs/gui/BufferReleaseChannel.cpp b/libs/gui/BufferReleaseChannel.cpp
index e9c6ef3..e9cb013 100644
--- a/libs/gui/BufferReleaseChannel.cpp
+++ b/libs/gui/BufferReleaseChannel.cpp
@@ -35,35 +35,35 @@
namespace {
template <typename T>
-static void readAligned(const void*& buffer, size_t& size, T& value) {
+void readAligned(const void*& buffer, size_t& size, T& value) {
size -= FlattenableUtils::align<alignof(T)>(buffer);
FlattenableUtils::read(buffer, size, value);
}
template <typename T>
-static void writeAligned(void*& buffer, size_t& size, T value) {
+void writeAligned(void*& buffer, size_t& size, T value) {
size -= FlattenableUtils::align<alignof(T)>(buffer);
FlattenableUtils::write(buffer, size, value);
}
template <typename T>
-static void addAligned(size_t& size, T /* value */) {
+void addAligned(size_t& size, T /* value */) {
size = FlattenableUtils::align<sizeof(T)>(size);
size += sizeof(T);
}
template <typename T>
-static inline constexpr uint32_t low32(const T n) {
+inline constexpr uint32_t low32(const T n) {
return static_cast<uint32_t>(static_cast<uint64_t>(n));
}
template <typename T>
-static inline constexpr uint32_t high32(const T n) {
+inline constexpr uint32_t high32(const T n) {
return static_cast<uint32_t>(static_cast<uint64_t>(n) >> 32);
}
template <typename T>
-static inline constexpr T to64(const uint32_t lo, const uint32_t hi) {
+inline constexpr T to64(const uint32_t lo, const uint32_t hi) {
return static_cast<T>(static_cast<uint64_t>(hi) << 32 | lo);
}
@@ -139,19 +139,18 @@
std::lock_guard lock{mMutex};
Message message;
mFlattenedBuffer.resize(message.getFlattenedSize());
- std::array<uint8_t, CMSG_SPACE(sizeof(int))> controlMessageBuffer;
+ std::array<uint8_t, CMSG_SPACE(sizeof(int))> controlMessageBuffer{};
iovec iov{
.iov_base = mFlattenedBuffer.data(),
.iov_len = mFlattenedBuffer.size(),
};
- msghdr msg{
- .msg_iov = &iov,
- .msg_iovlen = 1,
- .msg_control = controlMessageBuffer.data(),
- .msg_controllen = controlMessageBuffer.size(),
- };
+ msghdr msg{};
+ msg.msg_iov = &iov;
+ msg.msg_iovlen = 1;
+ msg.msg_control = controlMessageBuffer.data();
+ msg.msg_controllen = controlMessageBuffer.size();
ssize_t result;
do {
@@ -161,7 +160,7 @@
if (errno == EWOULDBLOCK || errno == EAGAIN) {
return WOULD_BLOCK;
}
- ALOGE("Error reading release fence from socket: error %#x (%s)", errno, strerror(errno));
+ ALOGE("Error reading release fence from socket: error %d (%s)", errno, strerror(errno));
return UNKNOWN_ERROR;
}
@@ -200,9 +199,9 @@
return OK;
}
-int BufferReleaseChannel::ProducerEndpoint::writeReleaseFence(const ReleaseCallbackId& callbackId,
- const sp<Fence>& fence,
- uint32_t maxAcquiredBufferCount) {
+status_t BufferReleaseChannel::ProducerEndpoint::writeReleaseFence(
+ const ReleaseCallbackId& callbackId, const sp<Fence>& fence,
+ uint32_t maxAcquiredBufferCount) {
Message message{callbackId, fence ? fence : Fence::NO_FENCE, maxAcquiredBufferCount};
mFlattenedBuffer.resize(message.getFlattenedSize());
int flattenedFd;
@@ -213,25 +212,22 @@
size_t flattenedBufferSize = mFlattenedBuffer.size();
int* flattenedFdPtr = &flattenedFd;
size_t flattenedFdCount = 1;
- if (status_t err = message.flatten(flattenedBufferPtr, flattenedBufferSize, flattenedFdPtr,
- flattenedFdCount);
- err != OK) {
- ALOGE("Failed to flatten BufferReleaseChannel message.");
- return err;
+ if (status_t status = message.flatten(flattenedBufferPtr, flattenedBufferSize,
+ flattenedFdPtr, flattenedFdCount);
+ status != OK) {
+ return status;
}
}
- iovec iov{
- .iov_base = mFlattenedBuffer.data(),
- .iov_len = mFlattenedBuffer.size(),
- };
+ iovec iov{};
+ iov.iov_base = mFlattenedBuffer.data();
+ iov.iov_len = mFlattenedBuffer.size();
- msghdr msg{
- .msg_iov = &iov,
- .msg_iovlen = 1,
- };
+ msghdr msg{};
+ msg.msg_iov = &iov;
+ msg.msg_iovlen = 1;
- std::array<uint8_t, CMSG_SPACE(sizeof(int))> controlMessageBuffer;
+ std::array<uint8_t, CMSG_SPACE(sizeof(int))> controlMessageBuffer{};
if (fence && fence->isValid()) {
msg.msg_control = controlMessageBuffer.data();
msg.msg_controllen = controlMessageBuffer.size();
@@ -248,7 +244,6 @@
result = sendmsg(mFd, &msg, 0);
} while (result == -1 && errno == EINTR);
if (result == -1) {
- ALOGD("Error writing release fence to socket: error %#x (%s)", errno, strerror(errno));
return -errno;
}
@@ -344,13 +339,6 @@
return -errno;
}
- // Make the producer write-only
- if (shutdown(producerFd.get(), SHUT_RD) == -1) {
- ALOGE("[%s] Failed to shutdown reading on producer socket. errno=%d message='%s'",
- name.c_str(), errno, strerror(errno));
- return -errno;
- }
-
outConsumer = std::make_unique<ConsumerEndpoint>(name, std::move(consumerFd));
outProducer = std::make_shared<ProducerEndpoint>(std::move(name), std::move(producerFd));
return STATUS_OK;
diff --git a/libs/gui/BufferStuffing.md b/libs/gui/BufferStuffing.md
new file mode 100644
index 0000000..6ed8ad9
--- /dev/null
+++ b/libs/gui/BufferStuffing.md
@@ -0,0 +1,7 @@
+### Buffer Stuffing and Recovery ###
+
+Buffer stuffing happens on the client side when SurfaceFlinger misses a frame, but the client continues producing buffers at the same rate. This could occur anytime when SurfaceFlinger does not meet the expected timeline’s deadline to finish composing a frame. As a result, SurfaceFlinger cannot yet release the buffer for the frame that it missed and the client has one less buffer to render into. The client may then run out of buffers or have to wait for buffer release callbacks, increasing the chances of janking when clients render multiple windows.
+
+Recovery is implemented by first detecting when buffer stuffing occurs and ensuring that the elevated buffer counts in the server are from a relevant SurfaceControl (is a ViewRootImpl). Other SurfaceControl buffer producers such as games, media, and camera have other reasons for expectedly increased buffer counts, which do not need buffer stuffing recovery.
+
+The actual recovery adjusts the animation timeline in the Choreographer so that the client deadlines for subsequent frames are moved forward in time by one frame. This approach adjusts the client buffer production timeline such that SurfaceFlinger does not fall behind when it misses a frame because the client will simply match its frame production rate with SurfaceFlinger. Ordinarily, buffer stuffing is problematic because the client continues producing buffers when SurfaceFlinger is behind. However, if the client delays producing its buffers to match SurfaceFlinger’s rate, the animation has new frame deadlines that can be reasonably met. The animation is effectively paused for one frame longer than originally intended, and continues the remainder of the animation normally.
\ No newline at end of file
diff --git a/libs/gui/DisplayLuts.cpp b/libs/gui/DisplayLuts.cpp
new file mode 100644
index 0000000..8042976
--- /dev/null
+++ b/libs/gui/DisplayLuts.cpp
@@ -0,0 +1,81 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "include/gui/DisplayLuts.h"
+#include <gui/DisplayLuts.h>
+#include <private/gui/ParcelUtils.h>
+
+namespace android::gui {
+
+status_t DisplayLuts::Entry::readFromParcel(const android::Parcel* parcel) {
+ if (parcel == nullptr) {
+ ALOGE("%s: Null parcel", __func__);
+ return BAD_VALUE;
+ }
+
+ SAFE_PARCEL(parcel->readInt32, &dimension);
+ SAFE_PARCEL(parcel->readInt32, &size);
+ SAFE_PARCEL(parcel->readInt32, &samplingKey);
+
+ return OK;
+}
+
+status_t DisplayLuts::Entry::writeToParcel(android::Parcel* parcel) const {
+ if (parcel == nullptr) {
+ ALOGE("%s: Null parcel", __func__);
+ return BAD_VALUE;
+ }
+
+ SAFE_PARCEL(parcel->writeInt32, dimension);
+ SAFE_PARCEL(parcel->writeInt32, size);
+ SAFE_PARCEL(parcel->writeInt32, samplingKey);
+
+ return OK;
+}
+
+status_t DisplayLuts::readFromParcel(const android::Parcel* parcel) {
+ if (parcel == nullptr) {
+ ALOGE("%s: Null parcel", __func__);
+ return BAD_VALUE;
+ }
+
+ SAFE_PARCEL(parcel->readUniqueFileDescriptor, &fd);
+ SAFE_PARCEL(parcel->readInt32Vector, &offsets);
+ int32_t numLutProperties;
+ SAFE_PARCEL(parcel->readInt32, &numLutProperties);
+ lutProperties.reserve(numLutProperties);
+ for (int32_t i = 0; i < numLutProperties; i++) {
+ lutProperties.push_back({});
+ SAFE_PARCEL(lutProperties.back().readFromParcel, parcel);
+ }
+ return OK;
+}
+
+status_t DisplayLuts::writeToParcel(android::Parcel* parcel) const {
+ if (parcel == nullptr) {
+ ALOGE("%s: Null parcel", __func__);
+ return BAD_VALUE;
+ }
+
+ SAFE_PARCEL(parcel->writeUniqueFileDescriptor, fd);
+ SAFE_PARCEL(parcel->writeInt32Vector, offsets);
+ SAFE_PARCEL(parcel->writeInt32, static_cast<int32_t>(lutProperties.size()));
+ for (auto& entry : lutProperties) {
+ SAFE_PARCEL(entry.writeToParcel, parcel);
+ }
+ return OK;
+}
+} // namespace android::gui
\ No newline at end of file
diff --git a/libs/gui/Flags.cpp b/libs/gui/Flags.cpp
new file mode 100644
index 0000000..85ee2cd
--- /dev/null
+++ b/libs/gui/Flags.cpp
@@ -0,0 +1,73 @@
+/*
+ * Copyright 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <gui/Flags.h>
+#include <gui/IGraphicBufferProducer.h>
+#include <gui/Surface.h>
+#include <gui/view/Surface.h>
+
+namespace android {
+namespace flagtools {
+sp<SurfaceType> surfaceToSurfaceType(const sp<Surface>& surface) {
+#if WB_LIBCAMERASERVICE_WITH_DEPENDENCIES
+ return surface;
+#else
+ return surface->getIGraphicBufferProducer();
+#endif
+}
+
+sp<IGraphicBufferProducer> surfaceTypeToIGBP(const sp<SurfaceType>& surface) {
+#if WB_LIBCAMERASERVICE_WITH_DEPENDENCIES
+ return surface->getIGraphicBufferProducer();
+#else
+ return surface;
+#endif
+}
+
+bool isSurfaceTypeValid(const sp<SurfaceType>& surface) {
+#if WB_LIBCAMERASERVICE_WITH_DEPENDENCIES
+ return Surface::isValid(surface);
+#else
+ return surface != nullptr;
+#endif
+}
+
+ParcelableSurfaceType toParcelableSurfaceType(const view::Surface& surface) {
+#if WB_LIBCAMERASERVICE_WITH_DEPENDENCIES
+ return surface;
+#else
+ return surface.graphicBufferProducer;
+#endif
+}
+
+ParcelableSurfaceType convertSurfaceTypeToParcelable(sp<SurfaceType> surface) {
+#if WB_LIBCAMERASERVICE_WITH_DEPENDENCIES
+ return view::Surface::fromSurface(surface);
+#else
+ return surface;
+#endif
+}
+
+sp<SurfaceType> convertParcelableSurfaceTypeToSurface(const ParcelableSurfaceType& surface) {
+#if WB_LIBCAMERASERVICE_WITH_DEPENDENCIES
+ return surface.toSurface();
+#else
+ return surface;
+#endif
+}
+
+} // namespace flagtools
+} // namespace android
\ No newline at end of file
diff --git a/libs/gui/GLConsumer.cpp b/libs/gui/GLConsumer.cpp
index 95cce5c..f2173cd 100644
--- a/libs/gui/GLConsumer.cpp
+++ b/libs/gui/GLConsumer.cpp
@@ -314,7 +314,7 @@
// so... basically, nothing more to do here.
}
- err = releaseBufferLocked(buf, mSlots[buf].mGraphicBuffer, mEglDisplay, EGL_NO_SYNC_KHR);
+ err = releaseBufferLocked(buf, mSlots[buf].mGraphicBuffer);
if (err < NO_ERROR) {
GLC_LOGE("releaseTexImage: failed to release buffer: %s (%d)",
strerror(-err), err);
@@ -418,16 +418,14 @@
if (!mAttached) {
GLC_LOGE("updateAndRelease: GLConsumer is not attached to an OpenGL "
"ES context");
- releaseBufferLocked(slot, mSlots[slot].mGraphicBuffer,
- mEglDisplay, EGL_NO_SYNC_KHR);
+ releaseBufferLocked(slot, mSlots[slot].mGraphicBuffer);
return INVALID_OPERATION;
}
// Confirm state.
err = checkAndUpdateEglStateLocked();
if (err != NO_ERROR) {
- releaseBufferLocked(slot, mSlots[slot].mGraphicBuffer,
- mEglDisplay, EGL_NO_SYNC_KHR);
+ releaseBufferLocked(slot, mSlots[slot].mGraphicBuffer);
return err;
}
@@ -440,8 +438,7 @@
if (err != NO_ERROR) {
GLC_LOGW("updateAndRelease: unable to createImage on display=%p slot=%d",
mEglDisplay, slot);
- releaseBufferLocked(slot, mSlots[slot].mGraphicBuffer,
- mEglDisplay, EGL_NO_SYNC_KHR);
+ releaseBufferLocked(slot, mSlots[slot].mGraphicBuffer);
return UNKNOWN_ERROR;
}
@@ -453,8 +450,7 @@
// release the old buffer, so instead we just drop the new frame.
// As we are still under lock since acquireBuffer, it is safe to
// release by slot.
- releaseBufferLocked(slot, mSlots[slot].mGraphicBuffer,
- mEglDisplay, EGL_NO_SYNC_KHR);
+ releaseBufferLocked(slot, mSlots[slot].mGraphicBuffer);
return err;
}
}
diff --git a/libs/gui/IGraphicBufferConsumer.cpp b/libs/gui/IGraphicBufferConsumer.cpp
index c705d39..282957b 100644
--- a/libs/gui/IGraphicBufferConsumer.cpp
+++ b/libs/gui/IGraphicBufferConsumer.cpp
@@ -26,6 +26,7 @@
#include <utils/NativeHandle.h>
#include <utils/String8.h>
+#include <cstdint>
namespace android {
@@ -84,7 +85,8 @@
EGLDisplay display __attribute__((unused)),
EGLSyncKHR fence __attribute__((unused)),
const sp<Fence>& releaseFence) override {
- return callRemote<ReleaseBuffer>(Tag::RELEASE_BUFFER, buf, frameNumber, releaseFence);
+ using Signature = status_t (IGraphicBufferConsumer::*)(int, uint64_t, const sp<Fence>&);
+ return callRemote<Signature>(Tag::RELEASE_BUFFER, buf, frameNumber, releaseFence);
}
status_t consumerConnect(const sp<IConsumerListener>& consumer, bool controlledByApp) override {
@@ -188,8 +190,10 @@
return callLocal(data, reply, &IGraphicBufferConsumer::detachBuffer);
case Tag::ATTACH_BUFFER:
return callLocal(data, reply, &IGraphicBufferConsumer::attachBuffer);
- case Tag::RELEASE_BUFFER:
- return callLocal(data, reply, &IGraphicBufferConsumer::releaseHelper);
+ case Tag::RELEASE_BUFFER: {
+ using Signature = status_t (IGraphicBufferConsumer::*)(int, uint64_t, const sp<Fence>&);
+ return callLocal<Signature>(data, reply, &IGraphicBufferConsumer::releaseBuffer);
+ }
case Tag::CONSUMER_CONNECT:
return callLocal(data, reply, &IGraphicBufferConsumer::consumerConnect);
case Tag::CONSUMER_DISCONNECT:
diff --git a/libs/gui/IGraphicBufferProducerFlattenables.cpp b/libs/gui/IGraphicBufferProducerFlattenables.cpp
index c8b9b67..4e92a39 100644
--- a/libs/gui/IGraphicBufferProducerFlattenables.cpp
+++ b/libs/gui/IGraphicBufferProducerFlattenables.cpp
@@ -20,21 +20,19 @@
namespace android {
constexpr size_t IGraphicBufferProducer::QueueBufferInput::minFlattenedSize() {
- return sizeof(timestamp) +
- sizeof(isAutoTimestamp) +
- sizeof(dataSpace) +
- sizeof(crop) +
- sizeof(scalingMode) +
- sizeof(transform) +
- sizeof(stickyTransform) +
- sizeof(getFrameTimestamps) +
- sizeof(slot);
+ return sizeof(timestamp) + sizeof(isAutoTimestamp) + sizeof(dataSpace) + sizeof(crop) +
+ sizeof(scalingMode) + sizeof(transform) + sizeof(stickyTransform) +
+ sizeof(getFrameTimestamps) + sizeof(slot) +
+#if COM_ANDROID_GRAPHICS_LIBUI_FLAGS_APPLY_PICTURE_PROFILES
+ sizeof(decltype(pictureProfileHandle.has_value())) +
+ sizeof(decltype(pictureProfileHandle.getId()));
+#else
+ 0;
+#endif // COM_ANDROID_GRAPHICS_LIBUI_FLAGS_APPLY_PICTURE_PROFILES
}
size_t IGraphicBufferProducer::QueueBufferInput::getFlattenedSize() const {
- return minFlattenedSize() +
- fence->getFlattenedSize() +
- surfaceDamage.getFlattenedSize() +
+ return minFlattenedSize() + fence->getFlattenedSize() + surfaceDamage.getFlattenedSize() +
hdrMetadata.getFlattenedSize();
}
@@ -57,6 +55,12 @@
FlattenableUtils::write(buffer, size, transform);
FlattenableUtils::write(buffer, size, stickyTransform);
FlattenableUtils::write(buffer, size, getFrameTimestamps);
+#if COM_ANDROID_GRAPHICS_LIBUI_FLAGS_APPLY_PICTURE_PROFILES
+ FlattenableUtils::write(buffer, size, pictureProfileHandle.has_value());
+ FlattenableUtils::write(buffer, size,
+ pictureProfileHandle.has_value() ? pictureProfileHandle->getId()
+ : PictureProfileHandle::NONE.getId());
+#endif // COM_ANDROID_GRAPHICS_LIBUI_FLAGS_APPLY_PICTURE_PROFILES
status_t result = fence->flatten(buffer, size, fds, count);
if (result != NO_ERROR) {
@@ -91,6 +95,15 @@
FlattenableUtils::read(buffer, size, transform);
FlattenableUtils::read(buffer, size, stickyTransform);
FlattenableUtils::read(buffer, size, getFrameTimestamps);
+#if COM_ANDROID_GRAPHICS_LIBUI_FLAGS_APPLY_PICTURE_PROFILES
+ bool hasPictureProfileHandle;
+ FlattenableUtils::read(buffer, size, hasPictureProfileHandle);
+ PictureProfileId pictureProfileId;
+ FlattenableUtils::read(buffer, size, pictureProfileId);
+ pictureProfileHandle = hasPictureProfileHandle
+ ? std::optional(PictureProfileHandle(pictureProfileId))
+ : std::nullopt;
+#endif // COM_ANDROID_GRAPHICS_LIBUI_FLAGS_APPLY_PICTURE_PROFILES
fence = new Fence();
status_t result = fence->unflatten(buffer, size, fds, count);
diff --git a/libs/gui/LayerState.cpp b/libs/gui/LayerState.cpp
index 4b53134..c1a03fc 100644
--- a/libs/gui/LayerState.cpp
+++ b/libs/gui/LayerState.cpp
@@ -21,6 +21,7 @@
#include <android/gui/ISurfaceComposerClient.h>
#include <android/native_window.h>
#include <binder/Parcel.h>
+#include <com_android_graphics_libgui_flags.h>
#include <gui/FrameRateUtils.h>
#include <gui/IGraphicBufferProducer.h>
#include <gui/LayerState.h>
@@ -91,7 +92,9 @@
trustedOverlay(gui::TrustedOverlay::UNSET),
bufferCrop(Rect::INVALID_RECT),
destinationFrame(Rect::INVALID_RECT),
- dropInputMode(gui::DropInputMode::NONE) {
+ dropInputMode(gui::DropInputMode::NONE),
+ pictureProfileHandle(PictureProfileHandle::NONE),
+ appContentPriority(0) {
matrix.dsdx = matrix.dtdy = 1.0f;
matrix.dsdy = matrix.dtdx = 0.0f;
hdrMetadata.validTypes = 0;
@@ -202,6 +205,16 @@
if (hasBufferReleaseChannel) {
SAFE_PARCEL(output.writeParcelable, *bufferReleaseChannel);
}
+#if COM_ANDROID_GRAPHICS_LIBGUI_FLAGS_APPLY_PICTURE_PROFILES
+ SAFE_PARCEL(output.writeInt64, pictureProfileHandle.getId());
+ SAFE_PARCEL(output.writeInt32, appContentPriority);
+#endif // COM_ANDROID_GRAPHICS_LIBGUI_FLAGS_APPLY_PICTURE_PROFILES
+
+ const bool hasLuts = (luts != nullptr);
+ SAFE_PARCEL(output.writeBool, hasLuts);
+ if (hasLuts) {
+ SAFE_PARCEL(output.writeParcelable, *luts);
+ }
return NO_ERROR;
}
@@ -357,6 +370,21 @@
bufferReleaseChannel = std::make_shared<gui::BufferReleaseChannel::ProducerEndpoint>();
SAFE_PARCEL(input.readParcelable, bufferReleaseChannel.get());
}
+#if COM_ANDROID_GRAPHICS_LIBGUI_FLAGS_APPLY_PICTURE_PROFILES
+ int64_t pictureProfileId;
+ SAFE_PARCEL(input.readInt64, &pictureProfileId);
+ pictureProfileHandle = PictureProfileHandle(pictureProfileId);
+ SAFE_PARCEL(input.readInt32, &appContentPriority);
+#endif // COM_ANDROID_GRAPHICS_LIBGUI_FLAGS_APPLY_PICTURE_PROFILES
+
+ bool hasLuts;
+ SAFE_PARCEL(input.readBool, &hasLuts);
+ if (hasLuts) {
+ luts = std::make_shared<gui::DisplayLuts>();
+ SAFE_PARCEL(input.readParcelable, luts.get());
+ } else {
+ luts = nullptr;
+ }
return NO_ERROR;
}
@@ -745,6 +773,16 @@
what |= eBufferReleaseChannelChanged;
bufferReleaseChannel = other.bufferReleaseChannel;
}
+ if (com_android_graphics_libgui_flags_apply_picture_profiles()) {
+ if (other.what & ePictureProfileHandleChanged) {
+ what |= ePictureProfileHandleChanged;
+ pictureProfileHandle = other.pictureProfileHandle;
+ }
+ if (other.what & eAppContentPriorityChanged) {
+ what |= eAppContentPriorityChanged;
+ appContentPriority = other.appContentPriority;
+ }
+ }
if ((other.what & what) != other.what) {
ALOGE("Unmerged SurfaceComposer Transaction properties. LayerState::merge needs updating? "
"other.what=0x%" PRIX64 " what=0x%" PRIX64 " unmerged flags=0x%" PRIX64,
@@ -826,6 +864,8 @@
CHECK_DIFF(diff, eDimmingEnabledChanged, other, dimmingEnabled);
if (other.what & eBufferReleaseChannelChanged) diff |= eBufferReleaseChannelChanged;
if (other.what & eLutsChanged) diff |= eLutsChanged;
+ CHECK_DIFF(diff, ePictureProfileHandleChanged, other, pictureProfileHandle);
+ CHECK_DIFF(diff, eAppContentPriorityChanged, other, appContentPriority);
return diff;
}
diff --git a/libs/gui/StreamSplitter.cpp b/libs/gui/StreamSplitter.cpp
index 2f8e104..653b91b 100644
--- a/libs/gui/StreamSplitter.cpp
+++ b/libs/gui/StreamSplitter.cpp
@@ -234,8 +234,7 @@
LOG_ALWAYS_FATAL_IF(status != NO_ERROR,
"attaching buffer to input failed (%d)", status);
- status = mInput->releaseBuffer(consumerSlot, /* frameNumber */ 0,
- EGL_NO_DISPLAY, EGL_NO_SYNC_KHR, tracker->getMergedFence());
+ status = mInput->releaseBuffer(consumerSlot, /* frameNumber */ 0, tracker->getMergedFence());
LOG_ALWAYS_FATAL_IF(status != NO_ERROR,
"releasing buffer to input failed (%d)", status);
diff --git a/libs/gui/Surface.cpp b/libs/gui/Surface.cpp
index 66e7ddd..e41f9bb 100644
--- a/libs/gui/Surface.cpp
+++ b/libs/gui/Surface.cpp
@@ -2735,8 +2735,8 @@
bool Surface::waitForNextFrame(uint64_t lastFrame, nsecs_t timeout) {
Mutex::Autolock lock(mMutex);
- if (mNextFrameNumber > lastFrame) {
- return true;
+ if (mLastFrameNumber > lastFrame) {
+ return true;
}
return mQueueBufferCondition.waitRelative(mMutex, timeout) == OK;
}
diff --git a/libs/gui/SurfaceComposerClient.cpp b/libs/gui/SurfaceComposerClient.cpp
index a93fc92..be88b11 100644
--- a/libs/gui/SurfaceComposerClient.cpp
+++ b/libs/gui/SurfaceComposerClient.cpp
@@ -20,8 +20,6 @@
#include <stdint.h>
#include <sys/types.h>
-#include <com_android_graphics_libgui_flags.h>
-
#include <android/gui/BnWindowInfosReportedListener.h>
#include <android/gui/DisplayState.h>
#include <android/gui/EdgeExtensionParameters.h>
@@ -29,6 +27,7 @@
#include <android/gui/IWindowInfosListener.h>
#include <android/gui/TrustedPresentationThresholds.h>
#include <android/os/IInputConstants.h>
+#include <com_android_graphics_libgui_flags.h>
#include <gui/DisplayLuts.h>
#include <gui/FrameRateUtils.h>
#include <gui/TraceUtils.h>
@@ -57,6 +56,7 @@
#include <ui/DisplayMode.h>
#include <ui/DisplayState.h>
#include <ui/DynamicDisplayInfo.h>
+#include <ui/FrameRateCategoryRate.h>
#include <android-base/thread_annotations.h>
#include <gui/LayerStatePermissions.h>
@@ -91,6 +91,7 @@
}
constexpr int64_t INVALID_VSYNC = -1;
+const constexpr char* LOG_SURFACE_CONTROL_REGISTRY = "SurfaceControlRegistry";
} // namespace
@@ -872,6 +873,7 @@
const bool earlyWakeupEnd = parcel->readBool();
const int64_t desiredPresentTime = parcel->readInt64();
const bool isAutoTimestamp = parcel->readBool();
+ const bool logCallPoints = parcel->readBool();
FrameTimelineInfo frameTimelineInfo;
frameTimelineInfo.readFromParcel(parcel);
@@ -999,6 +1001,7 @@
parcel->writeBool(mEarlyWakeupEnd);
parcel->writeInt64(mDesiredPresentTime);
parcel->writeBool(mIsAutoTimestamp);
+ parcel->writeBool(mLogCallPoints);
mFrameTimelineInfo.writeToParcel(parcel);
parcel->writeStrongBinder(mApplyToken);
parcel->writeUint32(static_cast<uint32_t>(mDisplayStates.size()));
@@ -1134,6 +1137,12 @@
mergeFrameTimelineInfo(mFrameTimelineInfo, other.mFrameTimelineInfo);
+ mLogCallPoints |= other.mLogCallPoints;
+ if (mLogCallPoints) {
+ ALOG(LOG_DEBUG, LOG_SURFACE_CONTROL_REGISTRY,
+ "Transaction %" PRIu64 " merged with transaction %" PRIu64, other.getId(), mId);
+ }
+
other.clear();
return *this;
}
@@ -1153,6 +1162,7 @@
mFrameTimelineInfo = {};
mApplyToken = nullptr;
mMergedTransactionIds.clear();
+ mLogCallPoints = false;
}
uint64_t SurfaceComposerClient::Transaction::getId() {
@@ -1347,21 +1357,26 @@
sp<IBinder> applyToken = mApplyToken ? mApplyToken : getDefaultApplyToken();
sp<ISurfaceComposer> sf(ComposerService::getComposerService());
- sf->setTransactionState(mFrameTimelineInfo, composerStates, displayStates, flags, applyToken,
- mInputWindowCommands, mDesiredPresentTime, mIsAutoTimestamp,
- mUncacheBuffers, hasListenerCallbacks, listenerCallbacks, mId,
- mMergedTransactionIds);
+ status_t binderStatus =
+ sf->setTransactionState(mFrameTimelineInfo, composerStates, displayStates, flags,
+ applyToken, mInputWindowCommands, mDesiredPresentTime,
+ mIsAutoTimestamp, mUncacheBuffers, hasListenerCallbacks,
+ listenerCallbacks, mId, mMergedTransactionIds);
mId = generateId();
// Clear the current states and flags
clear();
- if (synchronous) {
+ if (synchronous && binderStatus == OK) {
syncCallback->wait();
}
+ if (mLogCallPoints) {
+ ALOG(LOG_DEBUG, LOG_SURFACE_CONTROL_REGISTRY, "Transaction %" PRIu64 " applied", mId);
+ }
+
mStatus = NO_ERROR;
- return NO_ERROR;
+ return binderStatus;
}
sp<IBinder> SurfaceComposerClient::Transaction::sApplyToken = new BBinder();
@@ -1375,7 +1390,7 @@
void SurfaceComposerClient::Transaction::setDefaultApplyToken(sp<IBinder> applyToken) {
std::scoped_lock lock{sApplyTokenMutex};
- sApplyToken = applyToken;
+ sApplyToken = std::move(applyToken);
}
status_t SurfaceComposerClient::Transaction::sendSurfaceFlushJankDataTransaction(
@@ -1390,6 +1405,11 @@
t.registerSurfaceControlForCallback(sc);
return t.apply(/*sync=*/false, /* oneWay=*/true);
}
+
+void SurfaceComposerClient::Transaction::enableDebugLogCallPoints() {
+ mLogCallPoints = true;
+}
+
// ---------------------------------------------------------------------------
sp<IBinder> SurfaceComposerClient::createVirtualDisplay(const std::string& displayName,
@@ -1950,9 +1970,13 @@
return *this;
}
- s->luts = std::make_shared<gui::DisplayLuts>(base::unique_fd(dup(lutFd.get())), offsets,
- dimensions, sizes, samplingKeys);
s->what |= layer_state_t::eLutsChanged;
+ if (lutFd.ok()) {
+ s->luts = std::make_shared<gui::DisplayLuts>(base::unique_fd(dup(lutFd.get())), offsets,
+ dimensions, sizes, samplingKeys);
+ } else {
+ s->luts = nullptr;
+ }
registerSurfaceControlForCallback(sc);
return *this;
@@ -2108,13 +2132,13 @@
}
SurfaceComposerClient::Transaction& SurfaceComposerClient::Transaction::setInputWindowInfo(
- const sp<SurfaceControl>& sc, const WindowInfo& info) {
+ const sp<SurfaceControl>& sc, sp<WindowInfoHandle> info) {
layer_state_t* s = getLayerState(sc);
if (!s) {
mStatus = BAD_INDEX;
return *this;
}
- s->windowInfoHandle = new WindowInfoHandle(info);
+ s->windowInfoHandle = std::move(info);
s->what |= layer_state_t::eInputInfoChanged;
return *this;
}
@@ -2426,6 +2450,40 @@
return *this;
}
+SurfaceComposerClient::Transaction& SurfaceComposerClient::Transaction::setPictureProfileHandle(
+ const sp<SurfaceControl>& sc, const PictureProfileHandle& pictureProfileHandle) {
+ if (com_android_graphics_libgui_flags_apply_picture_profiles()) {
+ layer_state_t* s = getLayerState(sc);
+ if (!s) {
+ mStatus = BAD_INDEX;
+ return *this;
+ }
+
+ s->what |= layer_state_t::ePictureProfileHandleChanged;
+ s->pictureProfileHandle = pictureProfileHandle;
+
+ registerSurfaceControlForCallback(sc);
+ }
+ return *this;
+}
+
+SurfaceComposerClient::Transaction& SurfaceComposerClient::Transaction::setContentPriority(
+ const sp<SurfaceControl>& sc, int32_t priority) {
+ if (com_android_graphics_libgui_flags_apply_picture_profiles()) {
+ layer_state_t* s = getLayerState(sc);
+ if (!s) {
+ mStatus = BAD_INDEX;
+ return *this;
+ }
+
+ s->what |= layer_state_t::eAppContentPriorityChanged;
+ s->appContentPriority = priority;
+
+ registerSurfaceControlForCallback(sc);
+ }
+ return *this;
+}
+
// ---------------------------------------------------------------------------
DisplayState& SurfaceComposerClient::Transaction::getDisplayState(const sp<IBinder>& token) {
@@ -2813,6 +2871,13 @@
outInfo->gameContentTypeSupported = ginfo.gameContentTypeSupported;
outInfo->preferredBootDisplayMode = ginfo.preferredBootDisplayMode;
outInfo->hasArrSupport = ginfo.hasArrSupport;
+ outInfo->frameRateCategoryRate = ui::FrameRateCategoryRate(ginfo.frameRateCategoryRate.normal,
+ ginfo.frameRateCategoryRate.high);
+ outInfo->supportedRefreshRates.clear();
+ outInfo->supportedRefreshRates.reserve(ginfo.supportedRefreshRates.size());
+ for (const auto rate : ginfo.supportedRefreshRates) {
+ outInfo->supportedRefreshRates.push_back(static_cast<float>(rate));
+ }
}
status_t SurfaceComposerClient::getDynamicDisplayInfoFromId(int64_t displayId,
@@ -2996,6 +3061,14 @@
ComposerServiceAIDL::getComposerService()->setPowerMode(token, mode);
}
+status_t SurfaceComposerClient::getMaxLayerPictureProfiles(const sp<IBinder>& token,
+ int32_t* outMaxProfiles) {
+ binder::Status status =
+ ComposerServiceAIDL::getComposerService()->getMaxLayerPictureProfiles(token,
+ outMaxProfiles);
+ return statusTFromBinderStatus(status);
+}
+
status_t SurfaceComposerClient::getCompositionPreference(
ui::Dataspace* defaultDataspace, ui::PixelFormat* defaultPixelFormat,
ui::Dataspace* wideColorGamutDataspace, ui::PixelFormat* wideColorGamutPixelFormat) {
@@ -3220,6 +3293,13 @@
return statusTFromBinderStatus(status);
}
+status_t SurfaceComposerClient::setActivePictureListener(
+ const sp<gui::IActivePictureListener>& listener) {
+ binder::Status status =
+ ComposerServiceAIDL::getComposerService()->setActivePictureListener(listener);
+ return statusTFromBinderStatus(status);
+}
+
status_t SurfaceComposerClient::notifyPowerBoost(int32_t boostId) {
binder::Status status = ComposerServiceAIDL::getComposerService()->notifyPowerBoost(boostId);
return statusTFromBinderStatus(status);
diff --git a/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockPowerHintSessionWrapper.cpp b/libs/gui/aidl/android/gui/ActivePicture.aidl
similarity index 61%
copy from services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockPowerHintSessionWrapper.cpp
copy to libs/gui/aidl/android/gui/ActivePicture.aidl
index d383283..9b83be1 100644
--- a/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockPowerHintSessionWrapper.cpp
+++ b/libs/gui/aidl/android/gui/ActivePicture.aidl
@@ -14,12 +14,19 @@
* limitations under the License.
*/
-#include "mock/DisplayHardware/MockPowerHintSessionWrapper.h"
+package android.gui;
-namespace android::Hwc2::mock {
+/**
+ * Visible content that is using picture processing.
+ * @hide
+ */
+parcelable ActivePicture {
+ /** The layer ID that is using picture processing. */
+ int layerId;
-// Explicit default instantiation is recommended.
-MockPowerHintSessionWrapper::MockPowerHintSessionWrapper()
- : power::PowerHintSessionWrapper(nullptr) {}
+ /** UID that owns layer using picture processing. */
+ int ownerUid;
-} // namespace android::Hwc2::mock
+ /** ID of the picture profile that was used to configure the picture processing. */
+ long pictureProfileId;
+}
diff --git a/libs/gui/aidl/android/gui/DynamicDisplayInfo.aidl b/libs/gui/aidl/android/gui/DynamicDisplayInfo.aidl
index 70873b0..26c12c5 100644
--- a/libs/gui/aidl/android/gui/DynamicDisplayInfo.aidl
+++ b/libs/gui/aidl/android/gui/DynamicDisplayInfo.aidl
@@ -17,6 +17,7 @@
package android.gui;
import android.gui.DisplayMode;
+import android.gui.FrameRateCategoryRate;
import android.gui.HdrCapabilities;
// Information about a physical display which may change on hotplug reconnect.
@@ -46,4 +47,10 @@
// Represents whether display supports ARR.
boolean hasArrSupport;
+
+ // Represents frame rate for FrameRateCategory Normal and High.
+ FrameRateCategoryRate frameRateCategoryRate;
+
+ // All the refresh rates supported for the default display mode.
+ float[] supportedRefreshRates;
}
diff --git a/libs/binder/trusty/ndk/include/android/llndk-versioning.h b/libs/gui/aidl/android/gui/FrameRateCategoryRate.aidl
similarity index 71%
rename from libs/binder/trusty/ndk/include/android/llndk-versioning.h
rename to libs/gui/aidl/android/gui/FrameRateCategoryRate.aidl
index e955a34..f302801 100644
--- a/libs/binder/trusty/ndk/include/android/llndk-versioning.h
+++ b/libs/gui/aidl/android/gui/FrameRateCategoryRate.aidl
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2024 The Android Open Source Project
+ * Copyright 2024 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -13,7 +13,12 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-#pragma once
-// TODO(b/349936395): set to true for Trusty
-#define API_LEVEL_AT_LEAST(sdk_api_level, vendor_api_level) (false)
+package android.gui;
+
+/** @hide */
+// Represents frame rate for FrameRateCategory Normal and High.
+parcelable FrameRateCategoryRate {
+ float normal;
+ float high;
+}
\ No newline at end of file
diff --git a/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockPowerHintSessionWrapper.cpp b/libs/gui/aidl/android/gui/IActivePictureListener.aidl
similarity index 62%
copy from services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockPowerHintSessionWrapper.cpp
copy to libs/gui/aidl/android/gui/IActivePictureListener.aidl
index d383283..ad310bb 100644
--- a/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockPowerHintSessionWrapper.cpp
+++ b/libs/gui/aidl/android/gui/IActivePictureListener.aidl
@@ -14,12 +14,17 @@
* limitations under the License.
*/
-#include "mock/DisplayHardware/MockPowerHintSessionWrapper.h"
+package android.gui;
-namespace android::Hwc2::mock {
+import android.gui.ActivePicture;
-// Explicit default instantiation is recommended.
-MockPowerHintSessionWrapper::MockPowerHintSessionWrapper()
- : power::PowerHintSessionWrapper(nullptr) {}
-
-} // namespace android::Hwc2::mock
+/**
+ * Receive callbacks whenever the visible content using picture profiles changes.
+ * @hide
+ */
+interface IActivePictureListener {
+ /**
+ * Callback reporting the visible content on the screen using picture profiles.
+ */
+ oneway void onActivePicturesChanged(in ActivePicture[] activePictures);
+}
diff --git a/libs/gui/aidl/android/gui/ISurfaceComposer.aidl b/libs/gui/aidl/android/gui/ISurfaceComposer.aidl
index ac14138..8c19bbb 100644
--- a/libs/gui/aidl/android/gui/ISurfaceComposer.aidl
+++ b/libs/gui/aidl/android/gui/ISurfaceComposer.aidl
@@ -33,6 +33,7 @@
import android.gui.FrameStats;
import android.gui.HdrConversionCapability;
import android.gui.HdrConversionStrategy;
+import android.gui.IActivePictureListener;
import android.gui.IDisplayEventConnection;
import android.gui.IFpsListener;
import android.gui.IHdrLayerInfoListener;
@@ -228,6 +229,11 @@
void setGameContentType(IBinder display, boolean on);
/**
+ * Gets the maximum number of picture profiles supported by the display.
+ */
+ int getMaxLayerPictureProfiles(IBinder display);
+
+ /**
* Capture the specified screen. This requires READ_FRAME_BUFFER
* permission. This function will fail if there is a secure window on
* screen and DisplayCaptureArgs.captureSecureLayers is false.
@@ -599,4 +605,10 @@
* past the provided VSync.
*/
oneway void removeJankListener(int layerId, IJankListener listener, long afterVsync);
+
+ /**
+ * Sets the listener used to monitor visible content that is being processed with picture
+ * profiles.
+ */
+ oneway void setActivePictureListener(IActivePictureListener listener);
}
diff --git a/libs/gui/aidl/android/gui/LutProperties.aidl b/libs/gui/aidl/android/gui/LutProperties.aidl
index 87b878c..84c7013 100644
--- a/libs/gui/aidl/android/gui/LutProperties.aidl
+++ b/libs/gui/aidl/android/gui/LutProperties.aidl
@@ -27,6 +27,6 @@
int size;
@Backing(type="int")
- enum SamplingKey { RGB, MAX_RGB }
+ enum SamplingKey { RGB, MAX_RGB, CIE_Y }
SamplingKey[] samplingKeys;
}
\ No newline at end of file
diff --git a/libs/gui/include/gui/BLASTBufferQueue.h b/libs/gui/include/gui/BLASTBufferQueue.h
index 99c64da..07558aa 100644
--- a/libs/gui/include/gui/BLASTBufferQueue.h
+++ b/libs/gui/include/gui/BLASTBufferQueue.h
@@ -17,7 +17,9 @@
#ifndef ANDROID_GUI_BLAST_BUFFER_QUEUE_H
#define ANDROID_GUI_BLAST_BUFFER_QUEUE_H
-#include <com_android_graphics_libgui_flags.h>
+#include <optional>
+#include <queue>
+
#include <gui/BufferItem.h>
#include <gui/BufferItemConsumer.h>
#include <gui/IGraphicBufferConsumer.h>
@@ -29,7 +31,6 @@
#include <utils/RefBase.h>
#include <system/window.h>
-#include <queue>
#include <com_android_graphics_libgui_flags.h>
@@ -222,6 +223,10 @@
ui::Size mRequestedSize GUARDED_BY(mMutex);
int32_t mFormat GUARDED_BY(mMutex);
+ // Keep a copy of the current picture profile handle, so it can be moved to a new
+ // SurfaceControl when BBQ migrates via ::update.
+ std::optional<PictureProfileHandle> mPictureProfileHandle;
+
struct BufferInfo {
bool hasBuffer = false;
uint32_t width;
@@ -325,6 +330,14 @@
std::unique_ptr<gui::BufferReleaseChannel::ConsumerEndpoint> mBufferReleaseConsumer;
std::shared_ptr<gui::BufferReleaseChannel::ProducerEndpoint> mBufferReleaseProducer;
+ void updateBufferReleaseProducer() REQUIRES(mMutex);
+ void drainBufferReleaseConsumer();
+
+ // BufferReleaseReader is used to do blocking but interruptible reads from the buffer
+ // release channel. To implement this, BufferReleaseReader owns an epoll file descriptor that
+ // is configured to wake up when either the BufferReleaseReader::ConsumerEndpoint or an eventfd
+ // becomes readable. Interrupts are necessary because a free buffer may become available for
+ // reasons other than a buffer release from the producer.
class BufferReleaseReader {
public:
explicit BufferReleaseReader(BLASTBufferQueue&);
@@ -353,19 +366,6 @@
};
std::optional<BufferReleaseReader> mBufferReleaseReader;
-
- std::atomic<int> mThreadsBlockingOnDequeue = 0;
-
- class BufferReleaseThread {
- public:
- BufferReleaseThread(const sp<BLASTBufferQueue>&);
- ~BufferReleaseThread();
-
- private:
- int mEventFd;
- };
-
- std::optional<BufferReleaseThread> mBufferReleaseThread;
#endif
};
diff --git a/libs/gui/include/gui/BufferItem.h b/libs/gui/include/gui/BufferItem.h
index 218bb42..2f85c62 100644
--- a/libs/gui/include/gui/BufferItem.h
+++ b/libs/gui/include/gui/BufferItem.h
@@ -17,9 +17,12 @@
#ifndef ANDROID_GUI_BUFFERITEM_H
#define ANDROID_GUI_BUFFERITEM_H
+#include <optional>
+
#include <gui/HdrMetadata.h>
#include <ui/FenceTime.h>
+#include <ui/PictureProfileHandle.h>
#include <ui/Rect.h>
#include <ui/Region.h>
@@ -91,6 +94,10 @@
// mHdrMetadata is the HDR metadata associated with this buffer slot.
HdrMetadata mHdrMetadata;
+ // mPictureProfileHandle is a handle that points to a set of parameters that configure picture
+ // processing hardware to enhance the quality of buffer contents.
+ std::optional<PictureProfileHandle> mPictureProfileHandle;
+
// mFrameNumber is the number of the queued frame for this slot.
uint64_t mFrameNumber;
diff --git a/libs/gui/include/gui/BufferSlot.h b/libs/gui/include/gui/BufferSlot.h
index 5b32710..e83d2e3 100644
--- a/libs/gui/include/gui/BufferSlot.h
+++ b/libs/gui/include/gui/BufferSlot.h
@@ -174,26 +174,30 @@
};
struct BufferSlot {
-
BufferSlot()
- : mGraphicBuffer(nullptr),
- mEglDisplay(EGL_NO_DISPLAY),
- mBufferState(),
- mRequestBufferCalled(false),
- mFrameNumber(0),
- mEglFence(EGL_NO_SYNC_KHR),
- mFence(Fence::NO_FENCE),
- mAcquireCalled(false),
- mNeedsReallocation(false) {
+ : mGraphicBuffer(nullptr),
+#if !COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(BQ_GL_FENCE_CLEANUP)
+ mEglDisplay(EGL_NO_DISPLAY),
+#endif
+ mBufferState(),
+ mRequestBufferCalled(false),
+ mFrameNumber(0),
+#if !COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(BQ_GL_FENCE_CLEANUP)
+ mEglFence(EGL_NO_SYNC_KHR),
+#endif
+ mFence(Fence::NO_FENCE),
+ mAcquireCalled(false),
+ mNeedsReallocation(false) {
}
// mGraphicBuffer points to the buffer allocated for this slot or is NULL
// if no buffer has been allocated.
sp<GraphicBuffer> mGraphicBuffer;
+#if !COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(BQ_GL_FENCE_CLEANUP)
// mEglDisplay is the EGLDisplay used to create EGLSyncKHR objects.
EGLDisplay mEglDisplay;
-
+#endif
// mBufferState is the current state of this buffer slot.
BufferState mBufferState;
@@ -207,12 +211,14 @@
// may be released before their release fence is signaled).
uint64_t mFrameNumber;
+#if !COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(BQ_GL_FENCE_CLEANUP)
// mEglFence is the EGL sync object that must signal before the buffer
// associated with this buffer slot may be dequeued. It is initialized
// to EGL_NO_SYNC_KHR when the buffer is created and may be set to a
// new sync object in releaseBuffer. (This is deprecated in favor of
// mFence, below.)
EGLSyncKHR mEglFence;
+#endif
// mFence is a fence which will signal when work initiated by the
// previous owner of the buffer is finished. When the buffer is FREE,
diff --git a/libs/gui/include/gui/DisplayEventReceiver.h b/libs/gui/include/gui/DisplayEventReceiver.h
index 4dbf9e1..40a6e79 100644
--- a/libs/gui/include/gui/DisplayEventReceiver.h
+++ b/libs/gui/include/gui/DisplayEventReceiver.h
@@ -119,7 +119,7 @@
HdcpLevelsChange hdcpLevelsChange;
};
};
- static_assert(sizeof(Event) == 216);
+ static_assert(sizeof(Event) == 224);
public:
/*
diff --git a/libs/gui/include/gui/DisplayLuts.h b/libs/gui/include/gui/DisplayLuts.h
index 16a360d..ab86ac4 100644
--- a/libs/gui/include/gui/DisplayLuts.h
+++ b/libs/gui/include/gui/DisplayLuts.h
@@ -16,16 +16,24 @@
#pragma once
#include <android-base/unique_fd.h>
+#include <binder/Parcel.h>
+#include <binder/Parcelable.h>
#include <vector>
namespace android::gui {
-struct DisplayLuts {
+struct DisplayLuts : public Parcelable {
public:
- struct Entry {
+ struct Entry : public Parcelable {
+ Entry() {};
+ Entry(int32_t lutDimension, int32_t lutSize, int32_t lutSamplingKey)
+ : dimension(lutDimension), size(lutSize), samplingKey(lutSamplingKey) {}
int32_t dimension;
int32_t size;
int32_t samplingKey;
+
+ status_t writeToParcel(android::Parcel* parcel) const override;
+ status_t readFromParcel(const android::Parcel* parcel) override;
};
DisplayLuts() {}
@@ -42,7 +50,10 @@
}
}
- base::unique_fd& getLutFileDescriptor() { return fd; }
+ status_t writeToParcel(android::Parcel* parcel) const override;
+ status_t readFromParcel(const android::Parcel* parcel) override;
+
+ const base::unique_fd& getLutFileDescriptor() const { return fd; }
std::vector<Entry> lutProperties;
std::vector<int32_t> offsets;
diff --git a/libs/gui/include/gui/Flags.h b/libs/gui/include/gui/Flags.h
index 735375a..845bc54 100644
--- a/libs/gui/include/gui/Flags.h
+++ b/libs/gui/include/gui/Flags.h
@@ -17,8 +17,40 @@
#pragma once
#include <com_android_graphics_libgui_flags.h>
+#include <utils/StrongPointer.h>
+
+namespace android {
+
+class IGraphicBufferProducer;
+class Surface;
+namespace view {
+class Surface;
+}
#define WB_CAMERA3_AND_PROCESSORS_WITH_DEPENDENCIES \
(COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(WB_CAMERA3_AND_PROCESSORS) && \
COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(WB_CONSUMER_BASE_OWNS_BQ) && \
- COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(WB_PLATFORM_API_IMPROVEMENTS))
\ No newline at end of file
+ COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(WB_PLATFORM_API_IMPROVEMENTS))
+
+#define WB_LIBCAMERASERVICE_WITH_DEPENDENCIES \
+ (WB_CAMERA3_AND_PROCESSORS_WITH_DEPENDENCIES && \
+ COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(WB_LIBCAMERASERVICE))
+
+#if WB_LIBCAMERASERVICE_WITH_DEPENDENCIES
+typedef android::Surface SurfaceType;
+typedef android::view::Surface ParcelableSurfaceType;
+#else
+typedef android::IGraphicBufferProducer SurfaceType;
+typedef android::sp<android::IGraphicBufferProducer> ParcelableSurfaceType;
+#endif
+
+namespace flagtools {
+sp<SurfaceType> surfaceToSurfaceType(const sp<Surface>& surface);
+ParcelableSurfaceType toParcelableSurfaceType(const view::Surface& surface);
+sp<IGraphicBufferProducer> surfaceTypeToIGBP(const sp<SurfaceType>& surface);
+bool isSurfaceTypeValid(const sp<SurfaceType>& surface);
+ParcelableSurfaceType convertSurfaceTypeToParcelable(sp<SurfaceType> surface);
+sp<SurfaceType> convertParcelableSurfaceTypeToSurface(const ParcelableSurfaceType& surface);
+} // namespace flagtools
+
+} // namespace android
diff --git a/libs/gui/include/gui/GLConsumer.h b/libs/gui/include/gui/GLConsumer.h
index bfe3eb3..8a66dc0 100644
--- a/libs/gui/include/gui/GLConsumer.h
+++ b/libs/gui/include/gui/GLConsumer.h
@@ -268,9 +268,9 @@
// releaseBufferLocked overrides the ConsumerBase method to update the
// mEglSlots array in addition to the ConsumerBase.
- virtual status_t releaseBufferLocked(int slot,
- const sp<GraphicBuffer> graphicBuffer,
- EGLDisplay display, EGLSyncKHR eglFence) override;
+ virtual status_t releaseBufferLocked(int slot, const sp<GraphicBuffer> graphicBuffer,
+ EGLDisplay display = EGL_NO_DISPLAY,
+ EGLSyncKHR eglFence = EGL_NO_SYNC_KHR) override;
status_t releaseBufferLocked(int slot,
const sp<GraphicBuffer> graphicBuffer, EGLSyncKHR eglFence) {
diff --git a/libs/gui/include/gui/IGraphicBufferConsumer.h b/libs/gui/include/gui/IGraphicBufferConsumer.h
index 0b92e7d..18f5488 100644
--- a/libs/gui/include/gui/IGraphicBufferConsumer.h
+++ b/libs/gui/include/gui/IGraphicBufferConsumer.h
@@ -137,16 +137,9 @@
virtual status_t releaseBuffer(int buf, uint64_t frameNumber, EGLDisplay display,
EGLSyncKHR fence, const sp<Fence>& releaseFence) = 0;
- status_t releaseHelper(int buf, uint64_t frameNumber, const sp<Fence>& releaseFence) {
+ status_t releaseBuffer(int buf, uint64_t frameNumber, const sp<Fence>& releaseFence) {
return releaseBuffer(buf, frameNumber, EGL_NO_DISPLAY, EGL_NO_SYNC_KHR, releaseFence);
}
- // This is explicitly *not* the actual signature of IGBC::releaseBuffer, but:
- // 1) We have no easy way to send the EGL objects across Binder
- // 2) This has always been broken, probably because
- // 3) IGBC is rarely remoted
- // For now, we will choose to bury our heads in the sand and ignore this problem until such time
- // as we can finally finish converting away from EGL sync to native Android sync
- using ReleaseBuffer = decltype(&IGraphicBufferConsumer::releaseHelper);
// consumerConnect connects a consumer to the BufferQueue. Only one consumer may be connected,
// and when that consumer disconnects the BufferQueue is placed into the "abandoned" state,
diff --git a/libs/gui/include/gui/IGraphicBufferProducer.h b/libs/gui/include/gui/IGraphicBufferProducer.h
index 3aac457..a42ddc4 100644
--- a/libs/gui/include/gui/IGraphicBufferProducer.h
+++ b/libs/gui/include/gui/IGraphicBufferProducer.h
@@ -19,6 +19,7 @@
#include <stdint.h>
#include <sys/types.h>
+#include <optional>
#include <utils/Errors.h>
#include <utils/RefBase.h>
@@ -28,6 +29,7 @@
#include <ui/BufferQueueDefs.h>
#include <ui/Fence.h>
#include <ui/GraphicBuffer.h>
+#include <ui/PictureProfileHandle.h>
#include <ui/Rect.h>
#include <ui/Region.h>
@@ -365,6 +367,14 @@
const HdrMetadata& getHdrMetadata() const { return hdrMetadata; }
void setHdrMetadata(const HdrMetadata& metadata) { hdrMetadata = metadata; }
+ const std::optional<PictureProfileHandle>& getPictureProfileHandle() const {
+ return pictureProfileHandle;
+ }
+ void setPictureProfileHandle(const PictureProfileHandle& profile) {
+ pictureProfileHandle = profile;
+ }
+ void clearPictureProfileHandle() { pictureProfileHandle = std::nullopt; }
+
int64_t timestamp{0};
int isAutoTimestamp{0};
android_dataspace dataSpace{HAL_DATASPACE_UNKNOWN};
@@ -377,6 +387,7 @@
bool getFrameTimestamps{false};
int slot{-1};
HdrMetadata hdrMetadata;
+ std::optional<PictureProfileHandle> pictureProfileHandle;
};
struct QueueBufferOutput : public Flattenable<QueueBufferOutput> {
@@ -403,7 +414,7 @@
uint64_t nextFrameNumber{0};
FrameEventHistoryDelta frameTimestamps;
bool bufferReplaced{false};
- int maxBufferCount{0};
+ int maxBufferCount{BufferQueueDefs::NUM_BUFFER_SLOTS};
status_t result{NO_ERROR};
};
diff --git a/libs/gui/include/gui/LayerState.h b/libs/gui/include/gui/LayerState.h
index 6bfeaec..1c31e46 100644
--- a/libs/gui/include/gui/LayerState.h
+++ b/libs/gui/include/gui/LayerState.h
@@ -47,6 +47,7 @@
#include <ui/BlurRegion.h>
#include <ui/GraphicTypes.h>
#include <ui/LayerStack.h>
+#include <ui/PictureProfileHandle.h>
#include <ui/Rect.h>
#include <ui/Region.h>
#include <ui/Rotation.h>
@@ -170,6 +171,10 @@
// Sets a property on this layer indicating that its visible region should be considered
// when computing TrustedPresentation Thresholds.
eCanOccludePresentation = 0x1000,
+ // Indicates that the SurfaceControl should recover from buffer stuffing when
+ // possible. This is the case when the SurfaceControl is the root SurfaceControl
+ // owned by ViewRootImpl.
+ eRecoverableFromBufferStuffing = 0x2000,
};
enum {
@@ -224,6 +229,8 @@
eExtendedRangeBrightnessChanged = 0x10000'00000000,
eEdgeExtensionChanged = 0x20000'00000000,
eBufferReleaseChannelChanged = 0x40000'00000000,
+ ePictureProfileHandleChanged = 0x80000'00000000,
+ eAppContentPriorityChanged = 0x100000'00000000,
};
layer_state_t();
@@ -267,7 +274,8 @@
layer_state_t::eColorSpaceAgnosticChanged | layer_state_t::eColorTransformChanged |
layer_state_t::eCornerRadiusChanged | layer_state_t::eDimmingEnabledChanged |
layer_state_t::eHdrMetadataChanged | layer_state_t::eShadowRadiusChanged |
- layer_state_t::eStretchChanged;
+ layer_state_t::eStretchChanged | layer_state_t::ePictureProfileHandleChanged |
+ layer_state_t::eAppContentPriorityChanged;
// Changes which invalidates the layer's visible region in CE.
static constexpr uint64_t CONTENT_DIRTY = layer_state_t::CONTENT_CHANGES |
@@ -412,6 +420,15 @@
float currentHdrSdrRatio = 1.f;
float desiredHdrSdrRatio = 1.f;
+ // Enhance the quality of the buffer contents by configurating a picture processing pipeline
+ // with values as specified by this picture profile.
+ PictureProfileHandle pictureProfileHandle{PictureProfileHandle::NONE};
+
+ // A value indicating the significance of the layer's content to the app's desired user
+ // experience. A lower priority will result in more likelihood of getting access to limited
+ // resources, such as picture processing hardware.
+ int32_t appContentPriority = 0;
+
gui::CachingHint cachingHint = gui::CachingHint::Enabled;
TrustedPresentationThresholds trustedPresentationThresholds;
diff --git a/libs/gui/include/gui/SurfaceComposerClient.h b/libs/gui/include/gui/SurfaceComposerClient.h
index 5ea0c16..0ce0c0a 100644
--- a/libs/gui/include/gui/SurfaceComposerClient.h
+++ b/libs/gui/include/gui/SurfaceComposerClient.h
@@ -38,6 +38,7 @@
#include <ui/EdgeExtensionEffect.h>
#include <ui/FrameStats.h>
#include <ui/GraphicTypes.h>
+#include <ui/PictureProfileHandle.h>
#include <ui/PixelFormat.h>
#include <ui/Rotation.h>
#include <ui/StaticDisplayInfo.h>
@@ -297,6 +298,8 @@
static status_t removeHdrLayerInfoListener(const sp<IBinder>& displayToken,
const sp<gui::IHdrLayerInfoListener>& listener);
+ static status_t setActivePictureListener(const sp<gui::IActivePictureListener>& listener);
+
/*
* Sends a power boost to the composer. This function is asynchronous.
*
@@ -342,6 +345,15 @@
static bool flagEdgeExtensionEffectUseShader();
+ /**
+ * Returns how many picture profiles are supported by the display.
+ *
+ * displayToken
+ * The token of the display.
+ */
+ static status_t getMaxLayerPictureProfiles(const sp<IBinder>& displayToken,
+ int32_t* outMaxProfiles);
+
// ------------------------------------------------------------------------
// surface creation / destruction
@@ -437,6 +449,8 @@
static void mergeFrameTimelineInfo(FrameTimelineInfo& t, const FrameTimelineInfo& other);
// Tracks registered callbacks
sp<TransactionCompletedListener> mTransactionCompletedListener = nullptr;
+ // Prints debug logs when enabled.
+ bool mLogCallPoints = false;
protected:
std::unordered_map<sp<IBinder>, ComposerState, IBinderHash> mComposerStates;
@@ -685,7 +699,8 @@
// ONLY FOR BLAST ADAPTER
Transaction& notifyProducerDisconnect(const sp<SurfaceControl>& sc);
- Transaction& setInputWindowInfo(const sp<SurfaceControl>& sc, const gui::WindowInfo& info);
+ Transaction& setInputWindowInfo(const sp<SurfaceControl>& sc,
+ sp<gui::WindowInfoHandle> info);
Transaction& setFocusedWindow(const gui::FocusRequest& request);
Transaction& addWindowInfosReportedListener(
@@ -773,6 +788,20 @@
const sp<SurfaceControl>& sc,
const std::shared_ptr<gui::BufferReleaseChannel::ProducerEndpoint>& channel);
+ /**
+ * Configures a surface control to use picture processing hardware, configured as specified
+ * by the picture profile, to enhance the quality of all subsequent buffer contents.
+ */
+ Transaction& setPictureProfileHandle(const sp<SurfaceControl>& sc,
+ const PictureProfileHandle& pictureProfileHandle);
+
+ /**
+ * Configures the relative importance of the contents of the layer with respect to the app's
+ * user experience. A lower priority value will give the layer preferred access to limited
+ * resources, such as picture processing, over a layer with a higher priority value.
+ */
+ Transaction& setContentPriority(const sp<SurfaceControl>& sc, int32_t contentPriority);
+
status_t setDisplaySurface(const sp<IBinder>& token,
const sp<IGraphicBufferProducer>& bufferProducer);
@@ -809,6 +838,7 @@
static void setDefaultApplyToken(sp<IBinder> applyToken);
static status_t sendSurfaceFlushJankDataTransaction(const sp<SurfaceControl>& sc);
+ void enableDebugLogCallPoints();
};
status_t clearLayerFrameStats(const sp<IBinder>& token) const;
diff --git a/libs/gui/include/gui/VsyncEventData.h b/libs/gui/include/gui/VsyncEventData.h
index b40a840..ced5130 100644
--- a/libs/gui/include/gui/VsyncEventData.h
+++ b/libs/gui/include/gui/VsyncEventData.h
@@ -36,6 +36,9 @@
// Size of frame timelines provided by the platform; max is kFrameTimelinesCapacity.
uint32_t frameTimelinesLength;
+ // Number of queued buffers to indicate if buffer stuffing mode is detected.
+ uint32_t numberQueuedBuffers;
+
struct alignas(8) FrameTimeline {
// The Vsync Id corresponsing to this vsync event. This will be used to
// populate ISurfaceComposer::setFrameTimelineVsync and
diff --git a/libs/gui/include/gui/view/Surface.h b/libs/gui/include/gui/view/Surface.h
index 7ddac81..bd8704d 100644
--- a/libs/gui/include/gui/view/Surface.h
+++ b/libs/gui/include/gui/view/Surface.h
@@ -24,7 +24,9 @@
#include <binder/IBinder.h>
#include <binder/Parcelable.h>
+#include <gui/Flags.h>
#include <gui/IGraphicBufferProducer.h>
+#include <gui/Surface.h>
namespace android {
@@ -46,6 +48,30 @@
sp<IGraphicBufferProducer> graphicBufferProducer;
sp<IBinder> surfaceControlHandle;
+#if WB_LIBCAMERASERVICE_WITH_DEPENDENCIES
+ // functions used to convert to a parcelable Surface so it can be passed over binder.
+ static Surface fromSurface(const sp<android::Surface>& surface);
+ sp<android::Surface> toSurface() const;
+
+ status_t getUniqueId(/* out */ uint64_t* id) const;
+
+ bool isEmpty() const;
+
+ bool operator==(const Surface& other) const {
+ return graphicBufferProducer == other.graphicBufferProducer;
+ }
+ bool operator!=(const Surface& other) const { return !(*this == other); }
+ bool operator==(const sp<android::Surface> other) const {
+ if (other == nullptr) return graphicBufferProducer == nullptr;
+ return graphicBufferProducer == other->getIGraphicBufferProducer();
+ }
+ bool operator!=(const sp<android::Surface> other) const { return !(*this == other); }
+ bool operator<(const Surface& other) const {
+ return graphicBufferProducer < other.graphicBufferProducer;
+ }
+ bool operator>(const Surface& other) const { return other < *this; }
+#endif
+
virtual status_t writeToParcel(Parcel* parcel) const override;
virtual status_t readFromParcel(const Parcel* parcel) override;
diff --git a/libs/gui/libgui_flags.aconfig b/libs/gui/libgui_flags.aconfig
index 1c7e0e4..6bf38c0 100644
--- a/libs/gui/libgui_flags.aconfig
+++ b/libs/gui/libgui_flags.aconfig
@@ -2,6 +2,14 @@
container: "system"
flag {
+ name: "apply_picture_profiles"
+ namespace: "tv_os_media"
+ description: "This flag controls sending picture profiles from BBQ to Composer HAL"
+ bug: "337330263"
+ is_fixed_read_only: true
+} # apply_picture_profiles
+
+flag {
name: "bq_setframerate"
namespace: "core_graphics"
description: "This flag controls plumbing setFrameRate thru BufferQueue"
@@ -123,3 +131,11 @@
bug: "359252619"
is_fixed_read_only: true
} # bq_producer_throttles_only_async_mode
+
+flag {
+ name: "bq_gl_fence_cleanup"
+ namespace: "core_graphics"
+ description: "Remove BufferQueueProducer::dequeue's wait on this fence (or the fence entirely) to prevent deadlocks"
+ bug: "339705065"
+ is_fixed_read_only: true
+} # bq_gl_fence_cleanup
diff --git a/libs/gui/tests/BufferQueue_test.cpp b/libs/gui/tests/BufferQueue_test.cpp
index 2e6ffcb..1606099 100644
--- a/libs/gui/tests/BufferQueue_test.cpp
+++ b/libs/gui/tests/BufferQueue_test.cpp
@@ -27,6 +27,7 @@
#include <gui/Surface.h>
#include <ui/GraphicBuffer.h>
+#include <ui/PictureProfileHandle.h>
#include <android-base/properties.h>
@@ -424,8 +425,7 @@
ASSERT_EQ(BAD_VALUE, mConsumer->attachBuffer(&newSlot, nullptr));
ASSERT_EQ(OK, mConsumer->attachBuffer(&newSlot, item.mGraphicBuffer));
- ASSERT_EQ(OK, mConsumer->releaseBuffer(newSlot, 0, EGL_NO_DISPLAY,
- EGL_NO_SYNC_KHR, Fence::NO_FENCE));
+ ASSERT_EQ(OK, mConsumer->releaseBuffer(newSlot, 0, Fence::NO_FENCE));
ASSERT_EQ(IGraphicBufferProducer::BUFFER_NEEDS_REALLOCATION,
mProducer->dequeueBuffer(&slot, &fence, 0, 0, 0, GRALLOC_USAGE_SW_WRITE_OFTEN,
@@ -608,8 +608,7 @@
ASSERT_EQ(true, item.mQueuedBuffer);
ASSERT_EQ(false, item.mAutoRefresh);
- ASSERT_EQ(OK, mConsumer->releaseBuffer(item.mSlot, item.mFrameNumber,
- EGL_NO_DISPLAY, EGL_NO_SYNC_KHR, Fence::NO_FENCE));
+ ASSERT_EQ(OK, mConsumer->releaseBuffer(item.mSlot, item.mFrameNumber, Fence::NO_FENCE));
// attempt to acquire a second time should return no buffer available
ASSERT_EQ(IGraphicBufferConsumer::NO_BUFFER_AVAILABLE,
@@ -652,8 +651,7 @@
ASSERT_EQ(i == 0, item.mQueuedBuffer);
ASSERT_EQ(true, item.mAutoRefresh);
- ASSERT_EQ(OK, mConsumer->releaseBuffer(item.mSlot, item.mFrameNumber,
- EGL_NO_DISPLAY, EGL_NO_SYNC_KHR, Fence::NO_FENCE));
+ ASSERT_EQ(OK, mConsumer->releaseBuffer(item.mSlot, item.mFrameNumber, Fence::NO_FENCE));
}
// Repeatedly queue and dequeue a buffer from the producer side, it should
@@ -683,8 +681,7 @@
ASSERT_EQ(i == 0, item.mQueuedBuffer);
ASSERT_EQ(true, item.mAutoRefresh);
- ASSERT_EQ(OK, mConsumer->releaseBuffer(item.mSlot, item.mFrameNumber,
- EGL_NO_DISPLAY, EGL_NO_SYNC_KHR, Fence::NO_FENCE));
+ ASSERT_EQ(OK, mConsumer->releaseBuffer(item.mSlot, item.mFrameNumber, Fence::NO_FENCE));
}
}
@@ -734,8 +731,7 @@
ASSERT_EQ(true, item.mQueuedBuffer);
ASSERT_EQ(false, item.mAutoRefresh);
- ASSERT_EQ(OK, mConsumer->releaseBuffer(item.mSlot, item.mFrameNumber,
- EGL_NO_DISPLAY, EGL_NO_SYNC_KHR, Fence::NO_FENCE));
+ ASSERT_EQ(OK, mConsumer->releaseBuffer(item.mSlot, item.mFrameNumber, Fence::NO_FENCE));
// attempt to acquire a second time should return no buffer available
ASSERT_EQ(IGraphicBufferConsumer::NO_BUFFER_AVAILABLE,
@@ -873,8 +869,7 @@
for (size_t i = 0; i < 2; ++i) {
BufferItem item;
ASSERT_EQ(OK, mConsumer->acquireBuffer(&item, 0));
- ASSERT_EQ(OK, mConsumer->releaseBuffer(item.mSlot, item.mFrameNumber,
- EGL_NO_DISPLAY, EGL_NO_SYNC_KHR, Fence::NO_FENCE));
+ ASSERT_EQ(OK, mConsumer->releaseBuffer(item.mSlot, item.mFrameNumber, Fence::NO_FENCE));
}
// Make sure we got the second buffer back
@@ -928,8 +923,7 @@
nullptr, nullptr));
ASSERT_EQ(OK, mProducer->queueBuffer(slot, input, &output));
ASSERT_EQ(OK, mConsumer->acquireBuffer(&item, 0));
- ASSERT_EQ(OK, mConsumer->releaseBuffer(item.mSlot, item.mFrameNumber,
- EGL_NO_DISPLAY, EGL_NO_SYNC_KHR, Fence::NO_FENCE));
+ ASSERT_EQ(OK, mConsumer->releaseBuffer(item.mSlot, item.mFrameNumber, Fence::NO_FENCE));
std::this_thread::sleep_for(16ms);
}
@@ -945,8 +939,7 @@
nullptr, nullptr));
ASSERT_EQ(OK, mProducer->queueBuffer(slot, input, &output));
ASSERT_EQ(OK, mConsumer->acquireBuffer(&item, 0));
- ASSERT_EQ(OK, mConsumer->releaseBuffer(item.mSlot, item.mFrameNumber,
- EGL_NO_DISPLAY, EGL_NO_SYNC_KHR, Fence::NO_FENCE));
+ ASSERT_EQ(OK, mConsumer->releaseBuffer(item.mSlot, item.mFrameNumber, Fence::NO_FENCE));
std::this_thread::sleep_for(16ms);
}
ASSERT_EQ(OK,
@@ -958,12 +951,10 @@
nullptr));
ASSERT_EQ(OK, mProducer->queueBuffer(slot, input, &output));
ASSERT_EQ(OK, mConsumer->acquireBuffer(&item, 0));
- ASSERT_EQ(OK, mConsumer->releaseBuffer(item.mSlot, item.mFrameNumber,
- EGL_NO_DISPLAY, EGL_NO_SYNC_KHR, Fence::NO_FENCE));
+ ASSERT_EQ(OK, mConsumer->releaseBuffer(item.mSlot, item.mFrameNumber, Fence::NO_FENCE));
std::this_thread::sleep_for(16ms);
ASSERT_EQ(OK, mConsumer->acquireBuffer(&item, 0));
- ASSERT_EQ(OK, mConsumer->releaseBuffer(item.mSlot, item.mFrameNumber,
- EGL_NO_DISPLAY, EGL_NO_SYNC_KHR, Fence::NO_FENCE));
+ ASSERT_EQ(OK, mConsumer->releaseBuffer(item.mSlot, item.mFrameNumber, Fence::NO_FENCE));
// Sleep between segments
std::this_thread::sleep_for(500ms);
@@ -980,13 +971,11 @@
nullptr, nullptr));
ASSERT_EQ(OK, mProducer->queueBuffer(slot, input, &output));
ASSERT_EQ(OK, mConsumer->acquireBuffer(&item, 0));
- ASSERT_EQ(OK, mConsumer->releaseBuffer(item.mSlot, item.mFrameNumber,
- EGL_NO_DISPLAY, EGL_NO_SYNC_KHR, Fence::NO_FENCE));
+ ASSERT_EQ(OK, mConsumer->releaseBuffer(item.mSlot, item.mFrameNumber, Fence::NO_FENCE));
std::this_thread::sleep_for(16ms);
}
ASSERT_EQ(OK, mConsumer->acquireBuffer(&item, 0));
- ASSERT_EQ(OK, mConsumer->releaseBuffer(item.mSlot, item.mFrameNumber,
- EGL_NO_DISPLAY, EGL_NO_SYNC_KHR, Fence::NO_FENCE));
+ ASSERT_EQ(OK, mConsumer->releaseBuffer(item.mSlot, item.mFrameNumber, Fence::NO_FENCE));
// Now we read the segments
std::vector<OccupancyTracker::Segment> history;
@@ -1107,8 +1096,7 @@
// Acquire and free 1 buffer
ASSERT_EQ(OK, mConsumer->acquireBuffer(&item, 0));
- ASSERT_EQ(OK, mConsumer->releaseBuffer(item.mSlot, item.mFrameNumber,
- EGL_NO_DISPLAY, EGL_NO_SYNC_KHR, Fence::NO_FENCE));
+ ASSERT_EQ(OK, mConsumer->releaseBuffer(item.mSlot, item.mFrameNumber, Fence::NO_FENCE));
int releasedSlot = item.mSlot;
// Acquire 1 buffer, leaving 1 filled buffer in queue
@@ -1375,8 +1363,7 @@
ASSERT_EQ(OK, mConsumer->acquireBuffer(&item, 0));
ASSERT_EQ(slot, item.mSlot);
ASSERT_NE(nullptr, item.mGraphicBuffer.get());
- ASSERT_EQ(OK, mConsumer->releaseBuffer(item.mSlot, item.mFrameNumber,
- EGL_NO_DISPLAY, EGL_NO_SYNC_KHR, Fence::NO_FENCE));
+ ASSERT_EQ(OK, mConsumer->releaseBuffer(item.mSlot, item.mFrameNumber, Fence::NO_FENCE));
// Dequeue and queue the buffer again
ASSERT_EQ(OK,
@@ -1389,8 +1376,7 @@
ASSERT_EQ(OK, mConsumer->acquireBuffer(&item, 0));
ASSERT_EQ(slot, item.mSlot);
ASSERT_EQ(nullptr, item.mGraphicBuffer.get());
- ASSERT_EQ(OK, mConsumer->releaseBuffer(item.mSlot, item.mFrameNumber,
- EGL_NO_DISPLAY, EGL_NO_SYNC_KHR, Fence::NO_FENCE));
+ ASSERT_EQ(OK, mConsumer->releaseBuffer(item.mSlot, item.mFrameNumber, Fence::NO_FENCE));
// Dequeue and queue the buffer again
ASSERT_EQ(OK,
@@ -1569,4 +1555,61 @@
EXPECT_EQ(ADATASPACE_UNKNOWN, dataSpace);
}
+TEST_F(BufferQueueTest, PassesThroughPictureProfileHandle) {
+ createBufferQueue();
+ sp<MockConsumer> mc(new MockConsumer);
+ mConsumer->consumerConnect(mc, false);
+
+ IGraphicBufferProducer::QueueBufferOutput qbo;
+ mProducer->connect(new StubProducerListener, NATIVE_WINDOW_API_CPU, false, &qbo);
+ mProducer->setMaxDequeuedBufferCount(2);
+ mConsumer->setMaxAcquiredBufferCount(2);
+
+ // First try to pass a valid picture profile handle
+ {
+ int slot;
+ sp<Fence> fence;
+ sp<GraphicBuffer> buf;
+ IGraphicBufferProducer::QueueBufferInput qbi(0, false, HAL_DATASPACE_UNKNOWN,
+ Rect(0, 0, 1, 1),
+ NATIVE_WINDOW_SCALING_MODE_FREEZE, 0,
+ Fence::NO_FENCE);
+ qbi.setPictureProfileHandle(PictureProfileHandle(1));
+
+ EXPECT_EQ(IGraphicBufferProducer::BUFFER_NEEDS_REALLOCATION,
+ mProducer->dequeueBuffer(&slot, &fence, 1, 1, 0, GRALLOC_USAGE_SW_READ_OFTEN,
+ nullptr, nullptr));
+ EXPECT_EQ(OK, mProducer->requestBuffer(slot, &buf));
+ EXPECT_EQ(OK, mProducer->queueBuffer(slot, qbi, &qbo));
+
+ BufferItem item;
+ EXPECT_EQ(OK, mConsumer->acquireBuffer(&item, 0));
+
+ ASSERT_TRUE(item.mPictureProfileHandle.has_value());
+ ASSERT_EQ(item.mPictureProfileHandle, PictureProfileHandle(1));
+ }
+
+ // Then validate that the picture profile handle isn't sticky and is reset for the next buffer
+ {
+ int slot;
+ sp<Fence> fence;
+ sp<GraphicBuffer> buf;
+ IGraphicBufferProducer::QueueBufferInput qbi(0, false, HAL_DATASPACE_UNKNOWN,
+ Rect(0, 0, 1, 1),
+ NATIVE_WINDOW_SCALING_MODE_FREEZE, 0,
+ Fence::NO_FENCE);
+
+ EXPECT_EQ(IGraphicBufferProducer::BUFFER_NEEDS_REALLOCATION,
+ mProducer->dequeueBuffer(&slot, &fence, 1, 1, 0, GRALLOC_USAGE_SW_READ_OFTEN,
+ nullptr, nullptr));
+ EXPECT_EQ(OK, mProducer->requestBuffer(slot, &buf));
+ EXPECT_EQ(OK, mProducer->queueBuffer(slot, qbi, &qbo));
+
+ BufferItem item;
+ EXPECT_EQ(OK, mConsumer->acquireBuffer(&item, 0));
+
+ ASSERT_FALSE(item.mPictureProfileHandle.has_value());
+ }
+}
+
} // namespace android
diff --git a/libs/gui/tests/BufferReleaseChannel_test.cpp b/libs/gui/tests/BufferReleaseChannel_test.cpp
index 11d122b..74f69e1 100644
--- a/libs/gui/tests/BufferReleaseChannel_test.cpp
+++ b/libs/gui/tests/BufferReleaseChannel_test.cpp
@@ -29,11 +29,11 @@
// Helper function to check if two file descriptors point to the same file.
bool is_same_file(int fd1, int fd2) {
- struct stat stat1;
+ struct stat stat1 {};
if (fstat(fd1, &stat1) != 0) {
return false;
}
- struct stat stat2;
+ struct stat stat2 {};
if (fstat(fd2, &stat2) != 0) {
return false;
}
@@ -42,7 +42,18 @@
} // namespace
-TEST(BufferReleaseChannelTest, MessageFlattenable) {
+class BufferReleaseChannelTest : public testing::Test {
+protected:
+ std::unique_ptr<BufferReleaseChannel::ConsumerEndpoint> mConsumer;
+ std::shared_ptr<BufferReleaseChannel::ProducerEndpoint> mProducer;
+
+ void SetUp() override {
+ ASSERT_EQ(OK,
+ BufferReleaseChannel::open("BufferReleaseChannelTest"s, mConsumer, mProducer));
+ }
+};
+
+TEST_F(BufferReleaseChannelTest, MessageFlattenable) {
ReleaseCallbackId releaseCallbackId{1, 2};
sp<Fence> releaseFence = sp<Fence>::make(memfd_create("fake-fence-fd", 0));
uint32_t maxAcquiredBufferCount = 5;
@@ -92,31 +103,23 @@
// Verify that the BufferReleaseChannel consume returns WOULD_BLOCK when there's no message
// available.
-TEST(BufferReleaseChannelTest, ConsumerEndpointIsNonBlocking) {
- std::unique_ptr<BufferReleaseChannel::ConsumerEndpoint> consumer;
- std::shared_ptr<BufferReleaseChannel::ProducerEndpoint> producer;
- ASSERT_EQ(OK, BufferReleaseChannel::open("test-channel"s, consumer, producer));
-
+TEST_F(BufferReleaseChannelTest, ConsumerEndpointIsNonBlocking) {
ReleaseCallbackId releaseCallbackId;
sp<Fence> releaseFence;
uint32_t maxAcquiredBufferCount;
ASSERT_EQ(WOULD_BLOCK,
- consumer->readReleaseFence(releaseCallbackId, releaseFence, maxAcquiredBufferCount));
+ mConsumer->readReleaseFence(releaseCallbackId, releaseFence, maxAcquiredBufferCount));
}
// Verify that we can write a message to the BufferReleaseChannel producer and read that message
// using the BufferReleaseChannel consumer.
-TEST(BufferReleaseChannelTest, ProduceAndConsume) {
- std::unique_ptr<BufferReleaseChannel::ConsumerEndpoint> consumer;
- std::shared_ptr<BufferReleaseChannel::ProducerEndpoint> producer;
- ASSERT_EQ(OK, BufferReleaseChannel::open("test-channel"s, consumer, producer));
-
+TEST_F(BufferReleaseChannelTest, ProduceAndConsume) {
sp<Fence> fence = sp<Fence>::make(memfd_create("fake-fence-fd", 0));
for (uint64_t i = 0; i < 64; i++) {
ReleaseCallbackId producerId{i, i + 1};
uint32_t maxAcquiredBufferCount = i + 2;
- ASSERT_EQ(OK, producer->writeReleaseFence(producerId, fence, maxAcquiredBufferCount));
+ ASSERT_EQ(OK, mProducer->writeReleaseFence(producerId, fence, maxAcquiredBufferCount));
}
for (uint64_t i = 0; i < 64; i++) {
@@ -127,7 +130,7 @@
sp<Fence> consumerFence;
uint32_t maxAcquiredBufferCount;
ASSERT_EQ(OK,
- consumer->readReleaseFence(consumerId, consumerFence, maxAcquiredBufferCount));
+ mConsumer->readReleaseFence(consumerId, consumerFence, maxAcquiredBufferCount));
ASSERT_EQ(expectedId, consumerId);
ASSERT_TRUE(is_same_file(fence->get(), consumerFence->get()));
@@ -135,4 +138,16 @@
}
}
+// Verify that BufferReleaseChannel::ConsumerEndpoint's socket can't be written to.
+TEST_F(BufferReleaseChannelTest, ConsumerSocketReadOnly) {
+ uint64_t data = 0;
+ ASSERT_EQ(-1, write(mConsumer->getFd().get(), &data, sizeof(uint64_t)));
+ ASSERT_EQ(errno, EPIPE);
+}
+
+// Verify that BufferReleaseChannel::ProducerEndpoint's socket can't be read from.
+TEST_F(BufferReleaseChannelTest, ProducerSocketWriteOnly) {
+ ASSERT_EQ(0, read(mProducer->getFd().get(), nullptr, sizeof(uint64_t)));
+}
+
} // namespace android
\ No newline at end of file
diff --git a/libs/gui/tests/DisplayEventStructLayout_test.cpp b/libs/gui/tests/DisplayEventStructLayout_test.cpp
index 29eeaa8..791f471 100644
--- a/libs/gui/tests/DisplayEventStructLayout_test.cpp
+++ b/libs/gui/tests/DisplayEventStructLayout_test.cpp
@@ -36,15 +36,16 @@
CHECK_OFFSET(DisplayEventReceiver::Event::VSync, vsyncData.frameInterval, 8);
CHECK_OFFSET(DisplayEventReceiver::Event::VSync, vsyncData.preferredFrameTimelineIndex, 16);
CHECK_OFFSET(DisplayEventReceiver::Event::VSync, vsyncData.frameTimelinesLength, 20);
- CHECK_OFFSET(DisplayEventReceiver::Event::VSync, vsyncData.frameTimelines, 24);
- CHECK_OFFSET(DisplayEventReceiver::Event::VSync, vsyncData.frameTimelines[0].vsyncId, 24);
+ CHECK_OFFSET(DisplayEventReceiver::Event::VSync, vsyncData.numberQueuedBuffers, 24);
+ CHECK_OFFSET(DisplayEventReceiver::Event::VSync, vsyncData.frameTimelines, 32);
+ CHECK_OFFSET(DisplayEventReceiver::Event::VSync, vsyncData.frameTimelines[0].vsyncId, 32);
CHECK_OFFSET(DisplayEventReceiver::Event::VSync, vsyncData.frameTimelines[0].deadlineTimestamp,
- 32);
+ 40);
CHECK_OFFSET(DisplayEventReceiver::Event::VSync,
- vsyncData.frameTimelines[0].expectedPresentationTime, 40);
+ vsyncData.frameTimelines[0].expectedPresentationTime, 48);
// Also test the offsets of the last frame timeline. A loop is not used because the non-const
// index cannot be used in static_assert.
- const int lastFrameTimelineOffset = /* Start of array */ 24 +
+ const int lastFrameTimelineOffset = /* Start of array */ 32 +
(VsyncEventData::kFrameTimelinesCapacity - 1) * /* Size of FrameTimeline */ 24;
CHECK_OFFSET(DisplayEventReceiver::Event::VSync,
vsyncData.frameTimelines[VsyncEventData::kFrameTimelinesCapacity - 1].vsyncId,
diff --git a/libs/gui/tests/EndToEndNativeInputTest.cpp b/libs/gui/tests/EndToEndNativeInputTest.cpp
index 7d0b512..0e84d68 100644
--- a/libs/gui/tests/EndToEndNativeInputTest.cpp
+++ b/libs/gui/tests/EndToEndNativeInputTest.cpp
@@ -112,7 +112,7 @@
mInputFlinger = getInputFlinger();
if (noInputChannel) {
- mInputInfo.setInputConfig(WindowInfo::InputConfig::NO_INPUT_CHANNEL, true);
+ mInputInfo->editInfo()->setInputConfig(WindowInfo::InputConfig::NO_INPUT_CHANNEL, true);
} else {
android::os::InputChannelCore tempChannel;
android::binder::Status result =
@@ -121,21 +121,21 @@
ADD_FAILURE() << "binder call to createInputChannel failed";
}
mClientChannel = InputChannel::create(std::move(tempChannel));
- mInputInfo.token = mClientChannel->getConnectionToken();
+ mInputInfo->editInfo()->token = mClientChannel->getConnectionToken();
mInputConsumer = new InputConsumer(mClientChannel);
}
- mInputInfo.name = "Test info";
- mInputInfo.dispatchingTimeout = 5s;
- mInputInfo.globalScaleFactor = 1.0;
- mInputInfo.touchableRegion.orSelf(Rect(0, 0, width, height));
+ mInputInfo->editInfo()->name = "Test info";
+ mInputInfo->editInfo()->dispatchingTimeout = 5s;
+ mInputInfo->editInfo()->globalScaleFactor = 1.0;
+ mInputInfo->editInfo()->touchableRegion.orSelf(Rect(0, 0, width, height));
InputApplicationInfo aInfo;
aInfo.token = new BBinder();
aInfo.name = "Test app info";
aInfo.dispatchingTimeoutMillis =
std::chrono::duration_cast<std::chrono::milliseconds>(DISPATCHING_TIMEOUT).count();
- mInputInfo.applicationInfo = aInfo;
+ mInputInfo->editInfo()->applicationInfo = aInfo;
}
static std::unique_ptr<InputSurface> makeColorInputSurface(const sp<SurfaceComposerClient>& scc,
@@ -183,20 +183,6 @@
return std::make_unique<InputSurface>(surfaceControl, width, height);
}
- InputEvent* consumeEvent(std::chrono::milliseconds timeout = 3000ms) {
- mClientChannel->waitForMessage(timeout);
-
- InputEvent* ev;
- uint32_t seqId;
- status_t consumed = mInputConsumer->consume(&mInputEventFactory, true, -1, &seqId, &ev);
- if (consumed != OK) {
- return nullptr;
- }
- status_t status = mInputConsumer->sendFinishedSignal(seqId, true);
- EXPECT_EQ(OK, status) << "Could not send finished signal";
- return ev;
- }
-
void assertFocusChange(bool hasFocus) {
InputEvent* ev = consumeEvent();
ASSERT_NE(ev, nullptr);
@@ -294,7 +280,7 @@
transactionBody) {
SurfaceComposerClient::Transaction t;
transactionBody(t, mSurfaceControl);
- t.apply(true);
+ t.apply(/*synchronously=*/true);
}
virtual void showAt(int x, int y, Rect crop = Rect(0, 0, 100, 100)) {
@@ -307,30 +293,46 @@
t.setAlpha(mSurfaceControl, 1);
auto reportedListener = sp<SynchronousWindowInfosReportedListener>::make();
t.addWindowInfosReportedListener(reportedListener);
- t.apply();
+ t.apply(/*synchronously=*/true);
reportedListener->wait();
}
void requestFocus(ui::LogicalDisplayId displayId = ui::LogicalDisplayId::DEFAULT) {
SurfaceComposerClient::Transaction t;
FocusRequest request;
- request.token = mInputInfo.token;
- request.windowName = mInputInfo.name;
+ request.token = mInputInfo->getInfo()->token;
+ request.windowName = mInputInfo->getInfo()->name;
request.timestamp = systemTime(SYSTEM_TIME_MONOTONIC);
request.displayId = displayId.val();
t.setFocusedWindow(request);
- t.apply(true);
+ t.apply(/*synchronously=*/true);
}
public:
+ // But should be private
+ sp<gui::WindowInfoHandle> mInputInfo = sp<gui::WindowInfoHandle>::make();
sp<SurfaceControl> mSurfaceControl;
+
+private:
std::shared_ptr<InputChannel> mClientChannel;
sp<IInputFlinger> mInputFlinger;
- WindowInfo mInputInfo;
-
PreallocatedInputEventFactory mInputEventFactory;
InputConsumer* mInputConsumer;
+
+ InputEvent* consumeEvent(std::chrono::milliseconds timeout = 3000ms) {
+ mClientChannel->waitForMessage(timeout);
+
+ InputEvent* ev;
+ uint32_t seqId;
+ status_t consumed = mInputConsumer->consume(&mInputEventFactory, true, -1, &seqId, &ev);
+ if (consumed != OK) {
+ return nullptr;
+ }
+ status_t status = mInputConsumer->sendFinishedSignal(seqId, true);
+ EXPECT_EQ(OK, status) << "Could not send finished signal";
+ return ev;
+ }
};
class BlastInputSurface : public InputSurface {
@@ -363,7 +365,7 @@
transactionBody) override {
SurfaceComposerClient::Transaction t;
transactionBody(t, mParentSurfaceControl);
- t.apply(true);
+ t.apply(/*synchronously=*/true);
}
void showAt(int x, int y, Rect crop = Rect(0, 0, 100, 100)) override {
@@ -377,7 +379,7 @@
t.setInputWindowInfo(mSurfaceControl, mInputInfo);
t.setCrop(mSurfaceControl, crop);
t.setAlpha(mSurfaceControl, 1);
- t.apply(true);
+ t.apply(/*synchronously=*/true);
}
private:
@@ -417,7 +419,7 @@
BufferUsage::COMPOSER_OVERLAY | BufferUsage::GPU_TEXTURE;
sp<GraphicBuffer> buffer =
new GraphicBuffer(w, h, PIXEL_FORMAT_RGBA_8888, 1, usageFlags, "test");
- Transaction().setBuffer(layer, buffer).apply(true);
+ Transaction().setBuffer(layer, buffer).apply(/*synchronously=*/true);
usleep(mBufferPostDelay);
}
@@ -458,7 +460,7 @@
injectTap(101, 101);
- EXPECT_NE(surface->consumeEvent(), nullptr);
+ surface->expectTap(1, 1);
}
/**
@@ -521,7 +523,7 @@
std::unique_ptr<InputSurface> fgSurface = makeSurface(100, 100);
bgSurface->showAt(100, 100);
- fgSurface->mInputInfo.surfaceInset = 5;
+ fgSurface->mInputInfo->editInfo()->surfaceInset = 5;
fgSurface->showAt(100, 100);
injectTap(106, 106);
@@ -536,8 +538,8 @@
std::unique_ptr<InputSurface> fgSurface = makeSurface(100, 100);
bgSurface->showAt(100, 100);
- fgSurface->mInputInfo.surfaceInset = 5;
- fgSurface->mInputInfo.replaceTouchableRegionWithCrop = true;
+ fgSurface->mInputInfo->editInfo()->surfaceInset = 5;
+ fgSurface->mInputInfo->editInfo()->replaceTouchableRegionWithCrop = true;
fgSurface->showAt(100, 100);
injectTap(106, 106);
@@ -553,7 +555,7 @@
std::unique_ptr<InputSurface> childSurface = makeSurface(100, 100);
parentSurface->showAt(100, 100);
- childSurface->mInputInfo.surfaceInset = 10;
+ childSurface->mInputInfo->editInfo()->surfaceInset = 10;
childSurface->showAt(100, 100);
childSurface->doTransaction([&](auto& t, auto& sc) {
@@ -574,7 +576,7 @@
std::unique_ptr<InputSurface> fgSurface = makeSurface(100, 100);
bgSurface->showAt(100, 100);
- fgSurface->mInputInfo.surfaceInset = 5;
+ fgSurface->mInputInfo->editInfo()->surfaceInset = 5;
fgSurface->showAt(100, 100);
fgSurface->doTransaction([&](auto& t, auto& sc) { t.setMatrix(sc, 2.0, 0, 0, 4.0); });
@@ -593,7 +595,7 @@
bgSurface->showAt(100, 100);
// In case we pass the very big inset without any checking.
- fgSurface->mInputInfo.surfaceInset = INT32_MAX;
+ fgSurface->mInputInfo->editInfo()->surfaceInset = INT32_MAX;
fgSurface->showAt(100, 100);
fgSurface->doTransaction([&](auto& t, auto& sc) { t.setMatrix(sc, 2.0, 0, 0, 2.0); });
@@ -606,13 +608,13 @@
TEST_F(InputSurfacesTest, touchable_region) {
std::unique_ptr<InputSurface> surface = makeSurface(100, 100);
- surface->mInputInfo.touchableRegion.set(Rect{19, 29, 21, 31});
+ surface->mInputInfo->editInfo()->touchableRegion.set(Rect{19, 29, 21, 31});
surface->showAt(11, 22);
// A tap within the surface but outside the touchable region should not be sent to the surface.
injectTap(20, 30);
- EXPECT_EQ(surface->consumeEvent(/*timeout=*/200ms), nullptr);
+ surface->assertNoEvent();
injectTap(31, 52);
surface->expectTap(20, 30);
@@ -627,7 +629,8 @@
// Since the surface is offset from the origin, the touchable region will be transformed into
// display space, which would trigger an overflow or an underflow. Ensure that we are protected
// against such a situation.
- fgSurface->mInputInfo.touchableRegion.orSelf(Rect{INT32_MIN, INT32_MIN, INT32_MAX, INT32_MAX});
+ fgSurface->mInputInfo->editInfo()->touchableRegion.orSelf(
+ Rect{INT32_MIN, INT32_MIN, INT32_MAX, INT32_MAX});
fgSurface->showAt(100, 100);
@@ -642,7 +645,8 @@
std::unique_ptr<InputSurface> fgSurface = makeSurface(100, 100);
bgSurface->showAt(0, 0);
- fgSurface->mInputInfo.touchableRegion.orSelf(Rect{INT32_MIN, INT32_MIN, INT32_MAX, INT32_MAX});
+ fgSurface->mInputInfo->editInfo()->touchableRegion.orSelf(
+ Rect{INT32_MIN, INT32_MIN, INT32_MAX, INT32_MAX});
fgSurface->showAt(0, 0);
fgSurface->doTransaction([&](auto& t, auto& sc) { t.setMatrix(sc, 2.0, 0, 0, 2.0); });
@@ -812,7 +816,7 @@
TEST_F(InputSurfacesTest, rotate_surface_with_scale_and_insets) {
std::unique_ptr<InputSurface> surface = makeSurface(100, 100);
- surface->mInputInfo.surfaceInset = 5;
+ surface->mInputInfo->editInfo()->surfaceInset = 5;
surface->showAt(100, 100);
surface->doTransaction([](auto& t, auto& sc) {
@@ -841,11 +845,12 @@
// Add non touchable window to fully cover touchable window. Window behind gets touch, but
// with flag AMOTION_EVENT_FLAG_WINDOW_IS_OBSCURED
std::unique_ptr<InputSurface> nonTouchableSurface = makeSurface(100, 100);
- nonTouchableSurface->mInputInfo.setInputConfig(WindowInfo::InputConfig::NOT_TOUCHABLE, true);
- nonTouchableSurface->mInputInfo.ownerUid = gui::Uid{22222};
+ nonTouchableSurface->mInputInfo->editInfo()
+ ->setInputConfig(WindowInfo::InputConfig::NOT_TOUCHABLE, true);
+ nonTouchableSurface->mInputInfo->editInfo()->ownerUid = gui::Uid{22222};
// Overriding occlusion mode otherwise the touch would be discarded at InputDispatcher by
// the default obscured/untrusted touch filter introduced in S.
- nonTouchableSurface->mInputInfo.touchOcclusionMode = TouchOcclusionMode::ALLOW;
+ nonTouchableSurface->mInputInfo->editInfo()->touchOcclusionMode = TouchOcclusionMode::ALLOW;
nonTouchableSurface->showAt(100, 100);
injectTap(190, 199);
@@ -861,10 +866,12 @@
// AMOTION_EVENT_FLAG_WINDOW_IS_PARTIALLY_OBSCURED
std::unique_ptr<InputSurface> parentSurface = makeSurface(100, 100);
std::unique_ptr<InputSurface> nonTouchableSurface = makeSurface(100, 100);
- nonTouchableSurface->mInputInfo.setInputConfig(WindowInfo::InputConfig::NOT_TOUCHABLE, true);
- parentSurface->mInputInfo.setInputConfig(WindowInfo::InputConfig::NOT_TOUCHABLE, true);
- nonTouchableSurface->mInputInfo.ownerUid = gui::Uid{22222};
- parentSurface->mInputInfo.ownerUid = gui::Uid{22222};
+ nonTouchableSurface->mInputInfo->editInfo()
+ ->setInputConfig(WindowInfo::InputConfig::NOT_TOUCHABLE, true);
+ parentSurface->mInputInfo->editInfo()->setInputConfig(WindowInfo::InputConfig::NOT_TOUCHABLE,
+ true);
+ nonTouchableSurface->mInputInfo->editInfo()->ownerUid = gui::Uid{22222};
+ parentSurface->mInputInfo->editInfo()->ownerUid = gui::Uid{22222};
nonTouchableSurface->showAt(0, 0);
parentSurface->showAt(100, 100);
@@ -885,10 +892,12 @@
// the touchable window. Window behind gets touch with no obscured flags.
std::unique_ptr<InputSurface> parentSurface = makeSurface(100, 100);
std::unique_ptr<InputSurface> nonTouchableSurface = makeSurface(100, 100);
- nonTouchableSurface->mInputInfo.setInputConfig(WindowInfo::InputConfig::NOT_TOUCHABLE, true);
- parentSurface->mInputInfo.setInputConfig(WindowInfo::InputConfig::NOT_TOUCHABLE, true);
- nonTouchableSurface->mInputInfo.ownerUid = gui::Uid{22222};
- parentSurface->mInputInfo.ownerUid = gui::Uid{22222};
+ nonTouchableSurface->mInputInfo->editInfo()
+ ->setInputConfig(WindowInfo::InputConfig::NOT_TOUCHABLE, true);
+ parentSurface->mInputInfo->editInfo()->setInputConfig(WindowInfo::InputConfig::NOT_TOUCHABLE,
+ true);
+ nonTouchableSurface->mInputInfo->editInfo()->ownerUid = gui::Uid{22222};
+ parentSurface->mInputInfo->editInfo()->ownerUid = gui::Uid{22222};
nonTouchableSurface->showAt(0, 0);
parentSurface->showAt(50, 50);
@@ -906,8 +915,9 @@
std::unique_ptr<InputSurface> bufferSurface =
InputSurface::makeBufferInputSurface(mComposerClient, 0, 0);
- bufferSurface->mInputInfo.setInputConfig(WindowInfo::InputConfig::NOT_TOUCHABLE, true);
- bufferSurface->mInputInfo.ownerUid = gui::Uid{22222};
+ bufferSurface->mInputInfo->editInfo()->setInputConfig(WindowInfo::InputConfig::NOT_TOUCHABLE,
+ true);
+ bufferSurface->mInputInfo->editInfo()->ownerUid = gui::Uid{22222};
surface->showAt(10, 10);
bufferSurface->showAt(50, 50, Rect::EMPTY_RECT);
@@ -921,8 +931,9 @@
std::unique_ptr<BlastInputSurface> bufferSurface =
BlastInputSurface::makeBlastInputSurface(mComposerClient, 0, 0);
- bufferSurface->mInputInfo.setInputConfig(WindowInfo::InputConfig::NOT_TOUCHABLE, true);
- bufferSurface->mInputInfo.ownerUid = gui::Uid{22222};
+ bufferSurface->mInputInfo->editInfo()->setInputConfig(WindowInfo::InputConfig::NOT_TOUCHABLE,
+ true);
+ bufferSurface->mInputInfo->editInfo()->ownerUid = gui::Uid{22222};
surface->showAt(10, 10);
bufferSurface->showAt(50, 50, Rect::EMPTY_RECT);
@@ -965,13 +976,14 @@
TEST_F(InputSurfacesTest, strict_unobscured_input_obscured_window) {
std::unique_ptr<InputSurface> surface = makeSurface(100, 100);
- surface->mInputInfo.ownerUid = gui::Uid{11111};
+ surface->mInputInfo->editInfo()->ownerUid = gui::Uid{11111};
surface->doTransaction(
[&](auto& t, auto& sc) { t.setDropInputMode(sc, gui::DropInputMode::OBSCURED); });
surface->showAt(100, 100);
std::unique_ptr<InputSurface> obscuringSurface = makeSurface(100, 100);
- obscuringSurface->mInputInfo.setInputConfig(WindowInfo::InputConfig::NOT_TOUCHABLE, true);
- obscuringSurface->mInputInfo.ownerUid = gui::Uid{22222};
+ obscuringSurface->mInputInfo->editInfo()->setInputConfig(WindowInfo::InputConfig::NOT_TOUCHABLE,
+ true);
+ obscuringSurface->mInputInfo->editInfo()->ownerUid = gui::Uid{22222};
obscuringSurface->showAt(100, 100);
injectTap(101, 101);
surface->assertNoEvent();
@@ -984,13 +996,14 @@
TEST_F(InputSurfacesTest, strict_unobscured_input_partially_obscured_window) {
std::unique_ptr<InputSurface> surface = makeSurface(100, 100);
- surface->mInputInfo.ownerUid = gui::Uid{11111};
+ surface->mInputInfo->editInfo()->ownerUid = gui::Uid{11111};
surface->doTransaction(
[&](auto& t, auto& sc) { t.setDropInputMode(sc, gui::DropInputMode::OBSCURED); });
surface->showAt(100, 100);
std::unique_ptr<InputSurface> obscuringSurface = makeSurface(100, 100);
- obscuringSurface->mInputInfo.setInputConfig(WindowInfo::InputConfig::NOT_TOUCHABLE, true);
- obscuringSurface->mInputInfo.ownerUid = gui::Uid{22222};
+ obscuringSurface->mInputInfo->editInfo()->setInputConfig(WindowInfo::InputConfig::NOT_TOUCHABLE,
+ true);
+ obscuringSurface->mInputInfo->editInfo()->ownerUid = gui::Uid{22222};
obscuringSurface->showAt(190, 190);
injectTap(101, 101);
@@ -1054,7 +1067,7 @@
BlastInputSurface::makeBlastInputSurface(mComposerClient, 0, 0);
surface->showAt(100, 100);
- bufferSurface->mInputInfo.touchableRegion.orSelf(Rect(0, 0, 200, 200));
+ bufferSurface->mInputInfo->editInfo()->touchableRegion.orSelf(Rect(0, 0, 200, 200));
bufferSurface->showAt(100, 100, Rect::EMPTY_RECT);
injectTap(101, 101);
@@ -1097,8 +1110,8 @@
InputSurface::makeContainerInputSurface(mComposerClient, 100, 100);
containerSurface->doTransaction(
[&](auto& t, auto& sc) { t.reparent(sc, parentContainer->mSurfaceControl); });
- containerSurface->mInputInfo.replaceTouchableRegionWithCrop = true;
- containerSurface->mInputInfo.touchableRegionCropHandle = nullptr;
+ containerSurface->mInputInfo->editInfo()->replaceTouchableRegionWithCrop = true;
+ containerSurface->mInputInfo->editInfo()->touchableRegionCropHandle = nullptr;
parentContainer->showAt(10, 10, Rect(0, 0, 20, 20));
containerSurface->showAt(10, 10, Rect(0, 0, 5, 5));
@@ -1116,14 +1129,19 @@
* in its parent's touchable region. The input events should be in the layer's coordinate space.
*/
TEST_F(InputSurfacesTest, uncropped_container_replaces_touchable_region_with_null_crop) {
+ std::unique_ptr<InputSurface> bgContainer =
+ InputSurface::makeContainerInputSurface(mComposerClient, 0, 0);
std::unique_ptr<InputSurface> parentContainer =
InputSurface::makeContainerInputSurface(mComposerClient, 0, 0);
std::unique_ptr<InputSurface> containerSurface =
InputSurface::makeContainerInputSurface(mComposerClient, 100, 100);
containerSurface->doTransaction(
[&](auto& t, auto& sc) { t.reparent(sc, parentContainer->mSurfaceControl); });
- containerSurface->mInputInfo.replaceTouchableRegionWithCrop = true;
- containerSurface->mInputInfo.touchableRegionCropHandle = nullptr;
+ containerSurface->mInputInfo->editInfo()->replaceTouchableRegionWithCrop = true;
+ containerSurface->mInputInfo->editInfo()->touchableRegionCropHandle = nullptr;
+ parentContainer->doTransaction(
+ [&](auto& t, auto& sc) { t.reparent(sc, bgContainer->mSurfaceControl); });
+ bgContainer->showAt(0, 0, Rect(0, 0, 100, 100));
parentContainer->showAt(10, 10, Rect(0, 0, 20, 20));
containerSurface->showAt(10, 10, Rect::INVALID_RECT);
@@ -1147,8 +1165,8 @@
std::unique_ptr<InputSurface> containerSurface =
InputSurface::makeContainerInputSurface(mComposerClient, 100, 100);
- containerSurface->mInputInfo.replaceTouchableRegionWithCrop = true;
- containerSurface->mInputInfo.touchableRegionCropHandle =
+ containerSurface->mInputInfo->editInfo()->replaceTouchableRegionWithCrop = true;
+ containerSurface->mInputInfo->editInfo()->touchableRegionCropHandle =
cropLayer->mSurfaceControl->getHandle();
containerSurface->showAt(10, 10, Rect::INVALID_RECT);
@@ -1207,7 +1225,7 @@
t.setDisplayLayerStack(token, layerStack);
t.setDisplayProjection(token, ui::ROTATION_0, {0, 0, width, height},
{offsetX, offsetY, offsetX + width, offsetY + height});
- t.apply(true);
+ t.apply(/*synchronously=*/true);
mVirtualDisplays.push_back(token);
}
diff --git a/libs/gui/tests/StreamSplitter_test.cpp b/libs/gui/tests/StreamSplitter_test.cpp
index f34b03e..1c439cd 100644
--- a/libs/gui/tests/StreamSplitter_test.cpp
+++ b/libs/gui/tests/StreamSplitter_test.cpp
@@ -95,8 +95,7 @@
ASSERT_EQ(*dataOut, TEST_DATA);
ASSERT_EQ(OK, item.mGraphicBuffer->unlock());
- ASSERT_EQ(OK, outputConsumer->releaseBuffer(item.mSlot, item.mFrameNumber,
- EGL_NO_DISPLAY, EGL_NO_SYNC_KHR, Fence::NO_FENCE));
+ ASSERT_EQ(OK, outputConsumer->releaseBuffer(item.mSlot, item.mFrameNumber, Fence::NO_FENCE));
// This should succeed even with allocation disabled since it will have
// received the buffer back from the output BufferQueue
@@ -168,9 +167,9 @@
ASSERT_EQ(*dataOut, TEST_DATA);
ASSERT_EQ(OK, item.mGraphicBuffer->unlock());
- ASSERT_EQ(OK, outputConsumers[output]->releaseBuffer(item.mSlot,
- item.mFrameNumber, EGL_NO_DISPLAY, EGL_NO_SYNC_KHR,
- Fence::NO_FENCE));
+ ASSERT_EQ(OK,
+ outputConsumers[output]->releaseBuffer(item.mSlot, item.mFrameNumber,
+ Fence::NO_FENCE));
}
// This should succeed even with allocation disabled since it will have
diff --git a/libs/gui/tests/Surface_test.cpp b/libs/gui/tests/Surface_test.cpp
index 88893b6..76362ff 100644
--- a/libs/gui/tests/Surface_test.cpp
+++ b/libs/gui/tests/Surface_test.cpp
@@ -21,6 +21,7 @@
#include <gtest/gtest.h>
#include <SurfaceFlingerProperties.h>
+#include <android/gui/IActivePictureListener.h>
#include <android/gui/IDisplayEventConnection.h>
#include <android/gui/ISurfaceComposer.h>
#include <android/hardware/configstore/1.0/ISurfaceFlingerConfigs.h>
@@ -201,9 +202,9 @@
releasedItems.resize(1+extraDiscardedBuffers);
for (size_t i = 0; i < releasedItems.size(); i++) {
ASSERT_EQ(NO_ERROR, consumer->acquireBuffer(&releasedItems[i], 0));
- ASSERT_EQ(NO_ERROR, consumer->releaseBuffer(releasedItems[i].mSlot,
- releasedItems[i].mFrameNumber, EGL_NO_DISPLAY, EGL_NO_SYNC_KHR,
- Fence::NO_FENCE));
+ ASSERT_EQ(NO_ERROR,
+ consumer->releaseBuffer(releasedItems[i].mSlot, releasedItems[i].mFrameNumber,
+ Fence::NO_FENCE));
}
int32_t expectedReleaseCb = (enableReleasedCb ? releasedItems.size() : 0);
if (hasSurfaceListener) {
@@ -1015,6 +1016,15 @@
return binder::Status::ok();
}
+ binder::Status setActivePictureListener(const sp<gui::IActivePictureListener>&) {
+ return binder::Status::ok();
+ }
+
+ binder::Status getMaxLayerPictureProfiles(const sp<IBinder>& /*display*/,
+ int32_t* /*outMaxProfiles*/) {
+ return binder::Status::ok();
+ }
+
protected:
IBinder* onAsBinder() override { return nullptr; }
diff --git a/libs/gui/view/Surface.cpp b/libs/gui/view/Surface.cpp
index 84c2a6a..2cf7081 100644
--- a/libs/gui/view/Surface.cpp
+++ b/libs/gui/view/Surface.cpp
@@ -121,6 +121,42 @@
return str.value_or(String16());
}
+#if WB_LIBCAMERASERVICE_WITH_DEPENDENCIES
+Surface Surface::fromSurface(const sp<android::Surface>& surface) {
+ if (surface == nullptr) {
+ ALOGE("%s: Error: view::Surface::fromSurface failed due to null surface.", __FUNCTION__);
+ return Surface();
+ }
+ Surface s;
+ s.name = String16(surface->getConsumerName());
+ s.graphicBufferProducer = surface->getIGraphicBufferProducer();
+ s.surfaceControlHandle = surface->getSurfaceControlHandle();
+ return s;
+}
+
+sp<android::Surface> Surface::toSurface() const {
+ if (graphicBufferProducer == nullptr) return nullptr;
+ return new android::Surface(graphicBufferProducer, false, surfaceControlHandle);
+}
+
+status_t Surface::getUniqueId(uint64_t* out_id) const {
+ if (graphicBufferProducer == nullptr) {
+ ALOGE("android::viewSurface::getUniqueId() failed because it's not initialized.");
+ return UNEXPECTED_NULL;
+ }
+ status_t status = graphicBufferProducer->getUniqueId(out_id);
+ if (status != OK) {
+ ALOGE("android::viewSurface::getUniqueId() failed.");
+ return status;
+ }
+ return OK;
+}
+
+bool Surface::isEmpty() const {
+ return graphicBufferProducer == nullptr;
+}
+#endif
+
std::string Surface::toString() const {
std::stringstream out;
out << name;
diff --git a/libs/input/Android.bp b/libs/input/Android.bp
index e4e81ad..a4ae54b 100644
--- a/libs/input/Android.bp
+++ b/libs/input/Android.bp
@@ -217,6 +217,7 @@
],
srcs: [
"AccelerationCurve.cpp",
+ "CoordinateFilter.cpp",
"Input.cpp",
"InputConsumer.cpp",
"InputConsumerNoResampling.cpp",
@@ -230,6 +231,7 @@
"KeyLayoutMap.cpp",
"MotionPredictor.cpp",
"MotionPredictorMetricsManager.cpp",
+ "OneEuroFilter.cpp",
"PrintTools.cpp",
"PropertyMap.cpp",
"Resampler.cpp",
diff --git a/libs/input/CoordinateFilter.cpp b/libs/input/CoordinateFilter.cpp
new file mode 100644
index 0000000..a32685b
--- /dev/null
+++ b/libs/input/CoordinateFilter.cpp
@@ -0,0 +1,31 @@
+/**
+ * Copyright 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#define LOG_TAG "CoordinateFilter"
+
+#include <input/CoordinateFilter.h>
+
+namespace android {
+
+CoordinateFilter::CoordinateFilter(float minCutoffFreq, float beta)
+ : mXFilter{minCutoffFreq, beta}, mYFilter{minCutoffFreq, beta} {}
+
+void CoordinateFilter::filter(std::chrono::nanoseconds timestamp, PointerCoords& coords) {
+ coords.setAxisValue(AMOTION_EVENT_AXIS_X, mXFilter.filter(timestamp, coords.getX()));
+ coords.setAxisValue(AMOTION_EVENT_AXIS_Y, mYFilter.filter(timestamp, coords.getY()));
+}
+
+} // namespace android
diff --git a/libs/input/Input.cpp b/libs/input/Input.cpp
index a2bb345..65a088e 100644
--- a/libs/input/Input.cpp
+++ b/libs/input/Input.cpp
@@ -685,11 +685,12 @@
const PointerCoords* MotionEvent::getRawPointerCoords(size_t pointerIndex) const {
if (CC_UNLIKELY(pointerIndex < 0 || pointerIndex >= getPointerCount())) {
- LOG(FATAL) << __func__ << ": Invalid pointer index " << pointerIndex << " for " << *this;
+ LOG(FATAL) << __func__ << ": Invalid pointer index " << pointerIndex << " for "
+ << safeDump();
}
const size_t position = getHistorySize() * getPointerCount() + pointerIndex;
if (CC_UNLIKELY(position < 0 || position >= mSamplePointerCoords.size())) {
- LOG(FATAL) << __func__ << ": Invalid array index " << position << " for " << *this;
+ LOG(FATAL) << __func__ << ": Invalid array index " << position << " for " << safeDump();
}
return &mSamplePointerCoords[position];
}
@@ -705,15 +706,16 @@
const PointerCoords* MotionEvent::getHistoricalRawPointerCoords(
size_t pointerIndex, size_t historicalIndex) const {
if (CC_UNLIKELY(pointerIndex < 0 || pointerIndex >= getPointerCount())) {
- LOG(FATAL) << __func__ << ": Invalid pointer index " << pointerIndex << " for " << *this;
+ LOG(FATAL) << __func__ << ": Invalid pointer index " << pointerIndex << " for "
+ << safeDump();
}
if (CC_UNLIKELY(historicalIndex < 0 || historicalIndex > getHistorySize())) {
LOG(FATAL) << __func__ << ": Invalid historical index " << historicalIndex << " for "
- << *this;
+ << safeDump();
}
const size_t position = historicalIndex * getPointerCount() + pointerIndex;
if (CC_UNLIKELY(position < 0 || position >= mSamplePointerCoords.size())) {
- LOG(FATAL) << __func__ << ": Invalid array index " << position << " for " << *this;
+ LOG(FATAL) << __func__ << ": Invalid array index " << position << " for " << safeDump();
}
return &mSamplePointerCoords[position];
}
@@ -1144,6 +1146,53 @@
// clang-format on
}
+std::string MotionEvent::safeDump() const {
+ std::stringstream out;
+ // Field names have the m prefix here to make it easy to distinguish safeDump output from
+ // operator<< output in logs.
+ out << "MotionEvent { mAction=" << MotionEvent::actionToString(mAction);
+ if (mActionButton != 0) {
+ out << ", mActionButton=" << mActionButton;
+ }
+ if (mButtonState != 0) {
+ out << ", mButtonState=" << mButtonState;
+ }
+ if (mClassification != MotionClassification::NONE) {
+ out << ", mClassification=" << motionClassificationToString(mClassification);
+ }
+ if (mMetaState != 0) {
+ out << ", mMetaState=" << mMetaState;
+ }
+ if (mFlags != 0) {
+ out << ", mFlags=0x" << std::hex << mFlags << std::dec;
+ }
+ if (mEdgeFlags != 0) {
+ out << ", mEdgeFlags=" << mEdgeFlags;
+ }
+ out << ", mDownTime=" << mDownTime;
+ out << ", mDeviceId=" << mDeviceId;
+ out << ", mSource=" << inputEventSourceToString(mSource);
+ out << ", mDisplayId=" << mDisplayId;
+ out << ", mEventId=0x" << std::hex << mId << std::dec;
+ // Since we're not assuming the data is at all valid, we also limit the number of items that
+ // might be printed from vectors, in case the vector's size field is corrupted.
+ out << ", mPointerProperties=(" << mPointerProperties.size() << ")[";
+ for (size_t i = 0; i < mPointerProperties.size() && i < MAX_POINTERS; i++) {
+ out << (i > 0 ? ", " : "") << mPointerProperties.at(i);
+ }
+ out << "], mSampleEventTimes=(" << mSampleEventTimes.size() << ")[";
+ for (size_t i = 0; i < mSampleEventTimes.size() && i < 256; i++) {
+ out << (i > 0 ? ", " : "") << mSampleEventTimes.at(i);
+ }
+ out << "], mSamplePointerCoords=(" << mSamplePointerCoords.size() << ")[";
+ for (size_t i = 0; i < mSamplePointerCoords.size() && i < MAX_POINTERS; i++) {
+ const PointerCoords& coords = mSamplePointerCoords.at(i);
+ out << (i > 0 ? ", " : "") << "(" << coords.getX() << ", " << coords.getY() << ")";
+ }
+ out << "] }";
+ return out.str();
+}
+
std::ostream& operator<<(std::ostream& out, const MotionEvent& event) {
out << "MotionEvent { action=" << MotionEvent::actionToString(event.getAction());
if (event.getActionButton() != 0) {
diff --git a/libs/input/InputConsumerNoResampling.cpp b/libs/input/InputConsumerNoResampling.cpp
index 9665de7..cd85821 100644
--- a/libs/input/InputConsumerNoResampling.cpp
+++ b/libs/input/InputConsumerNoResampling.cpp
@@ -169,6 +169,12 @@
msg.body.timeline.graphicsTimeline[GraphicsTimeline::PRESENT_TIME] = presentTime;
return msg;
}
+
+std::ostream& operator<<(std::ostream& out, const InputMessage& msg) {
+ out << ftl::enum_string(msg.header.type);
+ return out;
+}
+
} // namespace
// --- InputConsumerNoResampling ---
@@ -193,13 +199,6 @@
InputConsumerNoResampling::~InputConsumerNoResampling() {
ensureCalledOnLooperThread(__func__);
- while (!mOutboundQueue.empty()) {
- processOutboundEvents();
- // This is our last chance to ack the events. If we don't ack them here, we will get an ANR,
- // so keep trying to send the events as long as they are present in the queue.
- }
-
- setFdEvents(0);
// If there are any remaining unread batches, send an ack for them and don't deliver
// them to callbacks.
for (auto& [_, batches] : mBatches) {
@@ -208,6 +207,12 @@
batches.pop();
}
}
+
+ while (!mOutboundQueue.empty()) {
+ processOutboundEvents();
+ // This is our last chance to ack the events. If we don't ack them here, we will get an ANR,
+ // so keep trying to send the events as long as they are present in the queue.
+ }
// However, it is still up to the app to finish any events that have already been delivered
// to the callbacks. If we wanted to change that behaviour and auto-finish all unfinished events
// that were already sent to callbacks, we could potentially loop through "mConsumeTimes"
@@ -216,6 +221,10 @@
const size_t unfinishedEvents = mConsumeTimes.size();
LOG_IF(INFO, unfinishedEvents != 0)
<< getName() << " has " << unfinishedEvents << " unfinished event(s)";
+ // Remove the fd from epoll, so that Looper does not call 'handleReceiveCallback' anymore.
+ // This must be done at the end of the destructor; otherwise, some of the other functions may
+ // call 'setFdEvents' as a side-effect, thus adding the fd back to the epoll set of the looper.
+ setFdEvents(0);
}
int InputConsumerNoResampling::handleReceiveCallback(int events) {
@@ -269,6 +278,15 @@
return; // try again later
}
+ if (result == DEAD_OBJECT) {
+ // If there's no one to receive events in the channel, there's no point in sending them.
+ // Drop all outbound events.
+ LOG(INFO) << "Channel " << mChannel->getName() << " died. Dropping outbound event "
+ << outboundMsg;
+ mOutboundQueue.pop();
+ setFdEvents(0);
+ continue;
+ }
// Some other error. Give up
LOG(FATAL) << "Failed to send outbound event on channel '" << mChannel->getName()
<< "'. status=" << statusToString(result) << "(" << result << ")";
@@ -357,7 +375,8 @@
mBatches[deviceId].emplace(msg);
} else {
// consume all pending batches for this device immediately
- consumeBatchedInputEvents(deviceId, /*requestedFrameTime=*/std::nullopt);
+ consumeBatchedInputEvents(deviceId, /*requestedFrameTime=*/
+ std::numeric_limits<nsecs_t>::max());
if (canResample &&
(action == AMOTION_EVENT_ACTION_UP || action == AMOTION_EVENT_ACTION_CANCEL)) {
LOG_IF(INFO, mResamplers.erase(deviceId) == 0)
@@ -480,7 +499,7 @@
}
std::pair<std::unique_ptr<MotionEvent>, std::optional<uint32_t>>
-InputConsumerNoResampling::createBatchedMotionEvent(const nsecs_t requestedFrameTime,
+InputConsumerNoResampling::createBatchedMotionEvent(const std::optional<nsecs_t> requestedFrameTime,
std::queue<InputMessage>& messages) {
std::unique_ptr<MotionEvent> motionEvent;
std::optional<uint32_t> firstSeqForBatch;
@@ -491,7 +510,11 @@
const nanoseconds resampleLatency = (resampler != mResamplers.cend())
? resampler->second->getResampleLatency()
: nanoseconds{0};
- const nanoseconds adjustedFrameTime = nanoseconds{requestedFrameTime} - resampleLatency;
+ // When batching is not enabled, we want to consume all events. That's equivalent to having an
+ // infinite requestedFrameTime.
+ const nanoseconds adjustedFrameTime = (requestedFrameTime.has_value())
+ ? (nanoseconds{*requestedFrameTime} - resampleLatency)
+ : nanoseconds{std::numeric_limits<nsecs_t>::max()};
while (!messages.empty() &&
(messages.front().body.motion.eventTime <= adjustedFrameTime.count())) {
@@ -513,8 +536,9 @@
if (!messages.empty()) {
futureSample = &messages.front();
}
- if ((motionEvent != nullptr) && (resampler != mResamplers.cend())) {
- resampler->second->resampleMotionEvent(nanoseconds{requestedFrameTime}, *motionEvent,
+ if ((motionEvent != nullptr) && (resampler != mResamplers.cend()) &&
+ (requestedFrameTime.has_value())) {
+ resampler->second->resampleMotionEvent(nanoseconds{*requestedFrameTime}, *motionEvent,
futureSample);
}
@@ -524,16 +548,13 @@
bool InputConsumerNoResampling::consumeBatchedInputEvents(
std::optional<DeviceId> deviceId, std::optional<nsecs_t> requestedFrameTime) {
ensureCalledOnLooperThread(__func__);
- // When batching is not enabled, we want to consume all events. That's equivalent to having an
- // infinite requestedFrameTime.
- requestedFrameTime = requestedFrameTime.value_or(std::numeric_limits<nsecs_t>::max());
bool producedEvents = false;
for (auto deviceIdIter = (deviceId.has_value()) ? (mBatches.find(*deviceId))
: (mBatches.begin());
deviceIdIter != mBatches.cend(); ++deviceIdIter) {
std::queue<InputMessage>& messages = deviceIdIter->second;
- auto [motion, firstSeqForBatch] = createBatchedMotionEvent(*requestedFrameTime, messages);
+ auto [motion, firstSeqForBatch] = createBatchedMotionEvent(requestedFrameTime, messages);
if (motion != nullptr) {
LOG_ALWAYS_FATAL_IF(!firstSeqForBatch.has_value());
mCallbacks.onMotionEvent(std::move(motion), *firstSeqForBatch);
diff --git a/libs/input/InputEventLabels.cpp b/libs/input/InputEventLabels.cpp
index 8db0ca5..b537feb 100644
--- a/libs/input/InputEventLabels.cpp
+++ b/libs/input/InputEventLabels.cpp
@@ -350,7 +350,26 @@
DEFINE_KEYCODE(MACRO_3), \
DEFINE_KEYCODE(MACRO_4), \
DEFINE_KEYCODE(EMOJI_PICKER), \
- DEFINE_KEYCODE(SCREENSHOT)
+ DEFINE_KEYCODE(SCREENSHOT), \
+ DEFINE_KEYCODE(DICTATE), \
+ DEFINE_KEYCODE(NEW), \
+ DEFINE_KEYCODE(CLOSE), \
+ DEFINE_KEYCODE(DO_NOT_DISTURB), \
+ DEFINE_KEYCODE(PRINT), \
+ DEFINE_KEYCODE(LOCK), \
+ DEFINE_KEYCODE(FULLSCREEN), \
+ DEFINE_KEYCODE(F13), \
+ DEFINE_KEYCODE(F14), \
+ DEFINE_KEYCODE(F15), \
+ DEFINE_KEYCODE(F16), \
+ DEFINE_KEYCODE(F17), \
+ DEFINE_KEYCODE(F18), \
+ DEFINE_KEYCODE(F19),\
+ DEFINE_KEYCODE(F20), \
+ DEFINE_KEYCODE(F21), \
+ DEFINE_KEYCODE(F22), \
+ DEFINE_KEYCODE(F23), \
+ DEFINE_KEYCODE(F24)
// NOTE: If you add a new axis here you must also add it to several other files.
// Refer to frameworks/base/core/java/android/view/MotionEvent.java for the full list.
diff --git a/libs/input/InputTransport.cpp b/libs/input/InputTransport.cpp
index 77dcaa9..6a55726 100644
--- a/libs/input/InputTransport.cpp
+++ b/libs/input/InputTransport.cpp
@@ -583,15 +583,6 @@
StringPrintf("publishMotionEvent(inputChannel=%s, action=%s)",
mChannel->getName().c_str(),
MotionEvent::actionToString(action).c_str()));
- if (verifyEvents()) {
- Result<void> result =
- mInputVerifier.processMovement(deviceId, source, action, pointerCount,
- pointerProperties, pointerCoords, flags);
- if (!result.ok()) {
- LOG(ERROR) << "Bad stream: " << result.error();
- return BAD_VALUE;
- }
- }
if (debugTransportPublisher()) {
std::string transformString;
transform.dump(transformString, "transform", " ");
@@ -657,8 +648,18 @@
msg.body.motion.pointers[i].properties = pointerProperties[i];
msg.body.motion.pointers[i].coords = pointerCoords[i];
}
+ const status_t status = mChannel->sendMessage(&msg);
- return mChannel->sendMessage(&msg);
+ if (status == OK && verifyEvents()) {
+ Result<void> result =
+ mInputVerifier.processMovement(deviceId, source, action, pointerCount,
+ pointerProperties, pointerCoords, flags);
+ if (!result.ok()) {
+ LOG(ERROR) << "Bad stream: " << result.error();
+ return BAD_VALUE;
+ }
+ }
+ return status;
}
status_t InputPublisher::publishFocusEvent(uint32_t seq, int32_t eventId, bool hasFocus) {
diff --git a/libs/input/KeyboardClassifier.cpp b/libs/input/KeyboardClassifier.cpp
index 0c2c7be..2a83919 100644
--- a/libs/input/KeyboardClassifier.cpp
+++ b/libs/input/KeyboardClassifier.cpp
@@ -57,14 +57,14 @@
uint32_t deviceClasses) {
if (mRustClassifier) {
RustInputDeviceIdentifier rustIdentifier;
- rustIdentifier.name = identifier.name;
- rustIdentifier.location = identifier.location;
- rustIdentifier.unique_id = identifier.uniqueId;
+ rustIdentifier.name = rust::String::lossy(identifier.name);
+ rustIdentifier.location = rust::String::lossy(identifier.location);
+ rustIdentifier.unique_id = rust::String::lossy(identifier.uniqueId);
rustIdentifier.bus = identifier.bus;
rustIdentifier.vendor = identifier.vendor;
rustIdentifier.product = identifier.product;
rustIdentifier.version = identifier.version;
- rustIdentifier.descriptor = identifier.descriptor;
+ rustIdentifier.descriptor = rust::String::lossy(identifier.descriptor);
android::input::keyboardClassifier::notifyKeyboardChanged(**mRustClassifier, deviceId,
rustIdentifier, deviceClasses);
} else {
diff --git a/libs/input/OneEuroFilter.cpp b/libs/input/OneEuroFilter.cpp
new file mode 100644
index 0000000..7b0d104
--- /dev/null
+++ b/libs/input/OneEuroFilter.cpp
@@ -0,0 +1,87 @@
+/**
+ * Copyright 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#define LOG_TAG "OneEuroFilter"
+
+#include <chrono>
+#include <cmath>
+
+#include <android-base/logging.h>
+#include <input/CoordinateFilter.h>
+
+namespace android {
+namespace {
+
+using namespace std::literals::chrono_literals;
+
+const float kHertzPerGigahertz = 1E9f;
+const float kGigahertzPerHertz = 1E-9f;
+
+// filteredSpeed's units are position per nanosecond. beta's units are 1 / position.
+inline float cutoffFreq(float minCutoffFreq, float beta, float filteredSpeed) {
+ return kHertzPerGigahertz *
+ ((minCutoffFreq * kGigahertzPerHertz) + beta * std::abs(filteredSpeed));
+}
+
+inline float smoothingFactor(std::chrono::nanoseconds samplingPeriod, float cutoffFreq) {
+ const float constant = 2.0f * M_PI * samplingPeriod.count() * (cutoffFreq * kGigahertzPerHertz);
+ return constant / (constant + 1);
+}
+
+inline float lowPassFilter(float rawValue, float prevFilteredValue, float smoothingFactor) {
+ return smoothingFactor * rawValue + (1 - smoothingFactor) * prevFilteredValue;
+}
+
+} // namespace
+
+OneEuroFilter::OneEuroFilter(float minCutoffFreq, float beta, float speedCutoffFreq)
+ : mMinCutoffFreq{minCutoffFreq}, mBeta{beta}, mSpeedCutoffFreq{speedCutoffFreq} {}
+
+float OneEuroFilter::filter(std::chrono::nanoseconds timestamp, float rawPosition) {
+ LOG_IF(FATAL, mPrevTimestamp.has_value() && (*mPrevTimestamp >= timestamp))
+ << "Timestamp must be greater than mPrevTimestamp. Timestamp: " << timestamp.count()
+ << "ns. mPrevTimestamp: " << mPrevTimestamp->count() << "ns";
+
+ const std::chrono::nanoseconds samplingPeriod =
+ (mPrevTimestamp.has_value()) ? (timestamp - *mPrevTimestamp) : 1s;
+
+ const float rawVelocity = (mPrevFilteredPosition.has_value())
+ ? ((rawPosition - *mPrevFilteredPosition) / (samplingPeriod.count()))
+ : 0.0f;
+
+ const float speedSmoothingFactor = smoothingFactor(samplingPeriod, mSpeedCutoffFreq);
+
+ const float filteredVelocity = (mPrevFilteredVelocity.has_value())
+ ? lowPassFilter(rawVelocity, *mPrevFilteredVelocity, speedSmoothingFactor)
+ : rawVelocity;
+
+ const float positionCutoffFreq = cutoffFreq(mMinCutoffFreq, mBeta, filteredVelocity);
+
+ const float positionSmoothingFactor = smoothingFactor(samplingPeriod, positionCutoffFreq);
+
+ const float filteredPosition = (mPrevFilteredPosition.has_value())
+ ? lowPassFilter(rawPosition, *mPrevFilteredPosition, positionSmoothingFactor)
+ : rawPosition;
+
+ mPrevTimestamp = timestamp;
+ mPrevRawPosition = rawPosition;
+ mPrevFilteredVelocity = filteredVelocity;
+ mPrevFilteredPosition = filteredPosition;
+
+ return filteredPosition;
+}
+
+} // namespace android
diff --git a/libs/input/Resampler.cpp b/libs/input/Resampler.cpp
index 056db09..3ab132d 100644
--- a/libs/input/Resampler.cpp
+++ b/libs/input/Resampler.cpp
@@ -389,4 +389,34 @@
mLastRealSample = *(mLatestSamples.end() - 1);
}
+// --- FilteredLegacyResampler ---
+
+FilteredLegacyResampler::FilteredLegacyResampler(float minCutoffFreq, float beta)
+ : mResampler{}, mMinCutoffFreq{minCutoffFreq}, mBeta{beta} {}
+
+void FilteredLegacyResampler::resampleMotionEvent(std::chrono::nanoseconds requestedFrameTime,
+ MotionEvent& motionEvent,
+ const InputMessage* futureSample) {
+ mResampler.resampleMotionEvent(requestedFrameTime, motionEvent, futureSample);
+ const size_t numSamples = motionEvent.getHistorySize() + 1;
+ for (size_t sampleIndex = 0; sampleIndex < numSamples; ++sampleIndex) {
+ for (size_t pointerIndex = 0; pointerIndex < motionEvent.getPointerCount();
+ ++pointerIndex) {
+ const int32_t pointerId = motionEvent.getPointerProperties(pointerIndex)->id;
+ const nanoseconds eventTime =
+ nanoseconds{motionEvent.getHistoricalEventTime(sampleIndex)};
+ // Refer to the static function `setMotionEventPointerCoords` for a justification of
+ // casting away const.
+ PointerCoords& pointerCoords = const_cast<PointerCoords&>(
+ *(motionEvent.getHistoricalRawPointerCoords(pointerIndex, sampleIndex)));
+ const auto& [iter, _] = mFilteredPointers.try_emplace(pointerId, mMinCutoffFreq, mBeta);
+ iter->second.filter(eventTime, pointerCoords);
+ }
+ }
+}
+
+std::chrono::nanoseconds FilteredLegacyResampler::getResampleLatency() const {
+ return mResampler.getResampleLatency();
+}
+
} // namespace android
diff --git a/libs/input/input_flags.aconfig b/libs/input/input_flags.aconfig
index e93c04d..fd77048 100644
--- a/libs/input/input_flags.aconfig
+++ b/libs/input/input_flags.aconfig
@@ -94,13 +94,6 @@
}
flag {
- name: "enable_new_mouse_pointer_ballistics"
- namespace: "input"
- description: "Change the acceleration curves for mouse pointer movements to match the touchpad ones"
- bug: "315313622"
-}
-
-flag {
name: "rate_limit_user_activity_poke_in_dispatcher"
namespace: "input"
description: "Move user-activity poke rate-limiting from PowerManagerService to InputDispatcher."
@@ -221,3 +214,20 @@
description: "Set input device's power/wakeup sysfs node"
bug: "372812925"
}
+
+flag {
+ name: "enable_alphabetic_keyboard_wake"
+ namespace: "input"
+ description: "Enable wake from alphabetic keyboards."
+ bug: "352856881"
+ metadata {
+ purpose: PURPOSE_BUGFIX
+ }
+}
+
+flag {
+ name: "connected_displays_cursor"
+ namespace: "lse_desktop_experience"
+ description: "Allow cursor to transition across multiple connected displays"
+ bug: "362719483"
+}
diff --git a/libs/input/rust/data_store.rs b/libs/input/rust/data_store.rs
index 6bdcefd..beb6e23 100644
--- a/libs/input/rust/data_store.rs
+++ b/libs/input/rust/data_store.rs
@@ -17,7 +17,7 @@
//! Contains the DataStore, used to store input related data in a persistent way.
use crate::input::KeyboardType;
-use log::{debug, error};
+use log::{debug, error, info};
use serde::{Deserialize, Serialize};
use std::fs::File;
use std::io::{Read, Write};
@@ -157,7 +157,7 @@
let path = Path::new(&self.filepath);
let mut fs_string = String::new();
match File::open(path) {
- Err(e) => error!("couldn't open {:?}: {}", path, e),
+ Err(e) => info!("couldn't open {:?}: {}", path, e),
Ok(mut file) => match file.read_to_string(&mut fs_string) {
Err(e) => error!("Couldn't read from {:?}: {}", path, e),
Ok(_) => debug!("Successfully read from file {:?}", path),
diff --git a/libs/input/tests/Android.bp b/libs/input/tests/Android.bp
index 661c9f7..d1c564d 100644
--- a/libs/input/tests/Android.bp
+++ b/libs/input/tests/Android.bp
@@ -17,6 +17,7 @@
"IdGenerator_test.cpp",
"InputChannel_test.cpp",
"InputConsumer_test.cpp",
+ "InputConsumerFilteredResampling_test.cpp",
"InputConsumerResampling_test.cpp",
"InputDevice_test.cpp",
"InputEvent_test.cpp",
@@ -25,6 +26,7 @@
"InputVerifier_test.cpp",
"MotionPredictor_test.cpp",
"MotionPredictorMetricsManager_test.cpp",
+ "OneEuroFilter_test.cpp",
"Resampler_test.cpp",
"RingBuffer_test.cpp",
"TestInputChannel.cpp",
diff --git a/libs/input/tests/InputConsumerFilteredResampling_test.cpp b/libs/input/tests/InputConsumerFilteredResampling_test.cpp
new file mode 100644
index 0000000..757cd18
--- /dev/null
+++ b/libs/input/tests/InputConsumerFilteredResampling_test.cpp
@@ -0,0 +1,218 @@
+/**
+ * Copyright 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <input/InputConsumerNoResampling.h>
+
+#include <chrono>
+#include <iostream>
+#include <memory>
+#include <queue>
+
+#include <TestEventMatchers.h>
+#include <TestInputChannel.h>
+#include <android-base/logging.h>
+#include <gmock/gmock.h>
+#include <gtest/gtest.h>
+#include <input/Input.h>
+#include <input/InputEventBuilders.h>
+#include <input/Resampler.h>
+#include <utils/Looper.h>
+#include <utils/StrongPointer.h>
+
+namespace android {
+namespace {
+
+using std::chrono::nanoseconds;
+
+using ::testing::AllOf;
+using ::testing::Matcher;
+
+const int32_t ACTION_DOWN = AMOTION_EVENT_ACTION_DOWN;
+const int32_t ACTION_MOVE = AMOTION_EVENT_ACTION_MOVE;
+
+struct Pointer {
+ int32_t id{0};
+ ToolType toolType{ToolType::FINGER};
+ float x{0.0f};
+ float y{0.0f};
+ bool isResampled{false};
+
+ PointerBuilder asPointerBuilder() const {
+ return PointerBuilder{id, toolType}.x(x).y(y).isResampled(isResampled);
+ }
+};
+
+} // namespace
+
+class InputConsumerFilteredResamplingTest : public ::testing::Test, public InputConsumerCallbacks {
+protected:
+ InputConsumerFilteredResamplingTest()
+ : mClientTestChannel{std::make_shared<TestInputChannel>("TestChannel")},
+ mLooper{sp<Looper>::make(/*allowNonCallbacks=*/false)} {
+ Looper::setForThread(mLooper);
+ mConsumer = std::make_unique<
+ InputConsumerNoResampling>(mClientTestChannel, mLooper, *this, []() {
+ return std::make_unique<FilteredLegacyResampler>(/*minCutoffFreq=*/4.7, /*beta=*/0.01);
+ });
+ }
+
+ void invokeLooperCallback() const {
+ sp<LooperCallback> callback;
+ ASSERT_TRUE(mLooper->getFdStateDebug(mClientTestChannel->getFd(), /*ident=*/nullptr,
+ /*events=*/nullptr, &callback, /*data=*/nullptr));
+ ASSERT_NE(callback, nullptr);
+ callback->handleEvent(mClientTestChannel->getFd(), ALOOPER_EVENT_INPUT, /*data=*/nullptr);
+ }
+
+ void assertOnBatchedInputEventPendingWasCalled() {
+ ASSERT_GT(mOnBatchedInputEventPendingInvocationCount, 0UL)
+ << "onBatchedInputEventPending was not called";
+ --mOnBatchedInputEventPendingInvocationCount;
+ }
+
+ void assertReceivedMotionEvent(const Matcher<MotionEvent>& matcher) {
+ ASSERT_TRUE(!mMotionEvents.empty()) << "No motion events were received";
+ std::unique_ptr<MotionEvent> motionEvent = std::move(mMotionEvents.front());
+ mMotionEvents.pop();
+ ASSERT_NE(motionEvent, nullptr) << "The consumed motion event must not be nullptr";
+ EXPECT_THAT(*motionEvent, matcher);
+ }
+
+ InputMessage nextPointerMessage(nanoseconds eventTime, int32_t action, const Pointer& pointer);
+
+ std::shared_ptr<TestInputChannel> mClientTestChannel;
+ sp<Looper> mLooper;
+ std::unique_ptr<InputConsumerNoResampling> mConsumer;
+
+ // Batched input events
+ std::queue<std::unique_ptr<KeyEvent>> mKeyEvents;
+ std::queue<std::unique_ptr<MotionEvent>> mMotionEvents;
+ std::queue<std::unique_ptr<FocusEvent>> mFocusEvents;
+ std::queue<std::unique_ptr<CaptureEvent>> mCaptureEvents;
+ std::queue<std::unique_ptr<DragEvent>> mDragEvents;
+ std::queue<std::unique_ptr<TouchModeEvent>> mTouchModeEvents;
+
+private:
+ // InputConsumer callbacks
+ void onKeyEvent(std::unique_ptr<KeyEvent> event, uint32_t seq) override {
+ mKeyEvents.push(std::move(event));
+ mConsumer->finishInputEvent(seq, /*handled=*/true);
+ }
+
+ void onMotionEvent(std::unique_ptr<MotionEvent> event, uint32_t seq) override {
+ mMotionEvents.push(std::move(event));
+ mConsumer->finishInputEvent(seq, /*handled=*/true);
+ }
+
+ void onBatchedInputEventPending(int32_t pendingBatchSource) override {
+ if (!mConsumer->probablyHasInput()) {
+ ADD_FAILURE() << "Should deterministically have input because there is a batch";
+ }
+ ++mOnBatchedInputEventPendingInvocationCount;
+ }
+
+ void onFocusEvent(std::unique_ptr<FocusEvent> event, uint32_t seq) override {
+ mFocusEvents.push(std::move(event));
+ mConsumer->finishInputEvent(seq, /*handled=*/true);
+ }
+
+ void onCaptureEvent(std::unique_ptr<CaptureEvent> event, uint32_t seq) override {
+ mCaptureEvents.push(std::move(event));
+ mConsumer->finishInputEvent(seq, /*handled=*/true);
+ }
+
+ void onDragEvent(std::unique_ptr<DragEvent> event, uint32_t seq) override {
+ mDragEvents.push(std::move(event));
+ mConsumer->finishInputEvent(seq, /*handled=*/true);
+ }
+
+ void onTouchModeEvent(std::unique_ptr<TouchModeEvent> event, uint32_t seq) override {
+ mTouchModeEvents.push(std::move(event));
+ mConsumer->finishInputEvent(seq, /*handled=*/true);
+ }
+
+ uint32_t mLastSeq{0};
+ size_t mOnBatchedInputEventPendingInvocationCount{0};
+};
+
+InputMessage InputConsumerFilteredResamplingTest::nextPointerMessage(nanoseconds eventTime,
+ int32_t action,
+ const Pointer& pointer) {
+ ++mLastSeq;
+ return InputMessageBuilder{InputMessage::Type::MOTION, mLastSeq}
+ .eventTime(eventTime.count())
+ .source(AINPUT_SOURCE_TOUCHSCREEN)
+ .action(action)
+ .pointer(pointer.asPointerBuilder())
+ .build();
+}
+
+TEST_F(InputConsumerFilteredResamplingTest, NeighboringTimestampsDoNotResultInZeroDivision) {
+ mClientTestChannel->enqueueMessage(
+ nextPointerMessage(0ms, ACTION_DOWN, Pointer{.x = 0.0f, .y = 0.0f}));
+
+ invokeLooperCallback();
+
+ assertReceivedMotionEvent(AllOf(WithMotionAction(ACTION_DOWN), WithSampleCount(1)));
+
+ const std::chrono::nanoseconds initialTime{56'821'700'000'000};
+
+ mClientTestChannel->enqueueMessage(nextPointerMessage(initialTime + 4'929'000ns, ACTION_MOVE,
+ Pointer{.x = 1.0f, .y = 1.0f}));
+ mClientTestChannel->enqueueMessage(nextPointerMessage(initialTime + 9'352'000ns, ACTION_MOVE,
+ Pointer{.x = 2.0f, .y = 2.0f}));
+ mClientTestChannel->enqueueMessage(nextPointerMessage(initialTime + 14'531'000ns, ACTION_MOVE,
+ Pointer{.x = 3.0f, .y = 3.0f}));
+
+ invokeLooperCallback();
+ mConsumer->consumeBatchedInputEvents(initialTime.count() + 18'849'395 /*ns*/);
+
+ assertOnBatchedInputEventPendingWasCalled();
+ // Three samples are expected. The first two of the batch, and the resampled one. The
+ // coordinates of the resampled sample are hardcoded because the matcher requires them. However,
+ // the primary intention here is to check that the last sample is resampled.
+ assertReceivedMotionEvent(AllOf(WithMotionAction(ACTION_MOVE), WithSampleCount(3),
+ WithSample(/*sampleIndex=*/2,
+ Sample{initialTime + 13'849'395ns,
+ {PointerArgs{.x = 1.3286f,
+ .y = 1.3286f,
+ .isResampled = true}}})));
+
+ mClientTestChannel->enqueueMessage(nextPointerMessage(initialTime + 20'363'000ns, ACTION_MOVE,
+ Pointer{.x = 4.0f, .y = 4.0f}));
+ mClientTestChannel->enqueueMessage(nextPointerMessage(initialTime + 25'745'000ns, ACTION_MOVE,
+ Pointer{.x = 5.0f, .y = 5.0f}));
+ // This sample is part of the stream of messages, but should not be consumed because its
+ // timestamp is greater than the ajusted frame time.
+ mClientTestChannel->enqueueMessage(nextPointerMessage(initialTime + 31'337'000ns, ACTION_MOVE,
+ Pointer{.x = 6.0f, .y = 6.0f}));
+
+ invokeLooperCallback();
+ mConsumer->consumeBatchedInputEvents(initialTime.count() + 35'516'062 /*ns*/);
+
+ assertOnBatchedInputEventPendingWasCalled();
+ // Four samples are expected because the last sample of the previous batch was not consumed.
+ assertReceivedMotionEvent(AllOf(WithMotionAction(ACTION_MOVE), WithSampleCount(4)));
+
+ mClientTestChannel->assertFinishMessage(/*seq=*/1, /*handled=*/true);
+ mClientTestChannel->assertFinishMessage(/*seq=*/2, /*handled=*/true);
+ mClientTestChannel->assertFinishMessage(/*seq=*/3, /*handled=*/true);
+ mClientTestChannel->assertFinishMessage(/*seq=*/4, /*handled=*/true);
+ mClientTestChannel->assertFinishMessage(/*seq=*/5, /*handled=*/true);
+ mClientTestChannel->assertFinishMessage(/*seq=*/6, /*handled=*/true);
+}
+
+} // namespace android
diff --git a/libs/input/tests/InputConsumerResampling_test.cpp b/libs/input/tests/InputConsumerResampling_test.cpp
index 883ca82..97688a8 100644
--- a/libs/input/tests/InputConsumerResampling_test.cpp
+++ b/libs/input/tests/InputConsumerResampling_test.cpp
@@ -38,6 +38,8 @@
using std::chrono::nanoseconds;
using namespace std::chrono_literals;
+const std::chrono::milliseconds RESAMPLE_LATENCY{5};
+
struct Pointer {
int32_t id{0};
float x{0.0f};
@@ -440,7 +442,7 @@
{20ms, {Pointer{.id = 0, .x = 30.0f, .y = 30.0f}}, AMOTION_EVENT_ACTION_MOVE}));
invokeLooperCallback();
- mConsumer->consumeBatchedInputEvents(nanoseconds{20ms + 5ms /*RESAMPLE_LATENCY*/}.count());
+ mConsumer->consumeBatchedInputEvents(nanoseconds{20ms + RESAMPLE_LATENCY}.count());
// MotionEvent should not resampled because the resample time falls exactly on the existing
// event time.
@@ -496,14 +498,15 @@
{40ms, {Pointer{.id = 0, .x = 30.0f, .y = 30.0f}}, AMOTION_EVENT_ACTION_MOVE}));
invokeLooperCallback();
- mConsumer->consumeBatchedInputEvents(nanoseconds{45ms + 5ms /*RESAMPLE_LATENCY*/}.count());
+ mConsumer->consumeBatchedInputEvents(nanoseconds{45ms + RESAMPLE_LATENCY}.count());
+ // Original and resampled event should be both overwritten.
assertReceivedMotionEvent(
{InputEventEntry{40ms,
{Pointer{.id = 0, .x = 35.0f, .y = 30.0f, .isResampled = true}},
- AMOTION_EVENT_ACTION_MOVE}, // original event, rewritten
+ AMOTION_EVENT_ACTION_MOVE},
InputEventEntry{45ms,
{Pointer{.id = 0, .x = 35.0f, .y = 30.0f, .isResampled = true}},
- AMOTION_EVENT_ACTION_MOVE}}); // resampled event, rewritten
+ AMOTION_EVENT_ACTION_MOVE}});
mClientTestChannel->assertFinishMessage(/*seq=*/1, /*handled=*/true);
mClientTestChannel->assertFinishMessage(/*seq=*/2, /*handled=*/true);
@@ -552,13 +555,14 @@
invokeLooperCallback();
mConsumer->consumeBatchedInputEvents(nanoseconds{50ms}.count());
+ // Original and resampled event should be both overwritten.
assertReceivedMotionEvent(
{InputEventEntry{24ms,
{Pointer{.id = 0, .x = 35.0f, .y = 30.0f, .isResampled = true}},
- AMOTION_EVENT_ACTION_MOVE}, // original event, rewritten
+ AMOTION_EVENT_ACTION_MOVE},
InputEventEntry{26ms,
{Pointer{.id = 0, .x = 45.0f, .y = 30.0f, .isResampled = true}},
- AMOTION_EVENT_ACTION_MOVE}}); // resampled event, rewritten
+ AMOTION_EVENT_ACTION_MOVE}});
mClientTestChannel->assertFinishMessage(/*seq=*/1, /*handled=*/true);
mClientTestChannel->assertFinishMessage(/*seq=*/2, /*handled=*/true);
@@ -566,4 +570,175 @@
mClientTestChannel->assertFinishMessage(/*seq=*/4, /*handled=*/true);
}
+TEST_F(InputConsumerResamplingTest, DoNotResampleWhenFrameTimeIsNotAvailable) {
+ mClientTestChannel->enqueueMessage(nextPointerMessage(
+ {0ms, {Pointer{.id = 0, .x = 10.0f, .y = 20.0f}}, AMOTION_EVENT_ACTION_DOWN}));
+
+ invokeLooperCallback();
+ assertReceivedMotionEvent({InputEventEntry{0ms,
+ {Pointer{.id = 0, .x = 10.0f, .y = 20.0f}},
+ AMOTION_EVENT_ACTION_DOWN}});
+
+ mClientTestChannel->enqueueMessage(nextPointerMessage(
+ {10ms, {Pointer{.id = 0, .x = 20.0f, .y = 30.0f}}, AMOTION_EVENT_ACTION_MOVE}));
+ mClientTestChannel->enqueueMessage(nextPointerMessage(
+ {20ms, {Pointer{.id = 0, .x = 30.0f, .y = 30.0f}}, AMOTION_EVENT_ACTION_MOVE}));
+
+ invokeLooperCallback();
+ mConsumer->consumeBatchedInputEvents(std::nullopt);
+ assertReceivedMotionEvent({InputEventEntry{10ms,
+ {Pointer{.id = 0, .x = 20.0f, .y = 30.0f}},
+ AMOTION_EVENT_ACTION_MOVE},
+ InputEventEntry{20ms,
+ {Pointer{.id = 0, .x = 30.0f, .y = 30.0f}},
+ AMOTION_EVENT_ACTION_MOVE}});
+
+ mClientTestChannel->assertFinishMessage(/*seq=*/1, /*handled=*/true);
+ mClientTestChannel->assertFinishMessage(/*seq=*/2, /*handled=*/true);
+ mClientTestChannel->assertFinishMessage(/*seq=*/3, /*handled=*/true);
+}
+
+TEST_F(InputConsumerResamplingTest, TwoPointersAreResampledIndependently) {
+ // Full action for when a pointer with index=1 appears (some other pointer must already be
+ // present)
+ const int32_t actionPointer1Down =
+ AMOTION_EVENT_ACTION_POINTER_DOWN + (1 << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT);
+
+ // Full action for when a pointer with index=0 disappears (some other pointer must still remain)
+ const int32_t actionPointer0Up =
+ AMOTION_EVENT_ACTION_POINTER_UP + (0 << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT);
+
+ mClientTestChannel->enqueueMessage(nextPointerMessage(
+ {0ms, {Pointer{.id = 0, .x = 100.0f, .y = 100.0f}}, AMOTION_EVENT_ACTION_DOWN}));
+
+ mClientTestChannel->assertNoSentMessages();
+
+ invokeLooperCallback();
+ assertReceivedMotionEvent({InputEventEntry{0ms,
+ {Pointer{.id = 0, .x = 100.0f, .y = 100.0f}},
+ AMOTION_EVENT_ACTION_DOWN}});
+
+ mClientTestChannel->enqueueMessage(nextPointerMessage(
+ {10ms, {Pointer{.id = 0, .x = 100.0f, .y = 100.0f}}, AMOTION_EVENT_ACTION_MOVE}));
+
+ invokeLooperCallback();
+ mConsumer->consumeBatchedInputEvents(nanoseconds{10ms + RESAMPLE_LATENCY}.count());
+ // Not resampled value because requestedFrameTime - RESAMPLE_LATENCY == eventTime
+ assertReceivedMotionEvent({InputEventEntry{10ms,
+ {Pointer{.id = 0, .x = 100.0f, .y = 100.0f}},
+ AMOTION_EVENT_ACTION_MOVE}});
+
+ // Second pointer id=1 appears
+ mClientTestChannel->enqueueMessage(
+ nextPointerMessage({15ms,
+ {Pointer{.id = 0, .x = 100.0f, .y = 100.0f},
+ Pointer{.id = 1, .x = 500.0f, .y = 500.0f}},
+ actionPointer1Down}));
+
+ invokeLooperCallback();
+ mConsumer->consumeBatchedInputEvents(nanoseconds{20ms + RESAMPLE_LATENCY}.count());
+ // Not resampled value because requestedFrameTime - RESAMPLE_LATENCY == eventTime.
+ assertReceivedMotionEvent({InputEventEntry{15ms,
+ {Pointer{.id = 0, .x = 100.0f, .y = 100.0f},
+ Pointer{.id = 1, .x = 500.0f, .y = 500.0f}},
+ actionPointer1Down}});
+
+ // Both pointers move
+ mClientTestChannel->enqueueMessage(
+ nextPointerMessage({30ms,
+ {Pointer{.id = 0, .x = 100.0f, .y = 100.0f},
+ Pointer{.id = 1, .x = 500.0f, .y = 500.0f}},
+ AMOTION_EVENT_ACTION_MOVE}));
+ mClientTestChannel->enqueueMessage(
+ nextPointerMessage({40ms,
+ {Pointer{.id = 0, .x = 120.0f, .y = 120.0f},
+ Pointer{.id = 1, .x = 600.0f, .y = 600.0f}},
+ AMOTION_EVENT_ACTION_MOVE}));
+
+ invokeLooperCallback();
+ mConsumer->consumeBatchedInputEvents(nanoseconds{45ms + RESAMPLE_LATENCY}.count());
+ assertReceivedMotionEvent(
+ {InputEventEntry{30ms,
+ {Pointer{.id = 0, .x = 100.0f, .y = 100.0f},
+ Pointer{.id = 1, .x = 500.0f, .y = 500.0f}},
+ AMOTION_EVENT_ACTION_MOVE},
+ InputEventEntry{40ms,
+ {Pointer{.id = 0, .x = 120.0f, .y = 120.0f},
+ Pointer{.id = 1, .x = 600.0f, .y = 600.0f}},
+ AMOTION_EVENT_ACTION_MOVE},
+ InputEventEntry{45ms,
+ {Pointer{.id = 0, .x = 130.0f, .y = 130.0f, .isResampled = true},
+ Pointer{.id = 1, .x = 650.0f, .y = 650.0f, .isResampled = true}},
+ AMOTION_EVENT_ACTION_MOVE}});
+
+ // Both pointers move again
+ mClientTestChannel->enqueueMessage(
+ nextPointerMessage({60ms,
+ {Pointer{.id = 0, .x = 120.0f, .y = 120.0f},
+ Pointer{.id = 1, .x = 600.0f, .y = 600.0f}},
+ AMOTION_EVENT_ACTION_MOVE}));
+ mClientTestChannel->enqueueMessage(
+ nextPointerMessage({70ms,
+ {Pointer{.id = 0, .x = 130.0f, .y = 130.0f},
+ Pointer{.id = 1, .x = 700.0f, .y = 700.0f}},
+ AMOTION_EVENT_ACTION_MOVE}));
+
+ invokeLooperCallback();
+ mConsumer->consumeBatchedInputEvents(nanoseconds{75ms + RESAMPLE_LATENCY}.count());
+
+ /*
+ * The pointer id 0 at t = 60 should not be equal to 120 because the value was received twice,
+ * and resampled to 130. Therefore, if we reported 130, then we should continue to report it as
+ * such. Likewise, with pointer id 1.
+ */
+
+ // Not 120 because it matches a previous real event.
+ assertReceivedMotionEvent(
+ {InputEventEntry{60ms,
+ {Pointer{.id = 0, .x = 130.0f, .y = 130.0f, .isResampled = true},
+ Pointer{.id = 1, .x = 650.0f, .y = 650.0f, .isResampled = true}},
+ AMOTION_EVENT_ACTION_MOVE},
+ InputEventEntry{70ms,
+ {Pointer{.id = 0, .x = 130.0f, .y = 130.0f},
+ Pointer{.id = 1, .x = 700.0f, .y = 700.0f}},
+ AMOTION_EVENT_ACTION_MOVE},
+ InputEventEntry{75ms,
+ {Pointer{.id = 0, .x = 135.0f, .y = 135.0f, .isResampled = true},
+ Pointer{.id = 1, .x = 750.0f, .y = 750.0f, .isResampled = true}},
+ AMOTION_EVENT_ACTION_MOVE}});
+
+ // First pointer id=0 leaves the screen
+ mClientTestChannel->enqueueMessage(
+ nextPointerMessage({80ms,
+ {Pointer{.id = 0, .x = 120.0f, .y = 120.0f},
+ Pointer{.id = 1, .x = 600.0f, .y = 600.0f}},
+ actionPointer0Up}));
+
+ invokeLooperCallback();
+ // Not resampled event for ACTION_POINTER_UP
+ assertReceivedMotionEvent({InputEventEntry{80ms,
+ {Pointer{.id = 0, .x = 120.0f, .y = 120.0f},
+ Pointer{.id = 1, .x = 600.0f, .y = 600.0f}},
+ actionPointer0Up}});
+
+ // Remaining pointer id=1 is still present, but doesn't move
+ mClientTestChannel->enqueueMessage(nextPointerMessage(
+ {90ms, {Pointer{.id = 1, .x = 600.0f, .y = 600.0f}}, AMOTION_EVENT_ACTION_MOVE}));
+
+ invokeLooperCallback();
+ mConsumer->consumeBatchedInputEvents(nanoseconds{100ms}.count());
+
+ /*
+ * The latest event with ACTION_MOVE was at t = 70 with value = 700. Thus, the resampled value
+ * is 700 + ((95 - 70)/(90 - 70))*(600 - 700) = 575.
+ */
+ assertReceivedMotionEvent(
+ {InputEventEntry{90ms,
+ {Pointer{.id = 1, .x = 600.0f, .y = 600.0f}},
+ AMOTION_EVENT_ACTION_MOVE},
+ InputEventEntry{95ms,
+ {Pointer{.id = 1, .x = 575.0f, .y = 575.0f, .isResampled = true}},
+ AMOTION_EVENT_ACTION_MOVE}});
+}
+
} // namespace android
diff --git a/libs/input/tests/InputConsumer_test.cpp b/libs/input/tests/InputConsumer_test.cpp
index 6a3bbe5..226b892 100644
--- a/libs/input/tests/InputConsumer_test.cpp
+++ b/libs/input/tests/InputConsumer_test.cpp
@@ -70,11 +70,20 @@
[]() { return std::make_unique<LegacyResampler>(); });
}
- void invokeLooperCallback() const {
+ bool invokeLooperCallback() const {
sp<LooperCallback> callback;
- ASSERT_TRUE(mLooper->getFdStateDebug(mClientTestChannel->getFd(), /*ident=*/nullptr,
- /*events=*/nullptr, &callback, /*data=*/nullptr));
+ const bool found =
+ mLooper->getFdStateDebug(mClientTestChannel->getFd(), /*ident=*/nullptr,
+ /*events=*/nullptr, &callback, /*data=*/nullptr);
+ if (!found) {
+ return false;
+ }
+ if (callback == nullptr) {
+ LOG(FATAL) << "Looper has the fd of interest, but the callback is null!";
+ return false;
+ }
callback->handleEvent(mClientTestChannel->getFd(), ALOOPER_EVENT_INPUT, /*data=*/nullptr);
+ return true;
}
void assertOnBatchedInputEventPendingWasCalled() {
@@ -194,7 +203,7 @@
std::unique_ptr<MotionEvent> moveMotionEvent =
assertReceivedMotionEvent(WithMotionAction(ACTION_MOVE));
ASSERT_NE(moveMotionEvent, nullptr);
- EXPECT_EQ(moveMotionEvent->getHistorySize() + 1, 3UL);
+ EXPECT_EQ(moveMotionEvent->getHistorySize() + 1, 2UL);
mClientTestChannel->assertFinishMessage(/*seq=*/0, /*handled=*/true);
mClientTestChannel->assertFinishMessage(/*seq=*/1, /*handled=*/true);
@@ -271,6 +280,27 @@
}
/**
+ * Check what happens when looper invokes callback after consumer has been destroyed.
+ * This reproduces a crash where the LooperEventCallback was added back to the Looper during
+ * destructor, thus allowing the looper callback to be invoked onto a null consumer object.
+ */
+TEST_F(InputConsumerTest, LooperCallbackInvokedAfterConsumerDestroyed) {
+ mClientTestChannel->enqueueMessage(
+ InputMessageBuilder{InputMessage::Type::MOTION, /*seq=*/0}.action(ACTION_DOWN).build());
+ mClientTestChannel->enqueueMessage(
+ InputMessageBuilder{InputMessage::Type::MOTION, /*seq=*/1}.action(ACTION_MOVE).build());
+ ASSERT_TRUE(invokeLooperCallback());
+ assertOnBatchedInputEventPendingWasCalled();
+ assertReceivedMotionEvent(WithMotionAction(ACTION_DOWN));
+ mClientTestChannel->assertFinishMessage(/*seq=*/0, /*handled=*/true);
+
+ // Now, destroy the consumer and invoke the looper callback again after it's been destroyed.
+ mConsumer.reset();
+ mClientTestChannel->assertFinishMessage(/*seq=*/1, /*handled=*/false);
+ ASSERT_FALSE(invokeLooperCallback());
+}
+
+/**
* Send an event to the InputConsumer, but do not invoke "consumeBatchedInputEvents", thus leaving
* the input event unconsumed by the callbacks. Ensure that no crash occurs when the consumer is
* destroyed.
@@ -443,4 +473,5 @@
mClientTestChannel->assertFinishMessage(/*seq=*/8, /*handled=*/true);
mClientTestChannel->assertFinishMessage(/*seq=*/10, /*handled=*/true);
}
+
} // namespace android
diff --git a/libs/input/tests/InputPublisherAndConsumerNoResampling_test.cpp b/libs/input/tests/InputPublisherAndConsumerNoResampling_test.cpp
index 1210f71..1dadae9 100644
--- a/libs/input/tests/InputPublisherAndConsumerNoResampling_test.cpp
+++ b/libs/input/tests/InputPublisherAndConsumerNoResampling_test.cpp
@@ -14,15 +14,18 @@
* limitations under the License.
*/
+#include <TestEventMatchers.h>
#include <android-base/logging.h>
#include <attestation/HmacKeyManager.h>
#include <ftl/enum.h>
+#include <gmock/gmock.h>
#include <gtest/gtest.h>
#include <input/BlockingQueue.h>
#include <input/InputConsumerNoResampling.h>
#include <input/InputTransport.h>
using android::base::Result;
+using ::testing::Matcher;
namespace android {
@@ -278,7 +281,7 @@
void SetUp() override {
std::unique_ptr<InputChannel> serverChannel;
status_t result =
- InputChannel::openInputChannelPair("channel name", serverChannel, mClientChannel);
+ InputChannel::openInputChannelPair("test channel", serverChannel, mClientChannel);
ASSERT_EQ(OK, result);
mPublisher = std::make_unique<InputPublisher>(std::move(serverChannel));
@@ -316,6 +319,8 @@
protected:
// Interaction with the looper thread
+ void blockLooper();
+ void unblockLooper();
enum class LooperMessage : int {
CALL_PROBABLY_HAS_INPUT,
CREATE_CONSUMER,
@@ -336,6 +341,8 @@
// accessed on the test thread.
BlockingQueue<bool> mProbablyHasInputResponses;
+ std::unique_ptr<MotionEvent> assertReceivedMotionEvent(const Matcher<MotionEvent>& matcher);
+
private:
sp<MessageHandler> mMessageHandler;
void handleMessage(const Message& message);
@@ -384,11 +391,45 @@
};
};
+void InputPublisherAndConsumerNoResamplingTest::blockLooper() {
+ {
+ std::scoped_lock l(mLock);
+ mLooperMayProceed = false;
+ }
+ sendMessage(LooperMessage::BLOCK_LOOPER);
+ {
+ std::unique_lock l(mLock);
+ mNotifyLooperWaiting.wait(l, [this] { return mLooperIsBlocked; });
+ }
+}
+
+void InputPublisherAndConsumerNoResamplingTest::unblockLooper() {
+ {
+ std::scoped_lock l(mLock);
+ mLooperMayProceed = true;
+ }
+ mNotifyLooperMayProceed.notify_all();
+}
+
void InputPublisherAndConsumerNoResamplingTest::sendMessage(LooperMessage message) {
Message msg{ftl::to_underlying(message)};
mLooper->sendMessage(mMessageHandler, msg);
}
+std::unique_ptr<MotionEvent> InputPublisherAndConsumerNoResamplingTest::assertReceivedMotionEvent(
+ const Matcher<MotionEvent>& matcher) {
+ std::optional<std::unique_ptr<MotionEvent>> event = mMotionEvents.popWithTimeout(TIMEOUT);
+ if (!event) {
+ ADD_FAILURE() << "No event was received, but expected motion " << matcher;
+ return nullptr;
+ }
+ if (*event == nullptr) {
+ LOG(FATAL) << "Event was received, but it was null";
+ }
+ EXPECT_THAT(**event, matcher);
+ return std::move(*event);
+}
+
void InputPublisherAndConsumerNoResamplingTest::handleMessage(const Message& message) {
switch (static_cast<LooperMessage>(message.what)) {
case LooperMessage::CALL_PROBABLY_HAS_INPUT: {
@@ -572,8 +613,7 @@
const nsecs_t publishTimeOfDown = systemTime(SYSTEM_TIME_MONOTONIC);
publishMotionEvent(*mPublisher, argsDown);
- // Consume the DOWN event.
- ASSERT_TRUE(mMotionEvents.popWithTimeout(TIMEOUT).has_value());
+ assertReceivedMotionEvent(WithMotionAction(AMOTION_EVENT_ACTION_DOWN));
verifyFinishedSignal(*mPublisher, mSeq, publishTimeOfDown);
@@ -582,15 +622,7 @@
std::queue<uint32_t> publishedSequenceNumbers;
// Block Looper to increase the chance of batching events
- {
- std::scoped_lock l(mLock);
- mLooperMayProceed = false;
- }
- sendMessage(LooperMessage::BLOCK_LOOPER);
- {
- std::unique_lock l(mLock);
- mNotifyLooperWaiting.wait(l, [this] { return mLooperIsBlocked; });
- }
+ blockLooper();
uint32_t firstSampleId;
for (size_t i = 0; i < nSamples; ++i) {
@@ -611,21 +643,16 @@
std::vector<MotionEvent> singleSampledMotionEvents;
- // Unblock Looper
- {
- std::scoped_lock l(mLock);
- mLooperMayProceed = true;
- }
- mNotifyLooperMayProceed.notify_all();
+ unblockLooper();
// We have no control over the socket behavior, so the consumer can receive
// the motion as a batched event, or as a sequence of multiple single-sample MotionEvents (or a
// mix of those)
while (singleSampledMotionEvents.size() != nSamples) {
- const std::optional<std::unique_ptr<MotionEvent>> batchedMotionEvent =
- mMotionEvents.popWithTimeout(TIMEOUT);
+ const std::unique_ptr<MotionEvent> batchedMotionEvent =
+ assertReceivedMotionEvent(WithMotionAction(ACTION_MOVE));
// The events received by these calls are never null
- std::vector<MotionEvent> splitMotionEvents = splitBatchedMotionEvent(**batchedMotionEvent);
+ std::vector<MotionEvent> splitMotionEvents = splitBatchedMotionEvent(*batchedMotionEvent);
singleSampledMotionEvents.insert(singleSampledMotionEvents.end(), splitMotionEvents.begin(),
splitMotionEvents.end());
}
@@ -681,10 +708,7 @@
}
mNotifyLooperMayProceed.notify_all();
- std::optional<std::unique_ptr<MotionEvent>> optMotion = mMotionEvents.popWithTimeout(TIMEOUT);
- ASSERT_TRUE(optMotion.has_value());
- std::unique_ptr<MotionEvent> motion = std::move(*optMotion);
- ASSERT_EQ(ACTION_MOVE, motion->getAction());
+ assertReceivedMotionEvent(WithMotionAction(ACTION_MOVE));
verifyFinishedSignal(*mPublisher, seq, publishTime);
}
@@ -696,9 +720,7 @@
nsecs_t publishTime = systemTime(SYSTEM_TIME_MONOTONIC);
publishMotionEvent(*mPublisher, args);
- std::optional<std::unique_ptr<MotionEvent>> optMotion = mMotionEvents.popWithTimeout(TIMEOUT);
- ASSERT_TRUE(optMotion.has_value());
- std::unique_ptr<MotionEvent> event = std::move(*optMotion);
+ std::unique_ptr<MotionEvent> event = assertReceivedMotionEvent(WithMotionAction(action));
verifyArgsEqualToEvent(args, *event);
@@ -796,6 +818,15 @@
verifyFinishedSignal(*mPublisher, seq, publishTime);
}
+/**
+ * If the publisher has died, consumer should not crash when trying to send an outgoing message.
+ */
+TEST_F(InputPublisherAndConsumerNoResamplingTest, ConsumerWritesAfterPublisherDies) {
+ mPublisher.reset(); // The publisher has died
+ mReportTimelineArgs.emplace(/*inputEventId=*/10, /*gpuCompletedTime=*/20, /*presentTime=*/30);
+ sendMessage(LooperMessage::CALL_REPORT_TIMELINE);
+}
+
TEST_F(InputPublisherAndConsumerNoResamplingTest, SendTimeline) {
const int32_t inputEventId = 20;
const nsecs_t gpuCompletedTime = 30;
diff --git a/libs/input/tests/OneEuroFilter_test.cpp b/libs/input/tests/OneEuroFilter_test.cpp
new file mode 100644
index 0000000..8645508
--- /dev/null
+++ b/libs/input/tests/OneEuroFilter_test.cpp
@@ -0,0 +1,137 @@
+/**
+ * Copyright 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <input/OneEuroFilter.h>
+
+#include <algorithm>
+#include <chrono>
+#include <cmath>
+#include <numeric>
+#include <vector>
+
+#include <gtest/gtest.h>
+
+#include <input/Input.h>
+
+namespace android {
+namespace {
+
+using namespace std::literals::chrono_literals;
+using std::chrono::duration;
+
+struct Sample {
+ duration<double> timestamp{};
+ double value{};
+
+ friend bool operator<(const Sample& lhs, const Sample& rhs) { return lhs.value < rhs.value; }
+};
+
+/**
+ * Generates a sinusoidal signal with the passed frequency and amplitude.
+ */
+std::vector<Sample> generateSinusoidalSignal(duration<double> signalDuration,
+ double samplingFrequency, double signalFrequency,
+ double amplitude) {
+ std::vector<Sample> signal;
+ const duration<double> samplingPeriod{1.0 / samplingFrequency};
+ for (duration<double> timestamp{0.0}; timestamp < signalDuration; timestamp += samplingPeriod) {
+ signal.push_back(
+ Sample{timestamp,
+ amplitude * std::sin(2.0 * M_PI * signalFrequency * timestamp.count())});
+ }
+ return signal;
+}
+
+double meanAbsoluteError(const std::vector<Sample>& filteredSignal,
+ const std::vector<Sample>& signal) {
+ if (filteredSignal.size() != signal.size()) {
+ ADD_FAILURE() << "filteredSignal and signal do not have equal number of samples";
+ return std::numeric_limits<double>::max();
+ }
+ std::vector<double> absoluteError;
+ for (size_t sampleIndex = 0; sampleIndex < signal.size(); ++sampleIndex) {
+ absoluteError.push_back(
+ std::abs(filteredSignal[sampleIndex].value - signal[sampleIndex].value));
+ }
+ if (absoluteError.empty()) {
+ ADD_FAILURE() << "Zero division. absoluteError is empty";
+ return std::numeric_limits<double>::max();
+ }
+ return std::accumulate(absoluteError.begin(), absoluteError.end(), 0.0) / absoluteError.size();
+}
+
+double maxAbsoluteAmplitude(const std::vector<Sample>& signal) {
+ if (signal.empty()) {
+ ADD_FAILURE() << "Max absolute value amplitude does not exist. Signal is empty";
+ return std::numeric_limits<double>::max();
+ }
+ std::vector<Sample> absoluteSignal;
+ for (const Sample& sample : signal) {
+ absoluteSignal.push_back(Sample{sample.timestamp, std::abs(sample.value)});
+ }
+ return std::max_element(absoluteSignal.begin(), absoluteSignal.end())->value;
+}
+
+} // namespace
+
+class OneEuroFilterTest : public ::testing::Test {
+protected:
+ // The constructor's parameters are the ones that Chromium's using. The tuning was based on a 60
+ // Hz sampling frequency. Refer to their one_euro_filter.h header for additional information
+ // about these parameters.
+ OneEuroFilterTest() : mFilter{/*minCutoffFreq=*/4.7, /*beta=*/0.01} {}
+
+ std::vector<Sample> filterSignal(const std::vector<Sample>& signal) {
+ std::vector<Sample> filteredSignal;
+ for (const Sample& sample : signal) {
+ filteredSignal.push_back(
+ Sample{sample.timestamp,
+ mFilter.filter(std::chrono::duration_cast<std::chrono::nanoseconds>(
+ sample.timestamp),
+ sample.value)});
+ }
+ return filteredSignal;
+ }
+
+ OneEuroFilter mFilter;
+};
+
+TEST_F(OneEuroFilterTest, PassLowFrequencySignal) {
+ const std::vector<Sample> signal =
+ generateSinusoidalSignal(1s, /*samplingFrequency=*/60, /*signalFrequency=*/1,
+ /*amplitude=*/1);
+
+ const std::vector<Sample> filteredSignal = filterSignal(signal);
+
+ // The reason behind using the mean absolute error as a metric is that, ideally, a low frequency
+ // filtered signal is expected to be almost identical to the raw one. Therefore, the error
+ // between them should be minimal. The constant is heuristically chosen.
+ EXPECT_LT(meanAbsoluteError(filteredSignal, signal), 0.25);
+}
+
+TEST_F(OneEuroFilterTest, RejectHighFrequencySignal) {
+ const std::vector<Sample> signal =
+ generateSinusoidalSignal(1s, /*samplingFrequency=*/60, /*signalFrequency=*/22.5,
+ /*amplitude=*/1);
+
+ const std::vector<Sample> filteredSignal = filterSignal(signal);
+
+ // The filtered signal should consist of values that are much closer to zero. The comparison
+ // constant is heuristically chosen.
+ EXPECT_LT(maxAbsoluteAmplitude(filteredSignal), 0.25);
+}
+
+} // namespace android
diff --git a/libs/input/tests/TestEventMatchers.h b/libs/input/tests/TestEventMatchers.h
index 3589de5..56eaefd 100644
--- a/libs/input/tests/TestEventMatchers.h
+++ b/libs/input/tests/TestEventMatchers.h
@@ -17,6 +17,7 @@
#pragma once
#include <chrono>
+#include <cmath>
#include <ostream>
#include <vector>
@@ -75,12 +76,18 @@
using is_gtest_matcher = void;
explicit WithMotionActionMatcher(int32_t action) : mAction(action) {}
- bool MatchAndExplain(const MotionEvent& event, std::ostream*) const {
- bool matches = mAction == event.getAction();
- if (event.getAction() == AMOTION_EVENT_ACTION_CANCEL) {
- matches &= (event.getFlags() & AMOTION_EVENT_FLAG_CANCELED) != 0;
+ bool MatchAndExplain(const MotionEvent& event, testing::MatchResultListener* listener) const {
+ if (mAction != event.getAction()) {
+ *listener << "expected " << MotionEvent::actionToString(mAction) << ", but got "
+ << MotionEvent::actionToString(event.getAction());
+ return false;
}
- return matches;
+ if (event.getAction() == AMOTION_EVENT_ACTION_CANCEL &&
+ (event.getFlags() & AMOTION_EVENT_FLAG_CANCELED) == 0) {
+ *listener << "event with CANCEL action is missing FLAG_CANCELED";
+ return false;
+ }
+ return true;
}
void DescribeTo(std::ostream* os) const {
@@ -150,14 +157,18 @@
++pointerIndex) {
const PointerCoords& pointerCoords =
*(motionEvent.getHistoricalRawPointerCoords(pointerIndex, mSampleIndex));
- if ((pointerCoords.getX() != mSample.pointers[pointerIndex].x) ||
- (pointerCoords.getY() != mSample.pointers[pointerIndex].y)) {
+
+ if ((std::abs(pointerCoords.getX() - mSample.pointers[pointerIndex].x) >
+ MotionEvent::ROUNDING_PRECISION) ||
+ (std::abs(pointerCoords.getY() - mSample.pointers[pointerIndex].y) >
+ MotionEvent::ROUNDING_PRECISION)) {
*os << "sample coordinates mismatch at pointer index " << pointerIndex
<< ". sample: (" << pointerCoords.getX() << ", " << pointerCoords.getY()
<< ") expected: (" << mSample.pointers[pointerIndex].x << ", "
<< mSample.pointers[pointerIndex].y << ")";
return false;
}
+
if (motionEvent.isResampled(pointerIndex, mSampleIndex) !=
mSample.pointers[pointerIndex].isResampled) {
*os << "resampling flag mismatch. sample: "
diff --git a/libs/input/tests/TfLiteMotionPredictor_test.cpp b/libs/input/tests/TfLiteMotionPredictor_test.cpp
index c3ac0b7..0c19ebe 100644
--- a/libs/input/tests/TfLiteMotionPredictor_test.cpp
+++ b/libs/input/tests/TfLiteMotionPredictor_test.cpp
@@ -89,23 +89,23 @@
buffers.pushSample(/*timestamp=*/1,
{.position = {.x = 10, .y = 10},
.pressure = 0,
- .orientation = 0,
- .tilt = 0.2});
+ .tilt = 0.2,
+ .orientation = 0});
buffers.pushSample(/*timestamp=*/2,
{.position = {.x = 10, .y = 50},
.pressure = 0.4,
- .orientation = M_PI / 4,
- .tilt = 0.3});
+ .tilt = 0.3,
+ .orientation = M_PI / 4});
buffers.pushSample(/*timestamp=*/3,
{.position = {.x = 30, .y = 50},
.pressure = 0.5,
- .orientation = -M_PI / 4,
- .tilt = 0.4});
+ .tilt = 0.4,
+ .orientation = -M_PI / 4});
buffers.pushSample(/*timestamp=*/3,
{.position = {.x = 30, .y = 60},
.pressure = 0,
- .orientation = 0,
- .tilt = 0.5});
+ .tilt = 0.5,
+ .orientation = 0});
buffers.copyTo(*model);
const int zeroPadding = model->inputLength() - 3;
diff --git a/libs/input/tests/TouchResampling_test.cpp b/libs/input/tests/TouchResampling_test.cpp
index 8d8b530..9841c03 100644
--- a/libs/input/tests/TouchResampling_test.cpp
+++ b/libs/input/tests/TouchResampling_test.cpp
@@ -571,11 +571,12 @@
std::chrono::nanoseconds frameTime;
std::vector<InputEventEntry> entries, expectedEntries;
- // full action for when a pointer with id=1 appears (some other pointer must already be present)
+ // full action for when a pointer with index=1 appears (some other pointer must already be
+ // present)
constexpr int32_t actionPointer1Down =
AMOTION_EVENT_ACTION_POINTER_DOWN + (1 << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT);
- // full action for when a pointer with id=0 disappears (some other pointer must still remain)
+ // full action for when a pointer with index=0 disappears (some other pointer must still remain)
constexpr int32_t actionPointer0Up =
AMOTION_EVENT_ACTION_POINTER_UP + (0 << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT);
diff --git a/libs/nativedisplay/include/surfacetexture/SurfaceTexture.h b/libs/nativedisplay/include/surfacetexture/SurfaceTexture.h
index f1453bd..006a785 100644
--- a/libs/nativedisplay/include/surfacetexture/SurfaceTexture.h
+++ b/libs/nativedisplay/include/surfacetexture/SurfaceTexture.h
@@ -344,7 +344,8 @@
* mEglSlots array in addition to the ConsumerBase.
*/
virtual status_t releaseBufferLocked(int slot, const sp<GraphicBuffer> graphicBuffer,
- EGLDisplay display, EGLSyncKHR eglFence) override;
+ EGLDisplay display = EGL_NO_DISPLAY,
+ EGLSyncKHR eglFence = EGL_NO_SYNC_KHR) override;
/**
* freeBufferLocked frees up the given buffer slot. If the slot has been
diff --git a/libs/nativedisplay/surfacetexture/EGLConsumer.cpp b/libs/nativedisplay/surfacetexture/EGLConsumer.cpp
index 275b7a4..3959fce 100644
--- a/libs/nativedisplay/surfacetexture/EGLConsumer.cpp
+++ b/libs/nativedisplay/surfacetexture/EGLConsumer.cpp
@@ -150,8 +150,7 @@
}
}
- err = st.releaseBufferLocked(buf, st.mSlots[buf].mGraphicBuffer, mEglDisplay,
- EGL_NO_SYNC_KHR);
+ err = st.releaseBufferLocked(buf, st.mSlots[buf].mGraphicBuffer);
if (err < NO_ERROR) {
EGC_LOGE("releaseTexImage: failed to release buffer: %s (%d)", strerror(-err), err);
return err;
@@ -234,14 +233,14 @@
if (st.mOpMode != SurfaceTexture::OpMode::attachedToGL) {
EGC_LOGE("updateAndRelease: EGLConsumer is not attached to an OpenGL "
"ES context");
- st.releaseBufferLocked(slot, st.mSlots[slot].mGraphicBuffer, mEglDisplay, EGL_NO_SYNC_KHR);
+ st.releaseBufferLocked(slot, st.mSlots[slot].mGraphicBuffer);
return INVALID_OPERATION;
}
// Confirm state.
err = checkAndUpdateEglStateLocked(st);
if (err != NO_ERROR) {
- st.releaseBufferLocked(slot, st.mSlots[slot].mGraphicBuffer, mEglDisplay, EGL_NO_SYNC_KHR);
+ st.releaseBufferLocked(slot, st.mSlots[slot].mGraphicBuffer);
return err;
}
@@ -254,7 +253,7 @@
if (err != NO_ERROR) {
EGC_LOGW("updateAndRelease: unable to createImage on display=%p slot=%d", mEglDisplay,
slot);
- st.releaseBufferLocked(slot, st.mSlots[slot].mGraphicBuffer, mEglDisplay, EGL_NO_SYNC_KHR);
+ st.releaseBufferLocked(slot, st.mSlots[slot].mGraphicBuffer);
return UNKNOWN_ERROR;
}
@@ -266,8 +265,7 @@
// release the old buffer, so instead we just drop the new frame.
// As we are still under lock since acquireBuffer, it is safe to
// release by slot.
- st.releaseBufferLocked(slot, st.mSlots[slot].mGraphicBuffer, mEglDisplay,
- EGL_NO_SYNC_KHR);
+ st.releaseBufferLocked(slot, st.mSlots[slot].mGraphicBuffer);
return err;
}
}
diff --git a/libs/nativedisplay/surfacetexture/ImageConsumer.cpp b/libs/nativedisplay/surfacetexture/ImageConsumer.cpp
index 32b229d..60e87b5 100644
--- a/libs/nativedisplay/surfacetexture/ImageConsumer.cpp
+++ b/libs/nativedisplay/surfacetexture/ImageConsumer.cpp
@@ -64,8 +64,7 @@
// Wait on the producer fence for the buffer to be ready.
err = fenceWait(item.mFence->get(), fencePassThroughHandle);
if (err != OK) {
- st.releaseBufferLocked(slot, st.mSlots[slot].mGraphicBuffer, EGL_NO_DISPLAY,
- EGL_NO_SYNC_KHR);
+ st.releaseBufferLocked(slot, st.mSlots[slot].mGraphicBuffer);
return nullptr;
}
}
@@ -79,8 +78,7 @@
err = createFence(st.mUseFenceSync, &mImageSlots[slot].eglFence(), &display,
&releaseFenceId, fencePassThroughHandle);
if (OK != err) {
- st.releaseBufferLocked(slot, st.mSlots[slot].mGraphicBuffer, EGL_NO_DISPLAY,
- EGL_NO_SYNC_KHR);
+ st.releaseBufferLocked(slot, st.mSlots[slot].mGraphicBuffer);
return nullptr;
}
@@ -91,8 +89,7 @@
releaseFence);
if (err != OK) {
IMG_LOGE("dequeueImage: error adding release fence: %s (%d)", strerror(-err), err);
- st.releaseBufferLocked(slot, st.mSlots[slot].mGraphicBuffer, EGL_NO_DISPLAY,
- EGL_NO_SYNC_KHR);
+ st.releaseBufferLocked(slot, st.mSlots[slot].mGraphicBuffer);
return nullptr;
}
}
diff --git a/libs/nativewindow/ANativeWindow.cpp b/libs/nativewindow/ANativeWindow.cpp
index f97eed5..f5a1db5 100644
--- a/libs/nativewindow/ANativeWindow.cpp
+++ b/libs/nativewindow/ANativeWindow.cpp
@@ -222,6 +222,8 @@
static_cast<int>(HAL_DATASPACE_BT2020_ITU_HLG));
static_assert(static_cast<int>(ADATASPACE_DEPTH) == static_cast<int>(HAL_DATASPACE_DEPTH));
static_assert(static_cast<int>(ADATASPACE_DYNAMIC_DEPTH) == static_cast<int>(HAL_DATASPACE_DYNAMIC_DEPTH));
+ static_assert(static_cast<int>(ADATASPACE_DISPLAY_BT2020) ==
+ static_cast<int>(HAL_DATASPACE_DISPLAY_BT2020));
if (!window || !query(window, NATIVE_WINDOW_IS_VALID)) {
return -EINVAL;
diff --git a/libs/nativewindow/include/android/data_space.h b/libs/nativewindow/include/android/data_space.h
index 8056d9a..295a307 100644
--- a/libs/nativewindow/include/android/data_space.h
+++ b/libs/nativewindow/include/android/data_space.h
@@ -578,6 +578,13 @@
*/
ADATASPACE_BT2020_ITU_HLG = 302383104, // ADATASPACE_STANDARD_BT2020 | ADATASPACE_TRANSFER_HLG |
// ADATASPACE_RANGE_LIMITED
+ /**
+ * sRGB-encoded BT. 2020
+ *
+ * Uses full range, sRGB transfer and BT2020 standard.
+ */
+ ADATASPACE_DISPLAY_BT2020 = 142999552, // ADATASPACE_STANDARD_BT2020 | ADATASPACE_TRANSFER_SRGB
+ // | ADATASPACE_RANGE_FULL
/**
* Depth
diff --git a/libs/nativewindow/include/android/native_window.h b/libs/nativewindow/include/android/native_window.h
index be6623e..6f816bf 100644
--- a/libs/nativewindow/include/android/native_window.h
+++ b/libs/nativewindow/include/android/native_window.h
@@ -366,14 +366,13 @@
*
* See ANativeWindow_setFrameRateWithChangeStrategy().
*
- * Available since API level 34.
+ * Available since API level 31.
*
* \param window pointer to an ANativeWindow object.
*
* \return 0 for success, -EINVAL if the window value is invalid.
*/
-inline int32_t ANativeWindow_clearFrameRate(ANativeWindow* window)
- __INTRODUCED_IN(__ANDROID_API_U__) {
+inline int32_t ANativeWindow_clearFrameRate(ANativeWindow* window) __INTRODUCED_IN(31) {
return ANativeWindow_setFrameRateWithChangeStrategy(window, 0,
ANATIVEWINDOW_FRAME_RATE_COMPATIBILITY_DEFAULT,
ANATIVEWINDOW_CHANGE_FRAME_RATE_ONLY_IF_SEAMLESS);
diff --git a/libs/nativewindow/rust/src/lib.rs b/libs/nativewindow/rust/src/lib.rs
index a1d986e..9876362 100644
--- a/libs/nativewindow/rust/src/lib.rs
+++ b/libs/nativewindow/rust/src/lib.rs
@@ -31,11 +31,13 @@
};
use ffi::{
AHardwareBuffer, AHardwareBuffer_Desc, AHardwareBuffer_readFromParcel,
- AHardwareBuffer_writeToParcel,
+ AHardwareBuffer_writeToParcel, ARect,
};
+use std::ffi::c_void;
use std::fmt::{self, Debug, Formatter};
-use std::mem::ManuallyDrop;
-use std::ptr::{self, null_mut, NonNull};
+use std::mem::{forget, ManuallyDrop};
+use std::os::fd::{AsRawFd, BorrowedFd, FromRawFd, OwnedFd};
+use std::ptr::{self, null, null_mut, NonNull};
/// Wrapper around a C `AHardwareBuffer_Desc`.
#[derive(Clone, Debug, PartialEq, Eq)]
@@ -203,8 +205,8 @@
Self(buffer_ptr)
}
- /// Creates a new Rust HardwareBuffer to wrap the given AHardwareBuffer without taking ownership
- /// of it.
+ /// Creates a new Rust HardwareBuffer to wrap the given `AHardwareBuffer` without taking
+ /// ownership of it.
///
/// Unlike [`from_raw`](Self::from_raw) this method will increment the refcount on the buffer.
/// This means that the caller can continue to use the raw buffer it passed in, and must call
@@ -220,14 +222,20 @@
Self(buffer)
}
- /// Get the internal `AHardwareBuffer` pointer that is only valid when this `HardwareBuffer`
- /// exists. This can be used to provide a pointer to the AHB for a C/C++ API over the FFI.
+ /// Returns the internal `AHardwareBuffer` pointer.
+ ///
+ /// This is only valid as long as this `HardwareBuffer` exists, so shouldn't be stored. It can
+ /// be used to provide a pointer for a C/C++ API over FFI.
pub fn as_raw(&self) -> NonNull<AHardwareBuffer> {
self.0
}
- /// Get the internal `AHardwareBuffer` pointer without decrementing the refcount. This can
- /// be used to provide a pointer to the AHB for a C/C++ API over the FFI.
+ /// Gets the internal `AHardwareBuffer` pointer without decrementing the refcount. This can
+ /// be used for a C/C++ API which takes ownership of the pointer.
+ ///
+ /// The caller is responsible for releasing the `AHardwareBuffer` pointer by calling
+ /// `AHardwareBuffer_release` when it is finished with it, or may convert it back to a Rust
+ /// `HardwareBuffer` by calling [`HardwareBuffer::from_raw`].
pub fn into_raw(self) -> NonNull<AHardwareBuffer> {
let buffer = ManuallyDrop::new(self);
buffer.0
@@ -261,10 +269,141 @@
rfu0: 0,
rfu1: 0,
};
- // SAFETY: neither the buffer nor AHardwareBuffer_Desc pointers will be null.
+ // SAFETY: The `AHardwareBuffer` pointer we wrap is always valid, and the
+ // AHardwareBuffer_Desc pointer is valid because it comes from a reference.
unsafe { ffi::AHardwareBuffer_describe(self.0.as_ref(), &mut buffer_desc) };
HardwareBufferDescription(buffer_desc)
}
+
+ /// Locks the hardware buffer for direct CPU access.
+ ///
+ /// # Safety
+ ///
+ /// - If `fence` is `None`, the caller must ensure that all writes to the buffer have completed
+ /// before calling this function.
+ /// - If the buffer has `AHARDWAREBUFFER_FORMAT_BLOB`, multiple threads or process may lock the
+ /// buffer simultaneously, but the caller must ensure that they don't access it simultaneously
+ /// and break Rust's aliasing rules, like any other shared memory.
+ /// - Otherwise if `usage` includes `AHARDWAREBUFFER_USAGE_CPU_WRITE_RARELY` or
+ /// `AHARDWAREBUFFER_USAGE_CPU_WRITE_OFTEN`, the caller must ensure that no other threads or
+ /// processes lock the buffer simultaneously for any usage.
+ /// - Otherwise, the caller must ensure that no other threads lock the buffer for writing
+ /// simultaneously.
+ /// - If `rect` is not `None`, the caller must not modify the buffer outside of that rectangle.
+ pub unsafe fn lock<'a>(
+ &'a self,
+ usage: AHardwareBuffer_UsageFlags,
+ fence: Option<BorrowedFd>,
+ rect: Option<&ARect>,
+ ) -> Result<HardwareBufferGuard<'a>, StatusCode> {
+ let fence = if let Some(fence) = fence { fence.as_raw_fd() } else { -1 };
+ let rect = rect.map(ptr::from_ref).unwrap_or(null());
+ let mut address = null_mut();
+ // SAFETY: The `AHardwareBuffer` pointer we wrap is always valid, and the buffer address out
+ // pointer is valid because it comes from a reference. Our caller promises that writes have
+ // completed and there will be no simultaneous read/write locks.
+ let status = unsafe {
+ ffi::AHardwareBuffer_lock(self.0.as_ptr(), usage.0, fence, rect, &mut address)
+ };
+ status_result(status)?;
+ Ok(HardwareBufferGuard {
+ buffer: self,
+ address: NonNull::new(address)
+ .expect("AHardwareBuffer_lock set a null outVirtualAddress"),
+ })
+ }
+
+ /// Locks the hardware buffer for direct CPU access, returning information about the bytes per
+ /// pixel and stride as well.
+ ///
+ /// # Safety
+ ///
+ /// - If `fence` is `None`, the caller must ensure that all writes to the buffer have completed
+ /// before calling this function.
+ /// - If the buffer has `AHARDWAREBUFFER_FORMAT_BLOB`, multiple threads or process may lock the
+ /// buffer simultaneously, but the caller must ensure that they don't access it simultaneously
+ /// and break Rust's aliasing rules, like any other shared memory.
+ /// - Otherwise if `usage` includes `AHARDWAREBUFFER_USAGE_CPU_WRITE_RARELY` or
+ /// `AHARDWAREBUFFER_USAGE_CPU_WRITE_OFTEN`, the caller must ensure that no other threads or
+ /// processes lock the buffer simultaneously for any usage.
+ /// - Otherwise, the caller must ensure that no other threads lock the buffer for writing
+ /// simultaneously.
+ pub unsafe fn lock_and_get_info<'a>(
+ &'a self,
+ usage: AHardwareBuffer_UsageFlags,
+ fence: Option<BorrowedFd>,
+ rect: Option<&ARect>,
+ ) -> Result<LockedBufferInfo<'a>, StatusCode> {
+ let fence = if let Some(fence) = fence { fence.as_raw_fd() } else { -1 };
+ let rect = rect.map(ptr::from_ref).unwrap_or(null());
+ let mut address = null_mut();
+ let mut bytes_per_pixel = 0;
+ let mut stride = 0;
+ // SAFETY: The `AHardwareBuffer` pointer we wrap is always valid, and the various out
+ // pointers are valid because they come from references. Our caller promises that writes have
+ // completed and there will be no simultaneous read/write locks.
+ let status = unsafe {
+ ffi::AHardwareBuffer_lockAndGetInfo(
+ self.0.as_ptr(),
+ usage.0,
+ fence,
+ rect,
+ &mut address,
+ &mut bytes_per_pixel,
+ &mut stride,
+ )
+ };
+ status_result(status)?;
+ Ok(LockedBufferInfo {
+ guard: HardwareBufferGuard {
+ buffer: self,
+ address: NonNull::new(address)
+ .expect("AHardwareBuffer_lockAndGetInfo set a null outVirtualAddress"),
+ },
+ bytes_per_pixel: bytes_per_pixel as u32,
+ stride: stride as u32,
+ })
+ }
+
+ /// Unlocks the hardware buffer from direct CPU access.
+ ///
+ /// Must be called after all changes to the buffer are completed by the caller. This will block
+ /// until the unlocking is complete and the buffer contents are updated.
+ fn unlock(&self) -> Result<(), StatusCode> {
+ // SAFETY: The `AHardwareBuffer` pointer we wrap is always valid.
+ let status = unsafe { ffi::AHardwareBuffer_unlock(self.0.as_ptr(), null_mut()) };
+ status_result(status)?;
+ Ok(())
+ }
+
+ /// Unlocks the hardware buffer from direct CPU access.
+ ///
+ /// Must be called after all changes to the buffer are completed by the caller.
+ ///
+ /// This may not block until all work is completed, but rather will return a file descriptor
+ /// which will be signalled once the unlocking is complete and the buffer contents is updated.
+ /// If `Ok(None)` is returned then unlocking has already completed and no further waiting is
+ /// necessary. The file descriptor may be passed to a subsequent call to [`Self::lock`].
+ pub fn unlock_with_fence(
+ &self,
+ guard: HardwareBufferGuard,
+ ) -> Result<Option<OwnedFd>, StatusCode> {
+ // Forget the guard so that its `Drop` implementation doesn't try to unlock the
+ // HardwareBuffer again.
+ forget(guard);
+
+ let mut fence = -2;
+ // SAFETY: The `AHardwareBuffer` pointer we wrap is always valid.
+ let status = unsafe { ffi::AHardwareBuffer_unlock(self.0.as_ptr(), &mut fence) };
+ let fence = if fence < 0 {
+ None
+ } else {
+ // SAFETY: `AHardwareBuffer_unlock` gives us ownership of the fence file descriptor.
+ Some(unsafe { OwnedFd::from_raw_fd(fence) })
+ };
+ status_result(status)?;
+ Ok(fence)
+ }
}
impl Drop for HardwareBuffer {
@@ -340,6 +479,37 @@
// according to the docs on the underlying gralloc calls)
unsafe impl Sync for HardwareBuffer {}
+/// A guard for when a `HardwareBuffer` is locked.
+///
+/// The `HardwareBuffer` will be unlocked when this is dropped, or may be unlocked via
+/// [`HardwareBuffer::unlock_with_fence`].
+#[derive(Debug)]
+pub struct HardwareBufferGuard<'a> {
+ buffer: &'a HardwareBuffer,
+ /// The address of the buffer in memory.
+ pub address: NonNull<c_void>,
+}
+
+impl<'a> Drop for HardwareBufferGuard<'a> {
+ fn drop(&mut self) {
+ self.buffer
+ .unlock()
+ .expect("Failed to unlock HardwareBuffer when dropping HardwareBufferGuard");
+ }
+}
+
+/// A guard for when a `HardwareBuffer` is locked, with additional information about the number of
+/// bytes per pixel and stride.
+#[derive(Debug)]
+pub struct LockedBufferInfo<'a> {
+ /// The locked buffer guard.
+ pub guard: HardwareBufferGuard<'a>,
+ /// The number of bytes used for each pixel in the buffer.
+ pub bytes_per_pixel: u32,
+ /// The stride in bytes between rows in the buffer.
+ pub stride: u32,
+}
+
#[cfg(test)]
mod test {
use super::*;
@@ -493,4 +663,108 @@
assert_eq!(buffer.description(), buffer_description);
assert_eq!(buffer2.description(), buffer_description);
}
+
+ #[test]
+ fn lock() {
+ let buffer = HardwareBuffer::new(&HardwareBufferDescription::new(
+ 1024,
+ 512,
+ 1,
+ AHardwareBuffer_Format::AHARDWAREBUFFER_FORMAT_R8G8B8A8_UNORM,
+ AHardwareBuffer_UsageFlags::AHARDWAREBUFFER_USAGE_CPU_READ_OFTEN,
+ 0,
+ ))
+ .expect("Failed to create buffer");
+
+ // SAFETY: No other threads or processes have access to the buffer.
+ let guard = unsafe {
+ buffer.lock(
+ AHardwareBuffer_UsageFlags::AHARDWAREBUFFER_USAGE_CPU_READ_OFTEN,
+ None,
+ None,
+ )
+ }
+ .unwrap();
+
+ drop(guard);
+ }
+
+ #[test]
+ fn lock_with_rect() {
+ let buffer = HardwareBuffer::new(&HardwareBufferDescription::new(
+ 1024,
+ 512,
+ 1,
+ AHardwareBuffer_Format::AHARDWAREBUFFER_FORMAT_R8G8B8A8_UNORM,
+ AHardwareBuffer_UsageFlags::AHARDWAREBUFFER_USAGE_CPU_READ_OFTEN,
+ 0,
+ ))
+ .expect("Failed to create buffer");
+ let rect = ARect { left: 10, right: 20, top: 35, bottom: 45 };
+
+ // SAFETY: No other threads or processes have access to the buffer.
+ let guard = unsafe {
+ buffer.lock(
+ AHardwareBuffer_UsageFlags::AHARDWAREBUFFER_USAGE_CPU_READ_OFTEN,
+ None,
+ Some(&rect),
+ )
+ }
+ .unwrap();
+
+ drop(guard);
+ }
+
+ #[test]
+ fn unlock_with_fence() {
+ let buffer = HardwareBuffer::new(&HardwareBufferDescription::new(
+ 1024,
+ 512,
+ 1,
+ AHardwareBuffer_Format::AHARDWAREBUFFER_FORMAT_R8G8B8A8_UNORM,
+ AHardwareBuffer_UsageFlags::AHARDWAREBUFFER_USAGE_CPU_READ_OFTEN,
+ 0,
+ ))
+ .expect("Failed to create buffer");
+
+ // SAFETY: No other threads or processes have access to the buffer.
+ let guard = unsafe {
+ buffer.lock(
+ AHardwareBuffer_UsageFlags::AHARDWAREBUFFER_USAGE_CPU_READ_OFTEN,
+ None,
+ None,
+ )
+ }
+ .unwrap();
+
+ buffer.unlock_with_fence(guard).unwrap();
+ }
+
+ #[test]
+ fn lock_with_info() {
+ const WIDTH: u32 = 1024;
+ let buffer = HardwareBuffer::new(&HardwareBufferDescription::new(
+ WIDTH,
+ 512,
+ 1,
+ AHardwareBuffer_Format::AHARDWAREBUFFER_FORMAT_R8G8B8A8_UNORM,
+ AHardwareBuffer_UsageFlags::AHARDWAREBUFFER_USAGE_CPU_READ_OFTEN,
+ 0,
+ ))
+ .expect("Failed to create buffer");
+
+ // SAFETY: No other threads or processes have access to the buffer.
+ let info = unsafe {
+ buffer.lock_and_get_info(
+ AHardwareBuffer_UsageFlags::AHARDWAREBUFFER_USAGE_CPU_READ_OFTEN,
+ None,
+ None,
+ )
+ }
+ .unwrap();
+
+ assert_eq!(info.bytes_per_pixel, 4);
+ assert_eq!(info.stride, WIDTH * 4);
+ drop(info);
+ }
}
diff --git a/libs/renderengine/Android.bp b/libs/renderengine/Android.bp
index d248ea0..7f207f0 100644
--- a/libs/renderengine/Android.bp
+++ b/libs/renderengine/Android.bp
@@ -105,6 +105,7 @@
"skia/filters/KawaseBlurDualFilter.cpp",
"skia/filters/KawaseBlurFilter.cpp",
"skia/filters/LinearEffect.cpp",
+ "skia/filters/LutShader.cpp",
"skia/filters/MouriMap.cpp",
"skia/filters/StretchShaderFactory.cpp",
"skia/filters/EdgeExtensionShaderFactory.cpp",
diff --git a/libs/renderengine/include/renderengine/RenderEngine.h b/libs/renderengine/include/renderengine/RenderEngine.h
index 0fd982e..95c4d03 100644
--- a/libs/renderengine/include/renderengine/RenderEngine.h
+++ b/libs/renderengine/include/renderengine/RenderEngine.h
@@ -38,6 +38,14 @@
#define PROPERTY_DEBUG_RENDERENGINE_BACKEND "debug.renderengine.backend"
/**
+ * Allows opting particular devices into an initial preview rollout of RenderEngine on Graphite.
+ *
+ * Only applicable within SurfaceFlinger, and if relevant aconfig flags are enabled.
+ */
+#define PROPERTY_DEBUG_RENDERENGINE_GRAPHITE_PREVIEW_OPTIN \
+ "debug.renderengine.graphite_preview_optin"
+
+/**
* Turns on recording of skia commands in SkiaGL version of the RE. This property
* defines number of milliseconds for the recording to take place. A non zero value
* turns on the recording.
diff --git a/libs/renderengine/skia/GaneshVkRenderEngine.cpp b/libs/renderengine/skia/GaneshVkRenderEngine.cpp
index a3a43e2..cc73f40 100644
--- a/libs/renderengine/skia/GaneshVkRenderEngine.cpp
+++ b/libs/renderengine/skia/GaneshVkRenderEngine.cpp
@@ -21,12 +21,15 @@
#include <include/gpu/ganesh/vk/GrVkBackendSemaphore.h>
+#include <android-base/stringprintf.h>
#include <common/trace.h>
#include <log/log_main.h>
#include <sync/sync.h>
namespace android::renderengine::skia {
+using base::StringAppendF;
+
std::unique_ptr<GaneshVkRenderEngine> GaneshVkRenderEngine::create(
const RenderEngineCreationArgs& args) {
std::unique_ptr<GaneshVkRenderEngine> engine(new GaneshVkRenderEngine(args));
@@ -111,4 +114,9 @@
return res;
}
+void GaneshVkRenderEngine::appendBackendSpecificInfoToDump(std::string& result) {
+ StringAppendF(&result, "\n ------------RE Vulkan (Ganesh)----------\n");
+ SkiaVkRenderEngine::appendBackendSpecificInfoToDump(result);
+}
+
} // namespace android::renderengine::skia
diff --git a/libs/renderengine/skia/GaneshVkRenderEngine.h b/libs/renderengine/skia/GaneshVkRenderEngine.h
index e6123c2..ba17f71 100644
--- a/libs/renderengine/skia/GaneshVkRenderEngine.h
+++ b/libs/renderengine/skia/GaneshVkRenderEngine.h
@@ -28,6 +28,7 @@
std::unique_ptr<SkiaGpuContext> createContext(VulkanInterface& vulkanInterface) override;
void waitFence(SkiaGpuContext* context, base::borrowed_fd fenceFd) override;
base::unique_fd flushAndSubmit(SkiaGpuContext* context, sk_sp<SkSurface> dstSurface) override;
+ void appendBackendSpecificInfoToDump(std::string& result) override;
private:
GaneshVkRenderEngine(const RenderEngineCreationArgs& args) : SkiaVkRenderEngine(args) {}
diff --git a/libs/renderengine/skia/GraphiteVkRenderEngine.cpp b/libs/renderengine/skia/GraphiteVkRenderEngine.cpp
index 390ad6e..a9332fa 100644
--- a/libs/renderengine/skia/GraphiteVkRenderEngine.cpp
+++ b/libs/renderengine/skia/GraphiteVkRenderEngine.cpp
@@ -25,6 +25,7 @@
#include <include/gpu/graphite/Recording.h>
#include <include/gpu/graphite/vk/VulkanGraphiteTypes.h>
+#include <android-base/stringprintf.h>
#include <log/log_main.h>
#include <sync/sync.h>
@@ -33,6 +34,8 @@
namespace android::renderengine::skia {
+using base::StringAppendF;
+
std::unique_ptr<GraphiteVkRenderEngine> GraphiteVkRenderEngine::create(
const RenderEngineCreationArgs& args) {
std::unique_ptr<GraphiteVkRenderEngine> engine(new GraphiteVkRenderEngine(args));
@@ -139,4 +142,9 @@
return drawFenceFd;
}
+void GraphiteVkRenderEngine::appendBackendSpecificInfoToDump(std::string& result) {
+ StringAppendF(&result, "\n ------------RE Vulkan (Graphite)----------\n");
+ SkiaVkRenderEngine::appendBackendSpecificInfoToDump(result);
+}
+
} // namespace android::renderengine::skia
diff --git a/libs/renderengine/skia/GraphiteVkRenderEngine.h b/libs/renderengine/skia/GraphiteVkRenderEngine.h
index cf24a3b..33a47f1 100644
--- a/libs/renderengine/skia/GraphiteVkRenderEngine.h
+++ b/libs/renderengine/skia/GraphiteVkRenderEngine.h
@@ -30,6 +30,7 @@
std::unique_ptr<SkiaGpuContext> createContext(VulkanInterface& vulkanInterface) override;
void waitFence(SkiaGpuContext* context, base::borrowed_fd fenceFd) override;
base::unique_fd flushAndSubmit(SkiaGpuContext* context, sk_sp<SkSurface> dstSurface) override;
+ void appendBackendSpecificInfoToDump(std::string& result) override;
private:
GraphiteVkRenderEngine(const RenderEngineCreationArgs& args) : SkiaVkRenderEngine(args) {}
diff --git a/libs/renderengine/skia/SkiaGLRenderEngine.cpp b/libs/renderengine/skia/SkiaGLRenderEngine.cpp
index 4ef7d5b..ddae9fc 100644
--- a/libs/renderengine/skia/SkiaGLRenderEngine.cpp
+++ b/libs/renderengine/skia/SkiaGLRenderEngine.cpp
@@ -541,7 +541,7 @@
void SkiaGLRenderEngine::appendBackendSpecificInfoToDump(std::string& result) {
const GLExtensions& extensions = GLExtensions::getInstance();
- StringAppendF(&result, "\n ------------RE GLES------------\n");
+ StringAppendF(&result, "\n ------------RE GLES (Ganesh)------------\n");
StringAppendF(&result, "EGL implementation : %s\n", extensions.getEGLVersion());
StringAppendF(&result, "%s\n", extensions.getEGLExtensions());
StringAppendF(&result, "GLES: %s, %s, %s\n", extensions.getVendor(), extensions.getRenderer(),
diff --git a/libs/renderengine/skia/SkiaRenderEngine.cpp b/libs/renderengine/skia/SkiaRenderEngine.cpp
index ec9d3ef..b3284e4 100644
--- a/libs/renderengine/skia/SkiaRenderEngine.cpp
+++ b/libs/renderengine/skia/SkiaRenderEngine.cpp
@@ -543,6 +543,12 @@
}
}
+ if (graphicBuffer && parameters.layer.luts) {
+ shader = mLutShader.lutShader(shader, parameters.layer.luts,
+ parameters.layer.sourceDataspace,
+ toSkColorSpace(parameters.outputDataSpace));
+ }
+
if (parameters.requiresLinearEffect) {
const auto format = targetBuffer != nullptr
? std::optional<ui::PixelFormat>(
@@ -567,8 +573,10 @@
}
// disable tonemapping if we already locally tonemapped
- auto inputDataspace =
- usingLocalTonemap ? parameters.outputDataSpace : parameters.layer.sourceDataspace;
+ // skip tonemapping if the luts is in use
+ auto inputDataspace = usingLocalTonemap || (graphicBuffer && parameters.layer.luts)
+ ? parameters.outputDataSpace
+ : parameters.layer.sourceDataspace;
auto effect =
shaders::LinearEffect{.inputDataspace = inputDataspace,
.outputDataspace = parameters.outputDataSpace,
diff --git a/libs/renderengine/skia/SkiaRenderEngine.h b/libs/renderengine/skia/SkiaRenderEngine.h
index b5f8898..7be4c25 100644
--- a/libs/renderengine/skia/SkiaRenderEngine.h
+++ b/libs/renderengine/skia/SkiaRenderEngine.h
@@ -39,6 +39,7 @@
#include "filters/BlurFilter.h"
#include "filters/EdgeExtensionShaderFactory.h"
#include "filters/LinearEffect.h"
+#include "filters/LutShader.h"
#include "filters/StretchShaderFactory.h"
class SkData;
@@ -184,6 +185,7 @@
StretchShaderFactory mStretchShaderFactory;
EdgeExtensionShaderFactory mEdgeExtensionShaderFactory;
+ LutShader mLutShader;
sp<Fence> mLastDrawFence;
BlurFilter* mBlurFilter = nullptr;
diff --git a/libs/renderengine/skia/SkiaVkRenderEngine.cpp b/libs/renderengine/skia/SkiaVkRenderEngine.cpp
index 677a2b6..177abe6 100644
--- a/libs/renderengine/skia/SkiaVkRenderEngine.cpp
+++ b/libs/renderengine/skia/SkiaVkRenderEngine.cpp
@@ -169,24 +169,26 @@
}
void SkiaVkRenderEngine::appendBackendSpecificInfoToDump(std::string& result) {
- StringAppendF(&result, "\n ------------RE Vulkan----------\n");
- StringAppendF(&result, "\n Vulkan device initialized: %d\n", sVulkanInterface.isInitialized());
- StringAppendF(&result, "\n Vulkan protected device initialized: %d\n",
+ // Subclasses will prepend a backend-specific name / section header
+ StringAppendF(&result, "Vulkan device initialized: %d\n", sVulkanInterface.isInitialized());
+ StringAppendF(&result, "Vulkan protected device initialized: %d\n",
sProtectedContentVulkanInterface.isInitialized());
if (!sVulkanInterface.isInitialized()) {
return;
}
- StringAppendF(&result, "\n Instance extensions:\n");
+ StringAppendF(&result, "Instance extensions: [\n");
for (const auto& name : sVulkanInterface.getInstanceExtensionNames()) {
- StringAppendF(&result, "\n %s\n", name.c_str());
+ StringAppendF(&result, " %s\n", name.c_str());
}
+ StringAppendF(&result, "]\n");
- StringAppendF(&result, "\n Device extensions:\n");
+ StringAppendF(&result, "Device extensions: [\n");
for (const auto& name : sVulkanInterface.getDeviceExtensionNames()) {
- StringAppendF(&result, "\n %s\n", name.c_str());
+ StringAppendF(&result, " %s\n", name.c_str());
}
+ StringAppendF(&result, "]\n");
}
} // namespace skia
diff --git a/libs/renderengine/skia/SkiaVkRenderEngine.h b/libs/renderengine/skia/SkiaVkRenderEngine.h
index d2bb3d5..88b04df 100644
--- a/libs/renderengine/skia/SkiaVkRenderEngine.h
+++ b/libs/renderengine/skia/SkiaVkRenderEngine.h
@@ -81,7 +81,7 @@
SkiaRenderEngine::Contexts createContexts() override;
bool supportsProtectedContentImpl() const override;
bool useProtectedContextImpl(GrProtected isProtected) override;
- void appendBackendSpecificInfoToDump(std::string& result) override;
+ virtual void appendBackendSpecificInfoToDump(std::string& result) override;
// TODO: b/300533018 - refactor this to be non-static
static VulkanInterface& getVulkanInterface(bool protectedContext);
diff --git a/libs/renderengine/skia/filters/LutShader.cpp b/libs/renderengine/skia/filters/LutShader.cpp
new file mode 100644
index 0000000..5e9dfbb
--- /dev/null
+++ b/libs/renderengine/skia/filters/LutShader.cpp
@@ -0,0 +1,280 @@
+/*
+ * Copyright 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+#include "LutShader.h"
+
+#include <SkM44.h>
+#include <SkTileMode.h>
+#include <common/trace.h>
+#include <cutils/ashmem.h>
+#include <math/half.h>
+#include <sys/mman.h>
+#include <ui/ColorSpace.h>
+
+#include "include/core/SkColorSpace.h"
+#include "src/core/SkColorFilterPriv.h"
+
+using aidl::android::hardware::graphics::composer3::LutProperties;
+
+namespace android {
+namespace renderengine {
+namespace skia {
+
+static const SkString kShader = SkString(R"(
+ uniform shader image;
+ uniform shader lut;
+ uniform int size;
+ uniform int key;
+ uniform int dimension;
+ uniform vec3 luminanceCoefficients; // for CIE_Y
+
+ vec4 main(vec2 xy) {
+ float4 rgba = image.eval(xy);
+ float3 linear = toLinearSrgb(rgba.rgb);
+ if (dimension == 1) {
+ // RGB
+ if (key == 0) {
+ float indexR = linear.r * float(size - 1);
+ float indexG = linear.g * float(size - 1);
+ float indexB = linear.b * float(size - 1);
+ float gainR = lut.eval(vec2(indexR, 0.0) + 0.5).r;
+ float gainG = lut.eval(vec2(indexG, 0.0) + 0.5).r;
+ float gainB = lut.eval(vec2(indexB, 0.0) + 0.5).r;
+ return float4(linear.r * gainR, linear.g * gainG, linear.b * gainB, rgba.a);
+ // MAX_RGB
+ } else if (key == 1) {
+ float maxRGB = max(linear.r, max(linear.g, linear.b));
+ float index = maxRGB * float(size - 1);
+ float gain = lut.eval(vec2(index, 0.0) + 0.5).r;
+ return float4(linear * gain, rgba.a);
+ // CIE_Y
+ } else if (key == 2) {
+ float y = dot(linear, luminanceCoefficients) / 3.0;
+ float index = y * float(size - 1);
+ float gain = lut.eval(vec2(index, 0.0) + 0.5).r;
+ return float4(linear * gain, rgba.a);
+ }
+ } else if (dimension == 3) {
+ if (key == 0) {
+ float tx = linear.r * float(size - 1);
+ float ty = linear.g * float(size - 1);
+ float tz = linear.b * float(size - 1);
+
+ // calculate lower and upper bounds for each dimension
+ int x = int(tx);
+ int y = int(ty);
+ int z = int(tz);
+
+ int i000 = x + y * size + z * size * size;
+ int i100 = i000 + 1;
+ int i010 = i000 + size;
+ int i110 = i000 + size + 1;
+ int i001 = i000 + size * size;
+ int i101 = i000 + size * size + 1;
+ int i011 = i000 + size * size + size;
+ int i111 = i000 + size * size + size + 1;
+
+ // get 1d normalized indices
+ float c000 = float(i000) / float(size * size * size);
+ float c100 = float(i100) / float(size * size * size);
+ float c010 = float(i010) / float(size * size * size);
+ float c110 = float(i110) / float(size * size * size);
+ float c001 = float(i001) / float(size * size * size);
+ float c101 = float(i101) / float(size * size * size);
+ float c011 = float(i011) / float(size * size * size);
+ float c111 = float(i111) / float(size * size * size);
+
+ //TODO(b/377984618): support Tetrahedral interpolation
+ // perform trilinear interpolation
+ float3 c00 = mix(lut.eval(vec2(c000, 0.0) + 0.5).rgb,
+ lut.eval(vec2(c100, 0.0) + 0.5).rgb, linear.r);
+ float3 c01 = mix(lut.eval(vec2(c001, 0.0) + 0.5).rgb,
+ lut.eval(vec2(c101, 0.0) + 0.5).rgb, linear.r);
+ float3 c10 = mix(lut.eval(vec2(c010, 0.0) + 0.5).rgb,
+ lut.eval(vec2(c110, 0.0) + 0.5).rgb, linear.r);
+ float3 c11 = mix(lut.eval(vec2(c011, 0.0) + 0.5).rgb,
+ lut.eval(vec2(c111, 0.0) + 0.5).rgb, linear.r);
+
+ float3 c0 = mix(c00, c10, linear.g);
+ float3 c1 = mix(c01, c11, linear.g);
+
+ float3 val = mix(c0, c1, linear.b);
+
+ return float4(val, rgba.a);
+ }
+ }
+ return rgba;
+ })");
+
+// same as shader::toColorSpace function
+// TODO: put this function in a general place
+static ColorSpace toColorSpace(ui::Dataspace dataspace) {
+ switch (dataspace & HAL_DATASPACE_STANDARD_MASK) {
+ case HAL_DATASPACE_STANDARD_BT709:
+ return ColorSpace::sRGB();
+ case HAL_DATASPACE_STANDARD_DCI_P3:
+ return ColorSpace::DisplayP3();
+ case HAL_DATASPACE_STANDARD_BT2020:
+ case HAL_DATASPACE_STANDARD_BT2020_CONSTANT_LUMINANCE:
+ return ColorSpace::BT2020();
+ case HAL_DATASPACE_STANDARD_ADOBE_RGB:
+ return ColorSpace::AdobeRGB();
+ case HAL_DATASPACE_STANDARD_BT601_625:
+ case HAL_DATASPACE_STANDARD_BT601_625_UNADJUSTED:
+ case HAL_DATASPACE_STANDARD_BT601_525:
+ case HAL_DATASPACE_STANDARD_BT601_525_UNADJUSTED:
+ case HAL_DATASPACE_STANDARD_BT470M:
+ case HAL_DATASPACE_STANDARD_FILM:
+ case HAL_DATASPACE_STANDARD_UNSPECIFIED:
+ default:
+ return ColorSpace::sRGB();
+ }
+}
+
+sk_sp<SkShader> LutShader::generateLutShader(sk_sp<SkShader> input,
+ const std::vector<float>& buffers,
+ const int32_t offset, const int32_t length,
+ const int32_t dimension, const int32_t size,
+ const int32_t samplingKey,
+ ui::Dataspace srcDataspace) {
+ SFTRACE_NAME("lut shader");
+ std::vector<half> buffer(length * 4); // 4 is for RGBA
+ auto d = static_cast<LutProperties::Dimension>(dimension);
+ if (d == LutProperties::Dimension::ONE_D) {
+ auto it = buffers.begin() + offset;
+ std::generate(buffer.begin(), buffer.end(), [it, i = 0]() mutable {
+ float val = (i++ % 4 == 0) ? *it++ : 0.0f;
+ return half(val);
+ });
+ } else {
+ for (int i = 0; i < length; i++) {
+ buffer[i * 4] = half(buffers[offset + i]);
+ buffer[i * 4 + 1] = half(buffers[offset + length + i]);
+ buffer[i * 4 + 2] = half(buffers[offset + length * 2 + i]);
+ buffer[i * 4 + 3] = half(0);
+ }
+ }
+ /**
+ * 1D Lut RGB/MAX_RGB
+ * (R0, 0, 0, 0)
+ * (R1, 0, 0, 0)
+ *
+ * 1D Lut CIE_Y
+ * (Y0, 0, 0, 0)
+ * (Y1, 0, 0, 0)
+ * ...
+ *
+ * 3D Lut MAX_RGB
+ * (R0, G0, B0, 0)
+ * (R1, G1, B1, 0)
+ * ...
+ */
+ SkImageInfo info = SkImageInfo::Make(length /* the number of rgba */ * 4, 1,
+ kRGBA_F16_SkColorType, kPremul_SkAlphaType);
+ SkBitmap bitmap;
+ bitmap.allocPixels(info);
+ if (!bitmap.installPixels(info, buffer.data(), info.minRowBytes())) {
+ LOG_ALWAYS_FATAL("unable to install pixels");
+ }
+
+ sk_sp<SkImage> lutImage = SkImages::RasterFromBitmap(bitmap);
+ mBuilder->child("image") = input;
+ mBuilder->child("lut") =
+ lutImage->makeRawShader(SkTileMode::kClamp, SkTileMode::kClamp,
+ d == LutProperties::Dimension::ONE_D
+ ? SkSamplingOptions(SkFilterMode::kLinear)
+ : SkSamplingOptions());
+
+ const int uSize = static_cast<int>(size);
+ const int uKey = static_cast<int>(samplingKey);
+ const int uDimension = static_cast<int>(dimension);
+ if (static_cast<LutProperties::SamplingKey>(samplingKey) == LutProperties::SamplingKey::CIE_Y) {
+ // Use predefined colorspaces of input dataspace so that we can get D65 illuminant
+ mat3 toXYZMatrix(toColorSpace(srcDataspace).getRGBtoXYZ());
+ mBuilder->uniform("luminanceCoefficients") =
+ SkV3{toXYZMatrix[0][1], toXYZMatrix[1][1], toXYZMatrix[2][1]};
+ } else {
+ mBuilder->uniform("luminanceCoefficients") = SkV3{1.f, 1.f, 1.f};
+ }
+ mBuilder->uniform("size") = uSize;
+ mBuilder->uniform("key") = uKey;
+ mBuilder->uniform("dimension") = uDimension;
+ return mBuilder->makeShader();
+}
+
+sk_sp<SkShader> LutShader::lutShader(sk_sp<SkShader>& input,
+ std::shared_ptr<gui::DisplayLuts> displayLuts,
+ ui::Dataspace srcDataspace,
+ sk_sp<SkColorSpace> outColorSpace) {
+ if (mBuilder == nullptr) {
+ const static SkRuntimeEffect::Result instance = SkRuntimeEffect::MakeForShader(kShader);
+ mBuilder = std::make_unique<SkRuntimeShaderBuilder>(instance.effect);
+ }
+
+ auto& fd = displayLuts->getLutFileDescriptor();
+ if (fd.ok()) {
+ // de-gamma the image without changing the primaries
+ SkImage* baseImage = input->isAImage((SkMatrix*)nullptr, (SkTileMode*)nullptr);
+ sk_sp<SkColorSpace> baseColorSpace = baseImage && baseImage->colorSpace()
+ ? baseImage->refColorSpace()
+ : SkColorSpace::MakeSRGB();
+ sk_sp<SkColorSpace> lutMathColorSpace = baseColorSpace->makeLinearGamma();
+ input = input->makeWithWorkingColorSpace(lutMathColorSpace);
+
+ auto& offsets = displayLuts->offsets;
+ auto& lutProperties = displayLuts->lutProperties;
+ std::vector<float> buffers;
+ int fullLength = offsets[lutProperties.size() - 1];
+ if (lutProperties[lutProperties.size() - 1].dimension == 1) {
+ fullLength += lutProperties[lutProperties.size() - 1].size;
+ } else {
+ fullLength += (lutProperties[lutProperties.size() - 1].size *
+ lutProperties[lutProperties.size() - 1].size *
+ lutProperties[lutProperties.size() - 1].size * 3);
+ }
+ size_t bufferSize = fullLength * sizeof(float);
+
+ // decode the shared memory of luts
+ float* ptr =
+ (float*)mmap(NULL, bufferSize, PROT_READ | PROT_WRITE, MAP_SHARED, fd.get(), 0);
+ if (ptr == MAP_FAILED) {
+ LOG_ALWAYS_FATAL("mmap failed");
+ }
+ buffers = std::vector<float>(ptr, ptr + fullLength);
+ munmap(ptr, bufferSize);
+
+ for (size_t i = 0; i < offsets.size(); i++) {
+ int bufferSizePerLut = (i == offsets.size() - 1) ? buffers.size() - offsets[i]
+ : offsets[i + 1] - offsets[i];
+ // divide by 3 for 3d Lut because of 3 (RGB) channels
+ if (static_cast<LutProperties::Dimension>(lutProperties[i].dimension) ==
+ LutProperties::Dimension::THREE_D) {
+ bufferSizePerLut /= 3;
+ }
+ input = generateLutShader(input, buffers, offsets[i], bufferSizePerLut,
+ lutProperties[i].dimension, lutProperties[i].size,
+ lutProperties[i].samplingKey, srcDataspace);
+ }
+
+ auto colorXformLutToDst =
+ SkColorFilterPriv::MakeColorSpaceXform(lutMathColorSpace, outColorSpace);
+ input = input->makeWithColorFilter(colorXformLutToDst);
+ }
+ return input;
+}
+
+} // namespace skia
+} // namespace renderengine
+} // namespace android
\ No newline at end of file
diff --git a/libs/renderengine/skia/filters/LutShader.h b/libs/renderengine/skia/filters/LutShader.h
new file mode 100644
index 0000000..7c62fca
--- /dev/null
+++ b/libs/renderengine/skia/filters/LutShader.h
@@ -0,0 +1,45 @@
+/*
+ * Copyright 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *-
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+#pragma once
+
+#include <SkBitmap.h>
+#include <SkImage.h>
+#include <SkRuntimeEffect.h>
+
+#include <aidl/android/hardware/graphics/composer3/LutProperties.h>
+#include <gui/DisplayLuts.h>
+#include <ui/GraphicTypes.h>
+
+namespace android {
+namespace renderengine {
+namespace skia {
+
+class LutShader {
+public:
+ sk_sp<SkShader> lutShader(sk_sp<SkShader>& input, std::shared_ptr<gui::DisplayLuts> displayLuts,
+ ui::Dataspace srcDataspace, sk_sp<SkColorSpace> outColorSpace);
+
+private:
+ sk_sp<SkShader> generateLutShader(sk_sp<SkShader> input, const std::vector<float>& buffers,
+ const int32_t offset, const int32_t length,
+ const int32_t dimension, const int32_t size,
+ const int32_t samplingKey, ui::Dataspace srcDataspace);
+ std::unique_ptr<SkRuntimeShaderBuilder> mBuilder;
+};
+
+} // namespace skia
+} // namespace renderengine
+} // namespace android
diff --git a/libs/sensor/Sensor.cpp b/libs/sensor/Sensor.cpp
index eddd568..797efbe 100644
--- a/libs/sensor/Sensor.cpp
+++ b/libs/sensor/Sensor.cpp
@@ -306,7 +306,18 @@
}
if (halVersion > SENSORS_DEVICE_API_VERSION_1_0 && hwSensor.requiredPermission) {
mRequiredPermission = hwSensor.requiredPermission;
- if (!strcmp(mRequiredPermission, SENSOR_PERMISSION_BODY_SENSORS)) {
+ bool requiresBodySensorPermission =
+ !strcmp(mRequiredPermission, SENSOR_PERMISSION_BODY_SENSORS);
+ if (android::permission::flags::replace_body_sensor_permission_enabled()) {
+ if (requiresBodySensorPermission) {
+ ALOGE("Sensor %s using deprecated Body Sensor permission", mName.c_str());
+ }
+
+ AppOpsManager appOps;
+ // Lookup to see if an AppOp exists for the permission. If none
+ // does, the default value of -1 is used.
+ mRequiredAppOp = appOps.permissionToOpCode(String16(mRequiredPermission));
+ } else if (requiresBodySensorPermission) {
AppOpsManager appOps;
mRequiredAppOp = appOps.permissionToOpCode(String16(SENSOR_PERMISSION_BODY_SENSORS));
}
diff --git a/libs/tracing_perfetto/Android.bp b/libs/tracing_perfetto/Android.bp
index b5c56c5..9a2d4f7 100644
--- a/libs/tracing_perfetto/Android.bp
+++ b/libs/tracing_perfetto/Android.bp
@@ -47,4 +47,6 @@
],
host_supported: true,
+ // for vndbinder
+ vendor_available: true,
}
diff --git a/libs/tracing_perfetto/tracing_perfetto_internal.cpp b/libs/tracing_perfetto/tracing_perfetto_internal.cpp
index 9a0042a..c4f8663 100644
--- a/libs/tracing_perfetto/tracing_perfetto_internal.cpp
+++ b/libs/tracing_perfetto/tracing_perfetto_internal.cpp
@@ -253,15 +253,31 @@
void perfettoTraceAsyncBeginForTrack(const struct PerfettoTeCategory& category, const char* name,
const char* trackName, uint64_t cookie) {
PERFETTO_TE(
- category, PERFETTO_TE_SLICE_BEGIN(name),
- PERFETTO_TE_NAMED_TRACK(trackName, cookie, PerfettoTeProcessTrackUuid()));
+ category, PERFETTO_TE_SLICE_BEGIN(name),
+ PERFETTO_TE_PROTO_TRACK(
+ PerfettoTeNamedTrackUuid(trackName, cookie,
+ PerfettoTeProcessTrackUuid()),
+ PERFETTO_TE_PROTO_FIELD_CSTR(
+ perfetto_protos_TrackDescriptor_atrace_name_field_number,
+ trackName),
+ PERFETTO_TE_PROTO_FIELD_VARINT(
+ perfetto_protos_TrackDescriptor_parent_uuid_field_number,
+ PerfettoTeProcessTrackUuid())));
}
void perfettoTraceAsyncEndForTrack(const struct PerfettoTeCategory& category,
const char* trackName, uint64_t cookie) {
- PERFETTO_TE(
- category, PERFETTO_TE_SLICE_END(),
- PERFETTO_TE_NAMED_TRACK(trackName, cookie, PerfettoTeProcessTrackUuid()));
+ PERFETTO_TE(
+ category, PERFETTO_TE_SLICE_END(),
+ PERFETTO_TE_PROTO_TRACK(
+ PerfettoTeNamedTrackUuid(trackName, cookie,
+ PerfettoTeProcessTrackUuid()),
+ PERFETTO_TE_PROTO_FIELD_CSTR(
+ perfetto_protos_TrackDescriptor_atrace_name_field_number,
+ trackName),
+ PERFETTO_TE_PROTO_FIELD_VARINT(
+ perfetto_protos_TrackDescriptor_parent_uuid_field_number,
+ PerfettoTeProcessTrackUuid())));
}
void perfettoTraceAsyncBegin(const struct PerfettoTeCategory& category, const char* name,
@@ -281,14 +297,35 @@
void perfettoTraceInstantForTrack(const struct PerfettoTeCategory& category,
const char* trackName, const char* name) {
PERFETTO_TE(
- category, PERFETTO_TE_INSTANT(name),
- PERFETTO_TE_NAMED_TRACK(trackName, 1, PerfettoTeProcessTrackUuid()));
+ category, PERFETTO_TE_INSTANT(name),
+ PERFETTO_TE_PROTO_TRACK(
+ PerfettoTeNamedTrackUuid(trackName, 1,
+ PerfettoTeProcessTrackUuid()),
+ PERFETTO_TE_PROTO_FIELD_CSTR(
+ perfetto_protos_TrackDescriptor_atrace_name_field_number,
+ trackName),
+ PERFETTO_TE_PROTO_FIELD_VARINT(
+ perfetto_protos_TrackDescriptor_parent_uuid_field_number,
+ PerfettoTeProcessTrackUuid())));
}
void perfettoTraceCounter(const struct PerfettoTeCategory& category,
- [[maybe_unused]] const char* name, int64_t value) {
- PERFETTO_TE(category, PERFETTO_TE_COUNTER(),
- PERFETTO_TE_INT_COUNTER(value));
+ const char* name, int64_t value) {
+ PERFETTO_TE(
+ category, PERFETTO_TE_COUNTER(),
+ PERFETTO_TE_PROTO_TRACK(
+ PerfettoTeCounterTrackUuid(name,
+ PerfettoTeProcessTrackUuid()),
+ PERFETTO_TE_PROTO_FIELD_CSTR(
+ perfetto_protos_TrackDescriptor_atrace_name_field_number,
+ name),
+ PERFETTO_TE_PROTO_FIELD_VARINT(
+ perfetto_protos_TrackDescriptor_parent_uuid_field_number,
+ PerfettoTeProcessTrackUuid()),
+ PERFETTO_TE_PROTO_FIELD_BYTES(
+ perfetto_protos_TrackDescriptor_counter_field_number,
+ PERFETTO_NULL, 0)),
+ PERFETTO_TE_INT_COUNTER(value));
}
} // namespace internal
diff --git a/libs/ui/Android.bp b/libs/ui/Android.bp
index 12230f9..87e213e 100644
--- a/libs/ui/Android.bp
+++ b/libs/ui/Android.bp
@@ -136,6 +136,7 @@
"GraphicBuffer.cpp",
"GraphicBufferAllocator.cpp",
"GraphicBufferMapper.cpp",
+ "PictureProfileHandle.cpp",
"PixelFormat.cpp",
"PublicFormat.cpp",
"StaticAsserts.cpp",
diff --git a/libs/ui/DisplayIdentification.cpp b/libs/ui/DisplayIdentification.cpp
index 8b13d78..8d6f74b 100644
--- a/libs/ui/DisplayIdentification.cpp
+++ b/libs/ui/DisplayIdentification.cpp
@@ -19,9 +19,12 @@
#include <algorithm>
#include <cctype>
+#include <cstdint>
#include <numeric>
#include <optional>
#include <span>
+#include <string>
+#include <string_view>
#include <ftl/hash.h>
#include <log/log.h>
@@ -194,6 +197,21 @@
const uint16_t productId =
static_cast<uint16_t>(edid[kProductIdOffset] | (edid[kProductIdOffset + 1] << 8));
+ // Bytes 12-15: display serial number, in little-endian (LSB). This field is
+ // optional and its absence is marked by having all bytes set to 0x00.
+ // Values do not represent ASCII characters.
+ constexpr size_t kSerialNumberOffset = 12;
+ if (edid.size() < kSerialNumberOffset + sizeof(uint32_t)) {
+ ALOGE("Invalid EDID: block zero S/N is truncated.");
+ return {};
+ }
+ const uint32_t blockZeroSerialNumber = edid[kSerialNumberOffset] +
+ (edid[kSerialNumberOffset + 1] << 8) + (edid[kSerialNumberOffset + 2] << 16) +
+ (edid[kSerialNumberOffset + 3] << 24);
+ const auto hashedBlockZeroSNOpt = blockZeroSerialNumber == 0
+ ? std::nullopt
+ : ftl::stable_hash(std::string_view(std::to_string(blockZeroSerialNumber)));
+
constexpr size_t kManufactureWeekOffset = 16;
if (edid.size() < kManufactureWeekOffset + sizeof(uint8_t)) {
ALOGE("Invalid EDID: manufacture week is truncated.");
@@ -212,6 +230,15 @@
ALOGW_IF(manufactureOrModelYear <= 0xf,
"Invalid EDID: model year or manufacture year cannot be in the range [0x0, 0xf].");
+ constexpr size_t kMaxHorizontalPhysicalSizeOffset = 21;
+ constexpr size_t kMaxVerticalPhysicalSizeOffset = 22;
+ if (edid.size() < kMaxVerticalPhysicalSizeOffset + sizeof(uint8_t)) {
+ ALOGE("Invalid EDID: display's physical size is truncated.");
+ return {};
+ }
+ ui::Size maxPhysicalSizeInCm(edid[kMaxHorizontalPhysicalSizeOffset],
+ edid[kMaxVerticalPhysicalSizeOffset]);
+
constexpr size_t kDescriptorOffset = 54;
if (edid.size() < kDescriptorOffset) {
ALOGE("Invalid EDID: descriptors are missing.");
@@ -222,7 +249,8 @@
view = view.subspan(kDescriptorOffset);
std::string_view displayName;
- std::string_view serialNumber;
+ std::string_view descriptorBlockSerialNumber;
+ std::optional<uint64_t> hashedDescriptorBlockSNOpt = std::nullopt;
std::string_view asciiText;
ui::Size preferredDTDPixelSize;
ui::Size preferredDTDPhysicalSize;
@@ -247,7 +275,10 @@
asciiText = parseEdidText(descriptor);
break;
case 0xff:
- serialNumber = parseEdidText(descriptor);
+ descriptorBlockSerialNumber = parseEdidText(descriptor);
+ hashedDescriptorBlockSNOpt = descriptorBlockSerialNumber.empty()
+ ? std::nullopt
+ : ftl::stable_hash(descriptorBlockSerialNumber);
break;
}
} else if (isDetailedTimingDescriptor(view)) {
@@ -288,7 +319,7 @@
if (modelString.empty()) {
ALOGW("Invalid EDID: falling back to serial number due to missing display name.");
- modelString = serialNumber;
+ modelString = descriptorBlockSerialNumber;
}
if (modelString.empty()) {
ALOGW("Invalid EDID: falling back to ASCII text due to missing serial number.");
@@ -341,11 +372,14 @@
return Edid{
.manufacturerId = manufacturerId,
.productId = productId,
+ .hashedBlockZeroSerialNumberOpt = hashedBlockZeroSNOpt,
+ .hashedDescriptorBlockSerialNumberOpt = hashedDescriptorBlockSNOpt,
.pnpId = *pnpId,
.modelHash = modelHash,
.displayName = displayName,
.manufactureOrModelYear = manufactureOrModelYear,
.manufactureWeek = manufactureWeek,
+ .physicalSizeInCm = maxPhysicalSizeInCm,
.cea861Block = cea861Block,
.preferredDetailedTimingDescriptor = preferredDetailedTimingDescriptor,
};
diff --git a/libs/ui/Gralloc5.cpp b/libs/ui/Gralloc5.cpp
index c9ec036..2143f79 100644
--- a/libs/ui/Gralloc5.cpp
+++ b/libs/ui/Gralloc5.cpp
@@ -23,7 +23,6 @@
#include <aidlcommonsupport/NativeHandle.h>
#include <android/binder_manager.h>
#include <android/hardware/graphics/mapper/utils/IMapperMetadataTypes.h>
-#include <android/llndk-versioning.h>
#include <binder/IPCThreadState.h>
#include <dlfcn.h>
#include <ui/FatVector.h>
@@ -91,7 +90,7 @@
}
void* so = nullptr;
- if API_LEVEL_AT_LEAST (__ANDROID_API_V__, 202404) {
+ if (__builtin_available(android __ANDROID_API_V__, *)) {
so = AServiceManager_openDeclaredPassthroughHal("mapper", mapperSuffix.c_str(),
RTLD_LOCAL | RTLD_NOW);
} else {
diff --git a/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockPowerAdvisor.cpp b/libs/ui/PictureProfileHandle.cpp
similarity index 62%
copy from services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockPowerAdvisor.cpp
copy to libs/ui/PictureProfileHandle.cpp
index 1ba38a8..0701e90 100644
--- a/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockPowerAdvisor.cpp
+++ b/libs/ui/PictureProfileHandle.cpp
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2018 The Android Open Source Project
+ * Copyright (C) 2009 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.
@@ -14,12 +14,16 @@
* limitations under the License.
*/
-#include "MockPowerAdvisor.h"
+#include <ui/PictureProfileHandle.h>
-namespace android::Hwc2::mock {
+#include <format>
-// Explicit default instantiation is recommended.
-PowerAdvisor::PowerAdvisor() = default;
-PowerAdvisor::~PowerAdvisor() = default;
+namespace android {
-} // namespace android::Hwc2::mock
+const PictureProfileHandle PictureProfileHandle::NONE(0);
+
+::std::string toString(const PictureProfileHandle& handle) {
+ return std::format("{:#010x}", handle.getId());
+}
+
+} // namespace android
diff --git a/libs/ui/include/ui/DisplayIdentification.h b/libs/ui/include/ui/DisplayIdentification.h
index 648e024..cf67d7b 100644
--- a/libs/ui/include/ui/DisplayIdentification.h
+++ b/libs/ui/include/ui/DisplayIdentification.h
@@ -69,11 +69,14 @@
struct Edid {
uint16_t manufacturerId;
uint16_t productId;
+ std::optional<uint64_t> hashedBlockZeroSerialNumberOpt;
+ std::optional<uint64_t> hashedDescriptorBlockSerialNumberOpt;
PnpId pnpId;
uint32_t modelHash;
std::string_view displayName;
uint8_t manufactureOrModelYear;
uint8_t manufactureWeek;
+ ui::Size physicalSizeInCm;
std::optional<Cea861ExtensionBlock> cea861Block;
std::optional<DetailedTimingDescriptor> preferredDetailedTimingDescriptor;
};
diff --git a/libs/ui/include/ui/DynamicDisplayInfo.h b/libs/ui/include/ui/DynamicDisplayInfo.h
index 25a2b6e..9d97151 100644
--- a/libs/ui/include/ui/DynamicDisplayInfo.h
+++ b/libs/ui/include/ui/DynamicDisplayInfo.h
@@ -22,6 +22,7 @@
#include <optional>
#include <vector>
+#include <ui/FrameRateCategoryRate.h>
#include <ui/GraphicTypes.h>
#include <ui/HdrCapabilities.h>
@@ -55,6 +56,12 @@
std::optional<ui::DisplayMode> getActiveDisplayMode() const;
bool hasArrSupport;
+
+ // Represents frame rate for FrameRateCategory Normal and High.
+ ui::FrameRateCategoryRate frameRateCategoryRate;
+
+ // All the refresh rates supported for the default display mode.
+ std::vector<float> supportedRefreshRates;
};
} // namespace android::ui
diff --git a/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockPowerHintSessionWrapper.cpp b/libs/ui/include/ui/FrameRateCategoryRate.h
similarity index 60%
copy from services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockPowerHintSessionWrapper.cpp
copy to libs/ui/include/ui/FrameRateCategoryRate.h
index d383283..9c392d9 100644
--- a/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockPowerHintSessionWrapper.cpp
+++ b/libs/ui/include/ui/FrameRateCategoryRate.h
@@ -14,12 +14,22 @@
* limitations under the License.
*/
-#include "mock/DisplayHardware/MockPowerHintSessionWrapper.h"
+#pragma once
-namespace android::Hwc2::mock {
+namespace android::ui {
-// Explicit default instantiation is recommended.
-MockPowerHintSessionWrapper::MockPowerHintSessionWrapper()
- : power::PowerHintSessionWrapper(nullptr) {}
+// Represents frame rate for FrameRateCategory Normal and High.
+class FrameRateCategoryRate {
+public:
+ FrameRateCategoryRate(float normal = 0, float high = 0) : mNormal(normal), mHigh(high) {}
-} // namespace android::Hwc2::mock
+ float getNormal() const { return mNormal; }
+
+ float getHigh() const { return mHigh; }
+
+private:
+ float mNormal;
+ float mHigh;
+};
+
+} // namespace android::ui
\ No newline at end of file
diff --git a/libs/ui/include/ui/PictureProfileHandle.h b/libs/ui/include/ui/PictureProfileHandle.h
new file mode 100644
index 0000000..f840650
--- /dev/null
+++ b/libs/ui/include/ui/PictureProfileHandle.h
@@ -0,0 +1,60 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include <stdint.h>
+#include <array>
+#include <string>
+
+namespace android {
+
+/**
+ * An opaque value that uniquely identifies a picture profile, or a set of parameters, which
+ * describes the configuration of a picture processing pipeline that is applied to a graphic buffer
+ * to enhance its quality prior to rendering on the display.
+ */
+typedef int64_t PictureProfileId;
+
+/**
+ * A picture profile handle wraps the picture profile ID for type-safety, and represents an opaque
+ * handle that doesn't have the performance drawbacks of Binders.
+ */
+class PictureProfileHandle {
+public:
+ // A profile that represents no picture processing.
+ static const PictureProfileHandle NONE;
+
+ PictureProfileHandle() { *this = NONE; }
+ explicit PictureProfileHandle(PictureProfileId id) : mId(id) {}
+
+ PictureProfileId const& getId() const { return mId; }
+
+ inline bool operator==(const PictureProfileHandle& rhs) { return mId == rhs.mId; }
+ inline bool operator!=(const PictureProfileHandle& rhs) { return !(*this == rhs); }
+
+ // Is the picture profile effectively null, or not-specified?
+ inline bool operator!() const { return mId == NONE.mId; }
+
+ operator bool() const { return !!*this; }
+
+ friend ::std::string toString(const PictureProfileHandle& handle);
+
+private:
+ PictureProfileId mId;
+};
+
+} // namespace android
diff --git a/libs/ui/include_types/ui/HdrRenderTypeUtils.h b/libs/ui/include_types/ui/HdrRenderTypeUtils.h
index b0af878..70c50f0 100644
--- a/libs/ui/include_types/ui/HdrRenderTypeUtils.h
+++ b/libs/ui/include_types/ui/HdrRenderTypeUtils.h
@@ -61,4 +61,24 @@
return HdrRenderType::SDR;
}
-} // namespace android
\ No newline at end of file
+/**
+ * Returns the maximum headroom allowed for this content under "idealized"
+ * display conditions (low surround luminance, high-enough display brightness).
+ *
+ * TODO: take into account hdr metadata, but square it with the fact that some
+ * HLG content has CTA.861-3 metadata
+ */
+inline float getIdealizedMaxHeadroom(ui::Dataspace dataspace) {
+ const auto transfer = dataspace & HAL_DATASPACE_TRANSFER_MASK;
+
+ switch (transfer) {
+ case HAL_DATASPACE_TRANSFER_ST2084:
+ return 10000.0f / 203.0f;
+ case HAL_DATASPACE_TRANSFER_HLG:
+ return 1000.0f / 203.0f;
+ default:
+ return 1.0f;
+ }
+}
+
+} // namespace android
diff --git a/libs/ui/tests/DisplayIdentification_test.cpp b/libs/ui/tests/DisplayIdentification_test.cpp
index 76e3f66..d1699e7 100644
--- a/libs/ui/tests/DisplayIdentification_test.cpp
+++ b/libs/ui/tests/DisplayIdentification_test.cpp
@@ -33,7 +33,7 @@
namespace {
const unsigned char kInternalEdid[] =
- "\x00\xff\xff\xff\xff\xff\xff\x00\x4c\xa3\x42\x31\x00\x00\x00\x00"
+ "\x00\xff\xff\xff\xff\xff\xff\x00\x4c\xa3\x42\x31\x4e\x61\xbc\x00"
"\x00\x15\x01\x03\x80\x1a\x10\x78\x0a\xd3\xe5\x95\x5c\x60\x90\x27"
"\x19\x50\x54\x00\x00\x00\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01"
"\x01\x01\x01\x01\x01\x01\x9e\x1b\x00\xa0\x50\x20\x12\x30\x10\x30"
@@ -54,7 +54,7 @@
// Extended EDID with timing extension.
const unsigned char kExternalEedid[] =
- "\x00\xff\xff\xff\xff\xff\xff\x00\x4c\x2d\xfe\x08\x00\x00\x00\x00"
+ "\x00\xff\xff\xff\xff\xff\xff\x00\x4c\x2d\xfe\x08\xb1\x7f\x39\x05"
"\x29\x15\x01\x03\x80\x10\x09\x78\x0a\xee\x91\xa3\x54\x4c\x99\x26"
"\x0f\x50\x54\xbd\xef\x80\x71\x4f\x81\xc0\x81\x00\x81\x80\x95\x00"
"\xa9\xc0\xb3\x00\x01\x01\x02\x3a\x80\x18\x71\x38\x2d\x40\x58\x2c"
@@ -112,7 +112,7 @@
"\x07";
const unsigned char kCtlDisplayEdid[] =
- "\x00\xff\xff\xff\xff\xff\xff\x00\x0e\x8c\x9d\x24\x00\x00\x00\x00"
+ "\x00\xff\xff\xff\xff\xff\xff\x00\x0e\x8c\x9d\x24\x30\x41\xab\x00"
"\xff\x17\x01\x04\xa5\x34\x1d\x78\x3a\xa7\x25\xa4\x57\x51\xa0\x26"
"\x10\x50\x54\xbf\xef\x80\xb3\x00\xa9\x40\x95\x00\x81\x40\x81\x80"
"\x95\x0f\x71\x4f\x90\x40\x02\x3a\x80\x18\x71\x38\x2d\x40\x58\x2c"
@@ -191,8 +191,13 @@
EXPECT_EQ(hash("121AT11-801"), 626564263);
EXPECT_TRUE(edid->displayName.empty());
EXPECT_EQ(12610, edid->productId);
+ EXPECT_TRUE(edid->hashedBlockZeroSerialNumberOpt.has_value());
+ EXPECT_EQ(ftl::stable_hash("12345678"), edid->hashedBlockZeroSerialNumberOpt.value());
+ EXPECT_FALSE(edid->hashedDescriptorBlockSerialNumberOpt.has_value());
EXPECT_EQ(21, edid->manufactureOrModelYear);
EXPECT_EQ(0, edid->manufactureWeek);
+ EXPECT_EQ(26, edid->physicalSizeInCm.width);
+ EXPECT_EQ(16, edid->physicalSizeInCm.height);
EXPECT_FALSE(edid->cea861Block);
EXPECT_EQ(1280, edid->preferredDetailedTimingDescriptor->pixelSizeCount.width);
EXPECT_EQ(800, edid->preferredDetailedTimingDescriptor->pixelSizeCount.height);
@@ -207,8 +212,14 @@
EXPECT_EQ(hash("HP ZR30w"), 918492362);
EXPECT_EQ("HP ZR30w", edid->displayName);
EXPECT_EQ(10348, edid->productId);
+ EXPECT_TRUE(edid->hashedBlockZeroSerialNumberOpt.has_value());
+ EXPECT_EQ(ftl::stable_hash("16843009"), edid->hashedBlockZeroSerialNumberOpt.value());
+ EXPECT_TRUE(edid->hashedDescriptorBlockSerialNumberOpt.has_value());
+ EXPECT_EQ(ftl::stable_hash("CN4202137Q"), edid->hashedDescriptorBlockSerialNumberOpt.value());
EXPECT_EQ(22, edid->manufactureOrModelYear);
EXPECT_EQ(2, edid->manufactureWeek);
+ EXPECT_EQ(64, edid->physicalSizeInCm.width);
+ EXPECT_EQ(40, edid->physicalSizeInCm.height);
EXPECT_FALSE(edid->cea861Block);
EXPECT_EQ(1280, edid->preferredDetailedTimingDescriptor->pixelSizeCount.width);
EXPECT_EQ(800, edid->preferredDetailedTimingDescriptor->pixelSizeCount.height);
@@ -223,8 +234,13 @@
EXPECT_EQ(hash("SAMSUNG"), 1201368132);
EXPECT_EQ("SAMSUNG", edid->displayName);
EXPECT_EQ(2302, edid->productId);
+ EXPECT_TRUE(edid->hashedBlockZeroSerialNumberOpt.has_value());
+ EXPECT_EQ(ftl::stable_hash("87654321"), edid->hashedBlockZeroSerialNumberOpt.value());
+ EXPECT_FALSE(edid->hashedDescriptorBlockSerialNumberOpt.has_value());
EXPECT_EQ(21, edid->manufactureOrModelYear);
EXPECT_EQ(41, edid->manufactureWeek);
+ EXPECT_EQ(16, edid->physicalSizeInCm.width);
+ EXPECT_EQ(9, edid->physicalSizeInCm.height);
ASSERT_TRUE(edid->cea861Block);
ASSERT_TRUE(edid->cea861Block->hdmiVendorDataBlock);
auto physicalAddress = edid->cea861Block->hdmiVendorDataBlock->physicalAddress;
@@ -245,8 +261,13 @@
EXPECT_EQ(hash("Panasonic-TV"), 3876373262);
EXPECT_EQ("Panasonic-TV", edid->displayName);
EXPECT_EQ(41622, edid->productId);
+ EXPECT_TRUE(edid->hashedBlockZeroSerialNumberOpt.has_value());
+ EXPECT_EQ(ftl::stable_hash("16843009"), edid->hashedBlockZeroSerialNumberOpt.value());
+ EXPECT_FALSE(edid->hashedDescriptorBlockSerialNumberOpt.has_value());
EXPECT_EQ(29, edid->manufactureOrModelYear);
EXPECT_EQ(0, edid->manufactureWeek);
+ EXPECT_EQ(128, edid->physicalSizeInCm.width);
+ EXPECT_EQ(72, edid->physicalSizeInCm.height);
ASSERT_TRUE(edid->cea861Block);
ASSERT_TRUE(edid->cea861Block->hdmiVendorDataBlock);
physicalAddress = edid->cea861Block->hdmiVendorDataBlock->physicalAddress;
@@ -267,8 +288,12 @@
EXPECT_EQ(hash("Hisense"), 2859844809);
EXPECT_EQ("Hisense", edid->displayName);
EXPECT_EQ(0, edid->productId);
+ EXPECT_FALSE(edid->hashedBlockZeroSerialNumberOpt.has_value());
+ EXPECT_FALSE(edid->hashedDescriptorBlockSerialNumberOpt.has_value());
EXPECT_EQ(29, edid->manufactureOrModelYear);
EXPECT_EQ(18, edid->manufactureWeek);
+ EXPECT_EQ(0, edid->physicalSizeInCm.width);
+ EXPECT_EQ(0, edid->physicalSizeInCm.height);
ASSERT_TRUE(edid->cea861Block);
ASSERT_TRUE(edid->cea861Block->hdmiVendorDataBlock);
physicalAddress = edid->cea861Block->hdmiVendorDataBlock->physicalAddress;
@@ -289,8 +314,13 @@
EXPECT_EQ(hash("LP2361"), 1523181158);
EXPECT_EQ("LP2361", edid->displayName);
EXPECT_EQ(9373, edid->productId);
+ EXPECT_TRUE(edid->hashedBlockZeroSerialNumberOpt.has_value());
+ EXPECT_EQ(ftl::stable_hash("11223344"), edid->hashedBlockZeroSerialNumberOpt.value());
+ EXPECT_FALSE(edid->hashedDescriptorBlockSerialNumberOpt.has_value());
EXPECT_EQ(23, edid->manufactureOrModelYear);
EXPECT_EQ(0xff, edid->manufactureWeek);
+ EXPECT_EQ(52, edid->physicalSizeInCm.width);
+ EXPECT_EQ(29, edid->physicalSizeInCm.height);
ASSERT_TRUE(edid->cea861Block);
EXPECT_FALSE(edid->cea861Block->hdmiVendorDataBlock);
EXPECT_EQ(1360, edid->preferredDetailedTimingDescriptor->pixelSizeCount.width);
@@ -447,4 +477,4 @@
} // namespace android
// TODO(b/129481165): remove the #pragma below and fix conversion issues
-#pragma clang diagnostic pop // ignored "-Wextra"
\ No newline at end of file
+#pragma clang diagnostic pop // ignored "-Wextra"
diff --git a/opengl/tests/EGLTest/EGL_test.cpp b/opengl/tests/EGLTest/EGL_test.cpp
index 503d7df..839a5ca 100644
--- a/opengl/tests/EGLTest/EGL_test.cpp
+++ b/opengl/tests/EGLTest/EGL_test.cpp
@@ -765,6 +765,30 @@
}
}
+// Verify that eglCreateContext works when EGL_TELEMETRY_HINT_ANDROID is used with
+// NO_HINT = 0, SKIP_TELEMETRY = 1 and an invalid of value of 2
+TEST_F(EGLTest, EGLContextTelemetryHintExt) {
+ for (int i = 0; i < 3; i++) {
+ EGLConfig config;
+ get8BitConfig(config);
+ std::vector<EGLint> contextAttributes;
+ contextAttributes.reserve(4);
+ contextAttributes.push_back(EGL_TELEMETRY_HINT_ANDROID);
+ contextAttributes.push_back(i);
+ contextAttributes.push_back(EGL_NONE);
+ contextAttributes.push_back(EGL_NONE);
+
+ EGLContext eglContext = eglCreateContext(mEglDisplay, config, EGL_NO_CONTEXT,
+ contextAttributes.data());
+ EXPECT_NE(EGL_NO_CONTEXT, eglContext);
+ EXPECT_EQ(EGL_SUCCESS, eglGetError());
+
+ if (eglContext != EGL_NO_CONTEXT) {
+ eglDestroyContext(mEglDisplay, eglContext);
+ }
+ }
+}
+
// Emulate what a native application would do to create a
// 10:10:10:2 surface.
TEST_F(EGLTest, EGLConfig1010102) {
diff --git a/services/audiomanager/IAudioManager.cpp b/services/audiomanager/IAudioManager.cpp
index da1aae2..f8a38d1 100644
--- a/services/audiomanager/IAudioManager.cpp
+++ b/services/audiomanager/IAudioManager.cpp
@@ -87,12 +87,15 @@
}
virtual status_t playerEvent(audio_unique_id_t piid, player_state_t event,
- audio_port_handle_t eventId) {
+ const std::vector<audio_port_handle_t>& eventIds) {
Parcel data, reply;
data.writeInterfaceToken(IAudioManager::getInterfaceDescriptor());
data.writeInt32((int32_t) piid);
data.writeInt32((int32_t) event);
- data.writeInt32((int32_t) eventId);
+ data.writeInt32((int32_t) eventIds.size());
+ for (auto eventId: eventIds) {
+ data.writeInt32((int32_t) eventId);
+ }
return remote()->transact(PLAYER_EVENT, data, &reply, IBinder::FLAG_ONEWAY);
}
diff --git a/services/automotive/display/Android.bp b/services/automotive/display/Android.bp
index 72bd292..b63e919 100644
--- a/services/automotive/display/Android.bp
+++ b/services/automotive/display/Android.bp
@@ -23,6 +23,11 @@
default_applicable_licenses: ["frameworks_native_license"],
}
+vintf_fragment {
+ name: "manifest_android.frameworks.automotive.display@1.0.xml",
+ src: "manifest_android.frameworks.automotive.display@1.0.xml",
+}
+
cc_binary {
name: "android.frameworks.automotive.display@1.0-service",
defaults: ["hidl_defaults"],
@@ -50,7 +55,7 @@
"-DLOG_TAG=\"AutomotiveDisplayService\""
],
- vintf_fragments: [
+ vintf_fragment_modules: [
"manifest_android.frameworks.automotive.display@1.0.xml",
],
}
diff --git a/services/gpuservice/gpuwork/bpfprogs/gpuWork.c b/services/gpuservice/gpuwork/bpfprogs/gpuWork.c
index f470189..94abc69 100644
--- a/services/gpuservice/gpuwork/bpfprogs/gpuWork.c
+++ b/services/gpuservice/gpuwork/bpfprogs/gpuWork.c
@@ -20,11 +20,7 @@
#include <stddef.h>
#include <stdint.h>
-#ifdef MOCK_BPF
-#include <test/mock_bpf_helpers.h>
-#else
#include <bpf_helpers.h>
-#endif
#define S_IN_NS (1000000000)
#define SMALL_TIME_GAP_LIMIT_NS (S_IN_NS)
diff --git a/services/inputflinger/PointerChoreographer.cpp b/services/inputflinger/PointerChoreographer.cpp
index 006d507..38e5974 100644
--- a/services/inputflinger/PointerChoreographer.cpp
+++ b/services/inputflinger/PointerChoreographer.cpp
@@ -139,23 +139,24 @@
mShowTouchesEnabled(false),
mStylusPointerIconEnabled(false),
mCurrentFocusedDisplay(ui::LogicalDisplayId::DEFAULT),
+ mIsWindowInfoListenerRegistered(false),
+ mWindowInfoListener(sp<PointerChoreographerDisplayInfoListener>::make(this)),
mRegisterListener(registerListener),
mUnregisterListener(unregisterListener) {}
PointerChoreographer::~PointerChoreographer() {
- std::scoped_lock _l(mLock);
- if (mWindowInfoListener == nullptr) {
- return;
+ if (mIsWindowInfoListenerRegistered) {
+ mUnregisterListener(mWindowInfoListener);
+ mIsWindowInfoListenerRegistered = false;
}
mWindowInfoListener->onPointerChoreographerDestroyed();
- mUnregisterListener(mWindowInfoListener);
}
void PointerChoreographer::notifyInputDevicesChanged(const NotifyInputDevicesChangedArgs& args) {
PointerDisplayChange pointerDisplayChange;
{ // acquire lock
- std::scoped_lock _l(mLock);
+ std::scoped_lock _l(getLock());
mInputDeviceInfos = args.inputDeviceInfos;
pointerDisplayChange = updatePointerControllersLocked();
@@ -191,7 +192,7 @@
return;
}
- std::scoped_lock _l(mLock);
+ std::scoped_lock _l(getLock());
ui::LogicalDisplayId targetDisplay = args.displayId;
if (targetDisplay == ui::LogicalDisplayId::INVALID) {
targetDisplay = mCurrentFocusedDisplay;
@@ -204,7 +205,7 @@
}
NotifyMotionArgs PointerChoreographer::processMotion(const NotifyMotionArgs& args) {
- std::scoped_lock _l(mLock);
+ std::scoped_lock _l(getLock());
if (isFromMouse(args)) {
return processMouseEventLocked(args);
@@ -242,14 +243,7 @@
pc.setPosition(args.xCursorPosition, args.yCursorPosition);
} else {
// This is a relative mouse, so move the cursor by the specified amount.
- const float deltaX = args.pointerCoords[0].getAxisValue(AMOTION_EVENT_AXIS_RELATIVE_X);
- const float deltaY = args.pointerCoords[0].getAxisValue(AMOTION_EVENT_AXIS_RELATIVE_Y);
- pc.move(deltaX, deltaY);
- const auto [x, y] = pc.getPosition();
- newArgs.pointerCoords[0].setAxisValue(AMOTION_EVENT_AXIS_X, x);
- newArgs.pointerCoords[0].setAxisValue(AMOTION_EVENT_AXIS_Y, y);
- newArgs.xCursorPosition = x;
- newArgs.yCursorPosition = y;
+ processPointerDeviceMotionEventLocked(/*byref*/ newArgs, /*byref*/ pc);
}
if (canUnfadeOnDisplay(displayId)) {
pc.unfade(PointerControllerInterface::Transition::IMMEDIATE);
@@ -265,24 +259,9 @@
newArgs.displayId = displayId;
if (args.getPointerCount() == 1 && args.classification == MotionClassification::NONE) {
// This is a movement of the mouse pointer.
- const float deltaX = args.pointerCoords[0].getAxisValue(AMOTION_EVENT_AXIS_RELATIVE_X);
- const float deltaY = args.pointerCoords[0].getAxisValue(AMOTION_EVENT_AXIS_RELATIVE_Y);
- pc.move(deltaX, deltaY);
- if (canUnfadeOnDisplay(displayId)) {
- pc.unfade(PointerControllerInterface::Transition::IMMEDIATE);
- }
-
- const auto [x, y] = pc.getPosition();
- newArgs.pointerCoords[0].setAxisValue(AMOTION_EVENT_AXIS_X, x);
- newArgs.pointerCoords[0].setAxisValue(AMOTION_EVENT_AXIS_Y, y);
- newArgs.xCursorPosition = x;
- newArgs.yCursorPosition = y;
+ processPointerDeviceMotionEventLocked(/*byref*/ newArgs, /*byref*/ pc);
} else {
// This is a trackpad gesture with fake finger(s) that should not move the mouse pointer.
- if (canUnfadeOnDisplay(displayId)) {
- pc.unfade(PointerControllerInterface::Transition::IMMEDIATE);
- }
-
const auto [x, y] = pc.getPosition();
for (uint32_t i = 0; i < newArgs.getPointerCount(); i++) {
newArgs.pointerCoords[i].setAxisValue(AMOTION_EVENT_AXIS_X,
@@ -293,9 +272,25 @@
newArgs.xCursorPosition = x;
newArgs.yCursorPosition = y;
}
+ if (canUnfadeOnDisplay(displayId)) {
+ pc.unfade(PointerControllerInterface::Transition::IMMEDIATE);
+ }
return newArgs;
}
+void PointerChoreographer::processPointerDeviceMotionEventLocked(NotifyMotionArgs& newArgs,
+ PointerControllerInterface& pc) {
+ const float deltaX = newArgs.pointerCoords[0].getAxisValue(AMOTION_EVENT_AXIS_RELATIVE_X);
+ const float deltaY = newArgs.pointerCoords[0].getAxisValue(AMOTION_EVENT_AXIS_RELATIVE_Y);
+
+ pc.move(deltaX, deltaY);
+ const auto [x, y] = pc.getPosition();
+ newArgs.pointerCoords[0].setAxisValue(AMOTION_EVENT_AXIS_X, x);
+ newArgs.pointerCoords[0].setAxisValue(AMOTION_EVENT_AXIS_Y, y);
+ newArgs.xCursorPosition = x;
+ newArgs.yCursorPosition = y;
+}
+
void PointerChoreographer::processDrawingTabletEventLocked(const android::NotifyMotionArgs& args) {
if (args.displayId == ui::LogicalDisplayId::INVALID) {
return;
@@ -433,7 +428,7 @@
}
void PointerChoreographer::processDeviceReset(const NotifyDeviceResetArgs& args) {
- std::scoped_lock _l(mLock);
+ std::scoped_lock _l(getLock());
mTouchPointersByDevice.erase(args.deviceId);
mStylusPointersByDevice.erase(args.deviceId);
mDrawingTabletPointersByDevice.erase(args.deviceId);
@@ -447,17 +442,22 @@
bool requireListener = !mTouchPointersByDevice.empty() || !mMousePointersByDisplay.empty() ||
!mDrawingTabletPointersByDevice.empty() || !mStylusPointersByDevice.empty();
- if (requireListener && mWindowInfoListener == nullptr) {
- mWindowInfoListener = sp<PointerChoreographerDisplayInfoListener>::make(this);
- mWindowInfoListener->setInitialDisplayInfos(mRegisterListener(mWindowInfoListener));
- onPrivacySensitiveDisplaysChangedLocked(mWindowInfoListener->getPrivacySensitiveDisplays());
- } else if (!requireListener && mWindowInfoListener != nullptr) {
+ // PointerChoreographer uses Listener's lock which is already held by caller
+ base::ScopedLockAssertion assumeLocked(mWindowInfoListener->mLock);
+
+ if (requireListener && !mIsWindowInfoListenerRegistered) {
+ mIsWindowInfoListenerRegistered = true;
+ mWindowInfoListener->setInitialDisplayInfosLocked(mRegisterListener(mWindowInfoListener));
+ onPrivacySensitiveDisplaysChangedLocked(
+ mWindowInfoListener->getPrivacySensitiveDisplaysLocked());
+ } else if (!requireListener && mIsWindowInfoListenerRegistered) {
+ mIsWindowInfoListenerRegistered = false;
mUnregisterListener(mWindowInfoListener);
- mWindowInfoListener = nullptr;
- } else if (requireListener && mWindowInfoListener != nullptr) {
+ } else if (requireListener) {
// controller may have been added to an existing privacy sensitive display, we need to
// update all controllers again
- onPrivacySensitiveDisplaysChangedLocked(mWindowInfoListener->getPrivacySensitiveDisplays());
+ onPrivacySensitiveDisplaysChangedLocked(
+ mWindowInfoListener->getPrivacySensitiveDisplaysLocked());
}
}
@@ -494,7 +494,7 @@
void PointerChoreographer::notifyPointerCaptureChanged(
const NotifyPointerCaptureChangedArgs& args) {
if (args.request.isEnable()) {
- std::scoped_lock _l(mLock);
+ std::scoped_lock _l(getLock());
for (const auto& [_, mousePointerController] : mMousePointersByDisplay) {
mousePointerController->fade(PointerControllerInterface::Transition::IMMEDIATE);
}
@@ -502,14 +502,8 @@
mNextListener.notify(args);
}
-void PointerChoreographer::onPrivacySensitiveDisplaysChanged(
- const std::unordered_set<ui::LogicalDisplayId>& privacySensitiveDisplays) {
- std::scoped_lock _l(mLock);
- onPrivacySensitiveDisplaysChangedLocked(privacySensitiveDisplays);
-}
-
void PointerChoreographer::dump(std::string& dump) {
- std::scoped_lock _l(mLock);
+ std::scoped_lock _l(getLock());
dump += "PointerChoreographer:\n";
dump += StringPrintf(INDENT "Show Touches Enabled: %s\n",
@@ -579,6 +573,10 @@
return mDisplaysWithPointersHidden.find(displayId) == mDisplaysWithPointersHidden.end();
}
+std::mutex& PointerChoreographer::getLock() const {
+ return mWindowInfoListener->mLock;
+}
+
PointerChoreographer::PointerDisplayChange PointerChoreographer::updatePointerControllersLocked() {
std::set<ui::LogicalDisplayId /*displayId*/> mouseDisplaysToKeep;
std::set<DeviceId> touchDevicesToKeep;
@@ -641,7 +639,7 @@
std::erase_if(mDrawingTabletPointersByDevice, [&drawingTabletDevicesToKeep](const auto& pair) {
return drawingTabletDevicesToKeep.find(pair.first) == drawingTabletDevicesToKeep.end();
});
- std::erase_if(mMouseDevices, [&](DeviceId id) REQUIRES(mLock) {
+ std::erase_if(mMouseDevices, [&](DeviceId id) REQUIRES(getLock()) {
return std::find_if(mInputDeviceInfos.begin(), mInputDeviceInfos.end(),
[id](const auto& info) { return info.getId() == id; }) ==
mInputDeviceInfos.end();
@@ -677,7 +675,7 @@
PointerDisplayChange pointerDisplayChange;
{ // acquire lock
- std::scoped_lock _l(mLock);
+ std::scoped_lock _l(getLock());
mDefaultMouseDisplayId = displayId;
pointerDisplayChange = updatePointerControllersLocked();
@@ -690,7 +688,7 @@
PointerDisplayChange pointerDisplayChange;
{ // acquire lock
- std::scoped_lock _l(mLock);
+ std::scoped_lock _l(getLock());
for (const auto& viewport : viewports) {
const ui::LogicalDisplayId displayId = viewport.displayId;
if (const auto it = mMousePointersByDisplay.find(displayId);
@@ -719,7 +717,7 @@
std::optional<DisplayViewport> PointerChoreographer::getViewportForPointerDevice(
ui::LogicalDisplayId associatedDisplayId) {
- std::scoped_lock _l(mLock);
+ std::scoped_lock _l(getLock());
const ui::LogicalDisplayId resolvedDisplayId = getTargetMouseDisplayLocked(associatedDisplayId);
if (const auto viewport = findViewportByIdLocked(resolvedDisplayId); viewport) {
return *viewport;
@@ -728,7 +726,7 @@
}
FloatPoint PointerChoreographer::getMouseCursorPosition(ui::LogicalDisplayId displayId) {
- std::scoped_lock _l(mLock);
+ std::scoped_lock _l(getLock());
const ui::LogicalDisplayId resolvedDisplayId = getTargetMouseDisplayLocked(displayId);
if (auto it = mMousePointersByDisplay.find(resolvedDisplayId);
it != mMousePointersByDisplay.end()) {
@@ -741,7 +739,7 @@
PointerDisplayChange pointerDisplayChange;
{ // acquire lock
- std::scoped_lock _l(mLock);
+ std::scoped_lock _l(getLock());
if (mShowTouchesEnabled == enabled) {
return;
}
@@ -756,7 +754,7 @@
PointerDisplayChange pointerDisplayChange;
{ // acquire lock
- std::scoped_lock _l(mLock);
+ std::scoped_lock _l(getLock());
if (mStylusPointerIconEnabled == enabled) {
return;
}
@@ -770,7 +768,7 @@
bool PointerChoreographer::setPointerIcon(
std::variant<std::unique_ptr<SpriteIcon>, PointerIconStyle> icon,
ui::LogicalDisplayId displayId, DeviceId deviceId) {
- std::scoped_lock _l(mLock);
+ std::scoped_lock _l(getLock());
if (deviceId < 0) {
LOG(WARNING) << "Invalid device id " << deviceId << ". Cannot set pointer icon.";
return false;
@@ -821,7 +819,7 @@
}
void PointerChoreographer::setPointerIconVisibility(ui::LogicalDisplayId displayId, bool visible) {
- std::scoped_lock lock(mLock);
+ std::scoped_lock lock(getLock());
if (visible) {
mDisplaysWithPointersHidden.erase(displayId);
// We do not unfade the icons here, because we don't know when the last event happened.
@@ -843,14 +841,14 @@
}
void PointerChoreographer::setFocusedDisplay(ui::LogicalDisplayId displayId) {
- std::scoped_lock lock(mLock);
+ std::scoped_lock lock(getLock());
mCurrentFocusedDisplay = displayId;
}
PointerChoreographer::ControllerConstructor PointerChoreographer::getMouseControllerConstructor(
ui::LogicalDisplayId displayId) {
std::function<std::shared_ptr<PointerControllerInterface>()> ctor =
- [this, displayId]() REQUIRES(mLock) {
+ [this, displayId]() REQUIRES(getLock()) {
auto pc = mPolicy.createPointerController(
PointerControllerInterface::ControllerType::MOUSE);
if (const auto viewport = findViewportByIdLocked(displayId); viewport) {
@@ -864,7 +862,7 @@
PointerChoreographer::ControllerConstructor PointerChoreographer::getStylusControllerConstructor(
ui::LogicalDisplayId displayId) {
std::function<std::shared_ptr<PointerControllerInterface>()> ctor =
- [this, displayId]() REQUIRES(mLock) {
+ [this, displayId]() REQUIRES(getLock()) {
auto pc = mPolicy.createPointerController(
PointerControllerInterface::ControllerType::STYLUS);
if (const auto viewport = findViewportByIdLocked(displayId); viewport) {
@@ -875,9 +873,11 @@
return ConstructorDelegate(std::move(ctor));
}
+// --- PointerChoreographer::PointerChoreographerDisplayInfoListener ---
+
void PointerChoreographer::PointerChoreographerDisplayInfoListener::onWindowInfosChanged(
const gui::WindowInfosUpdate& windowInfosUpdate) {
- std::scoped_lock _l(mListenerLock);
+ std::scoped_lock _l(mLock);
if (mPointerChoreographer == nullptr) {
return;
}
@@ -885,25 +885,25 @@
getPrivacySensitiveDisplaysFromWindowInfos(windowInfosUpdate.windowInfos);
if (newPrivacySensitiveDisplays != mPrivacySensitiveDisplays) {
mPrivacySensitiveDisplays = std::move(newPrivacySensitiveDisplays);
- mPointerChoreographer->onPrivacySensitiveDisplaysChanged(mPrivacySensitiveDisplays);
+ // PointerChoreographer uses Listener's lock.
+ base::ScopedLockAssertion assumeLocked(mPointerChoreographer->getLock());
+ mPointerChoreographer->onPrivacySensitiveDisplaysChangedLocked(mPrivacySensitiveDisplays);
}
}
-void PointerChoreographer::PointerChoreographerDisplayInfoListener::setInitialDisplayInfos(
+void PointerChoreographer::PointerChoreographerDisplayInfoListener::setInitialDisplayInfosLocked(
const std::vector<gui::WindowInfo>& windowInfos) {
- std::scoped_lock _l(mListenerLock);
mPrivacySensitiveDisplays = getPrivacySensitiveDisplaysFromWindowInfos(windowInfos);
}
std::unordered_set<ui::LogicalDisplayId /*displayId*/>
-PointerChoreographer::PointerChoreographerDisplayInfoListener::getPrivacySensitiveDisplays() {
- std::scoped_lock _l(mListenerLock);
+PointerChoreographer::PointerChoreographerDisplayInfoListener::getPrivacySensitiveDisplaysLocked() {
return mPrivacySensitiveDisplays;
}
void PointerChoreographer::PointerChoreographerDisplayInfoListener::
onPointerChoreographerDestroyed() {
- std::scoped_lock _l(mListenerLock);
+ std::scoped_lock _l(mLock);
mPointerChoreographer = nullptr;
}
diff --git a/services/inputflinger/PointerChoreographer.h b/services/inputflinger/PointerChoreographer.h
index 635487b..fba1aef 100644
--- a/services/inputflinger/PointerChoreographer.h
+++ b/services/inputflinger/PointerChoreographer.h
@@ -118,31 +118,40 @@
private:
using PointerDisplayChange = std::optional<
std::tuple<ui::LogicalDisplayId /*displayId*/, FloatPoint /*cursorPosition*/>>;
- [[nodiscard]] PointerDisplayChange updatePointerControllersLocked() REQUIRES(mLock);
- [[nodiscard]] PointerDisplayChange calculatePointerDisplayChangeToNotify() REQUIRES(mLock);
+
+ // PointerChoreographer's DisplayInfoListener can outlive the PointerChoreographer because when
+ // the listener is registered and called from display thread, a strong pointer to the listener
+ // (which can extend its lifecycle) is given away.
+ // If we use two locks it can also cause deadlocks due to race in acquiring them between the
+ // display and reader thread.
+ // To avoid these problems we use DisplayInfoListener's lock in PointerChoreographer.
+ std::mutex& getLock() const;
+
+ [[nodiscard]] PointerDisplayChange updatePointerControllersLocked() REQUIRES(getLock());
+ [[nodiscard]] PointerDisplayChange calculatePointerDisplayChangeToNotify() REQUIRES(getLock());
const DisplayViewport* findViewportByIdLocked(ui::LogicalDisplayId displayId) const
- REQUIRES(mLock);
+ REQUIRES(getLock());
ui::LogicalDisplayId getTargetMouseDisplayLocked(ui::LogicalDisplayId associatedDisplayId) const
- REQUIRES(mLock);
+ REQUIRES(getLock());
std::pair<ui::LogicalDisplayId /*displayId*/, PointerControllerInterface&>
- ensureMouseControllerLocked(ui::LogicalDisplayId associatedDisplayId) REQUIRES(mLock);
- InputDeviceInfo* findInputDeviceLocked(DeviceId deviceId) REQUIRES(mLock);
- bool canUnfadeOnDisplay(ui::LogicalDisplayId displayId) REQUIRES(mLock);
+ ensureMouseControllerLocked(ui::LogicalDisplayId associatedDisplayId) REQUIRES(getLock());
+ InputDeviceInfo* findInputDeviceLocked(DeviceId deviceId) REQUIRES(getLock());
+ bool canUnfadeOnDisplay(ui::LogicalDisplayId displayId) REQUIRES(getLock());
void fadeMouseCursorOnKeyPress(const NotifyKeyArgs& args);
NotifyMotionArgs processMotion(const NotifyMotionArgs& args);
- NotifyMotionArgs processMouseEventLocked(const NotifyMotionArgs& args) REQUIRES(mLock);
- NotifyMotionArgs processTouchpadEventLocked(const NotifyMotionArgs& args) REQUIRES(mLock);
- void processDrawingTabletEventLocked(const NotifyMotionArgs& args) REQUIRES(mLock);
- void processTouchscreenAndStylusEventLocked(const NotifyMotionArgs& args) REQUIRES(mLock);
- void processStylusHoverEventLocked(const NotifyMotionArgs& args) REQUIRES(mLock);
+ NotifyMotionArgs processMouseEventLocked(const NotifyMotionArgs& args) REQUIRES(getLock());
+ NotifyMotionArgs processTouchpadEventLocked(const NotifyMotionArgs& args) REQUIRES(getLock());
+ void processDrawingTabletEventLocked(const NotifyMotionArgs& args) REQUIRES(getLock());
+ void processTouchscreenAndStylusEventLocked(const NotifyMotionArgs& args) REQUIRES(getLock());
+ void processStylusHoverEventLocked(const NotifyMotionArgs& args) REQUIRES(getLock());
+ void processPointerDeviceMotionEventLocked(NotifyMotionArgs& newArgs,
+ PointerControllerInterface& pc) REQUIRES(getLock());
void processDeviceReset(const NotifyDeviceResetArgs& args);
- void onControllerAddedOrRemovedLocked() REQUIRES(mLock);
+ void onControllerAddedOrRemovedLocked() REQUIRES(getLock());
void onPrivacySensitiveDisplaysChangedLocked(
const std::unordered_set<ui::LogicalDisplayId>& privacySensitiveDisplays)
- REQUIRES(mLock);
- void onPrivacySensitiveDisplaysChanged(
- const std::unordered_set<ui::LogicalDisplayId>& privacySensitiveDisplays);
+ REQUIRES(getLock());
/* This listener keeps tracks of visible privacy sensitive displays and updates the
* choreographer if there are any changes.
@@ -156,49 +165,50 @@
explicit PointerChoreographerDisplayInfoListener(PointerChoreographer* pc)
: mPointerChoreographer(pc){};
void onWindowInfosChanged(const gui::WindowInfosUpdate&) override;
- void setInitialDisplayInfos(const std::vector<gui::WindowInfo>& windowInfos);
- std::unordered_set<ui::LogicalDisplayId /*displayId*/> getPrivacySensitiveDisplays();
+ void setInitialDisplayInfosLocked(const std::vector<gui::WindowInfo>& windowInfos)
+ REQUIRES(mLock);
+ std::unordered_set<ui::LogicalDisplayId> getPrivacySensitiveDisplaysLocked()
+ REQUIRES(mLock);
void onPointerChoreographerDestroyed();
+ // This lock is also used by PointerChoreographer. See PointerChoreographer::getLock().
+ std::mutex mLock;
+
private:
- std::mutex mListenerLock;
- PointerChoreographer* mPointerChoreographer GUARDED_BY(mListenerLock);
+ PointerChoreographer* mPointerChoreographer GUARDED_BY(mLock);
std::unordered_set<ui::LogicalDisplayId /*displayId*/> mPrivacySensitiveDisplays
- GUARDED_BY(mListenerLock);
+ GUARDED_BY(mLock);
};
- sp<PointerChoreographerDisplayInfoListener> mWindowInfoListener GUARDED_BY(mLock);
using ControllerConstructor =
ConstructorDelegate<std::function<std::shared_ptr<PointerControllerInterface>()>>;
- ControllerConstructor mTouchControllerConstructor GUARDED_BY(mLock);
+ ControllerConstructor mTouchControllerConstructor GUARDED_BY(getLock());
ControllerConstructor getMouseControllerConstructor(ui::LogicalDisplayId displayId)
- REQUIRES(mLock);
+ REQUIRES(getLock());
ControllerConstructor getStylusControllerConstructor(ui::LogicalDisplayId displayId)
- REQUIRES(mLock);
-
- std::mutex mLock;
+ REQUIRES(getLock());
InputListenerInterface& mNextListener;
PointerChoreographerPolicyInterface& mPolicy;
std::map<ui::LogicalDisplayId, std::shared_ptr<PointerControllerInterface>>
- mMousePointersByDisplay GUARDED_BY(mLock);
+ mMousePointersByDisplay GUARDED_BY(getLock());
std::map<DeviceId, std::shared_ptr<PointerControllerInterface>> mTouchPointersByDevice
- GUARDED_BY(mLock);
+ GUARDED_BY(getLock());
std::map<DeviceId, std::shared_ptr<PointerControllerInterface>> mStylusPointersByDevice
- GUARDED_BY(mLock);
+ GUARDED_BY(getLock());
std::map<DeviceId, std::shared_ptr<PointerControllerInterface>> mDrawingTabletPointersByDevice
- GUARDED_BY(mLock);
+ GUARDED_BY(getLock());
- ui::LogicalDisplayId mDefaultMouseDisplayId GUARDED_BY(mLock);
- ui::LogicalDisplayId mNotifiedPointerDisplayId GUARDED_BY(mLock);
- std::vector<InputDeviceInfo> mInputDeviceInfos GUARDED_BY(mLock);
- std::set<DeviceId> mMouseDevices GUARDED_BY(mLock);
- std::vector<DisplayViewport> mViewports GUARDED_BY(mLock);
- bool mShowTouchesEnabled GUARDED_BY(mLock);
- bool mStylusPointerIconEnabled GUARDED_BY(mLock);
+ ui::LogicalDisplayId mDefaultMouseDisplayId GUARDED_BY(getLock());
+ ui::LogicalDisplayId mNotifiedPointerDisplayId GUARDED_BY(getLock());
+ std::vector<InputDeviceInfo> mInputDeviceInfos GUARDED_BY(getLock());
+ std::set<DeviceId> mMouseDevices GUARDED_BY(getLock());
+ std::vector<DisplayViewport> mViewports GUARDED_BY(getLock());
+ bool mShowTouchesEnabled GUARDED_BY(getLock());
+ bool mStylusPointerIconEnabled GUARDED_BY(getLock());
std::set<ui::LogicalDisplayId /*displayId*/> mDisplaysWithPointersHidden;
- ui::LogicalDisplayId mCurrentFocusedDisplay GUARDED_BY(mLock);
+ ui::LogicalDisplayId mCurrentFocusedDisplay GUARDED_BY(getLock());
protected:
using WindowListenerRegisterConsumer = std::function<std::vector<gui::WindowInfo>(
@@ -211,6 +221,10 @@
const WindowListenerUnregisterConsumer& unregisterListener);
private:
+ // WindowInfoListener object should always exist while PointerChoreographer exists, because we
+ // need to use the lock from it. But we don't always need to register the listener.
+ bool mIsWindowInfoListenerRegistered GUARDED_BY(getLock());
+ const sp<PointerChoreographerDisplayInfoListener> mWindowInfoListener;
const WindowListenerRegisterConsumer mRegisterListener;
const WindowListenerUnregisterConsumer mUnregisterListener;
};
diff --git a/services/inputflinger/TEST_MAPPING b/services/inputflinger/TEST_MAPPING
index 10fec74..43eaf67 100644
--- a/services/inputflinger/TEST_MAPPING
+++ b/services/inputflinger/TEST_MAPPING
@@ -35,109 +35,34 @@
]
},
{
- "name": "CtsHardwareTestCases",
- "options": [
- {
- "include-filter": "android.hardware.input.cts.tests"
- },
- {
- "exclude-annotation": "androidx.test.filters.FlakyTest"
- }
- ]
+ "name": "CtsHardwareTestCases_cts_tests"
},
{
"name": "CtsInputTestCases"
},
{
- "name": "CtsViewTestCases",
- "options": [
- {
- "include-filter": "android.view.cts.input"
- }
- ]
+ "name": "CtsViewTestCases_cts_input"
},
{
- "name": "CtsViewTestCases",
- "options": [
- {
- "include-filter": "android.view.cts.HoverTest"
- },
- {
- "include-filter": "android.view.cts.MotionEventTest"
- },
- {
- "include-filter": "android.view.cts.PointerCaptureTest"
- },
- {
- "include-filter": "android.view.cts.TooltipTest"
- },
- {
- "include-filter": "android.view.cts.TouchDelegateTest"
- },
- {
- "include-filter": "android.view.cts.VerifyInputEventTest"
- },
- {
- "include-filter": "android.view.cts.ViewTest"
- },
- {
- "include-filter": "android.view.cts.ViewUnbufferedTest"
- }
- ]
+ "name": "CtsViewTestCases_input_related"
},
{
- "name": "CtsWidgetTestCases",
- "options": [
- {
- "include-filter": "android.widget.cts.NumberPickerTest"
- },
- {
- "include-filter": "android.widget.cts.SeekBarTest"
- }
- ]
+ "name": "CtsWidgetTestCases_seekbar_and_numberpicker"
},
{
- "name": "FrameworksCoreTests",
- "options": [
- {
- "include-filter": "android.hardware.input"
- }
- ]
+ "name": "FrameworksCoreTests_hardware_input"
},
{
- "name": "FrameworksCoreTests",
- "options": [
- {
- "include-filter": "android.view.VerifiedKeyEventTest"
- },
- {
- "include-filter": "android.view.VerifiedMotionEventTest"
- }
- ]
+ "name": "FrameworksCoreTests_view_verified"
},
{
- "name": "CtsAppTestCases",
- "options": [
- {
- "include-filter": "android.app.cts.ToolbarActionBarTest"
- }
- ]
+ "name": "CtsAppTestCases_cts_toolbaractionbartest"
},
{
- "name": "FrameworksServicesTests",
- "options": [
- {
- "include-filter": "com.android.server.input"
- }
- ]
+ "name": "FrameworksServicesTests_android_server_input"
},
{
- "name": "CtsSecurityTestCases",
- "options": [
- {
- "include-filter": "android.security.cts.MotionEventTest"
- }
- ]
+ "name": "CtsSecurityTestCases_cts_motioneventtest"
},
{
"name": "CtsSecurityBulletinHostTestCases",
@@ -156,12 +81,7 @@
],
"postsubmit": [
{
- "name": "CtsWindowManagerDeviceWindow",
- "options": [
- {
- "include-filter": "android.server.wm.window.WindowInputTests"
- }
- ]
+ "name": "CtsWindowManagerDeviceWindow_window_windowinputtests"
},
{
"name": "libinput_tests"
@@ -187,98 +107,31 @@
]
},
{
- "name": "CtsHardwareTestCases",
- "options": [
- {
- "include-filter": "android.hardware.input.cts.tests"
- }
- ]
+ "name": "CtsHardwareTestCases_cts_tests"
},
{
"name": "CtsInputTestCases"
},
{
- "name": "CtsViewTestCases",
- "options": [
- {
- "include-filter": "android.view.cts.input"
- }
- ]
+ "name": "CtsViewTestCases_cts_input"
},
{
- "name": "CtsViewTestCases",
- "options": [
- {
- "include-filter": "android.view.cts.HoverTest"
- },
- {
- "include-filter": "android.view.cts.MotionEventTest"
- },
- {
- "include-filter": "android.view.cts.PointerCaptureTest"
- },
- {
- "include-filter": "android.view.cts.TooltipTest"
- },
- {
- "include-filter": "android.view.cts.TouchDelegateTest"
- },
- {
- "include-filter": "android.view.cts.VerifyInputEventTest"
- },
- {
- "include-filter": "android.view.cts.ViewTest"
- },
- {
- "include-filter": "android.view.cts.ViewUnbufferedTest"
- }
- ]
+ "name": "CtsViewTestCases_input_related"
},
{
- "name": "CtsWidgetTestCases",
- "options": [
- {
- "include-filter": "android.widget.cts.NumberPickerTest"
- },
- {
- "include-filter": "android.widget.cts.SeekBarTest"
- }
- ]
+ "name": "CtsWidgetTestCases_seekbar_and_numberpicker"
},
{
- "name": "FrameworksCoreTests",
- "options": [
- {
- "include-filter": "android.view.VerifiedKeyEventTest"
- },
- {
- "include-filter": "android.view.VerifiedMotionEventTest"
- }
- ]
+ "name": "FrameworksCoreTests_view_verified"
},
{
- "name": "CtsAppTestCases",
- "options": [
- {
- "include-filter": "android.app.cts.ToolbarActionBarTest"
- }
- ]
+ "name": "CtsAppTestCases_cts_toolbaractionbartest"
},
{
- "name": "FrameworksServicesTests",
- "options": [
- {
- "include-filter": "com.android.server.input"
- }
- ]
+ "name": "FrameworksServicesTests_server_input"
},
{
- "name": "CtsSecurityTestCases",
- "options": [
- {
- "include-filter": "android.security.cts.MotionEventTest"
- }
- ]
+ "name": "CtsSecurityTestCases_cts_motioneventtest"
},
{
"name": "CtsSecurityBulletinHostTestCases",
diff --git a/services/inputflinger/dispatcher/EventLogTags.logtags b/services/inputflinger/dispatcher/EventLogTags.logtags
index 2c5fe21..4dd6073 100644
--- a/services/inputflinger/dispatcher/EventLogTags.logtags
+++ b/services/inputflinger/dispatcher/EventLogTags.logtags
@@ -31,7 +31,7 @@
# 6: Percent
# Default value for data of type int/long is 2 (bytes).
#
-# See system/core/logcat/event.logtags for the master copy of the tags.
+# See system/logging/logcat/event.logtags for the master copy of the tags.
# 62000 - 62199 reserved for inputflinger
diff --git a/services/inputflinger/dispatcher/InputDispatcher.cpp b/services/inputflinger/dispatcher/InputDispatcher.cpp
index 4f9d9e4..cd4ed5c 100644
--- a/services/inputflinger/dispatcher/InputDispatcher.cpp
+++ b/services/inputflinger/dispatcher/InputDispatcher.cpp
@@ -836,13 +836,9 @@
}
Result<void> validateWindowInfosUpdate(const gui::WindowInfosUpdate& update) {
- struct HashFunction {
- size_t operator()(const WindowInfo& info) const { return info.id; }
- };
-
- std::unordered_set<WindowInfo, HashFunction> windowSet;
+ std::unordered_set<int32_t> windowIds;
for (const WindowInfo& info : update.windowInfos) {
- const auto [_, inserted] = windowSet.insert(info);
+ const auto [_, inserted] = windowIds.insert(info.id);
if (!inserted) {
return Error() << "Duplicate entry for " << info;
}
@@ -6809,14 +6805,15 @@
// The fallback keycode cannot change at any other point in the lifecycle.
if (initialDown) {
if (fallback) {
- *fallbackKeyCode = event.getKeyCode();
+ fallbackKeyCode = event.getKeyCode();
} else {
- *fallbackKeyCode = AKEYCODE_UNKNOWN;
+ fallbackKeyCode = AKEYCODE_UNKNOWN;
}
connection->inputState.setFallbackKey(originalKeyCode, *fallbackKeyCode);
}
- ALOG_ASSERT(fallbackKeyCode);
+ LOG_IF(FATAL, !fallbackKeyCode)
+ << "fallbackKeyCode is not initialized. initialDown = " << initialDown;
// Cancel the fallback key if the policy decides not to send it anymore.
// We will continue to dispatch the key to the policy but we will no
diff --git a/services/inputflinger/include/InputReaderBase.h b/services/inputflinger/include/InputReaderBase.h
index 6093c91..4d6b6c7 100644
--- a/services/inputflinger/include/InputReaderBase.h
+++ b/services/inputflinger/include/InputReaderBase.h
@@ -137,19 +137,18 @@
ui::LogicalDisplayId defaultPointerDisplayId;
// The mouse pointer speed, as a number from -7 (slowest) to 7 (fastest).
- //
- // Currently only used when the enable_new_mouse_pointer_ballistics flag is enabled.
int32_t mousePointerSpeed;
// Displays on which an acceleration curve shouldn't be applied for pointer movements from mice.
- //
- // Currently only used when the enable_new_mouse_pointer_ballistics flag is enabled.
std::set<ui::LogicalDisplayId> displaysWithMousePointerAccelerationDisabled;
- // Velocity control parameters for mouse pointer movements.
+ // Velocity control parameters for touchpad pointer movements on the old touchpad stack (based
+ // on TouchInputMapper).
//
- // If the enable_new_mouse_pointer_ballistics flag is enabled, these are ignored and the values
- // of mousePointerSpeed and mousePointerAccelerationEnabled used instead.
+ // For mice, these are ignored and the values of mousePointerSpeed and
+ // mousePointerAccelerationEnabled used instead.
+ //
+ // TODO(b/281840344): remove this.
VelocityControlParameters pointerVelocityControlParameters;
// Velocity control parameters for mouse wheel movements.
@@ -243,6 +242,12 @@
// context (a.k.a. "right") clicks.
bool touchpadRightClickZoneEnabled;
+ // True to use three-finger tap as a customizable shortcut; false to use it as a middle-click.
+ bool touchpadThreeFingerTapShortcutEnabled;
+
+ // True to enable system gestures (three- and four-finger swipes) on touchpads.
+ bool touchpadSystemGesturesEnabled;
+
// The set of currently disabled input devices.
std::set<int32_t> disabledDevices;
@@ -294,6 +299,8 @@
touchpadTapDraggingEnabled(false),
shouldNotifyTouchpadHardwareState(false),
touchpadRightClickZoneEnabled(false),
+ touchpadThreeFingerTapShortcutEnabled(false),
+ touchpadSystemGesturesEnabled(true),
stylusButtonMotionEventsEnabled(true),
stylusPointerIconEnabled(false),
mouseReverseVerticalScrollingEnabled(false),
@@ -359,6 +366,9 @@
/* Toggle Caps Lock */
virtual void toggleCapsLockState(int32_t deviceId) = 0;
+ /* Resets locked modifier state */
+ virtual void resetLockedModifierState() = 0;
+
/* Determine whether physical keys exist for the given framework-domain key codes. */
virtual bool hasKeys(int32_t deviceId, uint32_t sourceMask,
const std::vector<int32_t>& keyCodes, uint8_t* outFlags) = 0;
@@ -497,6 +507,9 @@
/* Sends the Info of gestures that happen on the touchpad. */
virtual void notifyTouchpadGestureInfo(GestureType type, int32_t deviceId) = 0;
+ /* Notifies the policy that the user has performed a three-finger touchpad tap. */
+ virtual void notifyTouchpadThreeFingerTap() = 0;
+
/* Gets the keyboard layout for a particular input device. */
virtual std::shared_ptr<KeyCharacterMap> getKeyboardLayoutOverlay(
const InputDeviceIdentifier& identifier,
diff --git a/services/inputflinger/reader/EventHub.cpp b/services/inputflinger/reader/EventHub.cpp
index 35ba48f..013ef86 100644
--- a/services/inputflinger/reader/EventHub.cpp
+++ b/services/inputflinger/reader/EventHub.cpp
@@ -129,7 +129,8 @@
{"multi_intensity", InputLightClass::MULTI_INTENSITY},
{"max_brightness", InputLightClass::MAX_BRIGHTNESS},
{"kbd_backlight", InputLightClass::KEYBOARD_BACKLIGHT},
- {"mic_mute", InputLightClass::KEYBOARD_MIC_MUTE}};
+ {"mic_mute", InputLightClass::KEYBOARD_MIC_MUTE},
+ {"mute", InputLightClass::KEYBOARD_VOLUME_MUTE}};
// Mapping for input multicolor led class node names.
// https://www.kernel.org/doc/html/latest/leds/leds-class-multicolor.html
diff --git a/services/inputflinger/reader/InputReader.cpp b/services/inputflinger/reader/InputReader.cpp
index a2b7e82..24919b6 100644
--- a/services/inputflinger/reader/InputReader.cpp
+++ b/services/inputflinger/reader/InputReader.cpp
@@ -19,6 +19,7 @@
#include "InputReader.h"
#include <android-base/stringprintf.h>
+#include <com_android_input_flags.h>
#include <errno.h>
#include <input/Keyboard.h>
#include <input/VirtualKeyMap.h>
@@ -589,6 +590,11 @@
}
}
+void InputReader::resetLockedModifierState() {
+ std::scoped_lock _l(mLock);
+ updateLedMetaStateLocked(0);
+}
+
bool InputReader::hasKeys(int32_t deviceId, uint32_t sourceMask,
const std::vector<int32_t>& keyCodes, uint8_t* outFlags) {
std::scoped_lock _l(mLock);
@@ -903,7 +909,9 @@
bool InputReader::setKernelWakeEnabled(int32_t deviceId, bool enabled) {
std::scoped_lock _l(mLock);
-
+ if (!com::android::input::flags::set_input_device_kernel_wake()){
+ return false;
+ }
InputDevice* device = findInputDeviceLocked(deviceId);
if (device) {
return device->setKernelWakeEnabled(enabled);
diff --git a/services/inputflinger/reader/controller/PeripheralController.cpp b/services/inputflinger/reader/controller/PeripheralController.cpp
index 49ad8b5..9eeb2b2 100644
--- a/services/inputflinger/reader/controller/PeripheralController.cpp
+++ b/services/inputflinger/reader/controller/PeripheralController.cpp
@@ -514,6 +514,8 @@
type = InputDeviceLightType::KEYBOARD_BACKLIGHT;
} else if (rawInfo.flags.test(InputLightClass::KEYBOARD_MIC_MUTE)) {
type = InputDeviceLightType::KEYBOARD_MIC_MUTE;
+ } else if (rawInfo.flags.test(InputLightClass::KEYBOARD_VOLUME_MUTE)) {
+ type = InputDeviceLightType::KEYBOARD_VOLUME_MUTE;
} else {
type = InputDeviceLightType::INPUT;
}
diff --git a/services/inputflinger/reader/include/EventHub.h b/services/inputflinger/reader/include/EventHub.h
index 4336945..5839b4c 100644
--- a/services/inputflinger/reader/include/EventHub.h
+++ b/services/inputflinger/reader/include/EventHub.h
@@ -179,6 +179,8 @@
KEYBOARD_BACKLIGHT = 0x00000100,
/* The input light has mic_mute name */
KEYBOARD_MIC_MUTE = 0x00000200,
+ /* The input light has mute name */
+ KEYBOARD_VOLUME_MUTE = 0x00000400,
};
enum class InputBatteryClass : uint32_t {
diff --git a/services/inputflinger/reader/include/InputDevice.h b/services/inputflinger/reader/include/InputDevice.h
index abe7a5f..4744dd0 100644
--- a/services/inputflinger/reader/include/InputDevice.h
+++ b/services/inputflinger/reader/include/InputDevice.h
@@ -48,7 +48,7 @@
inline InputReaderContext* getContext() { return mContext; }
inline int32_t getId() const { return mId; }
inline int32_t getControllerNumber() const { return mControllerNumber; }
- inline int32_t getGeneration() const { return mGeneration; }
+ inline virtual int32_t getGeneration() const { return mGeneration; }
inline const std::string getName() const { return mIdentifier.name; }
inline const std::string getDescriptor() { return mIdentifier.descriptor; }
inline std::optional<std::string> getBluetoothAddress() const {
@@ -59,7 +59,7 @@
inline virtual uint32_t getSources() const { return mSources; }
inline bool hasEventHubDevices() const { return !mDevices.empty(); }
- inline bool isExternal() { return mIsExternal; }
+ inline virtual bool isExternal() { return mIsExternal; }
inline std::optional<uint8_t> getAssociatedDisplayPort() const {
return mAssociatedDisplayPort;
}
@@ -79,7 +79,7 @@
inline bool isIgnored() { return !getMapperCount() && !mController; }
- inline KeyboardType getKeyboardType() const { return mKeyboardType; }
+ inline virtual KeyboardType getKeyboardType() const { return mKeyboardType; }
bool isEnabled();
@@ -124,7 +124,7 @@
int32_t getMetaState();
void setKeyboardType(KeyboardType keyboardType);
- void bumpGeneration();
+ virtual void bumpGeneration();
[[nodiscard]] NotifyDeviceResetArgs notifyReset(nsecs_t when);
diff --git a/services/inputflinger/reader/include/InputReader.h b/services/inputflinger/reader/include/InputReader.h
index 7614a05..1403ca2 100644
--- a/services/inputflinger/reader/include/InputReader.h
+++ b/services/inputflinger/reader/include/InputReader.h
@@ -69,6 +69,8 @@
void toggleCapsLockState(int32_t deviceId) override;
+ void resetLockedModifierState() override;
+
bool hasKeys(int32_t deviceId, uint32_t sourceMask, const std::vector<int32_t>& keyCodes,
uint8_t* outFlags) override;
diff --git a/services/inputflinger/reader/mapper/CursorInputMapper.cpp b/services/inputflinger/reader/mapper/CursorInputMapper.cpp
index 630bd9b..b33659c 100644
--- a/services/inputflinger/reader/mapper/CursorInputMapper.cpp
+++ b/services/inputflinger/reader/mapper/CursorInputMapper.cpp
@@ -33,8 +33,6 @@
#include "input/PrintTools.h"
-namespace input_flags = com::android::input::flags;
-
namespace android {
// The default velocity control parameters that has no effect.
@@ -77,8 +75,7 @@
CursorInputMapper::CursorInputMapper(InputDeviceContext& deviceContext,
const InputReaderConfiguration& readerConfig)
: InputMapper(deviceContext, readerConfig),
- mLastEventTime(std::numeric_limits<nsecs_t>::min()),
- mEnableNewMousePointerBallistics(input_flags::enable_new_mouse_pointer_ballistics()) {}
+ mLastEventTime(std::numeric_limits<nsecs_t>::min()) {}
uint32_t CursorInputMapper::getSources() const {
return mSource;
@@ -207,8 +204,7 @@
mDownTime = 0;
mLastEventTime = std::numeric_limits<nsecs_t>::min();
- mOldPointerVelocityControl.reset();
- mNewPointerVelocityControl.reset();
+ mPointerVelocityControl.reset();
mWheelXVelocityControl.reset();
mWheelYVelocityControl.reset();
@@ -291,11 +287,7 @@
mWheelYVelocityControl.move(when, nullptr, &vscroll);
mWheelXVelocityControl.move(when, &hscroll, nullptr);
- if (mEnableNewMousePointerBallistics) {
- mNewPointerVelocityControl.move(when, &deltaX, &deltaY);
- } else {
- mOldPointerVelocityControl.move(when, &deltaX, &deltaY);
- }
+ mPointerVelocityControl.move(when, &deltaX, &deltaY);
float xCursorPosition = AMOTION_EVENT_INVALID_CURSOR_POSITION;
float yCursorPosition = AMOTION_EVENT_INVALID_CURSOR_POSITION;
@@ -486,27 +478,15 @@
void CursorInputMapper::configureOnChangePointerSpeed(const InputReaderConfiguration& config) {
if (mParameters.mode == Parameters::Mode::POINTER_RELATIVE) {
// Disable any acceleration or scaling for the pointer when Pointer Capture is enabled.
- if (mEnableNewMousePointerBallistics) {
- mNewPointerVelocityControl.setAccelerationEnabled(false);
- } else {
- mOldPointerVelocityControl.setParameters(FLAT_VELOCITY_CONTROL_PARAMS);
- }
+ mPointerVelocityControl.setAccelerationEnabled(false);
mWheelXVelocityControl.setParameters(FLAT_VELOCITY_CONTROL_PARAMS);
mWheelYVelocityControl.setParameters(FLAT_VELOCITY_CONTROL_PARAMS);
} else {
- if (mEnableNewMousePointerBallistics) {
- mNewPointerVelocityControl.setAccelerationEnabled(
- config.displaysWithMousePointerAccelerationDisabled.count(
- mDisplayId.value_or(ui::LogicalDisplayId::INVALID)) == 0);
- mNewPointerVelocityControl.setCurve(
- createAccelerationCurveForPointerSensitivity(config.mousePointerSpeed));
- } else {
- mOldPointerVelocityControl.setParameters(
- (config.displaysWithMousePointerAccelerationDisabled.count(
- mDisplayId.value_or(ui::LogicalDisplayId::INVALID)) == 0)
- ? config.pointerVelocityControlParameters
- : FLAT_VELOCITY_CONTROL_PARAMS);
- }
+ mPointerVelocityControl.setAccelerationEnabled(
+ config.displaysWithMousePointerAccelerationDisabled.count(
+ mDisplayId.value_or(ui::LogicalDisplayId::INVALID)) == 0);
+ mPointerVelocityControl.setCurve(
+ createAccelerationCurveForPointerSensitivity(config.mousePointerSpeed));
mWheelXVelocityControl.setParameters(config.wheelVelocityControlParameters);
mWheelYVelocityControl.setParameters(config.wheelVelocityControlParameters);
}
diff --git a/services/inputflinger/reader/mapper/CursorInputMapper.h b/services/inputflinger/reader/mapper/CursorInputMapper.h
index 403e96d..8319922 100644
--- a/services/inputflinger/reader/mapper/CursorInputMapper.h
+++ b/services/inputflinger/reader/mapper/CursorInputMapper.h
@@ -104,8 +104,7 @@
// Velocity controls for mouse pointer and wheel movements.
// The controls for X and Y wheel movements are separate to keep them decoupled.
- SimpleVelocityControl mOldPointerVelocityControl;
- CurvedVelocityControl mNewPointerVelocityControl;
+ CurvedVelocityControl mPointerVelocityControl;
SimpleVelocityControl mWheelXVelocityControl;
SimpleVelocityControl mWheelYVelocityControl;
@@ -120,7 +119,6 @@
nsecs_t mDownTime;
nsecs_t mLastEventTime;
- const bool mEnableNewMousePointerBallistics;
bool mMouseReverseVerticalScrolling = false;
explicit CursorInputMapper(InputDeviceContext& deviceContext,
diff --git a/services/inputflinger/reader/mapper/KeyboardInputMapper.cpp b/services/inputflinger/reader/mapper/KeyboardInputMapper.cpp
index a433a72..fe3e4c2 100644
--- a/services/inputflinger/reader/mapper/KeyboardInputMapper.cpp
+++ b/services/inputflinger/reader/mapper/KeyboardInputMapper.cpp
@@ -344,12 +344,14 @@
}
KeyboardType keyboardType = getDeviceContext().getKeyboardType();
- // Any key down on an external keyboard should wake the device.
- // We don't do this for internal keyboards to prevent them from waking up in your pocket.
+ // Any key down on an external keyboard or internal alphanumeric keyboard should wake the
+ // device. We don't do this for non-alphanumeric internal keyboards to prevent them from
+ // waking up in your pocket.
// For internal keyboards and devices for which the default wake behavior is explicitly
// prevented (e.g. TV remotes), the key layout file should specify the policy flags for each
// wake key individually.
- if (down && getDeviceContext().isExternal() && !mParameters.doNotWakeByDefault &&
+ if (down && !mParameters.doNotWakeByDefault &&
+ (getDeviceContext().isExternal() || wakeOnAlphabeticKeyboard(keyboardType)) &&
!(keyboardType != KeyboardType::ALPHABETIC && isMediaKey(keyCode))) {
policyFlags |= POLICY_FLAG_WAKE;
}
@@ -507,4 +509,8 @@
return deviceSources & ALL_KEYBOARD_SOURCES;
}
+bool KeyboardInputMapper::wakeOnAlphabeticKeyboard(const KeyboardType keyboardType) const {
+ return mEnableAlphabeticKeyboardWakeFlag && (KeyboardType::ALPHABETIC == keyboardType);
+}
+
} // namespace android
diff --git a/services/inputflinger/reader/mapper/KeyboardInputMapper.h b/services/inputflinger/reader/mapper/KeyboardInputMapper.h
index 10bd424..7d9b3e4 100644
--- a/services/inputflinger/reader/mapper/KeyboardInputMapper.h
+++ b/services/inputflinger/reader/mapper/KeyboardInputMapper.h
@@ -16,6 +16,8 @@
#pragma once
+#include <com_android_input_flags.h>
+
#include "HidUsageAccumulator.h"
#include "InputMapper.h"
@@ -85,6 +87,10 @@
bool doNotWakeByDefault{};
} mParameters{};
+ // Store the value of enable wake for alphanumeric keyboard flag.
+ const bool mEnableAlphabeticKeyboardWakeFlag =
+ com::android::input::flags::enable_alphabetic_keyboard_wake();
+
KeyboardInputMapper(InputDeviceContext& deviceContext,
const InputReaderConfiguration& readerConfig, uint32_t source);
void configureParameters();
@@ -109,6 +115,8 @@
[[nodiscard]] std::list<NotifyArgs> cancelAllDownKeys(nsecs_t when);
void onKeyDownProcessed(nsecs_t downTime);
uint32_t getEventSource() const;
+
+ bool wakeOnAlphabeticKeyboard(const KeyboardType keyboardType) const;
};
} // namespace android
diff --git a/services/inputflinger/reader/mapper/SensorInputMapper.cpp b/services/inputflinger/reader/mapper/SensorInputMapper.cpp
index 4233f78..1f6600d 100644
--- a/services/inputflinger/reader/mapper/SensorInputMapper.cpp
+++ b/services/inputflinger/reader/mapper/SensorInputMapper.cpp
@@ -212,7 +212,7 @@
// One input device can only have 1 sensor for each sensor Type.
InputDeviceSensorInfo sensorInfo(identifier.name, std::to_string(identifier.vendor),
identifier.version, sensorType,
- InputDeviceSensorAccuracy::ACCURACY_HIGH,
+ InputDeviceSensorAccuracy::HIGH,
/*maxRange=*/axis.max, /*resolution=*/axis.scale,
/*power=*/config.getFloat(prefix + ".power").value_or(0.0f),
/*minDelay=*/config.getInt(prefix + ".minDelay").value_or(0),
diff --git a/services/inputflinger/reader/mapper/SensorInputMapper.h b/services/inputflinger/reader/mapper/SensorInputMapper.h
index 63bc151..7974efe 100644
--- a/services/inputflinger/reader/mapper/SensorInputMapper.h
+++ b/services/inputflinger/reader/mapper/SensorInputMapper.h
@@ -101,7 +101,7 @@
std::array<int32_t, SENSOR_VEC_LEN> dataVec;
void resetValue() {
this->enabled = false;
- this->accuracy = InputDeviceSensorAccuracy::ACCURACY_NONE;
+ this->accuracy = InputDeviceSensorAccuracy::NONE;
this->samplingPeriod = std::chrono::nanoseconds(0);
this->maxBatchReportLatency = std::chrono::nanoseconds(0);
this->lastSampleTimeNs = std::nullopt;
diff --git a/services/inputflinger/reader/mapper/TouchpadInputMapper.cpp b/services/inputflinger/reader/mapper/TouchpadInputMapper.cpp
index 9a36bfb..0c094e6 100644
--- a/services/inputflinger/reader/mapper/TouchpadInputMapper.cpp
+++ b/services/inputflinger/reader/mapper/TouchpadInputMapper.cpp
@@ -351,6 +351,7 @@
bumpGeneration();
}
+ std::list<NotifyArgs> out;
if (!changes.any() || changes.test(InputReaderConfiguration::Change::TOUCHPAD_SETTINGS)) {
mPropertyProvider.getProperty("Use Custom Touchpad Pointer Accel Curve")
.setBoolValues({true});
@@ -375,8 +376,11 @@
mPropertyProvider.getProperty("Button Right Click Zone Enable")
.setBoolValues({config.touchpadRightClickZoneEnabled});
mTouchpadHardwareStateNotificationsEnabled = config.shouldNotifyTouchpadHardwareState;
+ mGestureConverter.setThreeFingerTapShortcutEnabled(
+ config.touchpadThreeFingerTapShortcutEnabled);
+ out += mGestureConverter.setEnableSystemGestures(when,
+ config.touchpadSystemGesturesEnabled);
}
- std::list<NotifyArgs> out;
if ((!changes.any() && config.pointerCaptureRequest.isEnable()) ||
changes.test(InputReaderConfiguration::Change::POINTER_CAPTURE)) {
mPointerCaptured = config.pointerCaptureRequest.isEnable();
diff --git a/services/inputflinger/reader/mapper/gestures/GestureConverter.cpp b/services/inputflinger/reader/mapper/gestures/GestureConverter.cpp
index da2c683..6bd949a 100644
--- a/services/inputflinger/reader/mapper/gestures/GestureConverter.cpp
+++ b/services/inputflinger/reader/mapper/gestures/GestureConverter.cpp
@@ -99,6 +99,8 @@
out << "Current classification: " << ftl::enum_string(mCurrentClassification) << "\n";
out << "Is hovering: " << mIsHovering << "\n";
out << "Enable Tap Timestamp: " << mWhenToEnableTapToClick << "\n";
+ out << "Three finger tap shortcut enabled: "
+ << (mThreeFingerTapShortcutEnabled ? "enabled" : "disabled") << "\n";
return out.str();
}
@@ -130,6 +132,15 @@
return out;
}
+std::list<NotifyArgs> GestureConverter::setEnableSystemGestures(nsecs_t when, bool enable) {
+ std::list<NotifyArgs> out;
+ if (!enable && mCurrentClassification == MotionClassification::MULTI_FINGER_SWIPE) {
+ out += handleMultiFingerSwipeLift(when, when);
+ }
+ mEnableSystemGestures = enable;
+ return out;
+}
+
void GestureConverter::populateMotionRanges(InputDeviceInfo& info) const {
info.addMotionRange(AMOTION_EVENT_AXIS_PRESSURE, SOURCE, 0.0f, 1.0f, 0, 0, 0);
@@ -261,6 +272,14 @@
}
const uint32_t buttonsPressed = gesture.details.buttons.down;
+ const uint32_t buttonsReleased = gesture.details.buttons.up;
+
+ if (mThreeFingerTapShortcutEnabled && gesture.details.buttons.is_tap &&
+ buttonsPressed == GESTURES_BUTTON_MIDDLE && buttonsReleased == GESTURES_BUTTON_MIDDLE) {
+ mReaderContext.getPolicy()->notifyTouchpadThreeFingerTap();
+ return out;
+ }
+
bool pointerDown = isPointerDown(mButtonState) ||
buttonsPressed &
(GESTURES_BUTTON_LEFT | GESTURES_BUTTON_MIDDLE | GESTURES_BUTTON_RIGHT);
@@ -291,7 +310,6 @@
// changes: a set of buttons going down, followed by a set of buttons going up.
mButtonState = newButtonState;
- const uint32_t buttonsReleased = gesture.details.buttons.up;
for (uint32_t button = 1; button <= GESTURES_BUTTON_FORWARD; button <<= 1) {
if (buttonsReleased & button) {
uint32_t actionButton = gesturesButtonToMotionEventButton(button);
@@ -452,6 +470,9 @@
uint32_t fingerCount,
float dx, float dy) {
std::list<NotifyArgs> out = {};
+ if (!mEnableSystemGestures) {
+ return out;
+ }
if (mCurrentClassification != MotionClassification::MULTI_FINGER_SWIPE) {
// If the user changes the number of fingers mid-way through a swipe (e.g. they start with
diff --git a/services/inputflinger/reader/mapper/gestures/GestureConverter.h b/services/inputflinger/reader/mapper/gestures/GestureConverter.h
index c9a35c1..8d92ead 100644
--- a/services/inputflinger/reader/mapper/gestures/GestureConverter.h
+++ b/services/inputflinger/reader/mapper/gestures/GestureConverter.h
@@ -55,6 +55,12 @@
void setBoundsInLogicalDisplay(FloatRect bounds) { mBoundsInLogicalDisplay = bounds; }
+ void setThreeFingerTapShortcutEnabled(bool enabled) {
+ mThreeFingerTapShortcutEnabled = enabled;
+ }
+
+ [[nodiscard]] std::list<NotifyArgs> setEnableSystemGestures(nsecs_t when, bool enable);
+
void populateMotionRanges(InputDeviceInfo& info) const;
[[nodiscard]] std::list<NotifyArgs> handleGesture(nsecs_t when, nsecs_t readTime,
@@ -100,6 +106,9 @@
InputReaderContext& mReaderContext;
const bool mEnableFlingStop;
const bool mEnableNoFocusChange;
+ bool mEnableSystemGestures{true};
+
+ bool mThreeFingerTapShortcutEnabled;
std::optional<ui::LogicalDisplayId> mDisplayId;
FloatRect mBoundsInLogicalDisplay{};
diff --git a/services/inputflinger/tests/Android.bp b/services/inputflinger/tests/Android.bp
index 744cf4a..600ae52 100644
--- a/services/inputflinger/tests/Android.bp
+++ b/services/inputflinger/tests/Android.bp
@@ -78,6 +78,7 @@
"PreferStylusOverTouch_test.cpp",
"PropertyProvider_test.cpp",
"RotaryEncoderInputMapper_test.cpp",
+ "SensorInputMapper_test.cpp",
"SlopController_test.cpp",
"SwitchInputMapper_test.cpp",
"SyncQueue_test.cpp",
diff --git a/services/inputflinger/tests/CursorInputMapper_test.cpp b/services/inputflinger/tests/CursorInputMapper_test.cpp
index 1762a45..d4e8fdf 100644
--- a/services/inputflinger/tests/CursorInputMapper_test.cpp
+++ b/services/inputflinger/tests/CursorInputMapper_test.cpp
@@ -25,8 +25,10 @@
#include <android-base/logging.h>
#include <android_companion_virtualdevice_flags.h>
#include <com_android_input_flags.h>
+#include <gmock/gmock.h>
#include <gtest/gtest.h>
#include <input/DisplayViewport.h>
+#include <input/InputEventLabels.h>
#include <linux/input-event-codes.h>
#include <linux/input.h>
#include <utils/Timers.h>
@@ -52,6 +54,8 @@
constexpr auto BUTTON_RELEASE = AMOTION_EVENT_ACTION_BUTTON_RELEASE;
constexpr auto HOVER_MOVE = AMOTION_EVENT_ACTION_HOVER_MOVE;
constexpr auto INVALID_CURSOR_POSITION = AMOTION_EVENT_INVALID_CURSOR_POSITION;
+constexpr auto AXIS_X = AMOTION_EVENT_AXIS_X;
+constexpr auto AXIS_Y = AMOTION_EVENT_AXIS_Y;
constexpr ui::LogicalDisplayId DISPLAY_ID = ui::LogicalDisplayId::DEFAULT;
constexpr ui::LogicalDisplayId SECONDARY_DISPLAY_ID = ui::LogicalDisplayId{DISPLAY_ID.val() + 1};
constexpr int32_t DISPLAY_WIDTH = 480;
@@ -94,9 +98,35 @@
return v;
}
+// In a number of these tests, we want to check that some pointer motion is reported without
+// specifying an exact value, as that would require updating the tests every time the pointer
+// ballistics was changed. To do this, we make some matchers that only check the sign of a
+// particular axis.
+MATCHER_P(WithPositiveAxis, axis, "MotionEvent with a positive axis value") {
+ *result_listener << "expected 1 pointer with a positive "
+ << InputEventLookup::getAxisLabel(axis) << " axis but got "
+ << arg.pointerCoords.size() << " pointers, with axis value "
+ << arg.pointerCoords[0].getAxisValue(axis);
+ return arg.pointerCoords.size() == 1 && arg.pointerCoords[0].getAxisValue(axis) > 0;
+}
+
+MATCHER_P(WithZeroAxis, axis, "MotionEvent with a zero axis value") {
+ *result_listener << "expected 1 pointer with a zero " << InputEventLookup::getAxisLabel(axis)
+ << " axis but got " << arg.pointerCoords.size()
+ << " pointers, with axis value " << arg.pointerCoords[0].getAxisValue(axis);
+ return arg.pointerCoords.size() == 1 && arg.pointerCoords[0].getAxisValue(axis) == 0;
+}
+
+MATCHER_P(WithNegativeAxis, axis, "MotionEvent with a negative axis value") {
+ *result_listener << "expected 1 pointer with a negative "
+ << InputEventLookup::getAxisLabel(axis) << " axis but got "
+ << arg.pointerCoords.size() << " pointers, with axis value "
+ << arg.pointerCoords[0].getAxisValue(axis);
+ return arg.pointerCoords.size() == 1 && arg.pointerCoords[0].getAxisValue(axis) < 0;
+}
+
} // namespace
-namespace input_flags = com::android::input::flags;
namespace vd_flags = android::companion::virtualdevice::flags;
/**
@@ -150,24 +180,21 @@
ASSERT_GT(mDevice->getGeneration(), generation);
}
- void testMotionRotation(int32_t originalX, int32_t originalY, int32_t rotatedX,
- int32_t rotatedY) {
+ void testRotation(int32_t originalX, int32_t originalY,
+ const testing::Matcher<NotifyMotionArgs>& coordsMatcher) {
std::list<NotifyArgs> args;
args += process(ARBITRARY_TIME, EV_REL, REL_X, originalX);
args += process(ARBITRARY_TIME, EV_REL, REL_Y, originalY);
args += process(ARBITRARY_TIME, EV_SYN, SYN_REPORT, 0);
ASSERT_THAT(args,
ElementsAre(VariantWith<NotifyMotionArgs>(
- AllOf(WithMotionAction(ACTION_MOVE),
- WithCoords(float(rotatedX) / TRACKBALL_MOVEMENT_THRESHOLD,
- float(rotatedY) / TRACKBALL_MOVEMENT_THRESHOLD)))));
+ AllOf(WithMotionAction(ACTION_MOVE), coordsMatcher))));
}
};
class CursorInputMapperUnitTest : public CursorInputMapperUnitTestBase {
protected:
void SetUp() override {
- input_flags::enable_new_mouse_pointer_ballistics(false);
vd_flags::high_resolution_scroll(false);
CursorInputMapperUnitTestBase::SetUp();
}
@@ -344,14 +371,12 @@
args += process(EV_KEY, BTN_MOUSE, 0);
args += process(EV_SYN, SYN_REPORT, 0);
ASSERT_THAT(args,
- ElementsAre(VariantWith<NotifyMotionArgs>(
- AllOf(WithMotionAction(BUTTON_RELEASE),
- WithSource(AINPUT_SOURCE_MOUSE_RELATIVE),
- WithCoords(0.0f, 0.0f), WithPressure(0.0f))),
- VariantWith<NotifyMotionArgs>(
- AllOf(WithMotionAction(ACTION_UP),
- WithSource(AINPUT_SOURCE_MOUSE_RELATIVE),
- WithCoords(0.0f, 0.0f), WithPressure(0.0f)))));
+ ElementsAre(VariantWith<NotifyMotionArgs>(WithMotionAction(BUTTON_RELEASE)),
+ VariantWith<NotifyMotionArgs>(WithMotionAction(ACTION_UP))));
+ ASSERT_THAT(args,
+ Each(VariantWith<NotifyMotionArgs>(AllOf(WithSource(AINPUT_SOURCE_MOUSE_RELATIVE),
+ WithCoords(0.0f, 0.0f),
+ WithPressure(0.0f)))));
// Another move.
args.clear();
@@ -377,7 +402,8 @@
ElementsAre(VariantWith<NotifyMotionArgs>(
AllOf(WithMotionAction(HOVER_MOVE), WithSource(AINPUT_SOURCE_MOUSE),
expectedCoords, expectedCursorPosition,
- WithRelativeMotion(10.0f, 20.0f)))));
+ WithPositiveAxis(AMOTION_EVENT_AXIS_RELATIVE_X),
+ WithPositiveAxis(AMOTION_EVENT_AXIS_RELATIVE_Y)))));
}
TEST_F(CursorInputMapperUnitTest, PopulateDeviceInfoReturnsScaledRangeInNavigationMode) {
@@ -411,64 +437,40 @@
args += process(ARBITRARY_TIME, EV_KEY, BTN_MOUSE, 1);
args += process(ARBITRARY_TIME, EV_SYN, SYN_REPORT, 0);
EXPECT_THAT(args,
- ElementsAre(VariantWith<NotifyMotionArgs>(
- AllOf(WithEventTime(ARBITRARY_TIME), WithDeviceId(DEVICE_ID),
- WithSource(AINPUT_SOURCE_TRACKBALL), WithFlags(0),
- WithEdgeFlags(0), WithPolicyFlags(0),
- WithMotionAction(AMOTION_EVENT_ACTION_DOWN),
- WithMetaState(AMETA_SHIFT_LEFT_ON | AMETA_SHIFT_ON),
- WithButtonState(AMOTION_EVENT_BUTTON_PRIMARY),
- WithPointerCount(1), WithPointerId(0, 0),
- WithToolType(ToolType::MOUSE), WithCoords(0.0f, 0.0f),
- WithPressure(1.0f),
- WithPrecision(TRACKBALL_MOVEMENT_THRESHOLD,
- TRACKBALL_MOVEMENT_THRESHOLD),
- WithDownTime(ARBITRARY_TIME))),
- VariantWith<NotifyMotionArgs>(
- AllOf(WithEventTime(ARBITRARY_TIME), WithDeviceId(DEVICE_ID),
- WithSource(AINPUT_SOURCE_TRACKBALL), WithFlags(0),
- WithEdgeFlags(0), WithPolicyFlags(0),
- WithMotionAction(AMOTION_EVENT_ACTION_BUTTON_PRESS),
- WithMetaState(AMETA_SHIFT_LEFT_ON | AMETA_SHIFT_ON),
- WithButtonState(AMOTION_EVENT_BUTTON_PRIMARY),
- WithPointerCount(1), WithPointerId(0, 0),
- WithToolType(ToolType::MOUSE), WithCoords(0.0f, 0.0f),
- WithPressure(1.0f),
- WithPrecision(TRACKBALL_MOVEMENT_THRESHOLD,
- TRACKBALL_MOVEMENT_THRESHOLD),
- WithDownTime(ARBITRARY_TIME)))));
+ ElementsAre(VariantWith<NotifyMotionArgs>(WithMotionAction(ACTION_DOWN)),
+ VariantWith<NotifyMotionArgs>(WithMotionAction(BUTTON_PRESS))));
+ EXPECT_THAT(args,
+ Each(VariantWith<NotifyMotionArgs>(
+ AllOf(WithEventTime(ARBITRARY_TIME), WithDeviceId(DEVICE_ID),
+ WithSource(AINPUT_SOURCE_TRACKBALL), WithFlags(0), WithEdgeFlags(0),
+ WithPolicyFlags(0),
+ WithMetaState(AMETA_SHIFT_LEFT_ON | AMETA_SHIFT_ON),
+ WithButtonState(AMOTION_EVENT_BUTTON_PRIMARY), WithPointerCount(1),
+ WithPointerId(0, 0), WithToolType(ToolType::MOUSE),
+ WithCoords(0.0f, 0.0f), WithPressure(1.0f),
+ WithPrecision(TRACKBALL_MOVEMENT_THRESHOLD,
+ TRACKBALL_MOVEMENT_THRESHOLD),
+ WithDownTime(ARBITRARY_TIME)))));
args.clear();
// Button release. Should have same down time.
args += process(ARBITRARY_TIME + 1, EV_KEY, BTN_MOUSE, 0);
args += process(ARBITRARY_TIME + 1, EV_SYN, SYN_REPORT, 0);
EXPECT_THAT(args,
- ElementsAre(VariantWith<NotifyMotionArgs>(
- AllOf(WithEventTime(ARBITRARY_TIME + 1),
- WithDeviceId(DEVICE_ID),
- WithSource(AINPUT_SOURCE_TRACKBALL), WithFlags(0),
- WithEdgeFlags(0), WithPolicyFlags(0),
- WithMotionAction(AMOTION_EVENT_ACTION_BUTTON_RELEASE),
- WithMetaState(AMETA_SHIFT_LEFT_ON | AMETA_SHIFT_ON),
- WithButtonState(0), WithPointerCount(1),
- WithPointerId(0, 0), WithToolType(ToolType::MOUSE),
- WithCoords(0.0f, 0.0f), WithPressure(0.0f),
- WithPrecision(TRACKBALL_MOVEMENT_THRESHOLD,
- TRACKBALL_MOVEMENT_THRESHOLD),
- WithDownTime(ARBITRARY_TIME))),
- VariantWith<NotifyMotionArgs>(
- AllOf(WithEventTime(ARBITRARY_TIME + 1),
- WithDeviceId(DEVICE_ID),
- WithSource(AINPUT_SOURCE_TRACKBALL), WithFlags(0),
- WithEdgeFlags(0), WithPolicyFlags(0),
- WithMotionAction(AMOTION_EVENT_ACTION_UP),
- WithMetaState(AMETA_SHIFT_LEFT_ON | AMETA_SHIFT_ON),
- WithButtonState(0), WithPointerCount(1),
- WithPointerId(0, 0), WithToolType(ToolType::MOUSE),
- WithCoords(0.0f, 0.0f), WithPressure(0.0f),
- WithPrecision(TRACKBALL_MOVEMENT_THRESHOLD,
- TRACKBALL_MOVEMENT_THRESHOLD),
- WithDownTime(ARBITRARY_TIME)))));
+ ElementsAre(VariantWith<NotifyMotionArgs>(WithMotionAction(BUTTON_RELEASE)),
+ VariantWith<NotifyMotionArgs>(WithMotionAction(ACTION_UP))));
+ EXPECT_THAT(args,
+ Each(VariantWith<NotifyMotionArgs>(
+ AllOf(WithEventTime(ARBITRARY_TIME + 1), WithDeviceId(DEVICE_ID),
+ WithSource(AINPUT_SOURCE_TRACKBALL), WithFlags(0), WithEdgeFlags(0),
+ WithPolicyFlags(0),
+ WithMetaState(AMETA_SHIFT_LEFT_ON | AMETA_SHIFT_ON),
+ WithButtonState(0), WithPointerCount(1), WithPointerId(0, 0),
+ WithToolType(ToolType::MOUSE), WithCoords(0.0f, 0.0f),
+ WithPressure(0.0f),
+ WithPrecision(TRACKBALL_MOVEMENT_THRESHOLD,
+ TRACKBALL_MOVEMENT_THRESHOLD),
+ WithDownTime(ARBITRARY_TIME)))));
}
TEST_F(CursorInputMapperUnitTest, ProcessShouldHandleIndependentXYUpdates) {
@@ -482,9 +484,8 @@
args += process(ARBITRARY_TIME, EV_SYN, SYN_REPORT, 0);
EXPECT_THAT(args,
ElementsAre(VariantWith<NotifyMotionArgs>(
- AllOf(WithMotionAction(AMOTION_EVENT_ACTION_MOVE),
- WithCoords(1.0f / TRACKBALL_MOVEMENT_THRESHOLD, 0.0f),
- WithPressure(0.0f)))));
+ AllOf(WithMotionAction(ACTION_MOVE), WithPressure(0.0f),
+ WithPositiveAxis(AXIS_X), WithZeroAxis(AXIS_Y)))));
args.clear();
// Motion in Y but not X.
@@ -492,9 +493,8 @@
args += process(ARBITRARY_TIME, EV_SYN, SYN_REPORT, 0);
EXPECT_THAT(args,
ElementsAre(VariantWith<NotifyMotionArgs>(
- AllOf(WithMotionAction(AMOTION_EVENT_ACTION_MOVE),
- WithCoords(0.0f, -2.0f / TRACKBALL_MOVEMENT_THRESHOLD),
- WithPressure(0.0f)))));
+ AllOf(WithMotionAction(ACTION_MOVE), WithPressure(0.0f),
+ WithZeroAxis(AXIS_X), WithNegativeAxis(AXIS_Y)))));
args.clear();
}
@@ -508,24 +508,22 @@
args += process(ARBITRARY_TIME, EV_KEY, BTN_MOUSE, 1);
args += process(ARBITRARY_TIME, EV_SYN, SYN_REPORT, 0);
EXPECT_THAT(args,
- ElementsAre(VariantWith<NotifyMotionArgs>(
- AllOf(WithMotionAction(AMOTION_EVENT_ACTION_DOWN),
- WithCoords(0.0f, 0.0f), WithPressure(1.0f))),
- VariantWith<NotifyMotionArgs>(
- AllOf(WithMotionAction(AMOTION_EVENT_ACTION_BUTTON_PRESS),
- WithCoords(0.0f, 0.0f), WithPressure(1.0f)))));
+ ElementsAre(VariantWith<NotifyMotionArgs>(WithMotionAction(ACTION_DOWN)),
+ VariantWith<NotifyMotionArgs>(WithMotionAction(BUTTON_PRESS))));
+ EXPECT_THAT(args,
+ Each(VariantWith<NotifyMotionArgs>(
+ AllOf(WithCoords(0.0f, 0.0f), WithPressure(1.0f)))));
args.clear();
// Button release.
args += process(ARBITRARY_TIME, EV_KEY, BTN_MOUSE, 0);
args += process(ARBITRARY_TIME, EV_SYN, SYN_REPORT, 0);
EXPECT_THAT(args,
- ElementsAre(VariantWith<NotifyMotionArgs>(
- AllOf(WithMotionAction(AMOTION_EVENT_ACTION_BUTTON_RELEASE),
- WithCoords(0.0f, 0.0f), WithPressure(0.0f))),
- VariantWith<NotifyMotionArgs>(
- AllOf(WithMotionAction(AMOTION_EVENT_ACTION_UP),
- WithCoords(0.0f, 0.0f), WithPressure(0.0f)))));
+ ElementsAre(VariantWith<NotifyMotionArgs>(WithMotionAction(BUTTON_RELEASE)),
+ VariantWith<NotifyMotionArgs>(WithMotionAction(ACTION_UP))));
+ EXPECT_THAT(args,
+ Each(VariantWith<NotifyMotionArgs>(
+ AllOf(WithCoords(0.0f, 0.0f), WithPressure(0.0f)))));
}
TEST_F(CursorInputMapperUnitTest, ProcessShouldHandleCombinedXYAndButtonUpdates) {
@@ -540,16 +538,12 @@
args += process(ARBITRARY_TIME, EV_KEY, BTN_MOUSE, 1);
args += process(ARBITRARY_TIME, EV_SYN, SYN_REPORT, 0);
EXPECT_THAT(args,
- ElementsAre(VariantWith<NotifyMotionArgs>(
- AllOf(WithMotionAction(AMOTION_EVENT_ACTION_DOWN),
- WithCoords(1.0f / TRACKBALL_MOVEMENT_THRESHOLD,
- -2.0f / TRACKBALL_MOVEMENT_THRESHOLD),
- WithPressure(1.0f))),
- VariantWith<NotifyMotionArgs>(
- AllOf(WithMotionAction(AMOTION_EVENT_ACTION_BUTTON_PRESS),
- WithCoords(1.0f / TRACKBALL_MOVEMENT_THRESHOLD,
- -2.0f / TRACKBALL_MOVEMENT_THRESHOLD),
- WithPressure(1.0f)))));
+ ElementsAre(VariantWith<NotifyMotionArgs>(WithMotionAction(ACTION_DOWN)),
+ VariantWith<NotifyMotionArgs>(WithMotionAction(BUTTON_PRESS))));
+ EXPECT_THAT(args,
+ Each(VariantWith<NotifyMotionArgs>(AllOf(WithPositiveAxis(AXIS_X),
+ WithNegativeAxis(AXIS_Y),
+ WithPressure(1.0f)))));
args.clear();
// Move X, Y a bit while pressed.
@@ -558,22 +552,19 @@
args += process(ARBITRARY_TIME, EV_SYN, SYN_REPORT, 0);
EXPECT_THAT(args,
ElementsAre(VariantWith<NotifyMotionArgs>(
- AllOf(WithMotionAction(AMOTION_EVENT_ACTION_MOVE),
- WithCoords(2.0f / TRACKBALL_MOVEMENT_THRESHOLD,
- 1.0f / TRACKBALL_MOVEMENT_THRESHOLD),
- WithPressure(1.0f)))));
+ AllOf(WithMotionAction(ACTION_MOVE), WithPressure(1.0f),
+ WithPositiveAxis(AXIS_X), WithPositiveAxis(AXIS_Y)))));
args.clear();
// Release Button.
args += process(ARBITRARY_TIME, EV_KEY, BTN_MOUSE, 0);
args += process(ARBITRARY_TIME, EV_SYN, SYN_REPORT, 0);
EXPECT_THAT(args,
- ElementsAre(VariantWith<NotifyMotionArgs>(
- AllOf(WithMotionAction(AMOTION_EVENT_ACTION_BUTTON_RELEASE),
- WithCoords(0.0f, 0.0f), WithPressure(0.0f))),
- VariantWith<NotifyMotionArgs>(
- AllOf(WithMotionAction(AMOTION_EVENT_ACTION_UP),
- WithCoords(0.0f, 0.0f), WithPressure(0.0f)))));
+ ElementsAre(VariantWith<NotifyMotionArgs>(WithMotionAction(BUTTON_RELEASE)),
+ VariantWith<NotifyMotionArgs>(WithMotionAction(ACTION_UP))));
+ EXPECT_THAT(args,
+ Each(VariantWith<NotifyMotionArgs>(
+ AllOf(WithCoords(0.0f, 0.0f), WithPressure(0.0f)))));
args.clear();
}
@@ -586,14 +577,16 @@
.WillRepeatedly(Return(createPrimaryViewport(ui::Rotation::Rotation90)));
mMapper = createInputMapper<CursorInputMapper>(*mDeviceContext, mReaderConfiguration);
- ASSERT_NO_FATAL_FAILURE(testMotionRotation( 0, 1, 0, 1));
- ASSERT_NO_FATAL_FAILURE(testMotionRotation( 1, 1, 1, 1));
- ASSERT_NO_FATAL_FAILURE(testMotionRotation( 1, 0, 1, 0));
- ASSERT_NO_FATAL_FAILURE(testMotionRotation( 1, -1, 1, -1));
- ASSERT_NO_FATAL_FAILURE(testMotionRotation( 0, -1, 0, -1));
- ASSERT_NO_FATAL_FAILURE(testMotionRotation(-1, -1, -1, -1));
- ASSERT_NO_FATAL_FAILURE(testMotionRotation(-1, 0, -1, 0));
- ASSERT_NO_FATAL_FAILURE(testMotionRotation(-1, 1, -1, 1));
+ constexpr auto X = AXIS_X;
+ constexpr auto Y = AXIS_Y;
+ ASSERT_NO_FATAL_FAILURE(testRotation( 0, 1, AllOf(WithZeroAxis(X), WithPositiveAxis(Y))));
+ ASSERT_NO_FATAL_FAILURE(testRotation( 1, 1, AllOf(WithPositiveAxis(X), WithPositiveAxis(Y))));
+ ASSERT_NO_FATAL_FAILURE(testRotation( 1, 0, AllOf(WithPositiveAxis(X), WithZeroAxis(Y))));
+ ASSERT_NO_FATAL_FAILURE(testRotation( 1, -1, AllOf(WithPositiveAxis(X), WithNegativeAxis(Y))));
+ ASSERT_NO_FATAL_FAILURE(testRotation( 0, -1, AllOf(WithZeroAxis(X), WithNegativeAxis(Y))));
+ ASSERT_NO_FATAL_FAILURE(testRotation(-1, -1, AllOf(WithNegativeAxis(X), WithNegativeAxis(Y))));
+ ASSERT_NO_FATAL_FAILURE(testRotation(-1, 0, AllOf(WithNegativeAxis(X), WithZeroAxis(Y))));
+ ASSERT_NO_FATAL_FAILURE(testRotation(-1, 1, AllOf(WithNegativeAxis(X), WithPositiveAxis(Y))));
}
TEST_F(CursorInputMapperUnitTest, ProcessShouldRotateMotionsWhenNotOrientationAware) {
@@ -604,54 +597,56 @@
.WillRepeatedly(Return(createPrimaryViewport(ui::Rotation::Rotation0)));
mMapper = createInputMapper<CursorInputMapper>(*mDeviceContext, mReaderConfiguration);
- ASSERT_NO_FATAL_FAILURE(testMotionRotation( 0, 1, 0, 1));
- ASSERT_NO_FATAL_FAILURE(testMotionRotation( 1, 1, 1, 1));
- ASSERT_NO_FATAL_FAILURE(testMotionRotation( 1, 0, 1, 0));
- ASSERT_NO_FATAL_FAILURE(testMotionRotation( 1, -1, 1, -1));
- ASSERT_NO_FATAL_FAILURE(testMotionRotation( 0, -1, 0, -1));
- ASSERT_NO_FATAL_FAILURE(testMotionRotation(-1, -1, -1, -1));
- ASSERT_NO_FATAL_FAILURE(testMotionRotation(-1, 0, -1, 0));
- ASSERT_NO_FATAL_FAILURE(testMotionRotation(-1, 1, -1, 1));
+ constexpr auto X = AXIS_X;
+ constexpr auto Y = AXIS_Y;
+ ASSERT_NO_FATAL_FAILURE(testRotation( 0, 1, AllOf(WithZeroAxis(X), WithPositiveAxis(Y))));
+ ASSERT_NO_FATAL_FAILURE(testRotation( 1, 1, AllOf(WithPositiveAxis(X), WithPositiveAxis(Y))));
+ ASSERT_NO_FATAL_FAILURE(testRotation( 1, 0, AllOf(WithPositiveAxis(X), WithZeroAxis(Y))));
+ ASSERT_NO_FATAL_FAILURE(testRotation( 1, -1, AllOf(WithPositiveAxis(X), WithNegativeAxis(Y))));
+ ASSERT_NO_FATAL_FAILURE(testRotation( 0, -1, AllOf(WithZeroAxis(X), WithNegativeAxis(Y))));
+ ASSERT_NO_FATAL_FAILURE(testRotation(-1, -1, AllOf(WithNegativeAxis(X), WithNegativeAxis(Y))));
+ ASSERT_NO_FATAL_FAILURE(testRotation(-1, 0, AllOf(WithNegativeAxis(X), WithZeroAxis(Y))));
+ ASSERT_NO_FATAL_FAILURE(testRotation(-1, 1, AllOf(WithNegativeAxis(X), WithPositiveAxis(Y))));
EXPECT_CALL((*mDevice), getAssociatedViewport)
.WillRepeatedly(Return(createPrimaryViewport(ui::Rotation::Rotation90)));
std::list<NotifyArgs> args =
mMapper->reconfigure(ARBITRARY_TIME, mReaderConfiguration,
InputReaderConfiguration::Change::DISPLAY_INFO);
- ASSERT_NO_FATAL_FAILURE(testMotionRotation( 0, 1, -1, 0));
- ASSERT_NO_FATAL_FAILURE(testMotionRotation( 1, 1, -1, 1));
- ASSERT_NO_FATAL_FAILURE(testMotionRotation( 1, 0, 0, 1));
- ASSERT_NO_FATAL_FAILURE(testMotionRotation( 1, -1, 1, 1));
- ASSERT_NO_FATAL_FAILURE(testMotionRotation( 0, -1, 1, 0));
- ASSERT_NO_FATAL_FAILURE(testMotionRotation(-1, -1, 1, -1));
- ASSERT_NO_FATAL_FAILURE(testMotionRotation(-1, 0, 0, -1));
- ASSERT_NO_FATAL_FAILURE(testMotionRotation(-1, 1, -1, -1));
+ ASSERT_NO_FATAL_FAILURE(testRotation( 0, 1, AllOf(WithNegativeAxis(X), WithZeroAxis(Y))));
+ ASSERT_NO_FATAL_FAILURE(testRotation( 1, 1, AllOf(WithNegativeAxis(X), WithPositiveAxis(Y))));
+ ASSERT_NO_FATAL_FAILURE(testRotation( 1, 0, AllOf(WithZeroAxis(X), WithPositiveAxis(Y))));
+ ASSERT_NO_FATAL_FAILURE(testRotation( 1, -1, AllOf(WithPositiveAxis(X), WithPositiveAxis(Y))));
+ ASSERT_NO_FATAL_FAILURE(testRotation( 0, -1, AllOf(WithPositiveAxis(X), WithZeroAxis(Y))));
+ ASSERT_NO_FATAL_FAILURE(testRotation(-1, -1, AllOf(WithPositiveAxis(X), WithNegativeAxis(Y))));
+ ASSERT_NO_FATAL_FAILURE(testRotation(-1, 0, AllOf(WithZeroAxis(X), WithNegativeAxis(Y))));
+ ASSERT_NO_FATAL_FAILURE(testRotation(-1, 1, AllOf(WithNegativeAxis(X), WithNegativeAxis(Y))));
EXPECT_CALL((*mDevice), getAssociatedViewport)
.WillRepeatedly(Return(createPrimaryViewport(ui::Rotation::Rotation180)));
args = mMapper->reconfigure(ARBITRARY_TIME, mReaderConfiguration,
InputReaderConfiguration::Change::DISPLAY_INFO);
- ASSERT_NO_FATAL_FAILURE(testMotionRotation( 0, 1, 0, -1));
- ASSERT_NO_FATAL_FAILURE(testMotionRotation( 1, 1, -1, -1));
- ASSERT_NO_FATAL_FAILURE(testMotionRotation( 1, 0, -1, 0));
- ASSERT_NO_FATAL_FAILURE(testMotionRotation( 1, -1, -1, 1));
- ASSERT_NO_FATAL_FAILURE(testMotionRotation( 0, -1, 0, 1));
- ASSERT_NO_FATAL_FAILURE(testMotionRotation(-1, -1, 1, 1));
- ASSERT_NO_FATAL_FAILURE(testMotionRotation(-1, 0, 1, 0));
- ASSERT_NO_FATAL_FAILURE(testMotionRotation(-1, 1, 1, -1));
+ ASSERT_NO_FATAL_FAILURE(testRotation( 0, 1, AllOf(WithZeroAxis(X), WithNegativeAxis(Y))));
+ ASSERT_NO_FATAL_FAILURE(testRotation( 1, 1, AllOf(WithNegativeAxis(X), WithNegativeAxis(Y))));
+ ASSERT_NO_FATAL_FAILURE(testRotation( 1, 0, AllOf(WithNegativeAxis(X), WithZeroAxis(Y))));
+ ASSERT_NO_FATAL_FAILURE(testRotation( 1, -1, AllOf(WithNegativeAxis(X), WithPositiveAxis(Y))));
+ ASSERT_NO_FATAL_FAILURE(testRotation( 0, -1, AllOf(WithZeroAxis(X), WithPositiveAxis(Y))));
+ ASSERT_NO_FATAL_FAILURE(testRotation(-1, -1, AllOf(WithPositiveAxis(X), WithPositiveAxis(Y))));
+ ASSERT_NO_FATAL_FAILURE(testRotation(-1, 0, AllOf(WithPositiveAxis(X), WithZeroAxis(Y))));
+ ASSERT_NO_FATAL_FAILURE(testRotation(-1, 1, AllOf(WithPositiveAxis(X), WithNegativeAxis(Y))));
EXPECT_CALL((*mDevice), getAssociatedViewport)
.WillRepeatedly(Return(createPrimaryViewport(ui::Rotation::Rotation270)));
args = mMapper->reconfigure(ARBITRARY_TIME, mReaderConfiguration,
InputReaderConfiguration::Change::DISPLAY_INFO);
- ASSERT_NO_FATAL_FAILURE(testMotionRotation( 0, 1, 1, 0));
- ASSERT_NO_FATAL_FAILURE(testMotionRotation( 1, 1, 1, -1));
- ASSERT_NO_FATAL_FAILURE(testMotionRotation( 1, 0, 0, -1));
- ASSERT_NO_FATAL_FAILURE(testMotionRotation( 1, -1, -1, -1));
- ASSERT_NO_FATAL_FAILURE(testMotionRotation( 0, -1, -1, 0));
- ASSERT_NO_FATAL_FAILURE(testMotionRotation(-1, -1, -1, 1));
- ASSERT_NO_FATAL_FAILURE(testMotionRotation(-1, 0, 0, 1));
- ASSERT_NO_FATAL_FAILURE(testMotionRotation(-1, 1, 1, 1));
+ ASSERT_NO_FATAL_FAILURE(testRotation( 0, 1, AllOf(WithPositiveAxis(X), WithZeroAxis(Y))));
+ ASSERT_NO_FATAL_FAILURE(testRotation( 1, 1, AllOf(WithPositiveAxis(X), WithNegativeAxis(Y))));
+ ASSERT_NO_FATAL_FAILURE(testRotation( 1, 0, AllOf(WithZeroAxis(X), WithNegativeAxis(Y))));
+ ASSERT_NO_FATAL_FAILURE(testRotation( 1, -1, AllOf(WithNegativeAxis(X), WithNegativeAxis(Y))));
+ ASSERT_NO_FATAL_FAILURE(testRotation( 0, -1, AllOf(WithNegativeAxis(X), WithZeroAxis(Y))));
+ ASSERT_NO_FATAL_FAILURE(testRotation(-1, -1, AllOf(WithNegativeAxis(X), WithPositiveAxis(Y))));
+ ASSERT_NO_FATAL_FAILURE(testRotation(-1, 0, AllOf(WithZeroAxis(X), WithPositiveAxis(Y))));
+ ASSERT_NO_FATAL_FAILURE(testRotation(-1, 1, AllOf(WithPositiveAxis(X), WithPositiveAxis(Y))));
}
TEST_F(CursorInputMapperUnitTest, PopulateDeviceInfoReturnsRangeFromPolicy) {
@@ -742,30 +737,22 @@
args += process(ARBITRARY_TIME, EV_KEY, BTN_LEFT, 1);
args += process(ARBITRARY_TIME, EV_SYN, SYN_REPORT, 0);
EXPECT_THAT(args,
- ElementsAre(VariantWith<NotifyMotionArgs>(
- AllOf(WithMotionAction(AMOTION_EVENT_ACTION_DOWN),
- WithButtonState(AMOTION_EVENT_BUTTON_PRIMARY),
- WithCoords(0.0f, 0.0f), WithPressure(1.0f))),
- VariantWith<NotifyMotionArgs>(
- AllOf(WithMotionAction(AMOTION_EVENT_ACTION_BUTTON_PRESS),
- WithButtonState(AMOTION_EVENT_BUTTON_PRIMARY),
- WithCoords(0.0f, 0.0f), WithPressure(1.0f)))));
+ ElementsAre(VariantWith<NotifyMotionArgs>(WithMotionAction(ACTION_DOWN)),
+ VariantWith<NotifyMotionArgs>(WithMotionAction(BUTTON_PRESS))));
+ EXPECT_THAT(args,
+ Each(VariantWith<NotifyMotionArgs>(
+ AllOf(WithButtonState(AMOTION_EVENT_BUTTON_PRIMARY), WithCoords(0.0f, 0.0f),
+ WithPressure(1.0f)))));
args.clear();
args += process(ARBITRARY_TIME, EV_KEY, BTN_LEFT, 0);
args += process(ARBITRARY_TIME, EV_SYN, SYN_REPORT, 0);
EXPECT_THAT(args,
- ElementsAre(VariantWith<NotifyMotionArgs>(
- AllOf(WithMotionAction(AMOTION_EVENT_ACTION_BUTTON_RELEASE),
- WithButtonState(0), WithCoords(0.0f, 0.0f),
- WithPressure(0.0f))),
- VariantWith<NotifyMotionArgs>(
- AllOf(WithMotionAction(AMOTION_EVENT_ACTION_UP),
- WithButtonState(0), WithCoords(0.0f, 0.0f),
- WithPressure(0.0f))),
- VariantWith<NotifyMotionArgs>(
- AllOf(WithMotionAction(AMOTION_EVENT_ACTION_HOVER_MOVE),
- WithButtonState(0), WithCoords(0.0f, 0.0f),
- WithPressure(0.0f)))));
+ ElementsAre(VariantWith<NotifyMotionArgs>(WithMotionAction(BUTTON_RELEASE)),
+ VariantWith<NotifyMotionArgs>(WithMotionAction(ACTION_UP)),
+ VariantWith<NotifyMotionArgs>(WithMotionAction(HOVER_MOVE))));
+ EXPECT_THAT(args,
+ Each(VariantWith<NotifyMotionArgs>(
+ AllOf(WithButtonState(0), WithCoords(0.0f, 0.0f), WithPressure(0.0f)))));
args.clear();
// press BTN_RIGHT + BTN_MIDDLE, release BTN_RIGHT, release BTN_MIDDLE
@@ -774,49 +761,41 @@
args += process(ARBITRARY_TIME, EV_SYN, SYN_REPORT, 0);
EXPECT_THAT(args,
ElementsAre(VariantWith<NotifyMotionArgs>(
- AllOf(WithMotionAction(AMOTION_EVENT_ACTION_DOWN),
+ AllOf(WithMotionAction(ACTION_DOWN),
WithButtonState(AMOTION_EVENT_BUTTON_SECONDARY |
- AMOTION_EVENT_BUTTON_TERTIARY),
- WithCoords(0.0f, 0.0f), WithPressure(1.0f))),
+ AMOTION_EVENT_BUTTON_TERTIARY))),
VariantWith<NotifyMotionArgs>(
- AllOf(WithMotionAction(AMOTION_EVENT_ACTION_BUTTON_PRESS),
- WithButtonState(AMOTION_EVENT_BUTTON_TERTIARY),
- WithCoords(0.0f, 0.0f), WithPressure(1.0f))),
+ AllOf(WithMotionAction(BUTTON_PRESS),
+ WithButtonState(AMOTION_EVENT_BUTTON_TERTIARY))),
VariantWith<NotifyMotionArgs>(
- AllOf(WithMotionAction(AMOTION_EVENT_ACTION_BUTTON_PRESS),
+ AllOf(WithMotionAction(BUTTON_PRESS),
WithButtonState(AMOTION_EVENT_BUTTON_SECONDARY |
- AMOTION_EVENT_BUTTON_TERTIARY),
- WithCoords(0.0f, 0.0f), WithPressure(1.0f)))));
+ AMOTION_EVENT_BUTTON_TERTIARY)))));
+ EXPECT_THAT(args,
+ Each(VariantWith<NotifyMotionArgs>(
+ AllOf(WithCoords(0.0f, 0.0f), WithPressure(1.0f)))));
args.clear();
args += process(ARBITRARY_TIME, EV_KEY, BTN_RIGHT, 0);
args += process(ARBITRARY_TIME, EV_SYN, SYN_REPORT, 0);
EXPECT_THAT(args,
- ElementsAre(VariantWith<NotifyMotionArgs>(
- AllOf(WithMotionAction(AMOTION_EVENT_ACTION_BUTTON_RELEASE),
- WithButtonState(AMOTION_EVENT_BUTTON_TERTIARY),
- WithCoords(0.0f, 0.0f), WithPressure(1.0f))),
- VariantWith<NotifyMotionArgs>(
- AllOf(WithMotionAction(AMOTION_EVENT_ACTION_MOVE),
- WithButtonState(AMOTION_EVENT_BUTTON_TERTIARY),
- WithCoords(0.0f, 0.0f), WithPressure(1.0f)))));
+ ElementsAre(VariantWith<NotifyMotionArgs>(WithMotionAction(BUTTON_RELEASE)),
+ VariantWith<NotifyMotionArgs>(WithMotionAction(ACTION_MOVE))));
+ EXPECT_THAT(args,
+ Each(VariantWith<NotifyMotionArgs>(
+ AllOf(WithButtonState(AMOTION_EVENT_BUTTON_TERTIARY),
+ WithCoords(0.0f, 0.0f), WithPressure(1.0f)))));
args.clear();
args += process(ARBITRARY_TIME, EV_KEY, BTN_MIDDLE, 0);
args += process(ARBITRARY_TIME, EV_SYN, SYN_REPORT, 0);
EXPECT_THAT(args,
- ElementsAre(VariantWith<NotifyMotionArgs>(
- AllOf(WithMotionAction(AMOTION_EVENT_ACTION_BUTTON_RELEASE),
- WithButtonState(0), WithCoords(0.0f, 0.0f),
- WithPressure(0.0f))),
- VariantWith<NotifyMotionArgs>(
- AllOf(WithButtonState(0),
- WithMotionAction(AMOTION_EVENT_ACTION_UP),
- WithCoords(0.0f, 0.0f), WithPressure(0.0f))),
- VariantWith<NotifyMotionArgs>(
- AllOf(WithButtonState(0),
- WithMotionAction(AMOTION_EVENT_ACTION_HOVER_MOVE),
- WithCoords(0.0f, 0.0f), WithPressure(0.0f)))));
+ ElementsAre(VariantWith<NotifyMotionArgs>(WithMotionAction(BUTTON_RELEASE)),
+ VariantWith<NotifyMotionArgs>(WithMotionAction(ACTION_UP)),
+ VariantWith<NotifyMotionArgs>(WithMotionAction(HOVER_MOVE))));
+ EXPECT_THAT(args,
+ Each(VariantWith<NotifyMotionArgs>(
+ AllOf(WithButtonState(0), WithCoords(0.0f, 0.0f), WithPressure(0.0f)))));
}
class CursorInputMapperButtonKeyTest
@@ -838,11 +817,11 @@
ElementsAre(VariantWith<NotifyKeyArgs>(AllOf(WithKeyAction(AKEY_EVENT_ACTION_DOWN),
WithKeyCode(expectedKeyCode))),
VariantWith<NotifyMotionArgs>(
- AllOf(WithMotionAction(AMOTION_EVENT_ACTION_HOVER_MOVE),
+ AllOf(WithMotionAction(HOVER_MOVE),
WithButtonState(expectedButtonState),
WithCoords(0.0f, 0.0f), WithPressure(0.0f))),
VariantWith<NotifyMotionArgs>(
- AllOf(WithMotionAction(AMOTION_EVENT_ACTION_BUTTON_PRESS),
+ AllOf(WithMotionAction(BUTTON_PRESS),
WithButtonState(expectedButtonState),
WithCoords(0.0f, 0.0f), WithPressure(0.0f)))));
args.clear();
@@ -851,13 +830,11 @@
args += process(ARBITRARY_TIME, EV_SYN, SYN_REPORT, 0);
EXPECT_THAT(args,
ElementsAre(VariantWith<NotifyMotionArgs>(
- AllOf(WithMotionAction(AMOTION_EVENT_ACTION_BUTTON_RELEASE),
- WithButtonState(0), WithCoords(0.0f, 0.0f),
- WithPressure(0.0f))),
+ AllOf(WithMotionAction(BUTTON_RELEASE), WithButtonState(0),
+ WithCoords(0.0f, 0.0f), WithPressure(0.0f))),
VariantWith<NotifyMotionArgs>(
- AllOf(WithMotionAction(AMOTION_EVENT_ACTION_HOVER_MOVE),
- WithButtonState(0), WithCoords(0.0f, 0.0f),
- WithPressure(0.0f))),
+ AllOf(WithMotionAction(HOVER_MOVE), WithButtonState(0),
+ WithCoords(0.0f, 0.0f), WithPressure(0.0f))),
VariantWith<NotifyKeyArgs>(AllOf(WithKeyAction(AKEY_EVENT_ACTION_UP),
WithKeyCode(expectedKeyCode)))));
}
@@ -881,8 +858,7 @@
args += process(ARBITRARY_TIME, EV_SYN, SYN_REPORT, 0);
EXPECT_THAT(args,
ElementsAre(VariantWith<NotifyMotionArgs>(
- AllOf(WithSource(AINPUT_SOURCE_MOUSE),
- WithMotionAction(AMOTION_EVENT_ACTION_HOVER_MOVE),
+ AllOf(WithSource(AINPUT_SOURCE_MOUSE), WithMotionAction(HOVER_MOVE),
WithCoords(0.0f, 0.0f), WithPressure(0.0f), WithSize(0.0f),
WithTouchDimensions(0.0f, 0.0f), WithToolDimensions(0.0f, 0.0f),
WithOrientation(0.0f), WithDistance(0.0f)))));
@@ -897,13 +873,11 @@
args += process(ARBITRARY_TIME, EV_SYN, SYN_REPORT, 0);
EXPECT_THAT(args,
- ElementsAre(VariantWith<NotifyMotionArgs>(
- AllOf(WithSource(AINPUT_SOURCE_MOUSE),
- WithMotionAction(AMOTION_EVENT_ACTION_HOVER_MOVE))),
+ ElementsAre(VariantWith<NotifyMotionArgs>(WithMotionAction(HOVER_MOVE)),
VariantWith<NotifyMotionArgs>(
- AllOf(WithSource(AINPUT_SOURCE_MOUSE),
- WithMotionAction(AMOTION_EVENT_ACTION_SCROLL),
+ AllOf(WithMotionAction(AMOTION_EVENT_ACTION_SCROLL),
WithScroll(1.0f, 1.0f)))));
+ EXPECT_THAT(args, Each(VariantWith<NotifyMotionArgs>(WithSource(AINPUT_SOURCE_MOUSE))));
}
TEST_F(CursorInputMapperUnitTest, ProcessHighResScroll) {
@@ -920,13 +894,11 @@
args += process(ARBITRARY_TIME, EV_SYN, SYN_REPORT, 0);
EXPECT_THAT(args,
- ElementsAre(VariantWith<NotifyMotionArgs>(
- AllOf(WithSource(AINPUT_SOURCE_MOUSE),
- WithMotionAction(AMOTION_EVENT_ACTION_HOVER_MOVE))),
+ ElementsAre(VariantWith<NotifyMotionArgs>(WithMotionAction(HOVER_MOVE)),
VariantWith<NotifyMotionArgs>(
- AllOf(WithSource(AINPUT_SOURCE_MOUSE),
- WithMotionAction(AMOTION_EVENT_ACTION_SCROLL),
+ AllOf(WithMotionAction(AMOTION_EVENT_ACTION_SCROLL),
WithScroll(0.5f, 0.5f)))));
+ EXPECT_THAT(args, Each(VariantWith<NotifyMotionArgs>(WithSource(AINPUT_SOURCE_MOUSE))));
}
TEST_F(CursorInputMapperUnitTest, HighResScrollIgnoresRegularScroll) {
@@ -945,13 +917,11 @@
args += process(ARBITRARY_TIME, EV_SYN, SYN_REPORT, 0);
EXPECT_THAT(args,
- ElementsAre(VariantWith<NotifyMotionArgs>(
- AllOf(WithSource(AINPUT_SOURCE_MOUSE),
- WithMotionAction(AMOTION_EVENT_ACTION_HOVER_MOVE))),
+ ElementsAre(VariantWith<NotifyMotionArgs>(WithMotionAction(HOVER_MOVE)),
VariantWith<NotifyMotionArgs>(
- AllOf(WithSource(AINPUT_SOURCE_MOUSE),
- WithMotionAction(AMOTION_EVENT_ACTION_SCROLL),
+ AllOf(WithMotionAction(AMOTION_EVENT_ACTION_SCROLL),
WithScroll(0.5f, 0.5f)))));
+ EXPECT_THAT(args, Each(VariantWith<NotifyMotionArgs>(WithSource(AINPUT_SOURCE_MOUSE))));
}
TEST_F(CursorInputMapperUnitTest, ProcessReversedVerticalScroll) {
@@ -966,13 +936,11 @@
// Reversed vertical scrolling only affects the y-axis, expect it to be -1.0f to indicate the
// inverted scroll direction.
EXPECT_THAT(args,
- ElementsAre(VariantWith<NotifyMotionArgs>(
- AllOf(WithSource(AINPUT_SOURCE_MOUSE),
- WithMotionAction(AMOTION_EVENT_ACTION_HOVER_MOVE))),
+ ElementsAre(VariantWith<NotifyMotionArgs>(WithMotionAction(HOVER_MOVE)),
VariantWith<NotifyMotionArgs>(
- AllOf(WithSource(AINPUT_SOURCE_MOUSE),
- WithMotionAction(AMOTION_EVENT_ACTION_SCROLL),
+ AllOf(WithMotionAction(AMOTION_EVENT_ACTION_SCROLL),
WithScroll(1.0f, -1.0f)))));
+ EXPECT_THAT(args, Each(VariantWith<NotifyMotionArgs>(WithSource(AINPUT_SOURCE_MOUSE))));
}
TEST_F(CursorInputMapperUnitTest, ProcessHighResReversedVerticalScroll) {
@@ -990,13 +958,11 @@
args += process(ARBITRARY_TIME, EV_SYN, SYN_REPORT, 0);
EXPECT_THAT(args,
- ElementsAre(VariantWith<NotifyMotionArgs>(
- AllOf(WithSource(AINPUT_SOURCE_MOUSE),
- WithMotionAction(AMOTION_EVENT_ACTION_HOVER_MOVE))),
+ ElementsAre(VariantWith<NotifyMotionArgs>(WithMotionAction(HOVER_MOVE)),
VariantWith<NotifyMotionArgs>(
- AllOf(WithSource(AINPUT_SOURCE_MOUSE),
- WithMotionAction(AMOTION_EVENT_ACTION_SCROLL),
+ AllOf(WithMotionAction(AMOTION_EVENT_ACTION_SCROLL),
WithScroll(0.5f, -0.5f)))));
+ EXPECT_THAT(args, Each(VariantWith<NotifyMotionArgs>(WithSource(AINPUT_SOURCE_MOUSE))));
}
/**
@@ -1005,10 +971,6 @@
*/
TEST_F(CursorInputMapperUnitTest, PointerCaptureDisablesVelocityProcessing) {
mPropertyMap.addProperty("cursor.mode", "pointer");
- const VelocityControlParameters testParams(/*scale=*/5.f, /*lowThreshold=*/0.f,
- /*highThreshold=*/100.f, /*acceleration=*/10.f);
- mReaderConfiguration.pointerVelocityControlParameters = testParams;
- mFakePolicy->setVelocityControlParams(testParams);
createMapper();
NotifyMotionArgs motionArgs;
@@ -1020,8 +982,7 @@
args += process(ARBITRARY_TIME, EV_SYN, SYN_REPORT, 0);
EXPECT_THAT(args,
ElementsAre(VariantWith<NotifyMotionArgs>(
- AllOf(WithSource(AINPUT_SOURCE_MOUSE),
- WithMotionAction(AMOTION_EVENT_ACTION_HOVER_MOVE)))));
+ AllOf(WithSource(AINPUT_SOURCE_MOUSE), WithMotionAction(HOVER_MOVE)))));
motionArgs = std::get<NotifyMotionArgs>(args.front());
const float relX = motionArgs.pointerCoords[0].getAxisValue(AMOTION_EVENT_AXIS_RELATIVE_X);
const float relY = motionArgs.pointerCoords[0].getAxisValue(AMOTION_EVENT_AXIS_RELATIVE_Y);
@@ -1039,12 +1000,7 @@
EXPECT_THAT(args,
ElementsAre(VariantWith<NotifyMotionArgs>(
AllOf(WithSource(AINPUT_SOURCE_MOUSE_RELATIVE),
- WithMotionAction(AMOTION_EVENT_ACTION_MOVE)))));
- motionArgs = std::get<NotifyMotionArgs>(args.front());
- const float relX2 = motionArgs.pointerCoords[0].getAxisValue(AMOTION_EVENT_AXIS_RELATIVE_X);
- const float relY2 = motionArgs.pointerCoords[0].getAxisValue(AMOTION_EVENT_AXIS_RELATIVE_Y);
- ASSERT_EQ(10, relX2);
- ASSERT_EQ(20, relY2);
+ WithMotionAction(ACTION_MOVE), WithRelativeMotion(10, 20)))));
}
TEST_F(CursorInputMapperUnitTest, ConfigureDisplayIdNoAssociatedViewport) {
@@ -1067,54 +1023,12 @@
args += process(ARBITRARY_TIME, EV_SYN, SYN_REPORT, 0);
EXPECT_THAT(args,
ElementsAre(VariantWith<NotifyMotionArgs>(
- AllOf(WithMotionAction(AMOTION_EVENT_ACTION_HOVER_MOVE),
- WithSource(AINPUT_SOURCE_MOUSE),
+ AllOf(WithMotionAction(HOVER_MOVE), WithSource(AINPUT_SOURCE_MOUSE),
WithDisplayId(ui::LogicalDisplayId::INVALID),
WithCoords(0.0f, 0.0f)))));
}
-// TODO(b/320433834): De-duplicate the test cases once the flag is removed.
-class CursorInputMapperUnitTestWithNewBallistics : public CursorInputMapperUnitTestBase {
-protected:
- void SetUp() override {
- input_flags::enable_new_mouse_pointer_ballistics(true);
- CursorInputMapperUnitTestBase::SetUp();
- }
-};
-
-TEST_F(CursorInputMapperUnitTestWithNewBallistics, PointerCaptureDisablesVelocityProcessing) {
- mPropertyMap.addProperty("cursor.mode", "pointer");
- createMapper();
-
- NotifyMotionArgs motionArgs;
- std::list<NotifyArgs> args;
-
- // Move and verify scale is applied.
- args += process(ARBITRARY_TIME, EV_REL, REL_X, 10);
- args += process(ARBITRARY_TIME, EV_REL, REL_Y, 20);
- args += process(ARBITRARY_TIME, EV_SYN, SYN_REPORT, 0);
- motionArgs = std::get<NotifyMotionArgs>(args.front());
- const float relX = motionArgs.pointerCoords[0].getAxisValue(AMOTION_EVENT_AXIS_RELATIVE_X);
- const float relY = motionArgs.pointerCoords[0].getAxisValue(AMOTION_EVENT_AXIS_RELATIVE_Y);
- ASSERT_GT(relX, 10);
- ASSERT_GT(relY, 20);
- args.clear();
-
- // Enable Pointer Capture
- setPointerCapture(true);
-
- // Move and verify scale is not applied.
- args += process(ARBITRARY_TIME, EV_REL, REL_X, 10);
- args += process(ARBITRARY_TIME, EV_REL, REL_Y, 20);
- args += process(ARBITRARY_TIME, EV_SYN, SYN_REPORT, 0);
- motionArgs = std::get<NotifyMotionArgs>(args.front());
- const float relX2 = motionArgs.pointerCoords[0].getAxisValue(AMOTION_EVENT_AXIS_RELATIVE_X);
- const float relY2 = motionArgs.pointerCoords[0].getAxisValue(AMOTION_EVENT_AXIS_RELATIVE_Y);
- ASSERT_EQ(10, relX2);
- ASSERT_EQ(20, relY2);
-}
-
-TEST_F(CursorInputMapperUnitTestWithNewBallistics, ConfigureAccelerationWithAssociatedViewport) {
+TEST_F(CursorInputMapperUnitTest, ConfigureAccelerationWithAssociatedViewport) {
mPropertyMap.addProperty("cursor.mode", "pointer");
DisplayViewport primaryViewport = createPrimaryViewport(ui::Rotation::Rotation0);
mReaderConfiguration.setDisplayViewports({primaryViewport});
@@ -1149,7 +1063,7 @@
WithRelativeMotion(10, 20)))));
}
-TEST_F(CursorInputMapperUnitTestWithNewBallistics, ConfigureAccelerationOnDisplayChange) {
+TEST_F(CursorInputMapperUnitTest, ConfigureAccelerationOnDisplayChange) {
mPropertyMap.addProperty("cursor.mode", "pointer");
DisplayViewport primaryViewport = createPrimaryViewport(ui::Rotation::Rotation0);
mReaderConfiguration.setDisplayViewports({primaryViewport});
@@ -1186,72 +1100,6 @@
WithRelativeMotion(10, 20)))));
}
-TEST_F(CursorInputMapperUnitTestWithNewBallistics, ProcessRegularScroll) {
- createMapper();
-
- std::list<NotifyArgs> args;
- args += process(ARBITRARY_TIME, EV_REL, REL_WHEEL, 1);
- args += process(ARBITRARY_TIME, EV_REL, REL_HWHEEL, 1);
- args += process(ARBITRARY_TIME, EV_SYN, SYN_REPORT, 0);
-
- EXPECT_THAT(args,
- ElementsAre(VariantWith<NotifyMotionArgs>(
- AllOf(WithSource(AINPUT_SOURCE_MOUSE),
- WithMotionAction(AMOTION_EVENT_ACTION_HOVER_MOVE))),
- VariantWith<NotifyMotionArgs>(
- AllOf(WithSource(AINPUT_SOURCE_MOUSE),
- WithMotionAction(AMOTION_EVENT_ACTION_SCROLL),
- WithScroll(1.0f, 1.0f)))));
-}
-
-TEST_F(CursorInputMapperUnitTestWithNewBallistics, ProcessHighResScroll) {
- vd_flags::high_resolution_scroll(true);
- EXPECT_CALL(mMockEventHub, hasRelativeAxis(EVENTHUB_ID, REL_WHEEL_HI_RES))
- .WillRepeatedly(Return(true));
- EXPECT_CALL(mMockEventHub, hasRelativeAxis(EVENTHUB_ID, REL_HWHEEL_HI_RES))
- .WillRepeatedly(Return(true));
- createMapper();
-
- std::list<NotifyArgs> args;
- args += process(ARBITRARY_TIME, EV_REL, REL_WHEEL_HI_RES, 60);
- args += process(ARBITRARY_TIME, EV_REL, REL_HWHEEL_HI_RES, 60);
- args += process(ARBITRARY_TIME, EV_SYN, SYN_REPORT, 0);
-
- EXPECT_THAT(args,
- ElementsAre(VariantWith<NotifyMotionArgs>(
- AllOf(WithSource(AINPUT_SOURCE_MOUSE),
- WithMotionAction(AMOTION_EVENT_ACTION_HOVER_MOVE))),
- VariantWith<NotifyMotionArgs>(
- AllOf(WithSource(AINPUT_SOURCE_MOUSE),
- WithMotionAction(AMOTION_EVENT_ACTION_SCROLL),
- WithScroll(0.5f, 0.5f)))));
-}
-
-TEST_F(CursorInputMapperUnitTestWithNewBallistics, HighResScrollIgnoresRegularScroll) {
- vd_flags::high_resolution_scroll(true);
- EXPECT_CALL(mMockEventHub, hasRelativeAxis(EVENTHUB_ID, REL_WHEEL_HI_RES))
- .WillRepeatedly(Return(true));
- EXPECT_CALL(mMockEventHub, hasRelativeAxis(EVENTHUB_ID, REL_HWHEEL_HI_RES))
- .WillRepeatedly(Return(true));
- createMapper();
-
- std::list<NotifyArgs> args;
- args += process(ARBITRARY_TIME, EV_REL, REL_WHEEL_HI_RES, 60);
- args += process(ARBITRARY_TIME, EV_REL, REL_HWHEEL_HI_RES, 60);
- args += process(ARBITRARY_TIME, EV_REL, REL_WHEEL, 1);
- args += process(ARBITRARY_TIME, EV_REL, REL_HWHEEL, 1);
- args += process(ARBITRARY_TIME, EV_SYN, SYN_REPORT, 0);
-
- EXPECT_THAT(args,
- ElementsAre(VariantWith<NotifyMotionArgs>(
- AllOf(WithSource(AINPUT_SOURCE_MOUSE),
- WithMotionAction(AMOTION_EVENT_ACTION_HOVER_MOVE))),
- VariantWith<NotifyMotionArgs>(
- AllOf(WithSource(AINPUT_SOURCE_MOUSE),
- WithMotionAction(AMOTION_EVENT_ACTION_SCROLL),
- WithScroll(0.5f, 0.5f)))));
-}
-
namespace {
// Minimum timestamp separation between subsequent input events from a Bluetooth device.
@@ -1279,8 +1127,7 @@
argsList += process(kernelEventTime, EV_SYN, SYN_REPORT, 0);
EXPECT_THAT(argsList,
ElementsAre(VariantWith<NotifyMotionArgs>(
- AllOf(WithMotionAction(AMOTION_EVENT_ACTION_HOVER_MOVE),
- WithEventTime(expectedEventTime)))));
+ AllOf(WithMotionAction(HOVER_MOVE), WithEventTime(expectedEventTime)))));
argsList.clear();
// Process several events that come in quick succession, according to their timestamps.
@@ -1294,7 +1141,7 @@
argsList += process(kernelEventTime, EV_SYN, SYN_REPORT, 0);
EXPECT_THAT(argsList,
ElementsAre(VariantWith<NotifyMotionArgs>(
- AllOf(WithMotionAction(AMOTION_EVENT_ACTION_HOVER_MOVE),
+ AllOf(WithMotionAction(HOVER_MOVE),
WithEventTime(expectedEventTime)))));
argsList.clear();
}
@@ -1310,8 +1157,7 @@
argsList += process(ARBITRARY_TIME, EV_SYN, SYN_REPORT, 0);
EXPECT_THAT(argsList,
ElementsAre(VariantWith<NotifyMotionArgs>(
- AllOf(WithMotionAction(AMOTION_EVENT_ACTION_HOVER_MOVE),
- WithEventTime(expectedEventTime)))));
+ AllOf(WithMotionAction(HOVER_MOVE), WithEventTime(expectedEventTime)))));
argsList.clear();
// Process several events with the same timestamp from the kernel.
@@ -1325,7 +1171,7 @@
argsList += process(ARBITRARY_TIME, EV_SYN, SYN_REPORT, 0);
EXPECT_THAT(argsList,
ElementsAre(VariantWith<NotifyMotionArgs>(
- AllOf(WithMotionAction(AMOTION_EVENT_ACTION_HOVER_MOVE),
+ AllOf(WithMotionAction(HOVER_MOVE),
WithEventTime(expectedEventTime)))));
argsList.clear();
}
@@ -1338,8 +1184,7 @@
argsList += process(ARBITRARY_TIME, EV_SYN, SYN_REPORT, 0);
EXPECT_THAT(argsList,
ElementsAre(VariantWith<NotifyMotionArgs>(
- AllOf(WithMotionAction(AMOTION_EVENT_ACTION_HOVER_MOVE),
- WithEventTime(cappedEventTime)))));
+ AllOf(WithMotionAction(HOVER_MOVE), WithEventTime(cappedEventTime)))));
argsList.clear();
}
}
@@ -1355,8 +1200,7 @@
argsList += process(kernelEventTime, EV_SYN, SYN_REPORT, 0);
EXPECT_THAT(argsList,
ElementsAre(VariantWith<NotifyMotionArgs>(
- AllOf(WithMotionAction(AMOTION_EVENT_ACTION_HOVER_MOVE),
- WithEventTime(expectedEventTime)))));
+ AllOf(WithMotionAction(HOVER_MOVE), WithEventTime(expectedEventTime)))));
argsList.clear();
// If the next event has a timestamp that is sufficiently spaced out so that Bluetooth timestamp
@@ -1368,8 +1212,7 @@
argsList += process(kernelEventTime, EV_SYN, SYN_REPORT, 0);
EXPECT_THAT(argsList,
ElementsAre(VariantWith<NotifyMotionArgs>(
- AllOf(WithMotionAction(AMOTION_EVENT_ACTION_HOVER_MOVE),
- WithEventTime(expectedEventTime)))));
+ AllOf(WithMotionAction(HOVER_MOVE), WithEventTime(expectedEventTime)))));
argsList.clear();
}
diff --git a/services/inputflinger/tests/FakeInputReaderPolicy.cpp b/services/inputflinger/tests/FakeInputReaderPolicy.cpp
index f373cac..67b1e8c 100644
--- a/services/inputflinger/tests/FakeInputReaderPolicy.cpp
+++ b/services/inputflinger/tests/FakeInputReaderPolicy.cpp
@@ -80,6 +80,17 @@
ASSERT_TRUE(success) << "Timed out waiting for hardware state to be notified";
}
+void FakeInputReaderPolicy::assertTouchpadThreeFingerTapNotified() {
+ std::unique_lock lock(mLock);
+ base::ScopedLockAssertion assumeLocked(mLock);
+
+ const bool success =
+ mTouchpadThreeFingerTapNotified.wait_for(lock, WAIT_TIMEOUT, [this]() REQUIRES(mLock) {
+ return mTouchpadThreeFingerTapHasBeenReported;
+ });
+ ASSERT_TRUE(success) << "Timed out waiting for three-finger tap to be notified";
+}
+
void FakeInputReaderPolicy::clearViewports() {
mViewports.clear();
mConfig.setDisplayViewports(mViewports);
@@ -217,7 +228,6 @@
}
void FakeInputReaderPolicy::setVelocityControlParams(const VelocityControlParameters& params) {
- mConfig.pointerVelocityControlParameters = params;
mConfig.wheelVelocityControlParameters = params;
}
@@ -260,6 +270,12 @@
std::scoped_lock lock(mLock);
}
+void FakeInputReaderPolicy::notifyTouchpadThreeFingerTap() {
+ std::scoped_lock lock(mLock);
+ mTouchpadThreeFingerTapHasBeenReported = true;
+ mTouchpadThreeFingerTapNotified.notify_all();
+}
+
std::shared_ptr<KeyCharacterMap> FakeInputReaderPolicy::getKeyboardLayoutOverlay(
const InputDeviceIdentifier&, const std::optional<KeyboardLayoutInfo>) {
return nullptr;
diff --git a/services/inputflinger/tests/FakeInputReaderPolicy.h b/services/inputflinger/tests/FakeInputReaderPolicy.h
index 3a2b4e9..42c9567 100644
--- a/services/inputflinger/tests/FakeInputReaderPolicy.h
+++ b/services/inputflinger/tests/FakeInputReaderPolicy.h
@@ -43,6 +43,7 @@
void assertStylusGestureNotified(int32_t deviceId);
void assertStylusGestureNotNotified();
void assertTouchpadHardwareStateNotified();
+ void assertTouchpadThreeFingerTapNotified();
virtual void clearViewports();
std::optional<DisplayViewport> getDisplayViewportByUniqueId(const std::string& uniqueId) const;
@@ -86,6 +87,7 @@
void notifyTouchpadHardwareState(const SelfContainedHardwareState& schs,
int32_t deviceId) override;
void notifyTouchpadGestureInfo(GestureType type, int32_t deviceId) override;
+ void notifyTouchpadThreeFingerTap() override;
std::shared_ptr<KeyCharacterMap> getKeyboardLayoutOverlay(
const InputDeviceIdentifier&, const std::optional<KeyboardLayoutInfo>) override;
std::string getDeviceAlias(const InputDeviceIdentifier&) override;
@@ -109,6 +111,9 @@
std::condition_variable mTouchpadHardwareStateNotified;
std::optional<SelfContainedHardwareState> mTouchpadHardwareState GUARDED_BY(mLock){};
+ std::condition_variable mTouchpadThreeFingerTapNotified;
+ bool mTouchpadThreeFingerTapHasBeenReported{false};
+
uint32_t mNextPointerCaptureSequenceNumber{0};
};
diff --git a/services/inputflinger/tests/GestureConverter_test.cpp b/services/inputflinger/tests/GestureConverter_test.cpp
index 225ae0f..fe40a5e 100644
--- a/services/inputflinger/tests/GestureConverter_test.cpp
+++ b/services/inputflinger/tests/GestureConverter_test.cpp
@@ -48,6 +48,7 @@
using testing::AllOf;
using testing::Each;
using testing::ElementsAre;
+using testing::IsEmpty;
using testing::VariantWith;
class GestureConverterTest : public testing::Test {
@@ -849,6 +850,107 @@
WithDisplayId(ui::LogicalDisplayId::DEFAULT)))));
}
+TEST_F(GestureConverterTest, DisablingSystemGestures_IgnoresMultiFingerSwipe) {
+ InputDeviceContext deviceContext(*mDevice, EVENTHUB_ID);
+ GestureConverter converter(*mReader->getContext(), deviceContext, DEVICE_ID);
+ converter.setDisplayId(ui::LogicalDisplayId::DEFAULT);
+
+ std::list<NotifyArgs> args = converter.setEnableSystemGestures(ARBITRARY_TIME, false);
+ ASSERT_THAT(args, IsEmpty());
+
+ Gesture startGesture(kGestureSwipe, ARBITRARY_GESTURE_TIME, ARBITRARY_GESTURE_TIME, /*dx=*/0,
+ /*dy=*/10);
+ Gesture continueGesture(kGestureSwipe, ARBITRARY_GESTURE_TIME, ARBITRARY_GESTURE_TIME, /*dx=*/0,
+ /*dy=*/5);
+ Gesture liftGesture(kGestureSwipeLift, ARBITRARY_GESTURE_TIME, ARBITRARY_GESTURE_TIME);
+
+ args += converter.handleGesture(ARBITRARY_TIME, READ_TIME, ARBITRARY_TIME, startGesture);
+ args += converter.handleGesture(ARBITRARY_TIME, READ_TIME, ARBITRARY_TIME, continueGesture);
+ args += converter.handleGesture(ARBITRARY_TIME, READ_TIME, ARBITRARY_TIME, liftGesture);
+ ASSERT_THAT(args, IsEmpty());
+
+ args = converter.setEnableSystemGestures(ARBITRARY_TIME, true);
+ ASSERT_THAT(args, IsEmpty());
+
+ args = converter.handleGesture(ARBITRARY_TIME, READ_TIME, ARBITRARY_TIME, startGesture);
+ ASSERT_THAT(args,
+ ElementsAre(VariantWith<NotifyMotionArgs>(
+ WithMotionAction(AMOTION_EVENT_ACTION_DOWN)),
+ VariantWith<NotifyMotionArgs>(WithMotionAction(
+ AMOTION_EVENT_ACTION_POINTER_DOWN |
+ 1 << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT)),
+ VariantWith<NotifyMotionArgs>(WithMotionAction(
+ AMOTION_EVENT_ACTION_POINTER_DOWN |
+ 2 << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT)),
+ VariantWith<NotifyMotionArgs>(
+ WithMotionAction(AMOTION_EVENT_ACTION_MOVE))));
+ ASSERT_THAT(args,
+ Each(VariantWith<NotifyMotionArgs>(
+ WithMotionClassification(MotionClassification::MULTI_FINGER_SWIPE))));
+}
+
+TEST_F(GestureConverterTest, DisablingSystemGestures_EndsOngoingMultiFingerSwipe) {
+ InputDeviceContext deviceContext(*mDevice, EVENTHUB_ID);
+ GestureConverter converter(*mReader->getContext(), deviceContext, DEVICE_ID);
+ converter.setDisplayId(ui::LogicalDisplayId::DEFAULT);
+
+ Gesture startGesture(kGestureSwipe, ARBITRARY_GESTURE_TIME, ARBITRARY_GESTURE_TIME, /*dx=*/0,
+ /*dy=*/10);
+ std::list<NotifyArgs> args;
+ args = converter.handleGesture(ARBITRARY_TIME, READ_TIME, ARBITRARY_TIME, startGesture);
+ ASSERT_FALSE(args.empty());
+
+ // Disabling system gestures should end the swipe early.
+ args = converter.setEnableSystemGestures(ARBITRARY_TIME, false);
+ ASSERT_THAT(args,
+ ElementsAre(VariantWith<NotifyMotionArgs>(
+ AllOf(WithMotionAction(
+ AMOTION_EVENT_ACTION_POINTER_UP |
+ 2 << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT),
+ WithGestureOffset(0, 0, EPSILON),
+ WithMotionClassification(
+ MotionClassification::MULTI_FINGER_SWIPE),
+ WithPointerCount(3u))),
+ VariantWith<NotifyMotionArgs>(
+ AllOf(WithMotionAction(
+ AMOTION_EVENT_ACTION_POINTER_UP |
+ 1 << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT),
+ WithGestureOffset(0, 0, EPSILON),
+ WithMotionClassification(
+ MotionClassification::MULTI_FINGER_SWIPE),
+ WithPointerCount(2u))),
+ VariantWith<NotifyMotionArgs>(
+ AllOf(WithMotionAction(AMOTION_EVENT_ACTION_UP),
+ WithGestureOffset(0, 0, EPSILON),
+ WithMotionClassification(
+ MotionClassification::MULTI_FINGER_SWIPE),
+ WithPointerCount(1u))),
+ VariantWith<NotifyMotionArgs>(
+ AllOf(WithMotionAction(AMOTION_EVENT_ACTION_HOVER_ENTER),
+ WithMotionClassification(MotionClassification::NONE)))));
+ ASSERT_THAT(args,
+ Each(VariantWith<NotifyMotionArgs>(
+ AllOf(WithToolType(ToolType::FINGER),
+ WithDisplayId(ui::LogicalDisplayId::DEFAULT)))));
+
+ // Further movement in the same swipe should be ignored.
+ Gesture continueGesture(kGestureSwipe, ARBITRARY_GESTURE_TIME, ARBITRARY_GESTURE_TIME, /*dx=*/0,
+ /*dy=*/5);
+ args = converter.handleGesture(ARBITRARY_TIME, READ_TIME, ARBITRARY_TIME, continueGesture);
+ ASSERT_THAT(args, IsEmpty());
+ Gesture liftGesture(kGestureSwipeLift, ARBITRARY_GESTURE_TIME, ARBITRARY_GESTURE_TIME);
+ args = converter.handleGesture(ARBITRARY_TIME, READ_TIME, ARBITRARY_TIME, liftGesture);
+ ASSERT_THAT(args, IsEmpty());
+
+ // But single-finger pointer motion should be reported.
+ Gesture moveGesture(kGestureMove, ARBITRARY_GESTURE_TIME, ARBITRARY_GESTURE_TIME, -5, 10);
+ args = converter.handleGesture(ARBITRARY_TIME, READ_TIME, ARBITRARY_TIME, moveGesture);
+ ASSERT_THAT(args,
+ ElementsAre(VariantWith<NotifyMotionArgs>(
+ AllOf(WithMotionAction(AMOTION_EVENT_ACTION_HOVER_MOVE),
+ WithRelativeMotion(-5, 10), WithButtonState(0)))));
+}
+
TEST_F(GestureConverterTest, Pinch_Inwards) {
input_flags::enable_touchpad_no_focus_change(true);
@@ -1272,6 +1374,27 @@
WithDisplayId(ui::LogicalDisplayId::DEFAULT)))));
}
+TEST_F(GestureConverterTest, ThreeFingerTap_TriggersShortcut) {
+ InputDeviceContext deviceContext(*mDevice, EVENTHUB_ID);
+ GestureConverter converter(*mReader->getContext(), deviceContext, DEVICE_ID);
+ converter.setDisplayId(ui::LogicalDisplayId::DEFAULT);
+ converter.setThreeFingerTapShortcutEnabled(true);
+
+ Gesture flingGesture(kGestureFling, ARBITRARY_GESTURE_TIME, ARBITRARY_GESTURE_TIME, /*vx=*/0,
+ /*vy=*/0, GESTURES_FLING_TAP_DOWN);
+ std::list<NotifyArgs> args =
+ converter.handleGesture(ARBITRARY_TIME, READ_TIME, ARBITRARY_TIME, flingGesture);
+ // We don't need to check args here, since it's covered by the FlingTapDown test.
+
+ Gesture tapGesture(kGestureButtonsChange, ARBITRARY_GESTURE_TIME, ARBITRARY_GESTURE_TIME,
+ /*down=*/GESTURES_BUTTON_MIDDLE, /*up=*/GESTURES_BUTTON_MIDDLE,
+ /*is_tap=*/true);
+ args = converter.handleGesture(ARBITRARY_TIME, READ_TIME, ARBITRARY_TIME, tapGesture);
+
+ ASSERT_TRUE(args.empty());
+ mFakePolicy->assertTouchpadThreeFingerTapNotified();
+}
+
TEST_F(GestureConverterTest, Click) {
// Click should produce button press/release events
InputDeviceContext deviceContext(*mDevice, EVENTHUB_ID);
diff --git a/services/inputflinger/tests/InputMapperTest.cpp b/services/inputflinger/tests/InputMapperTest.cpp
index 7dff144..8235c90 100644
--- a/services/inputflinger/tests/InputMapperTest.cpp
+++ b/services/inputflinger/tests/InputMapperTest.cpp
@@ -53,13 +53,13 @@
}
void InputMapperUnitTest::setupAxis(int axis, bool valid, int32_t min, int32_t max,
- int32_t resolution) {
+ int32_t resolution, int32_t flat, int32_t fuzz) {
EXPECT_CALL(mMockEventHub, getAbsoluteAxisInfo(EVENTHUB_ID, axis))
.WillRepeatedly(Return(valid ? std::optional<RawAbsoluteAxisInfo>{{
.minValue = min,
.maxValue = max,
- .flat = 0,
- .fuzz = 0,
+ .flat = flat,
+ .fuzz = fuzz,
.resolution = resolution,
}}
: std::nullopt));
@@ -100,9 +100,14 @@
std::list<NotifyArgs> InputMapperUnitTest::process(nsecs_t when, int32_t type, int32_t code,
int32_t value) {
+ return process(when, when, type, code, value);
+}
+
+std::list<NotifyArgs> InputMapperUnitTest::process(nsecs_t when, nsecs_t readTime, int32_t type,
+ int32_t code, int32_t value) {
RawEvent event;
event.when = when;
- event.readTime = when;
+ event.readTime = readTime;
event.deviceId = mMapper->getDeviceContext().getEventHubId();
event.type = type;
event.code = code;
diff --git a/services/inputflinger/tests/InputMapperTest.h b/services/inputflinger/tests/InputMapperTest.h
index fc27e4f..10ef6f1 100644
--- a/services/inputflinger/tests/InputMapperTest.h
+++ b/services/inputflinger/tests/InputMapperTest.h
@@ -43,7 +43,8 @@
virtual void SetUp() override { SetUpWithBus(0); }
virtual void SetUpWithBus(int bus);
- void setupAxis(int axis, bool valid, int32_t min, int32_t max, int32_t resolution);
+ void setupAxis(int axis, bool valid, int32_t min, int32_t max, int32_t resolution,
+ int32_t flat = 0, int32_t fuzz = 0);
void expectScanCodes(bool present, std::set<int> scanCodes);
@@ -55,6 +56,8 @@
std::list<NotifyArgs> process(int32_t type, int32_t code, int32_t value);
std::list<NotifyArgs> process(nsecs_t when, int32_t type, int32_t code, int32_t value);
+ std::list<NotifyArgs> process(nsecs_t when, nsecs_t readTime, int32_t type, int32_t code,
+ int32_t value);
InputDeviceIdentifier mIdentifier;
MockEventHubInterface mMockEventHub;
diff --git a/services/inputflinger/tests/InputReader_test.cpp b/services/inputflinger/tests/InputReader_test.cpp
index 19b738e..9d2256f 100644
--- a/services/inputflinger/tests/InputReader_test.cpp
+++ b/services/inputflinger/tests/InputReader_test.cpp
@@ -28,7 +28,6 @@
#include <MultiTouchInputMapper.h>
#include <NotifyArgsBuilders.h>
#include <PeripheralController.h>
-#include <SensorInputMapper.h>
#include <SingleTouchInputMapper.h>
#include <TestEventMatchers.h>
#include <TestInputListener.h>
@@ -36,6 +35,7 @@
#include <UinputDevice.h>
#include <android-base/thread_annotations.h>
#include <com_android_input_flags.h>
+#include <flag_macros.h>
#include <ftl/enum.h>
#include <gtest/gtest.h>
#include <ui/Rotation.h>
@@ -3031,1163 +3031,6 @@
mapper.assertProcessWasCalled();
}
-// --- SensorInputMapperTest ---
-
-class SensorInputMapperTest : public InputMapperTest {
-protected:
- static const int32_t ACCEL_RAW_MIN;
- static const int32_t ACCEL_RAW_MAX;
- static const int32_t ACCEL_RAW_FUZZ;
- static const int32_t ACCEL_RAW_FLAT;
- static const int32_t ACCEL_RAW_RESOLUTION;
-
- static const int32_t GYRO_RAW_MIN;
- static const int32_t GYRO_RAW_MAX;
- static const int32_t GYRO_RAW_FUZZ;
- static const int32_t GYRO_RAW_FLAT;
- static const int32_t GYRO_RAW_RESOLUTION;
-
- static const float GRAVITY_MS2_UNIT;
- static const float DEGREE_RADIAN_UNIT;
-
- void prepareAccelAxes();
- void prepareGyroAxes();
- void setAccelProperties();
- void setGyroProperties();
- void SetUp() override { InputMapperTest::SetUp(DEVICE_CLASSES | InputDeviceClass::SENSOR); }
-};
-
-const int32_t SensorInputMapperTest::ACCEL_RAW_MIN = -32768;
-const int32_t SensorInputMapperTest::ACCEL_RAW_MAX = 32768;
-const int32_t SensorInputMapperTest::ACCEL_RAW_FUZZ = 16;
-const int32_t SensorInputMapperTest::ACCEL_RAW_FLAT = 0;
-const int32_t SensorInputMapperTest::ACCEL_RAW_RESOLUTION = 8192;
-
-const int32_t SensorInputMapperTest::GYRO_RAW_MIN = -2097152;
-const int32_t SensorInputMapperTest::GYRO_RAW_MAX = 2097152;
-const int32_t SensorInputMapperTest::GYRO_RAW_FUZZ = 16;
-const int32_t SensorInputMapperTest::GYRO_RAW_FLAT = 0;
-const int32_t SensorInputMapperTest::GYRO_RAW_RESOLUTION = 1024;
-
-const float SensorInputMapperTest::GRAVITY_MS2_UNIT = 9.80665f;
-const float SensorInputMapperTest::DEGREE_RADIAN_UNIT = 0.0174533f;
-
-void SensorInputMapperTest::prepareAccelAxes() {
- mFakeEventHub->addAbsoluteAxis(EVENTHUB_ID, ABS_X, ACCEL_RAW_MIN, ACCEL_RAW_MAX, ACCEL_RAW_FUZZ,
- ACCEL_RAW_FLAT, ACCEL_RAW_RESOLUTION);
- mFakeEventHub->addAbsoluteAxis(EVENTHUB_ID, ABS_Y, ACCEL_RAW_MIN, ACCEL_RAW_MAX, ACCEL_RAW_FUZZ,
- ACCEL_RAW_FLAT, ACCEL_RAW_RESOLUTION);
- mFakeEventHub->addAbsoluteAxis(EVENTHUB_ID, ABS_Z, ACCEL_RAW_MIN, ACCEL_RAW_MAX, ACCEL_RAW_FUZZ,
- ACCEL_RAW_FLAT, ACCEL_RAW_RESOLUTION);
-}
-
-void SensorInputMapperTest::prepareGyroAxes() {
- mFakeEventHub->addAbsoluteAxis(EVENTHUB_ID, ABS_RX, GYRO_RAW_MIN, GYRO_RAW_MAX, GYRO_RAW_FUZZ,
- GYRO_RAW_FLAT, GYRO_RAW_RESOLUTION);
- mFakeEventHub->addAbsoluteAxis(EVENTHUB_ID, ABS_RY, GYRO_RAW_MIN, GYRO_RAW_MAX, GYRO_RAW_FUZZ,
- GYRO_RAW_FLAT, GYRO_RAW_RESOLUTION);
- mFakeEventHub->addAbsoluteAxis(EVENTHUB_ID, ABS_RZ, GYRO_RAW_MIN, GYRO_RAW_MAX, GYRO_RAW_FUZZ,
- GYRO_RAW_FLAT, GYRO_RAW_RESOLUTION);
-}
-
-void SensorInputMapperTest::setAccelProperties() {
- mFakeEventHub->addSensorAxis(EVENTHUB_ID, /* absCode */ 0, InputDeviceSensorType::ACCELEROMETER,
- /* sensorDataIndex */ 0);
- mFakeEventHub->addSensorAxis(EVENTHUB_ID, /* absCode */ 1, InputDeviceSensorType::ACCELEROMETER,
- /* sensorDataIndex */ 1);
- mFakeEventHub->addSensorAxis(EVENTHUB_ID, /* absCode */ 2, InputDeviceSensorType::ACCELEROMETER,
- /* sensorDataIndex */ 2);
- mFakeEventHub->setMscEvent(EVENTHUB_ID, MSC_TIMESTAMP);
- addConfigurationProperty("sensor.accelerometer.reportingMode", "0");
- addConfigurationProperty("sensor.accelerometer.maxDelay", "100000");
- addConfigurationProperty("sensor.accelerometer.minDelay", "5000");
- addConfigurationProperty("sensor.accelerometer.power", "1.5");
-}
-
-void SensorInputMapperTest::setGyroProperties() {
- mFakeEventHub->addSensorAxis(EVENTHUB_ID, /* absCode */ 3, InputDeviceSensorType::GYROSCOPE,
- /* sensorDataIndex */ 0);
- mFakeEventHub->addSensorAxis(EVENTHUB_ID, /* absCode */ 4, InputDeviceSensorType::GYROSCOPE,
- /* sensorDataIndex */ 1);
- mFakeEventHub->addSensorAxis(EVENTHUB_ID, /* absCode */ 5, InputDeviceSensorType::GYROSCOPE,
- /* sensorDataIndex */ 2);
- mFakeEventHub->setMscEvent(EVENTHUB_ID, MSC_TIMESTAMP);
- addConfigurationProperty("sensor.gyroscope.reportingMode", "0");
- addConfigurationProperty("sensor.gyroscope.maxDelay", "100000");
- addConfigurationProperty("sensor.gyroscope.minDelay", "5000");
- addConfigurationProperty("sensor.gyroscope.power", "0.8");
-}
-
-TEST_F(SensorInputMapperTest, GetSources) {
- SensorInputMapper& mapper = constructAndAddMapper<SensorInputMapper>();
-
- ASSERT_EQ(static_cast<uint32_t>(AINPUT_SOURCE_SENSOR), mapper.getSources());
-}
-
-TEST_F(SensorInputMapperTest, ProcessAccelerometerSensor) {
- setAccelProperties();
- prepareAccelAxes();
- SensorInputMapper& mapper = constructAndAddMapper<SensorInputMapper>();
-
- ASSERT_TRUE(mapper.enableSensor(InputDeviceSensorType::ACCELEROMETER,
- std::chrono::microseconds(10000),
- std::chrono::microseconds(0)));
- ASSERT_TRUE(mFakeEventHub->isDeviceEnabled(EVENTHUB_ID));
- process(mapper, ARBITRARY_TIME, READ_TIME, EV_ABS, ABS_X, 20000);
- process(mapper, ARBITRARY_TIME, READ_TIME, EV_ABS, ABS_Y, -20000);
- process(mapper, ARBITRARY_TIME, READ_TIME, EV_ABS, ABS_Z, 40000);
- process(mapper, ARBITRARY_TIME, READ_TIME, EV_MSC, MSC_TIMESTAMP, 1000);
- process(mapper, ARBITRARY_TIME, READ_TIME, EV_SYN, SYN_REPORT, 0);
-
- NotifySensorArgs args;
- std::vector<float> values = {20000.0f / ACCEL_RAW_RESOLUTION * GRAVITY_MS2_UNIT,
- -20000.0f / ACCEL_RAW_RESOLUTION * GRAVITY_MS2_UNIT,
- 40000.0f / ACCEL_RAW_RESOLUTION * GRAVITY_MS2_UNIT};
-
- ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifySensorWasCalled(&args));
- ASSERT_EQ(args.source, AINPUT_SOURCE_SENSOR);
- ASSERT_EQ(args.deviceId, DEVICE_ID);
- ASSERT_EQ(args.sensorType, InputDeviceSensorType::ACCELEROMETER);
- ASSERT_EQ(args.accuracy, InputDeviceSensorAccuracy::ACCURACY_HIGH);
- ASSERT_EQ(args.hwTimestamp, ARBITRARY_TIME);
- ASSERT_EQ(args.values, values);
- mapper.flushSensor(InputDeviceSensorType::ACCELEROMETER);
-}
-
-TEST_F(SensorInputMapperTest, ProcessGyroscopeSensor) {
- setGyroProperties();
- prepareGyroAxes();
- SensorInputMapper& mapper = constructAndAddMapper<SensorInputMapper>();
-
- ASSERT_TRUE(mapper.enableSensor(InputDeviceSensorType::GYROSCOPE,
- std::chrono::microseconds(10000),
- std::chrono::microseconds(0)));
- ASSERT_TRUE(mFakeEventHub->isDeviceEnabled(EVENTHUB_ID));
- process(mapper, ARBITRARY_TIME, READ_TIME, EV_ABS, ABS_RX, 20000);
- process(mapper, ARBITRARY_TIME, READ_TIME, EV_ABS, ABS_RY, -20000);
- process(mapper, ARBITRARY_TIME, READ_TIME, EV_ABS, ABS_RZ, 40000);
- process(mapper, ARBITRARY_TIME, READ_TIME, EV_MSC, MSC_TIMESTAMP, 1000);
- process(mapper, ARBITRARY_TIME, READ_TIME, EV_SYN, SYN_REPORT, 0);
-
- NotifySensorArgs args;
- std::vector<float> values = {20000.0f / GYRO_RAW_RESOLUTION * DEGREE_RADIAN_UNIT,
- -20000.0f / GYRO_RAW_RESOLUTION * DEGREE_RADIAN_UNIT,
- 40000.0f / GYRO_RAW_RESOLUTION * DEGREE_RADIAN_UNIT};
-
- ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifySensorWasCalled(&args));
- ASSERT_EQ(args.source, AINPUT_SOURCE_SENSOR);
- ASSERT_EQ(args.deviceId, DEVICE_ID);
- ASSERT_EQ(args.sensorType, InputDeviceSensorType::GYROSCOPE);
- ASSERT_EQ(args.accuracy, InputDeviceSensorAccuracy::ACCURACY_HIGH);
- ASSERT_EQ(args.hwTimestamp, ARBITRARY_TIME);
- ASSERT_EQ(args.values, values);
- mapper.flushSensor(InputDeviceSensorType::GYROSCOPE);
-}
-
-// --- KeyboardInputMapperTest ---
-
-class KeyboardInputMapperTest : public InputMapperTest {
-protected:
- void SetUp() override {
- InputMapperTest::SetUp(DEVICE_CLASSES | InputDeviceClass::KEYBOARD |
- InputDeviceClass::ALPHAKEY);
- }
- const std::string UNIQUE_ID = "local:0";
- const KeyboardLayoutInfo DEVICE_KEYBOARD_LAYOUT_INFO = KeyboardLayoutInfo("en-US", "qwerty");
- void prepareDisplay(ui::Rotation orientation);
-
- void testDPadKeyRotation(KeyboardInputMapper& mapper, int32_t originalScanCode,
- int32_t originalKeyCode, int32_t rotatedKeyCode,
- ui::LogicalDisplayId displayId = ui::LogicalDisplayId::INVALID);
-};
-
-/* Similar to setDisplayInfoAndReconfigure, but pre-populates all parameters except for the
- * orientation.
- */
-void KeyboardInputMapperTest::prepareDisplay(ui::Rotation orientation) {
- setDisplayInfoAndReconfigure(DISPLAY_ID, DISPLAY_WIDTH, DISPLAY_HEIGHT, orientation, UNIQUE_ID,
- NO_PORT, ViewportType::INTERNAL);
-}
-
-void KeyboardInputMapperTest::testDPadKeyRotation(KeyboardInputMapper& mapper,
- int32_t originalScanCode, int32_t originalKeyCode,
- int32_t rotatedKeyCode,
- ui::LogicalDisplayId displayId) {
- NotifyKeyArgs args;
-
- process(mapper, ARBITRARY_TIME, READ_TIME, EV_KEY, originalScanCode, 1);
- ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyKeyWasCalled(&args));
- ASSERT_EQ(AKEY_EVENT_ACTION_DOWN, args.action);
- ASSERT_EQ(originalScanCode, args.scanCode);
- ASSERT_EQ(rotatedKeyCode, args.keyCode);
- ASSERT_EQ(displayId, args.displayId);
-
- process(mapper, ARBITRARY_TIME, READ_TIME, EV_KEY, originalScanCode, 0);
- ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyKeyWasCalled(&args));
- ASSERT_EQ(AKEY_EVENT_ACTION_UP, args.action);
- ASSERT_EQ(originalScanCode, args.scanCode);
- ASSERT_EQ(rotatedKeyCode, args.keyCode);
- ASSERT_EQ(displayId, args.displayId);
-}
-
-TEST_F(KeyboardInputMapperTest, GetSources) {
- KeyboardInputMapper& mapper =
- constructAndAddMapper<KeyboardInputMapper>(AINPUT_SOURCE_KEYBOARD);
-
- ASSERT_EQ(AINPUT_SOURCE_KEYBOARD, mapper.getSources());
-}
-
-TEST_F(KeyboardInputMapperTest, Process_SimpleKeyPress) {
- const int32_t USAGE_A = 0x070004;
- const int32_t USAGE_UNKNOWN = 0x07ffff;
- mFakeEventHub->addKey(EVENTHUB_ID, KEY_HOME, 0, AKEYCODE_HOME, POLICY_FLAG_WAKE);
- mFakeEventHub->addKey(EVENTHUB_ID, 0, USAGE_A, AKEYCODE_A, POLICY_FLAG_WAKE);
- mFakeEventHub->addKey(EVENTHUB_ID, 0, KEY_NUMLOCK, AKEYCODE_NUM_LOCK, POLICY_FLAG_WAKE);
- mFakeEventHub->addKey(EVENTHUB_ID, 0, KEY_CAPSLOCK, AKEYCODE_CAPS_LOCK, POLICY_FLAG_WAKE);
- mFakeEventHub->addKey(EVENTHUB_ID, 0, KEY_SCROLLLOCK, AKEYCODE_SCROLL_LOCK, POLICY_FLAG_WAKE);
-
- KeyboardInputMapper& mapper =
- constructAndAddMapper<KeyboardInputMapper>(AINPUT_SOURCE_KEYBOARD);
- // Initial metastate is AMETA_NONE.
- ASSERT_EQ(AMETA_NONE, mapper.getMetaState());
-
- // Key down by scan code.
- process(mapper, ARBITRARY_TIME, READ_TIME, EV_KEY, KEY_HOME, 1);
- NotifyKeyArgs args;
- ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyKeyWasCalled(&args));
- ASSERT_EQ(DEVICE_ID, args.deviceId);
- ASSERT_EQ(AINPUT_SOURCE_KEYBOARD, args.source);
- ASSERT_EQ(ARBITRARY_TIME, args.eventTime);
- ASSERT_EQ(AKEY_EVENT_ACTION_DOWN, args.action);
- ASSERT_EQ(AKEYCODE_HOME, args.keyCode);
- ASSERT_EQ(KEY_HOME, args.scanCode);
- ASSERT_EQ(AMETA_NONE, args.metaState);
- ASSERT_EQ(AKEY_EVENT_FLAG_FROM_SYSTEM, args.flags);
- ASSERT_EQ(POLICY_FLAG_WAKE, args.policyFlags);
- ASSERT_EQ(ARBITRARY_TIME, args.downTime);
-
- // Key up by scan code.
- process(mapper, ARBITRARY_TIME + 1, READ_TIME, EV_KEY, KEY_HOME, 0);
- ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyKeyWasCalled(&args));
- ASSERT_EQ(DEVICE_ID, args.deviceId);
- ASSERT_EQ(AINPUT_SOURCE_KEYBOARD, args.source);
- ASSERT_EQ(ARBITRARY_TIME + 1, args.eventTime);
- ASSERT_EQ(AKEY_EVENT_ACTION_UP, args.action);
- ASSERT_EQ(AKEYCODE_HOME, args.keyCode);
- ASSERT_EQ(KEY_HOME, args.scanCode);
- ASSERT_EQ(AMETA_NONE, args.metaState);
- ASSERT_EQ(AKEY_EVENT_FLAG_FROM_SYSTEM, args.flags);
- ASSERT_EQ(POLICY_FLAG_WAKE, args.policyFlags);
- ASSERT_EQ(ARBITRARY_TIME, args.downTime);
-
- // Key down by usage code.
- process(mapper, ARBITRARY_TIME, READ_TIME, EV_MSC, MSC_SCAN, USAGE_A);
- process(mapper, ARBITRARY_TIME, READ_TIME, EV_KEY, 0, 1);
- ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyKeyWasCalled(&args));
- ASSERT_EQ(DEVICE_ID, args.deviceId);
- ASSERT_EQ(AINPUT_SOURCE_KEYBOARD, args.source);
- ASSERT_EQ(ARBITRARY_TIME, args.eventTime);
- ASSERT_EQ(AKEY_EVENT_ACTION_DOWN, args.action);
- ASSERT_EQ(AKEYCODE_A, args.keyCode);
- ASSERT_EQ(0, args.scanCode);
- ASSERT_EQ(AMETA_NONE, args.metaState);
- ASSERT_EQ(AKEY_EVENT_FLAG_FROM_SYSTEM, args.flags);
- ASSERT_EQ(POLICY_FLAG_WAKE, args.policyFlags);
- ASSERT_EQ(ARBITRARY_TIME, args.downTime);
-
- // Key up by usage code.
- process(mapper, ARBITRARY_TIME, READ_TIME, EV_MSC, MSC_SCAN, USAGE_A);
- process(mapper, ARBITRARY_TIME + 1, READ_TIME, EV_KEY, 0, 0);
- ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyKeyWasCalled(&args));
- ASSERT_EQ(DEVICE_ID, args.deviceId);
- ASSERT_EQ(AINPUT_SOURCE_KEYBOARD, args.source);
- ASSERT_EQ(ARBITRARY_TIME + 1, args.eventTime);
- ASSERT_EQ(AKEY_EVENT_ACTION_UP, args.action);
- ASSERT_EQ(AKEYCODE_A, args.keyCode);
- ASSERT_EQ(0, args.scanCode);
- ASSERT_EQ(AMETA_NONE, args.metaState);
- ASSERT_EQ(AKEY_EVENT_FLAG_FROM_SYSTEM, args.flags);
- ASSERT_EQ(POLICY_FLAG_WAKE, args.policyFlags);
- ASSERT_EQ(ARBITRARY_TIME, args.downTime);
-
- // Key down with unknown scan code or usage code.
- process(mapper, ARBITRARY_TIME, READ_TIME, EV_MSC, MSC_SCAN, USAGE_UNKNOWN);
- process(mapper, ARBITRARY_TIME, READ_TIME, EV_KEY, KEY_UNKNOWN, 1);
- ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyKeyWasCalled(&args));
- ASSERT_EQ(DEVICE_ID, args.deviceId);
- ASSERT_EQ(AINPUT_SOURCE_KEYBOARD, args.source);
- ASSERT_EQ(ARBITRARY_TIME, args.eventTime);
- ASSERT_EQ(AKEY_EVENT_ACTION_DOWN, args.action);
- ASSERT_EQ(0, args.keyCode);
- ASSERT_EQ(KEY_UNKNOWN, args.scanCode);
- ASSERT_EQ(AMETA_NONE, args.metaState);
- ASSERT_EQ(AKEY_EVENT_FLAG_FROM_SYSTEM, args.flags);
- ASSERT_EQ(0U, args.policyFlags);
- ASSERT_EQ(ARBITRARY_TIME, args.downTime);
-
- // Key up with unknown scan code or usage code.
- process(mapper, ARBITRARY_TIME, READ_TIME, EV_MSC, MSC_SCAN, USAGE_UNKNOWN);
- process(mapper, ARBITRARY_TIME + 1, READ_TIME, EV_KEY, KEY_UNKNOWN, 0);
- ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyKeyWasCalled(&args));
- ASSERT_EQ(DEVICE_ID, args.deviceId);
- ASSERT_EQ(AINPUT_SOURCE_KEYBOARD, args.source);
- ASSERT_EQ(ARBITRARY_TIME + 1, args.eventTime);
- ASSERT_EQ(AKEY_EVENT_ACTION_UP, args.action);
- ASSERT_EQ(0, args.keyCode);
- ASSERT_EQ(KEY_UNKNOWN, args.scanCode);
- ASSERT_EQ(AMETA_NONE, args.metaState);
- ASSERT_EQ(AKEY_EVENT_FLAG_FROM_SYSTEM, args.flags);
- ASSERT_EQ(0U, args.policyFlags);
- ASSERT_EQ(ARBITRARY_TIME, args.downTime);
-}
-
-TEST_F(KeyboardInputMapperTest, Process_KeyRemapping) {
- mFakeEventHub->addKey(EVENTHUB_ID, KEY_A, 0, AKEYCODE_A, 0);
- mFakeEventHub->addKey(EVENTHUB_ID, KEY_B, 0, AKEYCODE_B, 0);
-
- KeyboardInputMapper& mapper =
- constructAndAddMapper<KeyboardInputMapper>(AINPUT_SOURCE_KEYBOARD);
-
- mFakeEventHub->setKeyRemapping(EVENTHUB_ID, {{AKEYCODE_A, AKEYCODE_B}});
- // Key down by scan code.
- process(mapper, ARBITRARY_TIME, READ_TIME, EV_KEY, KEY_A, 1);
- NotifyKeyArgs args;
- ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyKeyWasCalled(&args));
- ASSERT_EQ(AKEYCODE_B, args.keyCode);
-
- // Key up by scan code.
- process(mapper, ARBITRARY_TIME + 1, READ_TIME, EV_KEY, KEY_A, 0);
- ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyKeyWasCalled(&args));
- ASSERT_EQ(AKEYCODE_B, args.keyCode);
-}
-
-/**
- * Ensure that the readTime is set to the time when the EV_KEY is received.
- */
-TEST_F(KeyboardInputMapperTest, Process_SendsReadTime) {
- mFakeEventHub->addKey(EVENTHUB_ID, KEY_HOME, 0, AKEYCODE_HOME, POLICY_FLAG_WAKE);
-
- KeyboardInputMapper& mapper =
- constructAndAddMapper<KeyboardInputMapper>(AINPUT_SOURCE_KEYBOARD);
- NotifyKeyArgs args;
-
- // Key down
- process(mapper, ARBITRARY_TIME, /*readTime=*/12, EV_KEY, KEY_HOME, 1);
- ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyKeyWasCalled(&args));
- ASSERT_EQ(12, args.readTime);
-
- // Key up
- process(mapper, ARBITRARY_TIME, /*readTime=*/15, EV_KEY, KEY_HOME, 1);
- ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyKeyWasCalled(&args));
- ASSERT_EQ(15, args.readTime);
-}
-
-TEST_F(KeyboardInputMapperTest, Process_ShouldUpdateMetaState) {
- mFakeEventHub->addKey(EVENTHUB_ID, KEY_LEFTSHIFT, 0, AKEYCODE_SHIFT_LEFT, 0);
- mFakeEventHub->addKey(EVENTHUB_ID, KEY_A, 0, AKEYCODE_A, 0);
- mFakeEventHub->addKey(EVENTHUB_ID, 0, KEY_NUMLOCK, AKEYCODE_NUM_LOCK, 0);
- mFakeEventHub->addKey(EVENTHUB_ID, 0, KEY_CAPSLOCK, AKEYCODE_CAPS_LOCK, 0);
- mFakeEventHub->addKey(EVENTHUB_ID, 0, KEY_SCROLLLOCK, AKEYCODE_SCROLL_LOCK, 0);
-
- KeyboardInputMapper& mapper =
- constructAndAddMapper<KeyboardInputMapper>(AINPUT_SOURCE_KEYBOARD);
-
- // Initial metastate is AMETA_NONE.
- ASSERT_EQ(AMETA_NONE, mapper.getMetaState());
-
- // Metakey down.
- process(mapper, ARBITRARY_TIME, READ_TIME, EV_KEY, KEY_LEFTSHIFT, 1);
- NotifyKeyArgs args;
- ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyKeyWasCalled(&args));
- ASSERT_EQ(AMETA_SHIFT_LEFT_ON | AMETA_SHIFT_ON, args.metaState);
- ASSERT_EQ(AMETA_SHIFT_LEFT_ON | AMETA_SHIFT_ON, mapper.getMetaState());
- ASSERT_NO_FATAL_FAILURE(mReader->getContext()->assertUpdateGlobalMetaStateWasCalled());
-
- // Key down.
- process(mapper, ARBITRARY_TIME + 1, READ_TIME, EV_KEY, KEY_A, 1);
- ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyKeyWasCalled(&args));
- ASSERT_EQ(AMETA_SHIFT_LEFT_ON | AMETA_SHIFT_ON, args.metaState);
- ASSERT_EQ(AMETA_SHIFT_LEFT_ON | AMETA_SHIFT_ON, mapper.getMetaState());
-
- // Key up.
- process(mapper, ARBITRARY_TIME + 2, READ_TIME, EV_KEY, KEY_A, 0);
- ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyKeyWasCalled(&args));
- ASSERT_EQ(AMETA_SHIFT_LEFT_ON | AMETA_SHIFT_ON, args.metaState);
- ASSERT_EQ(AMETA_SHIFT_LEFT_ON | AMETA_SHIFT_ON, mapper.getMetaState());
-
- // Metakey up.
- process(mapper, ARBITRARY_TIME + 3, READ_TIME, EV_KEY, KEY_LEFTSHIFT, 0);
- ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyKeyWasCalled(&args));
- ASSERT_EQ(AMETA_NONE, args.metaState);
- ASSERT_EQ(AMETA_NONE, mapper.getMetaState());
- ASSERT_NO_FATAL_FAILURE(mReader->getContext()->assertUpdateGlobalMetaStateWasCalled());
-}
-
-TEST_F(KeyboardInputMapperTest, Process_WhenNotOrientationAware_ShouldNotRotateDPad) {
- mFakeEventHub->addKey(EVENTHUB_ID, KEY_UP, 0, AKEYCODE_DPAD_UP, 0);
- mFakeEventHub->addKey(EVENTHUB_ID, KEY_RIGHT, 0, AKEYCODE_DPAD_RIGHT, 0);
- mFakeEventHub->addKey(EVENTHUB_ID, KEY_DOWN, 0, AKEYCODE_DPAD_DOWN, 0);
- mFakeEventHub->addKey(EVENTHUB_ID, KEY_LEFT, 0, AKEYCODE_DPAD_LEFT, 0);
-
- KeyboardInputMapper& mapper =
- constructAndAddMapper<KeyboardInputMapper>(AINPUT_SOURCE_KEYBOARD);
-
- prepareDisplay(ui::ROTATION_90);
- ASSERT_NO_FATAL_FAILURE(testDPadKeyRotation(mapper,
- KEY_UP, AKEYCODE_DPAD_UP, AKEYCODE_DPAD_UP));
- ASSERT_NO_FATAL_FAILURE(testDPadKeyRotation(mapper,
- KEY_RIGHT, AKEYCODE_DPAD_RIGHT, AKEYCODE_DPAD_RIGHT));
- ASSERT_NO_FATAL_FAILURE(testDPadKeyRotation(mapper,
- KEY_DOWN, AKEYCODE_DPAD_DOWN, AKEYCODE_DPAD_DOWN));
- ASSERT_NO_FATAL_FAILURE(testDPadKeyRotation(mapper,
- KEY_LEFT, AKEYCODE_DPAD_LEFT, AKEYCODE_DPAD_LEFT));
-}
-
-TEST_F(KeyboardInputMapperTest, Process_WhenOrientationAware_ShouldRotateDPad) {
- mFakeEventHub->addKey(EVENTHUB_ID, KEY_UP, 0, AKEYCODE_DPAD_UP, 0);
- mFakeEventHub->addKey(EVENTHUB_ID, KEY_RIGHT, 0, AKEYCODE_DPAD_RIGHT, 0);
- mFakeEventHub->addKey(EVENTHUB_ID, KEY_DOWN, 0, AKEYCODE_DPAD_DOWN, 0);
- mFakeEventHub->addKey(EVENTHUB_ID, KEY_LEFT, 0, AKEYCODE_DPAD_LEFT, 0);
-
- addConfigurationProperty("keyboard.orientationAware", "1");
- KeyboardInputMapper& mapper =
- constructAndAddMapper<KeyboardInputMapper>(AINPUT_SOURCE_KEYBOARD);
-
- prepareDisplay(ui::ROTATION_0);
- ASSERT_NO_FATAL_FAILURE(
- testDPadKeyRotation(mapper, KEY_UP, AKEYCODE_DPAD_UP, AKEYCODE_DPAD_UP, DISPLAY_ID));
- ASSERT_NO_FATAL_FAILURE(testDPadKeyRotation(mapper, KEY_RIGHT, AKEYCODE_DPAD_RIGHT,
- AKEYCODE_DPAD_RIGHT, DISPLAY_ID));
- ASSERT_NO_FATAL_FAILURE(testDPadKeyRotation(mapper, KEY_DOWN, AKEYCODE_DPAD_DOWN,
- AKEYCODE_DPAD_DOWN, DISPLAY_ID));
- ASSERT_NO_FATAL_FAILURE(testDPadKeyRotation(mapper, KEY_LEFT, AKEYCODE_DPAD_LEFT,
- AKEYCODE_DPAD_LEFT, DISPLAY_ID));
-
- clearViewports();
- prepareDisplay(ui::ROTATION_90);
- ASSERT_NO_FATAL_FAILURE(
- testDPadKeyRotation(mapper, KEY_UP, AKEYCODE_DPAD_UP, AKEYCODE_DPAD_LEFT, DISPLAY_ID));
- ASSERT_NO_FATAL_FAILURE(testDPadKeyRotation(mapper, KEY_RIGHT, AKEYCODE_DPAD_RIGHT,
- AKEYCODE_DPAD_UP, DISPLAY_ID));
- ASSERT_NO_FATAL_FAILURE(testDPadKeyRotation(mapper, KEY_DOWN, AKEYCODE_DPAD_DOWN,
- AKEYCODE_DPAD_RIGHT, DISPLAY_ID));
- ASSERT_NO_FATAL_FAILURE(testDPadKeyRotation(mapper, KEY_LEFT, AKEYCODE_DPAD_LEFT,
- AKEYCODE_DPAD_DOWN, DISPLAY_ID));
-
- clearViewports();
- prepareDisplay(ui::ROTATION_180);
- ASSERT_NO_FATAL_FAILURE(
- testDPadKeyRotation(mapper, KEY_UP, AKEYCODE_DPAD_UP, AKEYCODE_DPAD_DOWN, DISPLAY_ID));
- ASSERT_NO_FATAL_FAILURE(testDPadKeyRotation(mapper, KEY_RIGHT, AKEYCODE_DPAD_RIGHT,
- AKEYCODE_DPAD_LEFT, DISPLAY_ID));
- ASSERT_NO_FATAL_FAILURE(testDPadKeyRotation(mapper, KEY_DOWN, AKEYCODE_DPAD_DOWN,
- AKEYCODE_DPAD_UP, DISPLAY_ID));
- ASSERT_NO_FATAL_FAILURE(testDPadKeyRotation(mapper, KEY_LEFT, AKEYCODE_DPAD_LEFT,
- AKEYCODE_DPAD_RIGHT, DISPLAY_ID));
-
- clearViewports();
- prepareDisplay(ui::ROTATION_270);
- ASSERT_NO_FATAL_FAILURE(
- testDPadKeyRotation(mapper, KEY_UP, AKEYCODE_DPAD_UP, AKEYCODE_DPAD_RIGHT, DISPLAY_ID));
- ASSERT_NO_FATAL_FAILURE(testDPadKeyRotation(mapper, KEY_RIGHT, AKEYCODE_DPAD_RIGHT,
- AKEYCODE_DPAD_DOWN, DISPLAY_ID));
- ASSERT_NO_FATAL_FAILURE(testDPadKeyRotation(mapper, KEY_DOWN, AKEYCODE_DPAD_DOWN,
- AKEYCODE_DPAD_LEFT, DISPLAY_ID));
- ASSERT_NO_FATAL_FAILURE(testDPadKeyRotation(mapper, KEY_LEFT, AKEYCODE_DPAD_LEFT,
- AKEYCODE_DPAD_UP, DISPLAY_ID));
-
- // Special case: if orientation changes while key is down, we still emit the same keycode
- // in the key up as we did in the key down.
- NotifyKeyArgs args;
- clearViewports();
- prepareDisplay(ui::ROTATION_270);
- process(mapper, ARBITRARY_TIME, READ_TIME, EV_KEY, KEY_UP, 1);
- ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyKeyWasCalled(&args));
- ASSERT_EQ(AKEY_EVENT_ACTION_DOWN, args.action);
- ASSERT_EQ(KEY_UP, args.scanCode);
- ASSERT_EQ(AKEYCODE_DPAD_RIGHT, args.keyCode);
-
- clearViewports();
- prepareDisplay(ui::ROTATION_180);
- process(mapper, ARBITRARY_TIME, READ_TIME, EV_KEY, KEY_UP, 0);
- ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyKeyWasCalled(&args));
- ASSERT_EQ(AKEY_EVENT_ACTION_UP, args.action);
- ASSERT_EQ(KEY_UP, args.scanCode);
- ASSERT_EQ(AKEYCODE_DPAD_RIGHT, args.keyCode);
-}
-
-TEST_F(KeyboardInputMapperTest, DisplayIdConfigurationChange_NotOrientationAware) {
- // If the keyboard is not orientation aware,
- // key events should not be associated with a specific display id
- mFakeEventHub->addKey(EVENTHUB_ID, KEY_UP, 0, AKEYCODE_DPAD_UP, 0);
-
- KeyboardInputMapper& mapper =
- constructAndAddMapper<KeyboardInputMapper>(AINPUT_SOURCE_KEYBOARD);
- NotifyKeyArgs args;
-
- // Display id should be LogicalDisplayId::INVALID without any display configuration.
- process(mapper, ARBITRARY_TIME, READ_TIME, EV_KEY, KEY_UP, 1);
- ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyKeyWasCalled(&args));
- process(mapper, ARBITRARY_TIME, READ_TIME, EV_KEY, KEY_UP, 0);
- ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyKeyWasCalled(&args));
- ASSERT_EQ(ui::LogicalDisplayId::INVALID, args.displayId);
-
- prepareDisplay(ui::ROTATION_0);
- process(mapper, ARBITRARY_TIME, READ_TIME, EV_KEY, KEY_UP, 1);
- ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyKeyWasCalled(&args));
- process(mapper, ARBITRARY_TIME, READ_TIME, EV_KEY, KEY_UP, 0);
- ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyKeyWasCalled(&args));
- ASSERT_EQ(ui::LogicalDisplayId::INVALID, args.displayId);
-}
-
-TEST_F(KeyboardInputMapperTest, DisplayIdConfigurationChange_OrientationAware) {
- // If the keyboard is orientation aware,
- // key events should be associated with the internal viewport
- mFakeEventHub->addKey(EVENTHUB_ID, KEY_UP, 0, AKEYCODE_DPAD_UP, 0);
-
- addConfigurationProperty("keyboard.orientationAware", "1");
- KeyboardInputMapper& mapper =
- constructAndAddMapper<KeyboardInputMapper>(AINPUT_SOURCE_KEYBOARD);
- NotifyKeyArgs args;
-
- // Display id should be LogicalDisplayId::INVALID without any display configuration.
- // ^--- already checked by the previous test
-
- setDisplayInfoAndReconfigure(DISPLAY_ID, DISPLAY_WIDTH, DISPLAY_HEIGHT, ui::ROTATION_0,
- UNIQUE_ID, NO_PORT, ViewportType::INTERNAL);
- process(mapper, ARBITRARY_TIME, READ_TIME, EV_KEY, KEY_UP, 1);
- ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyKeyWasCalled(&args));
- process(mapper, ARBITRARY_TIME, READ_TIME, EV_KEY, KEY_UP, 0);
- ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyKeyWasCalled(&args));
- ASSERT_EQ(DISPLAY_ID, args.displayId);
-
- constexpr ui::LogicalDisplayId newDisplayId = ui::LogicalDisplayId{2};
- clearViewports();
- setDisplayInfoAndReconfigure(newDisplayId, DISPLAY_WIDTH, DISPLAY_HEIGHT, ui::ROTATION_0,
- UNIQUE_ID, NO_PORT, ViewportType::INTERNAL);
- process(mapper, ARBITRARY_TIME, READ_TIME, EV_KEY, KEY_UP, 1);
- ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyKeyWasCalled(&args));
- process(mapper, ARBITRARY_TIME, READ_TIME, EV_KEY, KEY_UP, 0);
- ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyKeyWasCalled(&args));
- ASSERT_EQ(newDisplayId, args.displayId);
-}
-
-TEST_F(KeyboardInputMapperTest, GetKeyCodeState) {
- KeyboardInputMapper& mapper =
- constructAndAddMapper<KeyboardInputMapper>(AINPUT_SOURCE_KEYBOARD);
-
- mFakeEventHub->setKeyCodeState(EVENTHUB_ID, AKEYCODE_A, 1);
- ASSERT_EQ(1, mapper.getKeyCodeState(AINPUT_SOURCE_ANY, AKEYCODE_A));
-
- mFakeEventHub->setKeyCodeState(EVENTHUB_ID, AKEYCODE_A, 0);
- ASSERT_EQ(0, mapper.getKeyCodeState(AINPUT_SOURCE_ANY, AKEYCODE_A));
-}
-
-TEST_F(KeyboardInputMapperTest, GetKeyCodeForKeyLocation) {
- KeyboardInputMapper& mapper =
- constructAndAddMapper<KeyboardInputMapper>(AINPUT_SOURCE_KEYBOARD);
-
- mFakeEventHub->addKeyCodeMapping(EVENTHUB_ID, AKEYCODE_Y, AKEYCODE_Z);
- ASSERT_EQ(AKEYCODE_Z, mapper.getKeyCodeForKeyLocation(AKEYCODE_Y))
- << "If a mapping is available, the result is equal to the mapping";
-
- ASSERT_EQ(AKEYCODE_A, mapper.getKeyCodeForKeyLocation(AKEYCODE_A))
- << "If no mapping is available, the result is the key location";
-}
-
-TEST_F(KeyboardInputMapperTest, GetScanCodeState) {
- KeyboardInputMapper& mapper =
- constructAndAddMapper<KeyboardInputMapper>(AINPUT_SOURCE_KEYBOARD);
-
- mFakeEventHub->setScanCodeState(EVENTHUB_ID, KEY_A, 1);
- ASSERT_EQ(1, mapper.getScanCodeState(AINPUT_SOURCE_ANY, KEY_A));
-
- mFakeEventHub->setScanCodeState(EVENTHUB_ID, KEY_A, 0);
- ASSERT_EQ(0, mapper.getScanCodeState(AINPUT_SOURCE_ANY, KEY_A));
-}
-
-TEST_F(KeyboardInputMapperTest, MarkSupportedKeyCodes) {
- KeyboardInputMapper& mapper =
- constructAndAddMapper<KeyboardInputMapper>(AINPUT_SOURCE_KEYBOARD);
-
- mFakeEventHub->addKey(EVENTHUB_ID, KEY_A, 0, AKEYCODE_A, 0);
-
- uint8_t flags[2] = { 0, 0 };
- ASSERT_TRUE(mapper.markSupportedKeyCodes(AINPUT_SOURCE_ANY, {AKEYCODE_A, AKEYCODE_B}, flags));
- ASSERT_TRUE(flags[0]);
- ASSERT_FALSE(flags[1]);
-}
-
-TEST_F(KeyboardInputMapperTest, Process_LockedKeysShouldToggleMetaStateAndLeds) {
- mFakeEventHub->addLed(EVENTHUB_ID, LED_CAPSL, true /*initially on*/);
- mFakeEventHub->addLed(EVENTHUB_ID, LED_NUML, false /*initially off*/);
- mFakeEventHub->addLed(EVENTHUB_ID, LED_SCROLLL, false /*initially off*/);
- mFakeEventHub->addKey(EVENTHUB_ID, KEY_CAPSLOCK, 0, AKEYCODE_CAPS_LOCK, 0);
- mFakeEventHub->addKey(EVENTHUB_ID, KEY_NUMLOCK, 0, AKEYCODE_NUM_LOCK, 0);
- mFakeEventHub->addKey(EVENTHUB_ID, KEY_SCROLLLOCK, 0, AKEYCODE_SCROLL_LOCK, 0);
-
- KeyboardInputMapper& mapper =
- constructAndAddMapper<KeyboardInputMapper>(AINPUT_SOURCE_KEYBOARD);
- // Initial metastate is AMETA_NONE.
- ASSERT_EQ(AMETA_NONE, mapper.getMetaState());
-
- // Initialization should have turned all of the lights off.
- ASSERT_FALSE(mFakeEventHub->getLedState(EVENTHUB_ID, LED_CAPSL));
- ASSERT_FALSE(mFakeEventHub->getLedState(EVENTHUB_ID, LED_NUML));
- ASSERT_FALSE(mFakeEventHub->getLedState(EVENTHUB_ID, LED_SCROLLL));
-
- // Toggle caps lock on.
- process(mapper, ARBITRARY_TIME, READ_TIME, EV_KEY, KEY_CAPSLOCK, 1);
- process(mapper, ARBITRARY_TIME, READ_TIME, EV_KEY, KEY_CAPSLOCK, 0);
- ASSERT_TRUE(mFakeEventHub->getLedState(EVENTHUB_ID, LED_CAPSL));
- ASSERT_FALSE(mFakeEventHub->getLedState(EVENTHUB_ID, LED_NUML));
- ASSERT_FALSE(mFakeEventHub->getLedState(EVENTHUB_ID, LED_SCROLLL));
- ASSERT_EQ(AMETA_CAPS_LOCK_ON, mapper.getMetaState());
-
- // Toggle num lock on.
- process(mapper, ARBITRARY_TIME, READ_TIME, EV_KEY, KEY_NUMLOCK, 1);
- process(mapper, ARBITRARY_TIME, READ_TIME, EV_KEY, KEY_NUMLOCK, 0);
- ASSERT_TRUE(mFakeEventHub->getLedState(EVENTHUB_ID, LED_CAPSL));
- ASSERT_TRUE(mFakeEventHub->getLedState(EVENTHUB_ID, LED_NUML));
- ASSERT_FALSE(mFakeEventHub->getLedState(EVENTHUB_ID, LED_SCROLLL));
- ASSERT_EQ(AMETA_CAPS_LOCK_ON | AMETA_NUM_LOCK_ON, mapper.getMetaState());
-
- // Toggle caps lock off.
- process(mapper, ARBITRARY_TIME, READ_TIME, EV_KEY, KEY_CAPSLOCK, 1);
- process(mapper, ARBITRARY_TIME, READ_TIME, EV_KEY, KEY_CAPSLOCK, 0);
- ASSERT_FALSE(mFakeEventHub->getLedState(EVENTHUB_ID, LED_CAPSL));
- ASSERT_TRUE(mFakeEventHub->getLedState(EVENTHUB_ID, LED_NUML));
- ASSERT_FALSE(mFakeEventHub->getLedState(EVENTHUB_ID, LED_SCROLLL));
- ASSERT_EQ(AMETA_NUM_LOCK_ON, mapper.getMetaState());
-
- // Toggle scroll lock on.
- process(mapper, ARBITRARY_TIME, READ_TIME, EV_KEY, KEY_SCROLLLOCK, 1);
- process(mapper, ARBITRARY_TIME, READ_TIME, EV_KEY, KEY_SCROLLLOCK, 0);
- ASSERT_FALSE(mFakeEventHub->getLedState(EVENTHUB_ID, LED_CAPSL));
- ASSERT_TRUE(mFakeEventHub->getLedState(EVENTHUB_ID, LED_NUML));
- ASSERT_TRUE(mFakeEventHub->getLedState(EVENTHUB_ID, LED_SCROLLL));
- ASSERT_EQ(AMETA_NUM_LOCK_ON | AMETA_SCROLL_LOCK_ON, mapper.getMetaState());
-
- // Toggle num lock off.
- process(mapper, ARBITRARY_TIME, READ_TIME, EV_KEY, KEY_NUMLOCK, 1);
- process(mapper, ARBITRARY_TIME, READ_TIME, EV_KEY, KEY_NUMLOCK, 0);
- ASSERT_FALSE(mFakeEventHub->getLedState(EVENTHUB_ID, LED_CAPSL));
- ASSERT_FALSE(mFakeEventHub->getLedState(EVENTHUB_ID, LED_NUML));
- ASSERT_TRUE(mFakeEventHub->getLedState(EVENTHUB_ID, LED_SCROLLL));
- ASSERT_EQ(AMETA_SCROLL_LOCK_ON, mapper.getMetaState());
-
- // Toggle scroll lock off.
- process(mapper, ARBITRARY_TIME, READ_TIME, EV_KEY, KEY_SCROLLLOCK, 1);
- process(mapper, ARBITRARY_TIME, READ_TIME, EV_KEY, KEY_SCROLLLOCK, 0);
- ASSERT_FALSE(mFakeEventHub->getLedState(EVENTHUB_ID, LED_CAPSL));
- ASSERT_FALSE(mFakeEventHub->getLedState(EVENTHUB_ID, LED_NUML));
- ASSERT_FALSE(mFakeEventHub->getLedState(EVENTHUB_ID, LED_SCROLLL));
- ASSERT_EQ(AMETA_NONE, mapper.getMetaState());
-}
-
-TEST_F(KeyboardInputMapperTest, Configure_AssignsDisplayPort) {
- // keyboard 1.
- mFakeEventHub->addKey(EVENTHUB_ID, KEY_UP, 0, AKEYCODE_DPAD_UP, 0);
- mFakeEventHub->addKey(EVENTHUB_ID, KEY_RIGHT, 0, AKEYCODE_DPAD_RIGHT, 0);
- mFakeEventHub->addKey(EVENTHUB_ID, KEY_DOWN, 0, AKEYCODE_DPAD_DOWN, 0);
- mFakeEventHub->addKey(EVENTHUB_ID, KEY_LEFT, 0, AKEYCODE_DPAD_LEFT, 0);
-
- // keyboard 2.
- const std::string USB2 = "USB2";
- const std::string DEVICE_NAME2 = "KEYBOARD2";
- constexpr int32_t SECOND_DEVICE_ID = DEVICE_ID + 1;
- constexpr int32_t SECOND_EVENTHUB_ID = EVENTHUB_ID + 1;
- std::shared_ptr<InputDevice> device2 =
- newDevice(SECOND_DEVICE_ID, DEVICE_NAME2, USB2, SECOND_EVENTHUB_ID,
- ftl::Flags<InputDeviceClass>(0));
-
- mFakeEventHub->addKey(SECOND_EVENTHUB_ID, KEY_UP, 0, AKEYCODE_DPAD_UP, 0);
- mFakeEventHub->addKey(SECOND_EVENTHUB_ID, KEY_RIGHT, 0, AKEYCODE_DPAD_RIGHT, 0);
- mFakeEventHub->addKey(SECOND_EVENTHUB_ID, KEY_DOWN, 0, AKEYCODE_DPAD_DOWN, 0);
- mFakeEventHub->addKey(SECOND_EVENTHUB_ID, KEY_LEFT, 0, AKEYCODE_DPAD_LEFT, 0);
-
- KeyboardInputMapper& mapper =
- constructAndAddMapper<KeyboardInputMapper>(AINPUT_SOURCE_KEYBOARD);
-
- device2->addEmptyEventHubDevice(SECOND_EVENTHUB_ID);
- KeyboardInputMapper& mapper2 =
- device2->constructAndAddMapper<KeyboardInputMapper>(SECOND_EVENTHUB_ID,
- mFakePolicy
- ->getReaderConfiguration(),
- AINPUT_SOURCE_KEYBOARD);
- std::list<NotifyArgs> unused =
- device2->configure(ARBITRARY_TIME, mFakePolicy->getReaderConfiguration(),
- /*changes=*/{});
- unused += device2->reset(ARBITRARY_TIME);
-
- // Prepared displays and associated info.
- constexpr uint8_t hdmi1 = 0;
- constexpr uint8_t hdmi2 = 1;
- const std::string SECONDARY_UNIQUE_ID = "local:1";
-
- mFakePolicy->addInputPortAssociation(DEVICE_LOCATION, hdmi1);
- mFakePolicy->addInputPortAssociation(USB2, hdmi2);
-
- // No associated display viewport found, should disable the device.
- unused += device2->configure(ARBITRARY_TIME, mFakePolicy->getReaderConfiguration(),
- InputReaderConfiguration::Change::DISPLAY_INFO);
- ASSERT_FALSE(device2->isEnabled());
-
- // Prepare second display.
- constexpr ui::LogicalDisplayId newDisplayId = ui::LogicalDisplayId{2};
- setDisplayInfoAndReconfigure(DISPLAY_ID, DISPLAY_WIDTH, DISPLAY_HEIGHT, ui::ROTATION_0,
- UNIQUE_ID, hdmi1, ViewportType::INTERNAL);
- setDisplayInfoAndReconfigure(newDisplayId, DISPLAY_WIDTH, DISPLAY_HEIGHT, ui::ROTATION_0,
- SECONDARY_UNIQUE_ID, hdmi2, ViewportType::EXTERNAL);
- // Default device will reconfigure above, need additional reconfiguration for another device.
- unused += device2->configure(ARBITRARY_TIME, mFakePolicy->getReaderConfiguration(),
- InputReaderConfiguration::Change::DISPLAY_INFO);
-
- // Device should be enabled after the associated display is found.
- ASSERT_TRUE(mDevice->isEnabled());
- ASSERT_TRUE(device2->isEnabled());
-
- // Test pad key events
- ASSERT_NO_FATAL_FAILURE(
- testDPadKeyRotation(mapper, KEY_UP, AKEYCODE_DPAD_UP, AKEYCODE_DPAD_UP, DISPLAY_ID));
- ASSERT_NO_FATAL_FAILURE(testDPadKeyRotation(mapper, KEY_RIGHT, AKEYCODE_DPAD_RIGHT,
- AKEYCODE_DPAD_RIGHT, DISPLAY_ID));
- ASSERT_NO_FATAL_FAILURE(testDPadKeyRotation(mapper, KEY_DOWN, AKEYCODE_DPAD_DOWN,
- AKEYCODE_DPAD_DOWN, DISPLAY_ID));
- ASSERT_NO_FATAL_FAILURE(testDPadKeyRotation(mapper, KEY_LEFT, AKEYCODE_DPAD_LEFT,
- AKEYCODE_DPAD_LEFT, DISPLAY_ID));
-
- ASSERT_NO_FATAL_FAILURE(
- testDPadKeyRotation(mapper2, KEY_UP, AKEYCODE_DPAD_UP, AKEYCODE_DPAD_UP, newDisplayId));
- ASSERT_NO_FATAL_FAILURE(testDPadKeyRotation(mapper2, KEY_RIGHT, AKEYCODE_DPAD_RIGHT,
- AKEYCODE_DPAD_RIGHT, newDisplayId));
- ASSERT_NO_FATAL_FAILURE(testDPadKeyRotation(mapper2, KEY_DOWN, AKEYCODE_DPAD_DOWN,
- AKEYCODE_DPAD_DOWN, newDisplayId));
- ASSERT_NO_FATAL_FAILURE(testDPadKeyRotation(mapper2, KEY_LEFT, AKEYCODE_DPAD_LEFT,
- AKEYCODE_DPAD_LEFT, newDisplayId));
-}
-
-TEST_F(KeyboardInputMapperTest, Process_LockedKeysShouldToggleAfterReattach) {
- mFakeEventHub->addLed(EVENTHUB_ID, LED_CAPSL, true /*initially on*/);
- mFakeEventHub->addLed(EVENTHUB_ID, LED_NUML, false /*initially off*/);
- mFakeEventHub->addLed(EVENTHUB_ID, LED_SCROLLL, false /*initially off*/);
- mFakeEventHub->addKey(EVENTHUB_ID, KEY_CAPSLOCK, 0, AKEYCODE_CAPS_LOCK, 0);
- mFakeEventHub->addKey(EVENTHUB_ID, KEY_NUMLOCK, 0, AKEYCODE_NUM_LOCK, 0);
- mFakeEventHub->addKey(EVENTHUB_ID, KEY_SCROLLLOCK, 0, AKEYCODE_SCROLL_LOCK, 0);
-
- KeyboardInputMapper& mapper =
- constructAndAddMapper<KeyboardInputMapper>(AINPUT_SOURCE_KEYBOARD);
- // Initial metastate is AMETA_NONE.
- ASSERT_EQ(AMETA_NONE, mapper.getMetaState());
-
- // Initialization should have turned all of the lights off.
- ASSERT_FALSE(mFakeEventHub->getLedState(EVENTHUB_ID, LED_CAPSL));
- ASSERT_FALSE(mFakeEventHub->getLedState(EVENTHUB_ID, LED_NUML));
- ASSERT_FALSE(mFakeEventHub->getLedState(EVENTHUB_ID, LED_SCROLLL));
-
- // Toggle caps lock on.
- process(mapper, ARBITRARY_TIME, READ_TIME, EV_KEY, KEY_CAPSLOCK, 1);
- process(mapper, ARBITRARY_TIME, READ_TIME, EV_KEY, KEY_CAPSLOCK, 0);
- ASSERT_TRUE(mFakeEventHub->getLedState(EVENTHUB_ID, LED_CAPSL));
- ASSERT_EQ(AMETA_CAPS_LOCK_ON, mapper.getMetaState());
-
- // Toggle num lock on.
- process(mapper, ARBITRARY_TIME, READ_TIME, EV_KEY, KEY_NUMLOCK, 1);
- process(mapper, ARBITRARY_TIME, READ_TIME, EV_KEY, KEY_NUMLOCK, 0);
- ASSERT_TRUE(mFakeEventHub->getLedState(EVENTHUB_ID, LED_NUML));
- ASSERT_EQ(AMETA_CAPS_LOCK_ON | AMETA_NUM_LOCK_ON, mapper.getMetaState());
-
- // Toggle scroll lock on.
- process(mapper, ARBITRARY_TIME, READ_TIME, EV_KEY, KEY_SCROLLLOCK, 1);
- process(mapper, ARBITRARY_TIME, READ_TIME, EV_KEY, KEY_SCROLLLOCK, 0);
- ASSERT_TRUE(mFakeEventHub->getLedState(EVENTHUB_ID, LED_SCROLLL));
- ASSERT_EQ(AMETA_CAPS_LOCK_ON | AMETA_NUM_LOCK_ON | AMETA_SCROLL_LOCK_ON, mapper.getMetaState());
-
- mFakeEventHub->removeDevice(EVENTHUB_ID);
- mReader->loopOnce();
-
- // keyboard 2 should default toggle keys.
- const std::string USB2 = "USB2";
- const std::string DEVICE_NAME2 = "KEYBOARD2";
- constexpr int32_t SECOND_DEVICE_ID = DEVICE_ID + 1;
- constexpr int32_t SECOND_EVENTHUB_ID = EVENTHUB_ID + 1;
- std::shared_ptr<InputDevice> device2 =
- newDevice(SECOND_DEVICE_ID, DEVICE_NAME2, USB2, SECOND_EVENTHUB_ID,
- ftl::Flags<InputDeviceClass>(0));
- mFakeEventHub->addLed(SECOND_EVENTHUB_ID, LED_CAPSL, true /*initially on*/);
- mFakeEventHub->addLed(SECOND_EVENTHUB_ID, LED_NUML, false /*initially off*/);
- mFakeEventHub->addLed(SECOND_EVENTHUB_ID, LED_SCROLLL, false /*initially off*/);
- mFakeEventHub->addKey(SECOND_EVENTHUB_ID, KEY_CAPSLOCK, 0, AKEYCODE_CAPS_LOCK, 0);
- mFakeEventHub->addKey(SECOND_EVENTHUB_ID, KEY_NUMLOCK, 0, AKEYCODE_NUM_LOCK, 0);
- mFakeEventHub->addKey(SECOND_EVENTHUB_ID, KEY_SCROLLLOCK, 0, AKEYCODE_SCROLL_LOCK, 0);
-
- device2->addEmptyEventHubDevice(SECOND_EVENTHUB_ID);
- KeyboardInputMapper& mapper2 =
- device2->constructAndAddMapper<KeyboardInputMapper>(SECOND_EVENTHUB_ID,
- mFakePolicy
- ->getReaderConfiguration(),
- AINPUT_SOURCE_KEYBOARD);
- std::list<NotifyArgs> unused =
- device2->configure(ARBITRARY_TIME, mFakePolicy->getReaderConfiguration(),
- /*changes=*/{});
- unused += device2->reset(ARBITRARY_TIME);
-
- ASSERT_TRUE(mFakeEventHub->getLedState(SECOND_EVENTHUB_ID, LED_CAPSL));
- ASSERT_TRUE(mFakeEventHub->getLedState(SECOND_EVENTHUB_ID, LED_NUML));
- ASSERT_TRUE(mFakeEventHub->getLedState(SECOND_EVENTHUB_ID, LED_SCROLLL));
- ASSERT_EQ(AMETA_CAPS_LOCK_ON | AMETA_NUM_LOCK_ON | AMETA_SCROLL_LOCK_ON,
- mapper2.getMetaState());
-}
-
-TEST_F(KeyboardInputMapperTest, Process_toggleCapsLockState) {
- mFakeEventHub->addKey(EVENTHUB_ID, KEY_CAPSLOCK, 0, AKEYCODE_CAPS_LOCK, 0);
- mFakeEventHub->addKey(EVENTHUB_ID, KEY_NUMLOCK, 0, AKEYCODE_NUM_LOCK, 0);
- mFakeEventHub->addKey(EVENTHUB_ID, KEY_SCROLLLOCK, 0, AKEYCODE_SCROLL_LOCK, 0);
-
- // Suppose we have two mappers. (DPAD + KEYBOARD)
- constructAndAddMapper<KeyboardInputMapper>(AINPUT_SOURCE_DPAD);
- KeyboardInputMapper& mapper =
- constructAndAddMapper<KeyboardInputMapper>(AINPUT_SOURCE_KEYBOARD);
- // Initial metastate is AMETA_NONE.
- ASSERT_EQ(AMETA_NONE, mapper.getMetaState());
-
- mReader->toggleCapsLockState(DEVICE_ID);
- ASSERT_EQ(AMETA_CAPS_LOCK_ON, mapper.getMetaState());
-}
-
-TEST_F(KeyboardInputMapperTest, Process_LockedKeysShouldToggleInMultiDevices) {
- // keyboard 1.
- mFakeEventHub->addLed(EVENTHUB_ID, LED_CAPSL, true /*initially on*/);
- mFakeEventHub->addLed(EVENTHUB_ID, LED_NUML, false /*initially off*/);
- mFakeEventHub->addLed(EVENTHUB_ID, LED_SCROLLL, false /*initially off*/);
- mFakeEventHub->addKey(EVENTHUB_ID, KEY_CAPSLOCK, 0, AKEYCODE_CAPS_LOCK, 0);
- mFakeEventHub->addKey(EVENTHUB_ID, KEY_NUMLOCK, 0, AKEYCODE_NUM_LOCK, 0);
- mFakeEventHub->addKey(EVENTHUB_ID, KEY_SCROLLLOCK, 0, AKEYCODE_SCROLL_LOCK, 0);
-
- KeyboardInputMapper& mapper1 =
- constructAndAddMapper<KeyboardInputMapper>(AINPUT_SOURCE_KEYBOARD);
-
- // keyboard 2.
- const std::string USB2 = "USB2";
- const std::string DEVICE_NAME2 = "KEYBOARD2";
- constexpr int32_t SECOND_DEVICE_ID = DEVICE_ID + 1;
- constexpr int32_t SECOND_EVENTHUB_ID = EVENTHUB_ID + 1;
- std::shared_ptr<InputDevice> device2 =
- newDevice(SECOND_DEVICE_ID, DEVICE_NAME2, USB2, SECOND_EVENTHUB_ID,
- ftl::Flags<InputDeviceClass>(0));
- mFakeEventHub->addLed(SECOND_EVENTHUB_ID, LED_CAPSL, true /*initially on*/);
- mFakeEventHub->addLed(SECOND_EVENTHUB_ID, LED_NUML, false /*initially off*/);
- mFakeEventHub->addLed(SECOND_EVENTHUB_ID, LED_SCROLLL, false /*initially off*/);
- mFakeEventHub->addKey(SECOND_EVENTHUB_ID, KEY_CAPSLOCK, 0, AKEYCODE_CAPS_LOCK, 0);
- mFakeEventHub->addKey(SECOND_EVENTHUB_ID, KEY_NUMLOCK, 0, AKEYCODE_NUM_LOCK, 0);
- mFakeEventHub->addKey(SECOND_EVENTHUB_ID, KEY_SCROLLLOCK, 0, AKEYCODE_SCROLL_LOCK, 0);
-
- device2->addEmptyEventHubDevice(SECOND_EVENTHUB_ID);
- KeyboardInputMapper& mapper2 =
- device2->constructAndAddMapper<KeyboardInputMapper>(SECOND_EVENTHUB_ID,
- mFakePolicy
- ->getReaderConfiguration(),
- AINPUT_SOURCE_KEYBOARD);
- std::list<NotifyArgs> unused =
- device2->configure(ARBITRARY_TIME, mFakePolicy->getReaderConfiguration(),
- /*changes=*/{});
- unused += device2->reset(ARBITRARY_TIME);
-
- // Initial metastate is AMETA_NONE.
- ASSERT_EQ(AMETA_NONE, mapper1.getMetaState());
- ASSERT_EQ(AMETA_NONE, mapper2.getMetaState());
-
- // Toggle num lock on and off.
- process(mapper1, ARBITRARY_TIME, READ_TIME, EV_KEY, KEY_NUMLOCK, 1);
- process(mapper1, ARBITRARY_TIME, READ_TIME, EV_KEY, KEY_NUMLOCK, 0);
- ASSERT_TRUE(mFakeEventHub->getLedState(EVENTHUB_ID, LED_NUML));
- ASSERT_EQ(AMETA_NUM_LOCK_ON, mapper1.getMetaState());
- ASSERT_EQ(AMETA_NUM_LOCK_ON, mapper2.getMetaState());
-
- process(mapper1, ARBITRARY_TIME, READ_TIME, EV_KEY, KEY_NUMLOCK, 1);
- process(mapper1, ARBITRARY_TIME, READ_TIME, EV_KEY, KEY_NUMLOCK, 0);
- ASSERT_FALSE(mFakeEventHub->getLedState(EVENTHUB_ID, LED_NUML));
- ASSERT_EQ(AMETA_NONE, mapper1.getMetaState());
- ASSERT_EQ(AMETA_NONE, mapper2.getMetaState());
-
- // Toggle caps lock on and off.
- process(mapper1, ARBITRARY_TIME, READ_TIME, EV_KEY, KEY_CAPSLOCK, 1);
- process(mapper1, ARBITRARY_TIME, READ_TIME, EV_KEY, KEY_CAPSLOCK, 0);
- ASSERT_TRUE(mFakeEventHub->getLedState(EVENTHUB_ID, LED_CAPSL));
- ASSERT_EQ(AMETA_CAPS_LOCK_ON, mapper1.getMetaState());
- ASSERT_EQ(AMETA_CAPS_LOCK_ON, mapper2.getMetaState());
-
- process(mapper1, ARBITRARY_TIME, READ_TIME, EV_KEY, KEY_CAPSLOCK, 1);
- process(mapper1, ARBITRARY_TIME, READ_TIME, EV_KEY, KEY_CAPSLOCK, 0);
- ASSERT_FALSE(mFakeEventHub->getLedState(EVENTHUB_ID, LED_CAPSL));
- ASSERT_EQ(AMETA_NONE, mapper1.getMetaState());
- ASSERT_EQ(AMETA_NONE, mapper2.getMetaState());
-
- // Toggle scroll lock on and off.
- process(mapper1, ARBITRARY_TIME, READ_TIME, EV_KEY, KEY_SCROLLLOCK, 1);
- process(mapper1, ARBITRARY_TIME, READ_TIME, EV_KEY, KEY_SCROLLLOCK, 0);
- ASSERT_TRUE(mFakeEventHub->getLedState(EVENTHUB_ID, LED_SCROLLL));
- ASSERT_EQ(AMETA_SCROLL_LOCK_ON, mapper1.getMetaState());
- ASSERT_EQ(AMETA_SCROLL_LOCK_ON, mapper2.getMetaState());
-
- process(mapper1, ARBITRARY_TIME, READ_TIME, EV_KEY, KEY_SCROLLLOCK, 1);
- process(mapper1, ARBITRARY_TIME, READ_TIME, EV_KEY, KEY_SCROLLLOCK, 0);
- ASSERT_FALSE(mFakeEventHub->getLedState(EVENTHUB_ID, LED_SCROLLL));
- ASSERT_EQ(AMETA_NONE, mapper1.getMetaState());
- ASSERT_EQ(AMETA_NONE, mapper2.getMetaState());
-}
-
-TEST_F(KeyboardInputMapperTest, Process_DisabledDevice) {
- const int32_t USAGE_A = 0x070004;
- mFakeEventHub->addKey(EVENTHUB_ID, KEY_HOME, 0, AKEYCODE_HOME, POLICY_FLAG_WAKE);
- mFakeEventHub->addKey(EVENTHUB_ID, 0, USAGE_A, AKEYCODE_A, POLICY_FLAG_WAKE);
-
- KeyboardInputMapper& mapper =
- constructAndAddMapper<KeyboardInputMapper>(AINPUT_SOURCE_KEYBOARD);
- // Key down by scan code.
- process(mapper, ARBITRARY_TIME, READ_TIME, EV_KEY, KEY_HOME, 1);
- NotifyKeyArgs args;
- ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyKeyWasCalled(&args));
- ASSERT_EQ(DEVICE_ID, args.deviceId);
- ASSERT_EQ(AINPUT_SOURCE_KEYBOARD, args.source);
- ASSERT_EQ(ARBITRARY_TIME, args.eventTime);
- ASSERT_EQ(AKEY_EVENT_ACTION_DOWN, args.action);
- ASSERT_EQ(AKEYCODE_HOME, args.keyCode);
- ASSERT_EQ(KEY_HOME, args.scanCode);
- ASSERT_EQ(AKEY_EVENT_FLAG_FROM_SYSTEM, args.flags);
-
- // Disable device, it should synthesize cancellation events for down events.
- mFakePolicy->addDisabledDevice(DEVICE_ID);
- configureDevice(InputReaderConfiguration::Change::ENABLED_STATE);
-
- ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyKeyWasCalled(&args));
- ASSERT_EQ(AKEY_EVENT_ACTION_UP, args.action);
- ASSERT_EQ(AKEYCODE_HOME, args.keyCode);
- ASSERT_EQ(KEY_HOME, args.scanCode);
- ASSERT_EQ(AKEY_EVENT_FLAG_FROM_SYSTEM | AKEY_EVENT_FLAG_CANCELED, args.flags);
-}
-
-TEST_F(KeyboardInputMapperTest, Configure_AssignKeyboardLayoutInfo) {
- constructAndAddMapper<KeyboardInputMapper>(AINPUT_SOURCE_KEYBOARD);
- std::list<NotifyArgs> unused =
- mDevice->configure(ARBITRARY_TIME, mFakePolicy->getReaderConfiguration(),
- /*changes=*/{});
-
- uint32_t generation = mReader->getContext()->getGeneration();
- mFakePolicy->addKeyboardLayoutAssociation(DEVICE_LOCATION, DEVICE_KEYBOARD_LAYOUT_INFO);
-
- unused += mDevice->configure(ARBITRARY_TIME, mFakePolicy->getReaderConfiguration(),
- InputReaderConfiguration::Change::KEYBOARD_LAYOUT_ASSOCIATION);
-
- InputDeviceInfo deviceInfo = mDevice->getDeviceInfo();
- ASSERT_EQ(DEVICE_KEYBOARD_LAYOUT_INFO.languageTag,
- deviceInfo.getKeyboardLayoutInfo()->languageTag);
- ASSERT_EQ(DEVICE_KEYBOARD_LAYOUT_INFO.layoutType,
- deviceInfo.getKeyboardLayoutInfo()->layoutType);
- ASSERT_TRUE(mReader->getContext()->getGeneration() != generation);
-
- // Call change layout association with the same values: Generation shouldn't change
- generation = mReader->getContext()->getGeneration();
- mFakePolicy->addKeyboardLayoutAssociation(DEVICE_LOCATION, DEVICE_KEYBOARD_LAYOUT_INFO);
- unused += mDevice->configure(ARBITRARY_TIME, mFakePolicy->getReaderConfiguration(),
- InputReaderConfiguration::Change::KEYBOARD_LAYOUT_ASSOCIATION);
- ASSERT_TRUE(mReader->getContext()->getGeneration() == generation);
-}
-
-TEST_F(KeyboardInputMapperTest, LayoutInfoCorrectlyMapped) {
- mFakeEventHub->setRawLayoutInfo(EVENTHUB_ID,
- RawLayoutInfo{.languageTag = "en", .layoutType = "extended"});
-
- // Configuration
- constructAndAddMapper<KeyboardInputMapper>(AINPUT_SOURCE_KEYBOARD);
- InputReaderConfiguration config;
- std::list<NotifyArgs> unused = mDevice->configure(ARBITRARY_TIME, config, /*changes=*/{});
-
- ASSERT_EQ("en", mDevice->getDeviceInfo().getKeyboardLayoutInfo()->languageTag);
- ASSERT_EQ("extended", mDevice->getDeviceInfo().getKeyboardLayoutInfo()->layoutType);
-}
-
-TEST_F(KeyboardInputMapperTest, Process_GesureEventToSetFlagKeepTouchMode) {
- mFakeEventHub->addKey(EVENTHUB_ID, KEY_LEFT, 0, AKEYCODE_DPAD_LEFT, POLICY_FLAG_GESTURE);
- KeyboardInputMapper& mapper =
- constructAndAddMapper<KeyboardInputMapper>(AINPUT_SOURCE_KEYBOARD);
- NotifyKeyArgs args;
-
- // Key down
- process(mapper, ARBITRARY_TIME, READ_TIME, EV_KEY, KEY_LEFT, 1);
- ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyKeyWasCalled(&args));
- ASSERT_EQ(AKEY_EVENT_FLAG_FROM_SYSTEM | AKEY_EVENT_FLAG_KEEP_TOUCH_MODE, args.flags);
-}
-
-/**
- * When there is more than one KeyboardInputMapper for an InputDevice, each mapper should produce
- * events that use the shared keyboard source across all mappers. This is to ensure that each
- * input device generates key events in a consistent manner, regardless of which mapper produces
- * the event.
- */
-TEST_F(KeyboardInputMapperTest, UsesSharedKeyboardSource) {
- mFakeEventHub->addKey(EVENTHUB_ID, KEY_HOME, 0, AKEYCODE_HOME, POLICY_FLAG_WAKE);
-
- // Add a mapper with SOURCE_KEYBOARD
- KeyboardInputMapper& keyboardMapper =
- constructAndAddMapper<KeyboardInputMapper>(AINPUT_SOURCE_KEYBOARD);
-
- process(keyboardMapper, ARBITRARY_TIME, 0, EV_KEY, KEY_HOME, 1);
- ASSERT_NO_FATAL_FAILURE(
- mFakeListener->assertNotifyKeyWasCalled(WithSource(AINPUT_SOURCE_KEYBOARD)));
- process(keyboardMapper, ARBITRARY_TIME, 0, EV_KEY, KEY_HOME, 0);
- ASSERT_NO_FATAL_FAILURE(
- mFakeListener->assertNotifyKeyWasCalled(WithSource(AINPUT_SOURCE_KEYBOARD)));
-
- // Add a mapper with SOURCE_DPAD
- KeyboardInputMapper& dpadMapper =
- constructAndAddMapper<KeyboardInputMapper>(AINPUT_SOURCE_DPAD);
- for (auto* mapper : {&keyboardMapper, &dpadMapper}) {
- process(*mapper, ARBITRARY_TIME, 0, EV_KEY, KEY_HOME, 1);
- ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyKeyWasCalled(
- WithSource(AINPUT_SOURCE_KEYBOARD | AINPUT_SOURCE_DPAD)));
- process(*mapper, ARBITRARY_TIME, 0, EV_KEY, KEY_HOME, 0);
- ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyKeyWasCalled(
- WithSource(AINPUT_SOURCE_KEYBOARD | AINPUT_SOURCE_DPAD)));
- }
-
- // Add a mapper with SOURCE_GAMEPAD
- KeyboardInputMapper& gamepadMapper =
- constructAndAddMapper<KeyboardInputMapper>(AINPUT_SOURCE_GAMEPAD);
- for (auto* mapper : {&keyboardMapper, &dpadMapper, &gamepadMapper}) {
- process(*mapper, ARBITRARY_TIME, 0, EV_KEY, KEY_HOME, 1);
- ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyKeyWasCalled(
- WithSource(AINPUT_SOURCE_KEYBOARD | AINPUT_SOURCE_DPAD | AINPUT_SOURCE_GAMEPAD)));
- process(*mapper, ARBITRARY_TIME, 0, EV_KEY, KEY_HOME, 0);
- ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyKeyWasCalled(
- WithSource(AINPUT_SOURCE_KEYBOARD | AINPUT_SOURCE_DPAD | AINPUT_SOURCE_GAMEPAD)));
- }
-}
-
-// --- KeyboardInputMapperTest_ExternalAlphabeticDevice ---
-
-class KeyboardInputMapperTest_ExternalAlphabeticDevice : public InputMapperTest {
-protected:
- void SetUp() override {
- InputMapperTest::SetUp(DEVICE_CLASSES | InputDeviceClass::KEYBOARD |
- InputDeviceClass::ALPHAKEY | InputDeviceClass::EXTERNAL);
- }
-};
-
-// --- KeyboardInputMapperTest_ExternalNonAlphabeticDevice ---
-
-class KeyboardInputMapperTest_ExternalNonAlphabeticDevice : public InputMapperTest {
-protected:
- void SetUp() override {
- InputMapperTest::SetUp(DEVICE_CLASSES | InputDeviceClass::KEYBOARD |
- InputDeviceClass::EXTERNAL);
- }
-};
-
-TEST_F(KeyboardInputMapperTest_ExternalAlphabeticDevice, WakeBehavior_AlphabeticKeyboard) {
- // For external devices, keys will trigger wake on key down. Media keys should also trigger
- // wake if triggered from external devices.
-
- mFakeEventHub->addKey(EVENTHUB_ID, KEY_HOME, 0, AKEYCODE_HOME, 0);
- mFakeEventHub->addKey(EVENTHUB_ID, KEY_PLAY, 0, AKEYCODE_MEDIA_PLAY, 0);
- mFakeEventHub->addKey(EVENTHUB_ID, KEY_PLAYPAUSE, 0, AKEYCODE_MEDIA_PLAY_PAUSE,
- POLICY_FLAG_WAKE);
-
- KeyboardInputMapper& mapper =
- constructAndAddMapper<KeyboardInputMapper>(AINPUT_SOURCE_KEYBOARD);
-
- process(mapper, ARBITRARY_TIME, READ_TIME, EV_KEY, KEY_HOME, 1);
- NotifyKeyArgs args;
- ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyKeyWasCalled(&args));
- ASSERT_EQ(POLICY_FLAG_WAKE, args.policyFlags);
-
- process(mapper, ARBITRARY_TIME + 1, READ_TIME, EV_KEY, KEY_HOME, 0);
- ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyKeyWasCalled(&args));
- ASSERT_EQ(uint32_t(0), args.policyFlags);
-
- process(mapper, ARBITRARY_TIME, READ_TIME, EV_KEY, KEY_PLAY, 1);
- ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyKeyWasCalled(&args));
- ASSERT_EQ(POLICY_FLAG_WAKE, args.policyFlags);
-
- process(mapper, ARBITRARY_TIME + 1, READ_TIME, EV_KEY, KEY_PLAY, 0);
- ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyKeyWasCalled(&args));
- ASSERT_EQ(uint32_t(0), args.policyFlags);
-
- process(mapper, ARBITRARY_TIME, READ_TIME, EV_KEY, KEY_PLAYPAUSE, 1);
- ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyKeyWasCalled(&args));
- ASSERT_EQ(POLICY_FLAG_WAKE, args.policyFlags);
-
- process(mapper, ARBITRARY_TIME + 1, READ_TIME, EV_KEY, KEY_PLAYPAUSE, 0);
- ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyKeyWasCalled(&args));
- ASSERT_EQ(POLICY_FLAG_WAKE, args.policyFlags);
-}
-
-TEST_F(KeyboardInputMapperTest_ExternalNonAlphabeticDevice, WakeBehavior_NonAlphabeticKeyboard) {
- // For external devices, keys will trigger wake on key down. Media keys should not trigger
- // wake if triggered from external non-alphaebtic keyboard (e.g. headsets).
-
- mFakeEventHub->addKey(EVENTHUB_ID, KEY_PLAY, 0, AKEYCODE_MEDIA_PLAY, 0);
- mFakeEventHub->addKey(EVENTHUB_ID, KEY_PLAYPAUSE, 0, AKEYCODE_MEDIA_PLAY_PAUSE,
- POLICY_FLAG_WAKE);
-
- KeyboardInputMapper& mapper =
- constructAndAddMapper<KeyboardInputMapper>(AINPUT_SOURCE_KEYBOARD);
-
- process(mapper, ARBITRARY_TIME, READ_TIME, EV_KEY, KEY_PLAY, 1);
- NotifyKeyArgs args;
- ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyKeyWasCalled(&args));
- ASSERT_EQ(uint32_t(0), args.policyFlags);
-
- process(mapper, ARBITRARY_TIME + 1, READ_TIME, EV_KEY, KEY_PLAY, 0);
- ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyKeyWasCalled(&args));
- ASSERT_EQ(uint32_t(0), args.policyFlags);
-
- process(mapper, ARBITRARY_TIME, READ_TIME, EV_KEY, KEY_PLAYPAUSE, 1);
- ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyKeyWasCalled(&args));
- ASSERT_EQ(POLICY_FLAG_WAKE, args.policyFlags);
-
- process(mapper, ARBITRARY_TIME + 1, READ_TIME, EV_KEY, KEY_PLAYPAUSE, 0);
- ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyKeyWasCalled(&args));
- ASSERT_EQ(POLICY_FLAG_WAKE, args.policyFlags);
-}
-
-TEST_F(KeyboardInputMapperTest_ExternalAlphabeticDevice, DoNotWakeByDefaultBehavior) {
- // Tv Remote key's wake behavior is prescribed by the keylayout file.
-
- mFakeEventHub->addKey(EVENTHUB_ID, KEY_HOME, 0, AKEYCODE_HOME, POLICY_FLAG_WAKE);
- mFakeEventHub->addKey(EVENTHUB_ID, KEY_DOWN, 0, AKEYCODE_DPAD_DOWN, 0);
- mFakeEventHub->addKey(EVENTHUB_ID, KEY_PLAY, 0, AKEYCODE_MEDIA_PLAY, POLICY_FLAG_WAKE);
-
- addConfigurationProperty("keyboard.doNotWakeByDefault", "1");
- KeyboardInputMapper& mapper =
- constructAndAddMapper<KeyboardInputMapper>(AINPUT_SOURCE_KEYBOARD);
-
- process(mapper, ARBITRARY_TIME, READ_TIME, EV_KEY, KEY_HOME, 1);
- NotifyKeyArgs args;
- ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyKeyWasCalled(&args));
- ASSERT_EQ(POLICY_FLAG_WAKE, args.policyFlags);
-
- process(mapper, ARBITRARY_TIME + 1, READ_TIME, EV_KEY, KEY_HOME, 0);
- ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyKeyWasCalled(&args));
- ASSERT_EQ(POLICY_FLAG_WAKE, args.policyFlags);
-
- process(mapper, ARBITRARY_TIME, READ_TIME, EV_KEY, KEY_DOWN, 1);
- ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyKeyWasCalled(&args));
- ASSERT_EQ(uint32_t(0), args.policyFlags);
-
- process(mapper, ARBITRARY_TIME + 1, READ_TIME, EV_KEY, KEY_DOWN, 0);
- ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyKeyWasCalled(&args));
- ASSERT_EQ(uint32_t(0), args.policyFlags);
-
- process(mapper, ARBITRARY_TIME, READ_TIME, EV_KEY, KEY_PLAY, 1);
- ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyKeyWasCalled(&args));
- ASSERT_EQ(POLICY_FLAG_WAKE, args.policyFlags);
-
- process(mapper, ARBITRARY_TIME + 1, READ_TIME, EV_KEY, KEY_PLAY, 0);
- ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyKeyWasCalled(&args));
- ASSERT_EQ(POLICY_FLAG_WAKE, args.policyFlags);
-}
-
// --- TouchInputMapperTest ---
class TouchInputMapperTest : public InputMapperTest {
diff --git a/services/inputflinger/tests/InterfaceMocks.h b/services/inputflinger/tests/InterfaceMocks.h
index 25e2b45..6f7c2e5 100644
--- a/services/inputflinger/tests/InterfaceMocks.h
+++ b/services/inputflinger/tests/InterfaceMocks.h
@@ -201,7 +201,9 @@
MOCK_METHOD(uint32_t, getSources, (), (const, override));
MOCK_METHOD(std::optional<DisplayViewport>, getAssociatedViewport, (), (const));
+ MOCK_METHOD(KeyboardType, getKeyboardType, (), (const, override));
MOCK_METHOD(bool, isEnabled, (), ());
+ MOCK_METHOD(bool, isExternal, (), (override));
MOCK_METHOD(void, dump, (std::string& dump, const std::string& eventHubDevStr), ());
MOCK_METHOD(void, addEmptyEventHubDevice, (int32_t eventHubId), ());
@@ -249,8 +251,6 @@
MOCK_METHOD(int32_t, getMetaState, (), ());
MOCK_METHOD(void, setKeyboardType, (KeyboardType keyboardType), ());
- MOCK_METHOD(void, bumpGeneration, (), ());
-
MOCK_METHOD(const PropertyMap&, getConfiguration, (), (const, override));
MOCK_METHOD(NotifyDeviceResetArgs, notifyReset, (nsecs_t when), ());
@@ -260,5 +260,11 @@
MOCK_METHOD(void, updateLedState, (bool reset), ());
MOCK_METHOD(size_t, getMapperCount, (), ());
+
+ virtual int32_t getGeneration() const override { return mGeneration; }
+ virtual void bumpGeneration() override { mGeneration++; }
+
+private:
+ int32_t mGeneration = 0;
};
} // namespace android
diff --git a/services/inputflinger/tests/KeyboardInputMapper_test.cpp b/services/inputflinger/tests/KeyboardInputMapper_test.cpp
index bcc6062..1dd32c4 100644
--- a/services/inputflinger/tests/KeyboardInputMapper_test.cpp
+++ b/services/inputflinger/tests/KeyboardInputMapper_test.cpp
@@ -16,29 +16,82 @@
#include "KeyboardInputMapper.h"
-#include <gtest/gtest.h>
+#include <cstdint>
+#include <list>
+#include <memory>
+#include <optional>
+#include <string>
+#include <android/input.h>
+#include <android/keycodes.h>
+#include <com_android_input_flags.h>
+#include <flag_macros.h>
+#include <ftl/flags.h>
+#include <gtest/gtest.h>
+#include <input/DisplayViewport.h>
+#include <input/Input.h>
+#include <input/InputDevice.h>
+#include <ui/LogicalDisplayId.h>
+#include <ui/Rotation.h>
+#include <utils/Errors.h>
+
+#include "EventHub.h"
#include "InputMapperTest.h"
#include "InterfaceMocks.h"
+#include "NotifyArgs.h"
+#include "TestConstants.h"
#include "TestEventMatchers.h"
#define TAG "KeyboardInputMapper_test"
namespace android {
+using namespace ftl::flag_operators;
using testing::_;
using testing::AllOf;
+using testing::AnyOf;
using testing::Args;
using testing::DoAll;
+using testing::IsEmpty;
using testing::Return;
+using testing::ReturnArg;
+using testing::SaveArg;
using testing::SetArgPointee;
using testing::VariantWith;
+namespace {
+
+// Arbitrary display properties.
+constexpr ui::LogicalDisplayId DISPLAY_ID = ui::LogicalDisplayId::DEFAULT;
+constexpr int32_t DISPLAY_WIDTH = 480;
+constexpr int32_t DISPLAY_HEIGHT = 800;
+
+DisplayViewport createPrimaryViewport(ui::Rotation orientation) {
+ const bool isRotated =
+ orientation == ui::Rotation::Rotation90 || orientation == ui::Rotation::Rotation270;
+ DisplayViewport v;
+ v.displayId = DISPLAY_ID;
+ v.orientation = orientation;
+ v.logicalRight = isRotated ? DISPLAY_HEIGHT : DISPLAY_WIDTH;
+ v.logicalBottom = isRotated ? DISPLAY_WIDTH : DISPLAY_HEIGHT;
+ v.physicalRight = isRotated ? DISPLAY_HEIGHT : DISPLAY_WIDTH;
+ v.physicalBottom = isRotated ? DISPLAY_WIDTH : DISPLAY_HEIGHT;
+ v.deviceWidth = isRotated ? DISPLAY_HEIGHT : DISPLAY_WIDTH;
+ v.deviceHeight = isRotated ? DISPLAY_WIDTH : DISPLAY_HEIGHT;
+ v.isActive = true;
+ v.uniqueId = "local:1";
+ return v;
+}
+
+} // namespace
+
/**
* Unit tests for KeyboardInputMapper.
*/
class KeyboardInputMapperUnitTest : public InputMapperUnitTest {
protected:
+ const KeyboardLayoutInfo DEVICE_KEYBOARD_LAYOUT_INFO = KeyboardLayoutInfo("en-US", "qwerty");
+
sp<FakeInputReaderPolicy> mFakePolicy;
const std::unordered_map<int32_t, int32_t> mKeyCodeMap{{KEY_0, AKEYCODE_0},
{KEY_A, AKEYCODE_A},
@@ -60,9 +113,8 @@
InputMapperUnitTest::SetUp();
// set key-codes expected in tests
- for (const auto& [scanCode, outKeycode] : mKeyCodeMap) {
- EXPECT_CALL(mMockEventHub, mapKey(EVENTHUB_ID, scanCode, _, _, _, _, _))
- .WillRepeatedly(DoAll(SetArgPointee<4>(outKeycode), Return(NO_ERROR)));
+ for (const auto& [evdevCode, outKeycode] : mKeyCodeMap) {
+ addKeyByEvdevCode(evdevCode, outKeycode);
}
mFakePolicy = sp<FakeInputReaderPolicy>::make();
@@ -73,8 +125,79 @@
mMapper = createInputMapper<KeyboardInputMapper>(*mDeviceContext, mReaderConfiguration,
AINPUT_SOURCE_KEYBOARD);
}
+
+ void addKeyByEvdevCode(int32_t evdevCode, int32_t keyCode, int32_t flags = 0) {
+ EXPECT_CALL(mMockEventHub, mapKey(EVENTHUB_ID, evdevCode, _, _, _, _, _))
+ .WillRepeatedly([=](int32_t, int32_t, int32_t, int32_t metaState,
+ int32_t* outKeycode, int32_t* outMetaState,
+ uint32_t* outFlags) {
+ if (outKeycode != nullptr) {
+ *outKeycode = keyCode;
+ }
+ if (outMetaState != nullptr) {
+ *outMetaState = metaState;
+ }
+ if (outFlags != nullptr) {
+ *outFlags = flags;
+ }
+ return NO_ERROR;
+ });
+ }
+
+ void addKeyByUsageCode(int32_t usageCode, int32_t keyCode, int32_t flags = 0) {
+ EXPECT_CALL(mMockEventHub, mapKey(EVENTHUB_ID, _, usageCode, _, _, _, _))
+ .WillRepeatedly([=](int32_t, int32_t, int32_t, int32_t metaState,
+ int32_t* outKeycode, int32_t* outMetaState,
+ uint32_t* outFlags) {
+ if (outKeycode != nullptr) {
+ *outKeycode = keyCode;
+ }
+ if (outMetaState != nullptr) {
+ *outMetaState = metaState;
+ }
+ if (outFlags != nullptr) {
+ *outFlags = flags;
+ }
+ return NO_ERROR;
+ });
+ }
+
+ void setDisplayOrientation(ui::Rotation orientation) {
+ EXPECT_CALL((*mDevice), getAssociatedViewport)
+ .WillRepeatedly(Return(createPrimaryViewport(orientation)));
+ std::list<NotifyArgs> args =
+ mMapper->reconfigure(ARBITRARY_TIME, mReaderConfiguration,
+ InputReaderConfiguration::Change::DISPLAY_INFO);
+ ASSERT_EQ(0u, args.size());
+ }
+
+ NotifyKeyArgs expectSingleKeyArg(const std::list<NotifyArgs>& args) {
+ EXPECT_EQ(1u, args.size());
+ return std::get<NotifyKeyArgs>(args.front());
+ }
+
+ void testDPadKeyRotation(int32_t originalEvdevCode, int32_t originalKeyCode,
+ int32_t rotatedKeyCode, ui::LogicalDisplayId displayId = DISPLAY_ID) {
+ std::list<NotifyArgs> argsList = process(ARBITRARY_TIME, EV_KEY, originalEvdevCode, 1);
+ NotifyKeyArgs args = expectSingleKeyArg(argsList);
+ ASSERT_EQ(AKEY_EVENT_ACTION_DOWN, args.action);
+ ASSERT_EQ(originalEvdevCode, args.scanCode);
+ ASSERT_EQ(rotatedKeyCode, args.keyCode);
+ ASSERT_EQ(displayId, args.displayId);
+
+ argsList = process(ARBITRARY_TIME, EV_KEY, originalEvdevCode, 0);
+ args = expectSingleKeyArg(argsList);
+ ASSERT_EQ(AKEY_EVENT_ACTION_UP, args.action);
+ ASSERT_EQ(originalEvdevCode, args.scanCode);
+ ASSERT_EQ(rotatedKeyCode, args.keyCode);
+ ASSERT_EQ(displayId, args.displayId);
+ }
};
+TEST_F(KeyboardInputMapperUnitTest, GetSources) {
+ ASSERT_EQ(AINPUT_SOURCE_KEYBOARD, mMapper->getSources());
+}
+
TEST_F(KeyboardInputMapperUnitTest, KeyPressTimestampRecorded) {
nsecs_t when = ARBITRARY_TIME;
std::vector<int32_t> keyCodes{KEY_0, KEY_A, KEY_LEFTCTRL, KEY_RIGHTALT, KEY_LEFTSHIFT};
@@ -109,4 +232,962 @@
WithScanCode(KEY_0)))));
}
+TEST_F(KeyboardInputMapperUnitTest, Process_SimpleKeyPress) {
+ const int32_t USAGE_A = 0x070004;
+ addKeyByEvdevCode(KEY_HOME, AKEYCODE_HOME, POLICY_FLAG_WAKE);
+ addKeyByUsageCode(USAGE_A, AKEYCODE_A, POLICY_FLAG_WAKE);
+
+ // Initial metastate is AMETA_NONE.
+ ASSERT_EQ(AMETA_NONE, mMapper->getMetaState());
+
+ // Key down by evdev code.
+ std::list<NotifyArgs> argsList = process(ARBITRARY_TIME, EV_KEY, KEY_HOME, 1);
+ NotifyKeyArgs args = expectSingleKeyArg(argsList);
+ ASSERT_EQ(DEVICE_ID, args.deviceId);
+ ASSERT_EQ(AINPUT_SOURCE_KEYBOARD, args.source);
+ ASSERT_EQ(ARBITRARY_TIME, args.eventTime);
+ ASSERT_EQ(AKEY_EVENT_ACTION_DOWN, args.action);
+ ASSERT_EQ(AKEYCODE_HOME, args.keyCode);
+ ASSERT_EQ(KEY_HOME, args.scanCode);
+ ASSERT_EQ(AMETA_NONE, args.metaState);
+ ASSERT_EQ(AKEY_EVENT_FLAG_FROM_SYSTEM, args.flags);
+ ASSERT_EQ(POLICY_FLAG_WAKE, args.policyFlags);
+ ASSERT_EQ(ARBITRARY_TIME, args.downTime);
+
+ // Key up by evdev code.
+ argsList = process(ARBITRARY_TIME + 1, EV_KEY, KEY_HOME, 0);
+ args = expectSingleKeyArg(argsList);
+ ASSERT_EQ(DEVICE_ID, args.deviceId);
+ ASSERT_EQ(AINPUT_SOURCE_KEYBOARD, args.source);
+ ASSERT_EQ(ARBITRARY_TIME + 1, args.eventTime);
+ ASSERT_EQ(AKEY_EVENT_ACTION_UP, args.action);
+ ASSERT_EQ(AKEYCODE_HOME, args.keyCode);
+ ASSERT_EQ(KEY_HOME, args.scanCode);
+ ASSERT_EQ(AMETA_NONE, args.metaState);
+ ASSERT_EQ(AKEY_EVENT_FLAG_FROM_SYSTEM, args.flags);
+ ASSERT_EQ(POLICY_FLAG_WAKE, args.policyFlags);
+ ASSERT_EQ(ARBITRARY_TIME, args.downTime);
+
+ // Key down by usage code.
+ argsList = process(ARBITRARY_TIME, EV_MSC, MSC_SCAN, USAGE_A);
+ argsList += process(ARBITRARY_TIME, EV_KEY, 0, 1);
+ args = expectSingleKeyArg(argsList);
+ ASSERT_EQ(DEVICE_ID, args.deviceId);
+ ASSERT_EQ(AINPUT_SOURCE_KEYBOARD, args.source);
+ ASSERT_EQ(ARBITRARY_TIME, args.eventTime);
+ ASSERT_EQ(AKEY_EVENT_ACTION_DOWN, args.action);
+ ASSERT_EQ(AKEYCODE_A, args.keyCode);
+ ASSERT_EQ(0, args.scanCode);
+ ASSERT_EQ(AMETA_NONE, args.metaState);
+ ASSERT_EQ(AKEY_EVENT_FLAG_FROM_SYSTEM, args.flags);
+ ASSERT_EQ(POLICY_FLAG_WAKE, args.policyFlags);
+ ASSERT_EQ(ARBITRARY_TIME, args.downTime);
+
+ // Key up by usage code.
+ argsList = process(ARBITRARY_TIME, EV_MSC, MSC_SCAN, USAGE_A);
+ argsList += process(ARBITRARY_TIME + 1, EV_KEY, 0, 0);
+ args = expectSingleKeyArg(argsList);
+ ASSERT_EQ(DEVICE_ID, args.deviceId);
+ ASSERT_EQ(AINPUT_SOURCE_KEYBOARD, args.source);
+ ASSERT_EQ(ARBITRARY_TIME + 1, args.eventTime);
+ ASSERT_EQ(AKEY_EVENT_ACTION_UP, args.action);
+ ASSERT_EQ(AKEYCODE_A, args.keyCode);
+ ASSERT_EQ(0, args.scanCode);
+ ASSERT_EQ(AMETA_NONE, args.metaState);
+ ASSERT_EQ(AKEY_EVENT_FLAG_FROM_SYSTEM, args.flags);
+ ASSERT_EQ(POLICY_FLAG_WAKE, args.policyFlags);
+ ASSERT_EQ(ARBITRARY_TIME, args.downTime);
+}
+
+TEST_F(KeyboardInputMapperUnitTest, Process_UnknownKey) {
+ const int32_t USAGE_UNKNOWN = 0x07ffff;
+ EXPECT_CALL(mMockEventHub, mapKey(EVENTHUB_ID, KEY_UNKNOWN, USAGE_UNKNOWN, _, _, _, _))
+ .WillRepeatedly(Return(NAME_NOT_FOUND));
+
+ // Key down with unknown scan code or usage code.
+ std::list<NotifyArgs> argsList = process(ARBITRARY_TIME, EV_MSC, MSC_SCAN, USAGE_UNKNOWN);
+ argsList += process(ARBITRARY_TIME, EV_KEY, KEY_UNKNOWN, 1);
+ NotifyKeyArgs args = expectSingleKeyArg(argsList);
+ ASSERT_EQ(DEVICE_ID, args.deviceId);
+ ASSERT_EQ(AINPUT_SOURCE_KEYBOARD, args.source);
+ ASSERT_EQ(ARBITRARY_TIME, args.eventTime);
+ ASSERT_EQ(AKEY_EVENT_ACTION_DOWN, args.action);
+ ASSERT_EQ(0, args.keyCode);
+ ASSERT_EQ(KEY_UNKNOWN, args.scanCode);
+ ASSERT_EQ(AMETA_NONE, args.metaState);
+ ASSERT_EQ(AKEY_EVENT_FLAG_FROM_SYSTEM, args.flags);
+ ASSERT_EQ(0U, args.policyFlags);
+ ASSERT_EQ(ARBITRARY_TIME, args.downTime);
+
+ // Key up with unknown scan code or usage code.
+ argsList = process(ARBITRARY_TIME, EV_MSC, MSC_SCAN, USAGE_UNKNOWN);
+ argsList += process(ARBITRARY_TIME + 1, EV_KEY, KEY_UNKNOWN, 0);
+ args = expectSingleKeyArg(argsList);
+ ASSERT_EQ(DEVICE_ID, args.deviceId);
+ ASSERT_EQ(AINPUT_SOURCE_KEYBOARD, args.source);
+ ASSERT_EQ(ARBITRARY_TIME + 1, args.eventTime);
+ ASSERT_EQ(AKEY_EVENT_ACTION_UP, args.action);
+ ASSERT_EQ(0, args.keyCode);
+ ASSERT_EQ(KEY_UNKNOWN, args.scanCode);
+ ASSERT_EQ(AMETA_NONE, args.metaState);
+ ASSERT_EQ(AKEY_EVENT_FLAG_FROM_SYSTEM, args.flags);
+ ASSERT_EQ(0U, args.policyFlags);
+ ASSERT_EQ(ARBITRARY_TIME, args.downTime);
+}
+
+/**
+ * Ensure that the readTime is set to the time when the EV_KEY is received.
+ */
+TEST_F(KeyboardInputMapperUnitTest, Process_SendsReadTime) {
+ addKeyByEvdevCode(KEY_HOME, AKEYCODE_HOME);
+
+ // Key down
+ std::list<NotifyArgs> argsList = process(ARBITRARY_TIME, /*readTime=*/12, EV_KEY, KEY_HOME, 1);
+ ASSERT_EQ(12, expectSingleKeyArg(argsList).readTime);
+
+ // Key up
+ argsList = process(ARBITRARY_TIME, /*readTime=*/15, EV_KEY, KEY_HOME, 1);
+ ASSERT_EQ(15, expectSingleKeyArg(argsList).readTime);
+}
+
+TEST_F(KeyboardInputMapperUnitTest, Process_ShouldUpdateMetaState) {
+ addKeyByEvdevCode(KEY_LEFTSHIFT, AKEYCODE_SHIFT_LEFT);
+ addKeyByEvdevCode(KEY_A, AKEYCODE_A);
+
+ EXPECT_CALL(mMockInputReaderContext, updateGlobalMetaState()).Times(2);
+
+ // Initial metastate is AMETA_NONE.
+ ASSERT_EQ(AMETA_NONE, mMapper->getMetaState());
+
+ // Metakey down.
+ std::list<NotifyArgs> argsList = process(ARBITRARY_TIME, EV_KEY, KEY_LEFTSHIFT, 1);
+ ASSERT_EQ(AMETA_SHIFT_LEFT_ON | AMETA_SHIFT_ON, expectSingleKeyArg(argsList).metaState);
+ ASSERT_EQ(AMETA_SHIFT_LEFT_ON | AMETA_SHIFT_ON, mMapper->getMetaState());
+
+ // Key down.
+ argsList = process(ARBITRARY_TIME + 1, EV_KEY, KEY_A, 1);
+ ASSERT_EQ(AMETA_SHIFT_LEFT_ON | AMETA_SHIFT_ON, expectSingleKeyArg(argsList).metaState);
+ ASSERT_EQ(AMETA_SHIFT_LEFT_ON | AMETA_SHIFT_ON, mMapper->getMetaState());
+
+ // Key up.
+ argsList = process(ARBITRARY_TIME + 2, EV_KEY, KEY_A, 0);
+ ASSERT_EQ(AMETA_SHIFT_LEFT_ON | AMETA_SHIFT_ON, expectSingleKeyArg(argsList).metaState);
+ ASSERT_EQ(AMETA_SHIFT_LEFT_ON | AMETA_SHIFT_ON, mMapper->getMetaState());
+
+ // Metakey up.
+ argsList = process(ARBITRARY_TIME + 3, EV_KEY, KEY_LEFTSHIFT, 0);
+ ASSERT_EQ(AMETA_NONE, expectSingleKeyArg(argsList).metaState);
+ ASSERT_EQ(AMETA_NONE, mMapper->getMetaState());
+}
+
+TEST_F(KeyboardInputMapperUnitTest, Process_WhenNotOrientationAware_ShouldNotRotateDPad) {
+ addKeyByEvdevCode(KEY_UP, AKEYCODE_DPAD_UP);
+ addKeyByEvdevCode(KEY_RIGHT, AKEYCODE_DPAD_RIGHT);
+ addKeyByEvdevCode(KEY_DOWN, AKEYCODE_DPAD_DOWN);
+ addKeyByEvdevCode(KEY_LEFT, AKEYCODE_DPAD_LEFT);
+
+ setDisplayOrientation(ui::Rotation::Rotation90);
+ ASSERT_NO_FATAL_FAILURE(testDPadKeyRotation(KEY_UP, AKEYCODE_DPAD_UP, AKEYCODE_DPAD_UP));
+ ASSERT_NO_FATAL_FAILURE(
+ testDPadKeyRotation(KEY_RIGHT, AKEYCODE_DPAD_RIGHT, AKEYCODE_DPAD_RIGHT));
+ ASSERT_NO_FATAL_FAILURE(testDPadKeyRotation(KEY_DOWN, AKEYCODE_DPAD_DOWN, AKEYCODE_DPAD_DOWN));
+ ASSERT_NO_FATAL_FAILURE(testDPadKeyRotation(KEY_LEFT, AKEYCODE_DPAD_LEFT, AKEYCODE_DPAD_LEFT));
+}
+
+TEST_F(KeyboardInputMapperUnitTest, Process_WhenOrientationAware_ShouldRotateDPad) {
+ addKeyByEvdevCode(KEY_UP, AKEYCODE_DPAD_UP);
+ addKeyByEvdevCode(KEY_RIGHT, AKEYCODE_DPAD_RIGHT);
+ addKeyByEvdevCode(KEY_DOWN, AKEYCODE_DPAD_DOWN);
+ addKeyByEvdevCode(KEY_LEFT, AKEYCODE_DPAD_LEFT);
+
+ mPropertyMap.addProperty("keyboard.orientationAware", "1");
+ mMapper = createInputMapper<KeyboardInputMapper>(*mDeviceContext, mReaderConfiguration,
+ AINPUT_SOURCE_KEYBOARD);
+ setDisplayOrientation(ui::ROTATION_0);
+
+ ASSERT_NO_FATAL_FAILURE(testDPadKeyRotation(KEY_UP, AKEYCODE_DPAD_UP, AKEYCODE_DPAD_UP));
+ ASSERT_NO_FATAL_FAILURE(
+ testDPadKeyRotation(KEY_RIGHT, AKEYCODE_DPAD_RIGHT, AKEYCODE_DPAD_RIGHT));
+ ASSERT_NO_FATAL_FAILURE(testDPadKeyRotation(KEY_DOWN, AKEYCODE_DPAD_DOWN, AKEYCODE_DPAD_DOWN));
+ ASSERT_NO_FATAL_FAILURE(testDPadKeyRotation(KEY_LEFT, AKEYCODE_DPAD_LEFT, AKEYCODE_DPAD_LEFT));
+
+ setDisplayOrientation(ui::ROTATION_90);
+ ASSERT_NO_FATAL_FAILURE(testDPadKeyRotation(KEY_UP, AKEYCODE_DPAD_UP, AKEYCODE_DPAD_LEFT));
+ ASSERT_NO_FATAL_FAILURE(testDPadKeyRotation(KEY_RIGHT, AKEYCODE_DPAD_RIGHT, AKEYCODE_DPAD_UP));
+ ASSERT_NO_FATAL_FAILURE(testDPadKeyRotation(KEY_DOWN, AKEYCODE_DPAD_DOWN, AKEYCODE_DPAD_RIGHT));
+ ASSERT_NO_FATAL_FAILURE(testDPadKeyRotation(KEY_LEFT, AKEYCODE_DPAD_LEFT, AKEYCODE_DPAD_DOWN));
+
+ setDisplayOrientation(ui::ROTATION_180);
+ ASSERT_NO_FATAL_FAILURE(testDPadKeyRotation(KEY_UP, AKEYCODE_DPAD_UP, AKEYCODE_DPAD_DOWN));
+ ASSERT_NO_FATAL_FAILURE(
+ testDPadKeyRotation(KEY_RIGHT, AKEYCODE_DPAD_RIGHT, AKEYCODE_DPAD_LEFT));
+ ASSERT_NO_FATAL_FAILURE(testDPadKeyRotation(KEY_DOWN, AKEYCODE_DPAD_DOWN, AKEYCODE_DPAD_UP));
+ ASSERT_NO_FATAL_FAILURE(testDPadKeyRotation(KEY_LEFT, AKEYCODE_DPAD_LEFT, AKEYCODE_DPAD_RIGHT));
+
+ setDisplayOrientation(ui::ROTATION_270);
+ ASSERT_NO_FATAL_FAILURE(testDPadKeyRotation(KEY_UP, AKEYCODE_DPAD_UP, AKEYCODE_DPAD_RIGHT));
+ ASSERT_NO_FATAL_FAILURE(
+ testDPadKeyRotation(KEY_RIGHT, AKEYCODE_DPAD_RIGHT, AKEYCODE_DPAD_DOWN));
+ ASSERT_NO_FATAL_FAILURE(testDPadKeyRotation(KEY_DOWN, AKEYCODE_DPAD_DOWN, AKEYCODE_DPAD_LEFT));
+ ASSERT_NO_FATAL_FAILURE(testDPadKeyRotation(KEY_LEFT, AKEYCODE_DPAD_LEFT, AKEYCODE_DPAD_UP));
+
+ // Special case: if orientation changes while key is down, we still emit the same keycode
+ // in the key up as we did in the key down.
+ setDisplayOrientation(ui::ROTATION_270);
+ std::list<NotifyArgs> argsList = process(ARBITRARY_TIME, EV_KEY, KEY_UP, 1);
+ NotifyKeyArgs args = expectSingleKeyArg(argsList);
+ ASSERT_EQ(AKEY_EVENT_ACTION_DOWN, args.action);
+ ASSERT_EQ(KEY_UP, args.scanCode);
+ ASSERT_EQ(AKEYCODE_DPAD_RIGHT, args.keyCode);
+
+ setDisplayOrientation(ui::ROTATION_180);
+ argsList = process(ARBITRARY_TIME, EV_KEY, KEY_UP, 0);
+ args = expectSingleKeyArg(argsList);
+ ASSERT_EQ(AKEY_EVENT_ACTION_UP, args.action);
+ ASSERT_EQ(KEY_UP, args.scanCode);
+ ASSERT_EQ(AKEYCODE_DPAD_RIGHT, args.keyCode);
+}
+
+TEST_F(KeyboardInputMapperUnitTest, DisplayIdConfigurationChange_NotOrientationAware) {
+ // If the keyboard is not orientation aware,
+ // key events should not be associated with a specific display id
+ addKeyByEvdevCode(KEY_UP, AKEYCODE_DPAD_UP);
+
+ // Display id should be LogicalDisplayId::INVALID without any display configuration.
+ std::list<NotifyArgs> argsList = process(ARBITRARY_TIME, EV_KEY, KEY_UP, 1);
+ ASSERT_GT(argsList.size(), 0u);
+ argsList = process(ARBITRARY_TIME, EV_KEY, KEY_UP, 0);
+ ASSERT_GT(argsList.size(), 0u);
+ ASSERT_EQ(ui::LogicalDisplayId::INVALID, std::get<NotifyKeyArgs>(argsList.front()).displayId);
+}
+
+TEST_F(KeyboardInputMapperUnitTest, DisplayIdConfigurationChange_OrientationAware) {
+ // If the keyboard is orientation aware,
+ // key events should be associated with the internal viewport
+ addKeyByEvdevCode(KEY_UP, AKEYCODE_DPAD_UP);
+
+ mPropertyMap.addProperty("keyboard.orientationAware", "1");
+ mMapper = createInputMapper<KeyboardInputMapper>(*mDeviceContext, mReaderConfiguration,
+ AINPUT_SOURCE_KEYBOARD);
+
+ // Display id should be LogicalDisplayId::INVALID without any display configuration.
+ // ^--- already checked by the previous test
+
+ setDisplayOrientation(ui::ROTATION_0);
+ std::list<NotifyArgs> argsList = process(ARBITRARY_TIME, EV_KEY, KEY_UP, 1);
+ ASSERT_GT(argsList.size(), 0u);
+ argsList = process(ARBITRARY_TIME, EV_KEY, KEY_UP, 0);
+ ASSERT_GT(argsList.size(), 0u);
+ ASSERT_EQ(DISPLAY_ID, std::get<NotifyKeyArgs>(argsList.front()).displayId);
+
+ constexpr ui::LogicalDisplayId newDisplayId = ui::LogicalDisplayId{2};
+ DisplayViewport newViewport = createPrimaryViewport(ui::ROTATION_0);
+ newViewport.displayId = newDisplayId;
+ EXPECT_CALL((*mDevice), getAssociatedViewport).WillRepeatedly(Return(newViewport));
+ argsList = mMapper->reconfigure(ARBITRARY_TIME, mReaderConfiguration,
+ InputReaderConfiguration::Change::DISPLAY_INFO);
+ ASSERT_EQ(0u, argsList.size());
+ argsList = process(ARBITRARY_TIME, EV_KEY, KEY_UP, 1);
+ ASSERT_GT(argsList.size(), 0u);
+ argsList = process(ARBITRARY_TIME, EV_KEY, KEY_UP, 0);
+ ASSERT_GT(argsList.size(), 0u);
+ ASSERT_EQ(newDisplayId, std::get<NotifyKeyArgs>(argsList.front()).displayId);
+}
+
+TEST_F(KeyboardInputMapperUnitTest, GetKeyCodeState) {
+ EXPECT_CALL(mMockEventHub, getKeyCodeState(EVENTHUB_ID, AKEYCODE_A))
+ .WillRepeatedly(Return(AKEY_STATE_DOWN));
+ ASSERT_EQ(AKEY_STATE_DOWN, mMapper->getKeyCodeState(AINPUT_SOURCE_ANY, AKEYCODE_A));
+
+ EXPECT_CALL(mMockEventHub, getKeyCodeState(EVENTHUB_ID, AKEYCODE_A))
+ .WillRepeatedly(Return(AKEY_STATE_UP));
+ ASSERT_EQ(AKEY_STATE_UP, mMapper->getKeyCodeState(AINPUT_SOURCE_ANY, AKEYCODE_A));
+}
+
+TEST_F(KeyboardInputMapperUnitTest, GetKeyCodeForKeyLocation) {
+ EXPECT_CALL(mMockEventHub, getKeyCodeForKeyLocation(EVENTHUB_ID, _))
+ .WillRepeatedly(ReturnArg<1>());
+ EXPECT_CALL(mMockEventHub, getKeyCodeForKeyLocation(EVENTHUB_ID, AKEYCODE_Y))
+ .WillRepeatedly(Return(AKEYCODE_Z));
+ ASSERT_EQ(AKEYCODE_Z, mMapper->getKeyCodeForKeyLocation(AKEYCODE_Y))
+ << "If a mapping is available, the result is equal to the mapping";
+
+ ASSERT_EQ(AKEYCODE_A, mMapper->getKeyCodeForKeyLocation(AKEYCODE_A))
+ << "If no mapping is available, the result is the key location";
+}
+
+TEST_F(KeyboardInputMapperUnitTest, GetScanCodeState) {
+ EXPECT_CALL(mMockEventHub, getScanCodeState(EVENTHUB_ID, KEY_A))
+ .WillRepeatedly(Return(AKEY_STATE_DOWN));
+ ASSERT_EQ(AKEY_STATE_DOWN, mMapper->getScanCodeState(AINPUT_SOURCE_ANY, KEY_A));
+
+ EXPECT_CALL(mMockEventHub, getScanCodeState(EVENTHUB_ID, KEY_A))
+ .WillRepeatedly(Return(AKEY_STATE_UP));
+ ASSERT_EQ(AKEY_STATE_UP, mMapper->getScanCodeState(AINPUT_SOURCE_ANY, KEY_A));
+}
+
+TEST_F(KeyboardInputMapperUnitTest, Process_LockedKeysShouldToggleMetaStateAndLeds) {
+ EXPECT_CALL(mMockEventHub,
+ hasLed(EVENTHUB_ID, AnyOf(LED_CAPSL, LED_NUML, LED_SCROLLL /*NOTYPO*/)))
+ .WillRepeatedly(Return(true));
+ bool capsLockLed = true; // Initially on
+ bool numLockLed = false; // Initially off
+ bool scrollLockLed = false; // Initially off
+ EXPECT_CALL(mMockEventHub, setLedState(EVENTHUB_ID, LED_CAPSL, _))
+ .WillRepeatedly(SaveArg<2>(&capsLockLed));
+ EXPECT_CALL(mMockEventHub, setLedState(EVENTHUB_ID, LED_NUML, _))
+ .WillRepeatedly(SaveArg<2>(&numLockLed));
+ EXPECT_CALL(mMockEventHub, setLedState(EVENTHUB_ID, LED_SCROLLL /*NOTYPO*/, _))
+ .WillRepeatedly(SaveArg<2>(&scrollLockLed));
+ addKeyByEvdevCode(KEY_CAPSLOCK, AKEYCODE_CAPS_LOCK);
+ addKeyByEvdevCode(KEY_NUMLOCK, AKEYCODE_NUM_LOCK);
+ addKeyByEvdevCode(KEY_SCROLLLOCK, AKEYCODE_SCROLL_LOCK);
+
+ // In real operation, mappers pass new LED states to InputReader (via the context), which then
+ // calls back to the mappers to apply that state. Mimic the same thing here with mocks.
+ int32_t ledMetaState;
+ EXPECT_CALL(mMockInputReaderContext, updateLedMetaState(_))
+ .WillRepeatedly([&](int32_t newState) {
+ ledMetaState = newState;
+ mMapper->updateLedState(false);
+ });
+ EXPECT_CALL(mMockInputReaderContext, getLedMetaState())
+ .WillRepeatedly(testing::ReturnPointee(&ledMetaState));
+
+ ASSERT_THAT(mMapper->reset(ARBITRARY_TIME), IsEmpty());
+
+ // Initial metastate is AMETA_NONE.
+ ASSERT_EQ(AMETA_NONE, mMapper->getMetaState());
+
+ // Initialization should have turned all of the lights off.
+ ASSERT_FALSE(capsLockLed);
+ ASSERT_FALSE(numLockLed);
+ ASSERT_FALSE(scrollLockLed);
+
+ // Toggle caps lock on.
+ std::list<NotifyArgs> argsList = process(ARBITRARY_TIME, EV_KEY, KEY_CAPSLOCK, 1);
+ argsList = process(ARBITRARY_TIME, EV_KEY, KEY_CAPSLOCK, 0);
+ ASSERT_TRUE(capsLockLed);
+ ASSERT_FALSE(numLockLed);
+ ASSERT_FALSE(scrollLockLed);
+ ASSERT_EQ(AMETA_CAPS_LOCK_ON, mMapper->getMetaState());
+
+ // Toggle num lock on.
+ argsList = process(ARBITRARY_TIME, EV_KEY, KEY_NUMLOCK, 1);
+ argsList = process(ARBITRARY_TIME, EV_KEY, KEY_NUMLOCK, 0);
+ ASSERT_TRUE(capsLockLed);
+ ASSERT_TRUE(numLockLed);
+ ASSERT_FALSE(scrollLockLed);
+ ASSERT_EQ(AMETA_CAPS_LOCK_ON | AMETA_NUM_LOCK_ON, mMapper->getMetaState());
+
+ // Toggle caps lock off.
+ argsList = process(ARBITRARY_TIME, EV_KEY, KEY_CAPSLOCK, 1);
+ argsList = process(ARBITRARY_TIME, EV_KEY, KEY_CAPSLOCK, 0);
+ ASSERT_FALSE(capsLockLed);
+ ASSERT_TRUE(numLockLed);
+ ASSERT_FALSE(scrollLockLed);
+ ASSERT_EQ(AMETA_NUM_LOCK_ON, mMapper->getMetaState());
+
+ // Toggle scroll lock on.
+ argsList = process(ARBITRARY_TIME, EV_KEY, KEY_SCROLLLOCK, 1);
+ argsList = process(ARBITRARY_TIME, EV_KEY, KEY_SCROLLLOCK, 0);
+ ASSERT_FALSE(capsLockLed);
+ ASSERT_TRUE(numLockLed);
+ ASSERT_TRUE(scrollLockLed);
+ ASSERT_EQ(AMETA_NUM_LOCK_ON | AMETA_SCROLL_LOCK_ON, mMapper->getMetaState());
+
+ // Toggle num lock off.
+ argsList = process(ARBITRARY_TIME, EV_KEY, KEY_NUMLOCK, 1);
+ argsList = process(ARBITRARY_TIME, EV_KEY, KEY_NUMLOCK, 0);
+ ASSERT_FALSE(capsLockLed);
+ ASSERT_FALSE(numLockLed);
+ ASSERT_TRUE(scrollLockLed);
+ ASSERT_EQ(AMETA_SCROLL_LOCK_ON, mMapper->getMetaState());
+
+ // Toggle scroll lock off.
+ argsList = process(ARBITRARY_TIME, EV_KEY, KEY_SCROLLLOCK, 1);
+ argsList = process(ARBITRARY_TIME, EV_KEY, KEY_SCROLLLOCK, 0);
+ ASSERT_FALSE(capsLockLed);
+ ASSERT_FALSE(numLockLed);
+ ASSERT_FALSE(scrollLockLed);
+ ASSERT_EQ(AMETA_NONE, mMapper->getMetaState());
+}
+
+TEST_F(KeyboardInputMapperUnitTest, DisablingDeviceResetsPressedKeys) {
+ const int32_t USAGE_A = 0x070004;
+ addKeyByEvdevCode(KEY_HOME, AKEYCODE_HOME, POLICY_FLAG_WAKE);
+ addKeyByUsageCode(USAGE_A, AKEYCODE_A, POLICY_FLAG_WAKE);
+
+ // Key down by scan code.
+ std::list<NotifyArgs> argsList = process(ARBITRARY_TIME, EV_KEY, KEY_HOME, 1);
+ NotifyKeyArgs args = expectSingleKeyArg(argsList);
+ ASSERT_EQ(DEVICE_ID, args.deviceId);
+ ASSERT_EQ(AINPUT_SOURCE_KEYBOARD, args.source);
+ ASSERT_EQ(ARBITRARY_TIME, args.eventTime);
+ ASSERT_EQ(AKEY_EVENT_ACTION_DOWN, args.action);
+ ASSERT_EQ(AKEYCODE_HOME, args.keyCode);
+ ASSERT_EQ(KEY_HOME, args.scanCode);
+ ASSERT_EQ(AKEY_EVENT_FLAG_FROM_SYSTEM, args.flags);
+
+ // Disable device, it should synthesize cancellation events for down events.
+ mReaderConfiguration.disabledDevices.insert(DEVICE_ID);
+ argsList = mMapper->reconfigure(ARBITRARY_TIME, mReaderConfiguration,
+ InputReaderConfiguration::Change::ENABLED_STATE);
+ argsList += mMapper->reset(ARBITRARY_TIME);
+ args = expectSingleKeyArg(argsList);
+ ASSERT_EQ(AKEY_EVENT_ACTION_UP, args.action);
+ ASSERT_EQ(AKEYCODE_HOME, args.keyCode);
+ ASSERT_EQ(KEY_HOME, args.scanCode);
+ ASSERT_EQ(AKEY_EVENT_FLAG_FROM_SYSTEM | AKEY_EVENT_FLAG_CANCELED, args.flags);
+}
+
+TEST_F(KeyboardInputMapperUnitTest, Configure_AssignKeyboardLayoutInfo) {
+ std::list<NotifyArgs> unused =
+ mMapper->reconfigure(ARBITRARY_TIME, mReaderConfiguration, /*changes=*/{});
+
+ int32_t generation = mDevice->getGeneration();
+ mReaderConfiguration.keyboardLayoutAssociations.insert(
+ {mIdentifier.location, DEVICE_KEYBOARD_LAYOUT_INFO});
+
+ unused += mMapper->reconfigure(ARBITRARY_TIME, mReaderConfiguration,
+ InputReaderConfiguration::Change::KEYBOARD_LAYOUT_ASSOCIATION);
+
+ InputDeviceInfo deviceInfo;
+ mMapper->populateDeviceInfo(deviceInfo);
+ ASSERT_EQ(DEVICE_KEYBOARD_LAYOUT_INFO.languageTag,
+ deviceInfo.getKeyboardLayoutInfo()->languageTag);
+ ASSERT_EQ(DEVICE_KEYBOARD_LAYOUT_INFO.layoutType,
+ deviceInfo.getKeyboardLayoutInfo()->layoutType);
+ ASSERT_GT(mDevice->getGeneration(), generation);
+
+ // Call change layout association with the same values: Generation shouldn't change
+ generation = mDevice->getGeneration();
+ unused += mMapper->reconfigure(ARBITRARY_TIME, mReaderConfiguration,
+ InputReaderConfiguration::Change::KEYBOARD_LAYOUT_ASSOCIATION);
+ ASSERT_EQ(mDevice->getGeneration(), generation);
+}
+
+TEST_F(KeyboardInputMapperUnitTest, LayoutInfoCorrectlyMapped) {
+ EXPECT_CALL(mMockEventHub, getRawLayoutInfo(EVENTHUB_ID))
+ .WillRepeatedly(Return(RawLayoutInfo{.languageTag = "en", .layoutType = "extended"}));
+
+ // Configuration
+ std::list<NotifyArgs> unused =
+ mMapper->reconfigure(ARBITRARY_TIME, mReaderConfiguration, /*changes=*/{});
+
+ InputDeviceInfo deviceInfo;
+ mMapper->populateDeviceInfo(deviceInfo);
+ ASSERT_EQ("en", deviceInfo.getKeyboardLayoutInfo()->languageTag);
+ ASSERT_EQ("extended", deviceInfo.getKeyboardLayoutInfo()->layoutType);
+}
+
+TEST_F(KeyboardInputMapperUnitTest, Process_GestureEventToSetFlagKeepTouchMode) {
+ addKeyByEvdevCode(KEY_LEFT, AKEYCODE_DPAD_LEFT, POLICY_FLAG_GESTURE);
+
+ // Key down
+ std::list<NotifyArgs> argsList = process(ARBITRARY_TIME, EV_KEY, KEY_LEFT, 1);
+ ASSERT_EQ(AKEY_EVENT_FLAG_FROM_SYSTEM | AKEY_EVENT_FLAG_KEEP_TOUCH_MODE,
+ expectSingleKeyArg(argsList).flags);
+}
+
+TEST_F_WITH_FLAGS(KeyboardInputMapperUnitTest, WakeBehavior_AlphabeticKeyboard,
+ REQUIRES_FLAGS_ENABLED(ACONFIG_FLAG(com::android::input::flags,
+ enable_alphabetic_keyboard_wake))) {
+ // For internal alphabetic devices, keys will trigger wake on key down.
+
+ addKeyByEvdevCode(KEY_A, AKEYCODE_A);
+ addKeyByEvdevCode(KEY_HOME, AKEYCODE_HOME);
+ addKeyByEvdevCode(KEY_PLAYPAUSE, AKEYCODE_MEDIA_PLAY_PAUSE);
+
+ std::list<NotifyArgs> argsList = process(ARBITRARY_TIME, EV_KEY, KEY_A, 1);
+ ASSERT_EQ(POLICY_FLAG_WAKE, expectSingleKeyArg(argsList).policyFlags);
+
+ argsList = process(ARBITRARY_TIME + 1, EV_KEY, KEY_A, 0);
+ ASSERT_EQ(uint32_t(0), expectSingleKeyArg(argsList).policyFlags);
+
+ argsList = process(ARBITRARY_TIME, EV_KEY, KEY_HOME, 1);
+ ASSERT_EQ(POLICY_FLAG_WAKE, expectSingleKeyArg(argsList).policyFlags);
+
+ argsList = process(ARBITRARY_TIME + 1, EV_KEY, KEY_HOME, 0);
+ ASSERT_EQ(uint32_t(0), expectSingleKeyArg(argsList).policyFlags);
+
+ argsList = process(ARBITRARY_TIME, EV_KEY, KEY_PLAYPAUSE, 1);
+ ASSERT_EQ(POLICY_FLAG_WAKE, expectSingleKeyArg(argsList).policyFlags);
+
+ argsList = process(ARBITRARY_TIME + 1, EV_KEY, KEY_PLAYPAUSE, 0);
+ ASSERT_EQ(uint32_t(0), expectSingleKeyArg(argsList).policyFlags);
+}
+
+// --- KeyboardInputMapperTest ---
+
+// TODO(b/283812079): convert the tests for this class, which use multiple mappers each, to use
+// InputMapperUnitTest.
+class KeyboardInputMapperTest : public InputMapperTest {
+protected:
+ void SetUp() override {
+ InputMapperTest::SetUp(DEVICE_CLASSES | InputDeviceClass::KEYBOARD |
+ InputDeviceClass::ALPHAKEY);
+ }
+ const std::string UNIQUE_ID = "local:0";
+
+ void testDPadKeyRotation(KeyboardInputMapper& mapper, int32_t originalScanCode,
+ int32_t originalKeyCode, int32_t rotatedKeyCode,
+ ui::LogicalDisplayId displayId = ui::LogicalDisplayId::INVALID);
+};
+
+void KeyboardInputMapperTest::testDPadKeyRotation(KeyboardInputMapper& mapper,
+ int32_t originalScanCode, int32_t originalKeyCode,
+ int32_t rotatedKeyCode,
+ ui::LogicalDisplayId displayId) {
+ NotifyKeyArgs args;
+
+ process(mapper, ARBITRARY_TIME, READ_TIME, EV_KEY, originalScanCode, 1);
+ ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyKeyWasCalled(&args));
+ ASSERT_EQ(AKEY_EVENT_ACTION_DOWN, args.action);
+ ASSERT_EQ(originalScanCode, args.scanCode);
+ ASSERT_EQ(rotatedKeyCode, args.keyCode);
+ ASSERT_EQ(displayId, args.displayId);
+
+ process(mapper, ARBITRARY_TIME, READ_TIME, EV_KEY, originalScanCode, 0);
+ ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyKeyWasCalled(&args));
+ ASSERT_EQ(AKEY_EVENT_ACTION_UP, args.action);
+ ASSERT_EQ(originalScanCode, args.scanCode);
+ ASSERT_EQ(rotatedKeyCode, args.keyCode);
+ ASSERT_EQ(displayId, args.displayId);
+}
+
+TEST_F(KeyboardInputMapperTest, Configure_AssignsDisplayPort) {
+ // keyboard 1.
+ mFakeEventHub->addKey(EVENTHUB_ID, KEY_UP, 0, AKEYCODE_DPAD_UP, 0);
+ mFakeEventHub->addKey(EVENTHUB_ID, KEY_RIGHT, 0, AKEYCODE_DPAD_RIGHT, 0);
+ mFakeEventHub->addKey(EVENTHUB_ID, KEY_DOWN, 0, AKEYCODE_DPAD_DOWN, 0);
+ mFakeEventHub->addKey(EVENTHUB_ID, KEY_LEFT, 0, AKEYCODE_DPAD_LEFT, 0);
+
+ // keyboard 2.
+ const std::string USB2 = "USB2";
+ const std::string DEVICE_NAME2 = "KEYBOARD2";
+ constexpr int32_t SECOND_DEVICE_ID = DEVICE_ID + 1;
+ constexpr int32_t SECOND_EVENTHUB_ID = EVENTHUB_ID + 1;
+ std::shared_ptr<InputDevice> device2 =
+ newDevice(SECOND_DEVICE_ID, DEVICE_NAME2, USB2, SECOND_EVENTHUB_ID,
+ ftl::Flags<InputDeviceClass>(0));
+
+ mFakeEventHub->addKey(SECOND_EVENTHUB_ID, KEY_UP, 0, AKEYCODE_DPAD_UP, 0);
+ mFakeEventHub->addKey(SECOND_EVENTHUB_ID, KEY_RIGHT, 0, AKEYCODE_DPAD_RIGHT, 0);
+ mFakeEventHub->addKey(SECOND_EVENTHUB_ID, KEY_DOWN, 0, AKEYCODE_DPAD_DOWN, 0);
+ mFakeEventHub->addKey(SECOND_EVENTHUB_ID, KEY_LEFT, 0, AKEYCODE_DPAD_LEFT, 0);
+
+ KeyboardInputMapper& mapper =
+ constructAndAddMapper<KeyboardInputMapper>(AINPUT_SOURCE_KEYBOARD);
+
+ device2->addEmptyEventHubDevice(SECOND_EVENTHUB_ID);
+ KeyboardInputMapper& mapper2 =
+ device2->constructAndAddMapper<KeyboardInputMapper>(SECOND_EVENTHUB_ID,
+ mFakePolicy
+ ->getReaderConfiguration(),
+ AINPUT_SOURCE_KEYBOARD);
+ std::list<NotifyArgs> unused =
+ device2->configure(ARBITRARY_TIME, mFakePolicy->getReaderConfiguration(),
+ /*changes=*/{});
+ unused += device2->reset(ARBITRARY_TIME);
+
+ // Prepared displays and associated info.
+ constexpr uint8_t hdmi1 = 0;
+ constexpr uint8_t hdmi2 = 1;
+ const std::string SECONDARY_UNIQUE_ID = "local:1";
+
+ mFakePolicy->addInputPortAssociation(DEVICE_LOCATION, hdmi1);
+ mFakePolicy->addInputPortAssociation(USB2, hdmi2);
+
+ // No associated display viewport found, should disable the device.
+ unused += device2->configure(ARBITRARY_TIME, mFakePolicy->getReaderConfiguration(),
+ InputReaderConfiguration::Change::DISPLAY_INFO);
+ ASSERT_FALSE(device2->isEnabled());
+
+ // Prepare second display.
+ constexpr ui::LogicalDisplayId newDisplayId = ui::LogicalDisplayId{2};
+ setDisplayInfoAndReconfigure(DISPLAY_ID, DISPLAY_WIDTH, DISPLAY_HEIGHT, ui::ROTATION_0,
+ UNIQUE_ID, hdmi1, ViewportType::INTERNAL);
+ setDisplayInfoAndReconfigure(newDisplayId, DISPLAY_WIDTH, DISPLAY_HEIGHT, ui::ROTATION_0,
+ SECONDARY_UNIQUE_ID, hdmi2, ViewportType::EXTERNAL);
+ // Default device will reconfigure above, need additional reconfiguration for another device.
+ unused += device2->configure(ARBITRARY_TIME, mFakePolicy->getReaderConfiguration(),
+ InputReaderConfiguration::Change::DISPLAY_INFO);
+
+ // Device should be enabled after the associated display is found.
+ ASSERT_TRUE(mDevice->isEnabled());
+ ASSERT_TRUE(device2->isEnabled());
+
+ // Test pad key events
+ ASSERT_NO_FATAL_FAILURE(
+ testDPadKeyRotation(mapper, KEY_UP, AKEYCODE_DPAD_UP, AKEYCODE_DPAD_UP, DISPLAY_ID));
+ ASSERT_NO_FATAL_FAILURE(testDPadKeyRotation(mapper, KEY_RIGHT, AKEYCODE_DPAD_RIGHT,
+ AKEYCODE_DPAD_RIGHT, DISPLAY_ID));
+ ASSERT_NO_FATAL_FAILURE(testDPadKeyRotation(mapper, KEY_DOWN, AKEYCODE_DPAD_DOWN,
+ AKEYCODE_DPAD_DOWN, DISPLAY_ID));
+ ASSERT_NO_FATAL_FAILURE(testDPadKeyRotation(mapper, KEY_LEFT, AKEYCODE_DPAD_LEFT,
+ AKEYCODE_DPAD_LEFT, DISPLAY_ID));
+
+ ASSERT_NO_FATAL_FAILURE(
+ testDPadKeyRotation(mapper2, KEY_UP, AKEYCODE_DPAD_UP, AKEYCODE_DPAD_UP, newDisplayId));
+ ASSERT_NO_FATAL_FAILURE(testDPadKeyRotation(mapper2, KEY_RIGHT, AKEYCODE_DPAD_RIGHT,
+ AKEYCODE_DPAD_RIGHT, newDisplayId));
+ ASSERT_NO_FATAL_FAILURE(testDPadKeyRotation(mapper2, KEY_DOWN, AKEYCODE_DPAD_DOWN,
+ AKEYCODE_DPAD_DOWN, newDisplayId));
+ ASSERT_NO_FATAL_FAILURE(testDPadKeyRotation(mapper2, KEY_LEFT, AKEYCODE_DPAD_LEFT,
+ AKEYCODE_DPAD_LEFT, newDisplayId));
+}
+
+TEST_F(KeyboardInputMapperTest, Process_LockedKeysShouldToggleAfterReattach) {
+ mFakeEventHub->addLed(EVENTHUB_ID, LED_CAPSL, true /*initially on*/);
+ mFakeEventHub->addLed(EVENTHUB_ID, LED_NUML, false /*initially off*/);
+ mFakeEventHub->addLed(EVENTHUB_ID, LED_SCROLLL, false /*initially off*/);
+ mFakeEventHub->addKey(EVENTHUB_ID, KEY_CAPSLOCK, 0, AKEYCODE_CAPS_LOCK, 0);
+ mFakeEventHub->addKey(EVENTHUB_ID, KEY_NUMLOCK, 0, AKEYCODE_NUM_LOCK, 0);
+ mFakeEventHub->addKey(EVENTHUB_ID, KEY_SCROLLLOCK, 0, AKEYCODE_SCROLL_LOCK, 0);
+
+ KeyboardInputMapper& mapper =
+ constructAndAddMapper<KeyboardInputMapper>(AINPUT_SOURCE_KEYBOARD);
+ // Initial metastate is AMETA_NONE.
+ ASSERT_EQ(AMETA_NONE, mapper.getMetaState());
+
+ // Initialization should have turned all of the lights off.
+ ASSERT_FALSE(mFakeEventHub->getLedState(EVENTHUB_ID, LED_CAPSL));
+ ASSERT_FALSE(mFakeEventHub->getLedState(EVENTHUB_ID, LED_NUML));
+ ASSERT_FALSE(mFakeEventHub->getLedState(EVENTHUB_ID, LED_SCROLLL));
+
+ // Toggle caps lock on.
+ process(mapper, ARBITRARY_TIME, READ_TIME, EV_KEY, KEY_CAPSLOCK, 1);
+ process(mapper, ARBITRARY_TIME, READ_TIME, EV_KEY, KEY_CAPSLOCK, 0);
+ ASSERT_TRUE(mFakeEventHub->getLedState(EVENTHUB_ID, LED_CAPSL));
+ ASSERT_EQ(AMETA_CAPS_LOCK_ON, mapper.getMetaState());
+
+ // Toggle num lock on.
+ process(mapper, ARBITRARY_TIME, READ_TIME, EV_KEY, KEY_NUMLOCK, 1);
+ process(mapper, ARBITRARY_TIME, READ_TIME, EV_KEY, KEY_NUMLOCK, 0);
+ ASSERT_TRUE(mFakeEventHub->getLedState(EVENTHUB_ID, LED_NUML));
+ ASSERT_EQ(AMETA_CAPS_LOCK_ON | AMETA_NUM_LOCK_ON, mapper.getMetaState());
+
+ // Toggle scroll lock on.
+ process(mapper, ARBITRARY_TIME, READ_TIME, EV_KEY, KEY_SCROLLLOCK, 1);
+ process(mapper, ARBITRARY_TIME, READ_TIME, EV_KEY, KEY_SCROLLLOCK, 0);
+ ASSERT_TRUE(mFakeEventHub->getLedState(EVENTHUB_ID, LED_SCROLLL));
+ ASSERT_EQ(AMETA_CAPS_LOCK_ON | AMETA_NUM_LOCK_ON | AMETA_SCROLL_LOCK_ON, mapper.getMetaState());
+
+ mFakeEventHub->removeDevice(EVENTHUB_ID);
+ mReader->loopOnce();
+
+ // keyboard 2 should default toggle keys.
+ const std::string USB2 = "USB2";
+ const std::string DEVICE_NAME2 = "KEYBOARD2";
+ constexpr int32_t SECOND_DEVICE_ID = DEVICE_ID + 1;
+ constexpr int32_t SECOND_EVENTHUB_ID = EVENTHUB_ID + 1;
+ std::shared_ptr<InputDevice> device2 =
+ newDevice(SECOND_DEVICE_ID, DEVICE_NAME2, USB2, SECOND_EVENTHUB_ID,
+ ftl::Flags<InputDeviceClass>(0));
+ mFakeEventHub->addLed(SECOND_EVENTHUB_ID, LED_CAPSL, true /*initially on*/);
+ mFakeEventHub->addLed(SECOND_EVENTHUB_ID, LED_NUML, false /*initially off*/);
+ mFakeEventHub->addLed(SECOND_EVENTHUB_ID, LED_SCROLLL, false /*initially off*/);
+ mFakeEventHub->addKey(SECOND_EVENTHUB_ID, KEY_CAPSLOCK, 0, AKEYCODE_CAPS_LOCK, 0);
+ mFakeEventHub->addKey(SECOND_EVENTHUB_ID, KEY_NUMLOCK, 0, AKEYCODE_NUM_LOCK, 0);
+ mFakeEventHub->addKey(SECOND_EVENTHUB_ID, KEY_SCROLLLOCK, 0, AKEYCODE_SCROLL_LOCK, 0);
+
+ device2->addEmptyEventHubDevice(SECOND_EVENTHUB_ID);
+ KeyboardInputMapper& mapper2 =
+ device2->constructAndAddMapper<KeyboardInputMapper>(SECOND_EVENTHUB_ID,
+ mFakePolicy
+ ->getReaderConfiguration(),
+ AINPUT_SOURCE_KEYBOARD);
+ std::list<NotifyArgs> unused =
+ device2->configure(ARBITRARY_TIME, mFakePolicy->getReaderConfiguration(),
+ /*changes=*/{});
+ unused += device2->reset(ARBITRARY_TIME);
+
+ ASSERT_TRUE(mFakeEventHub->getLedState(SECOND_EVENTHUB_ID, LED_CAPSL));
+ ASSERT_TRUE(mFakeEventHub->getLedState(SECOND_EVENTHUB_ID, LED_NUML));
+ ASSERT_TRUE(mFakeEventHub->getLedState(SECOND_EVENTHUB_ID, LED_SCROLLL));
+ ASSERT_EQ(AMETA_CAPS_LOCK_ON | AMETA_NUM_LOCK_ON | AMETA_SCROLL_LOCK_ON,
+ mapper2.getMetaState());
+}
+
+TEST_F(KeyboardInputMapperTest, Process_toggleCapsLockState) {
+ mFakeEventHub->addKey(EVENTHUB_ID, KEY_CAPSLOCK, 0, AKEYCODE_CAPS_LOCK, 0);
+ mFakeEventHub->addKey(EVENTHUB_ID, KEY_NUMLOCK, 0, AKEYCODE_NUM_LOCK, 0);
+ mFakeEventHub->addKey(EVENTHUB_ID, KEY_SCROLLLOCK, 0, AKEYCODE_SCROLL_LOCK, 0);
+
+ // Suppose we have two mappers. (DPAD + KEYBOARD)
+ constructAndAddMapper<KeyboardInputMapper>(AINPUT_SOURCE_DPAD);
+ KeyboardInputMapper& mapper =
+ constructAndAddMapper<KeyboardInputMapper>(AINPUT_SOURCE_KEYBOARD);
+ // Initial metastate is AMETA_NONE.
+ ASSERT_EQ(AMETA_NONE, mapper.getMetaState());
+
+ mReader->toggleCapsLockState(DEVICE_ID);
+ ASSERT_EQ(AMETA_CAPS_LOCK_ON, mapper.getMetaState());
+}
+
+TEST_F(KeyboardInputMapperTest, Process_ResetLockedModifierState) {
+ mFakeEventHub->addKey(EVENTHUB_ID, KEY_CAPSLOCK, 0, AKEYCODE_CAPS_LOCK, 0);
+ mFakeEventHub->addKey(EVENTHUB_ID, KEY_NUMLOCK, 0, AKEYCODE_NUM_LOCK, 0);
+ mFakeEventHub->addKey(EVENTHUB_ID, KEY_SCROLLLOCK, 0, AKEYCODE_SCROLL_LOCK, 0);
+
+ KeyboardInputMapper& mapper =
+ constructAndAddMapper<KeyboardInputMapper>(AINPUT_SOURCE_KEYBOARD);
+ // Initial metastate is AMETA_NONE.
+ ASSERT_EQ(AMETA_NONE, mapper.getMetaState());
+
+ // Toggle caps lock on.
+ process(mapper, ARBITRARY_TIME, READ_TIME, EV_KEY, KEY_CAPSLOCK, 1);
+ process(mapper, ARBITRARY_TIME, READ_TIME, EV_KEY, KEY_CAPSLOCK, 0);
+
+ // Toggle num lock on.
+ process(mapper, ARBITRARY_TIME, READ_TIME, EV_KEY, KEY_NUMLOCK, 1);
+ process(mapper, ARBITRARY_TIME, READ_TIME, EV_KEY, KEY_NUMLOCK, 0);
+
+ // Toggle scroll lock on.
+ process(mapper, ARBITRARY_TIME, READ_TIME, EV_KEY, KEY_SCROLLLOCK, 1);
+ process(mapper, ARBITRARY_TIME, READ_TIME, EV_KEY, KEY_SCROLLLOCK, 0);
+ ASSERT_EQ(AMETA_CAPS_LOCK_ON | AMETA_NUM_LOCK_ON | AMETA_SCROLL_LOCK_ON, mapper.getMetaState());
+
+ mReader->resetLockedModifierState();
+ ASSERT_EQ(AMETA_NONE, mapper.getMetaState());
+}
+
+TEST_F(KeyboardInputMapperTest, Process_LockedKeysShouldToggleInMultiDevices) {
+ // keyboard 1.
+ mFakeEventHub->addLed(EVENTHUB_ID, LED_CAPSL, true /*initially on*/);
+ mFakeEventHub->addLed(EVENTHUB_ID, LED_NUML, false /*initially off*/);
+ mFakeEventHub->addLed(EVENTHUB_ID, LED_SCROLLL, false /*initially off*/);
+ mFakeEventHub->addKey(EVENTHUB_ID, KEY_CAPSLOCK, 0, AKEYCODE_CAPS_LOCK, 0);
+ mFakeEventHub->addKey(EVENTHUB_ID, KEY_NUMLOCK, 0, AKEYCODE_NUM_LOCK, 0);
+ mFakeEventHub->addKey(EVENTHUB_ID, KEY_SCROLLLOCK, 0, AKEYCODE_SCROLL_LOCK, 0);
+
+ KeyboardInputMapper& mapper1 =
+ constructAndAddMapper<KeyboardInputMapper>(AINPUT_SOURCE_KEYBOARD);
+
+ // keyboard 2.
+ const std::string USB2 = "USB2";
+ const std::string DEVICE_NAME2 = "KEYBOARD2";
+ constexpr int32_t SECOND_DEVICE_ID = DEVICE_ID + 1;
+ constexpr int32_t SECOND_EVENTHUB_ID = EVENTHUB_ID + 1;
+ std::shared_ptr<InputDevice> device2 =
+ newDevice(SECOND_DEVICE_ID, DEVICE_NAME2, USB2, SECOND_EVENTHUB_ID,
+ ftl::Flags<InputDeviceClass>(0));
+ mFakeEventHub->addLed(SECOND_EVENTHUB_ID, LED_CAPSL, true /*initially on*/);
+ mFakeEventHub->addLed(SECOND_EVENTHUB_ID, LED_NUML, false /*initially off*/);
+ mFakeEventHub->addLed(SECOND_EVENTHUB_ID, LED_SCROLLL, false /*initially off*/);
+ mFakeEventHub->addKey(SECOND_EVENTHUB_ID, KEY_CAPSLOCK, 0, AKEYCODE_CAPS_LOCK, 0);
+ mFakeEventHub->addKey(SECOND_EVENTHUB_ID, KEY_NUMLOCK, 0, AKEYCODE_NUM_LOCK, 0);
+ mFakeEventHub->addKey(SECOND_EVENTHUB_ID, KEY_SCROLLLOCK, 0, AKEYCODE_SCROLL_LOCK, 0);
+
+ device2->addEmptyEventHubDevice(SECOND_EVENTHUB_ID);
+ KeyboardInputMapper& mapper2 =
+ device2->constructAndAddMapper<KeyboardInputMapper>(SECOND_EVENTHUB_ID,
+ mFakePolicy
+ ->getReaderConfiguration(),
+ AINPUT_SOURCE_KEYBOARD);
+ std::list<NotifyArgs> unused =
+ device2->configure(ARBITRARY_TIME, mFakePolicy->getReaderConfiguration(),
+ /*changes=*/{});
+ unused += device2->reset(ARBITRARY_TIME);
+
+ // Initial metastate is AMETA_NONE.
+ ASSERT_EQ(AMETA_NONE, mapper1.getMetaState());
+ ASSERT_EQ(AMETA_NONE, mapper2.getMetaState());
+
+ // Toggle num lock on and off.
+ process(mapper1, ARBITRARY_TIME, READ_TIME, EV_KEY, KEY_NUMLOCK, 1);
+ process(mapper1, ARBITRARY_TIME, READ_TIME, EV_KEY, KEY_NUMLOCK, 0);
+ ASSERT_TRUE(mFakeEventHub->getLedState(EVENTHUB_ID, LED_NUML));
+ ASSERT_EQ(AMETA_NUM_LOCK_ON, mapper1.getMetaState());
+ ASSERT_EQ(AMETA_NUM_LOCK_ON, mapper2.getMetaState());
+
+ process(mapper1, ARBITRARY_TIME, READ_TIME, EV_KEY, KEY_NUMLOCK, 1);
+ process(mapper1, ARBITRARY_TIME, READ_TIME, EV_KEY, KEY_NUMLOCK, 0);
+ ASSERT_FALSE(mFakeEventHub->getLedState(EVENTHUB_ID, LED_NUML));
+ ASSERT_EQ(AMETA_NONE, mapper1.getMetaState());
+ ASSERT_EQ(AMETA_NONE, mapper2.getMetaState());
+
+ // Toggle caps lock on and off.
+ process(mapper1, ARBITRARY_TIME, READ_TIME, EV_KEY, KEY_CAPSLOCK, 1);
+ process(mapper1, ARBITRARY_TIME, READ_TIME, EV_KEY, KEY_CAPSLOCK, 0);
+ ASSERT_TRUE(mFakeEventHub->getLedState(EVENTHUB_ID, LED_CAPSL));
+ ASSERT_EQ(AMETA_CAPS_LOCK_ON, mapper1.getMetaState());
+ ASSERT_EQ(AMETA_CAPS_LOCK_ON, mapper2.getMetaState());
+
+ process(mapper1, ARBITRARY_TIME, READ_TIME, EV_KEY, KEY_CAPSLOCK, 1);
+ process(mapper1, ARBITRARY_TIME, READ_TIME, EV_KEY, KEY_CAPSLOCK, 0);
+ ASSERT_FALSE(mFakeEventHub->getLedState(EVENTHUB_ID, LED_CAPSL));
+ ASSERT_EQ(AMETA_NONE, mapper1.getMetaState());
+ ASSERT_EQ(AMETA_NONE, mapper2.getMetaState());
+
+ // Toggle scroll lock on and off.
+ process(mapper1, ARBITRARY_TIME, READ_TIME, EV_KEY, KEY_SCROLLLOCK, 1);
+ process(mapper1, ARBITRARY_TIME, READ_TIME, EV_KEY, KEY_SCROLLLOCK, 0);
+ ASSERT_TRUE(mFakeEventHub->getLedState(EVENTHUB_ID, LED_SCROLLL));
+ ASSERT_EQ(AMETA_SCROLL_LOCK_ON, mapper1.getMetaState());
+ ASSERT_EQ(AMETA_SCROLL_LOCK_ON, mapper2.getMetaState());
+
+ process(mapper1, ARBITRARY_TIME, READ_TIME, EV_KEY, KEY_SCROLLLOCK, 1);
+ process(mapper1, ARBITRARY_TIME, READ_TIME, EV_KEY, KEY_SCROLLLOCK, 0);
+ ASSERT_FALSE(mFakeEventHub->getLedState(EVENTHUB_ID, LED_SCROLLL));
+ ASSERT_EQ(AMETA_NONE, mapper1.getMetaState());
+ ASSERT_EQ(AMETA_NONE, mapper2.getMetaState());
+}
+
+/**
+ * When there is more than one KeyboardInputMapper for an InputDevice, each mapper should produce
+ * events that use the shared keyboard source across all mappers. This is to ensure that each
+ * input device generates key events in a consistent manner, regardless of which mapper produces
+ * the event.
+ */
+TEST_F(KeyboardInputMapperTest, UsesSharedKeyboardSource) {
+ mFakeEventHub->addKey(EVENTHUB_ID, KEY_HOME, 0, AKEYCODE_HOME, POLICY_FLAG_WAKE);
+
+ // Add a mapper with SOURCE_KEYBOARD
+ KeyboardInputMapper& keyboardMapper =
+ constructAndAddMapper<KeyboardInputMapper>(AINPUT_SOURCE_KEYBOARD);
+
+ process(keyboardMapper, ARBITRARY_TIME, 0, EV_KEY, KEY_HOME, 1);
+ ASSERT_NO_FATAL_FAILURE(
+ mFakeListener->assertNotifyKeyWasCalled(WithSource(AINPUT_SOURCE_KEYBOARD)));
+ process(keyboardMapper, ARBITRARY_TIME, 0, EV_KEY, KEY_HOME, 0);
+ ASSERT_NO_FATAL_FAILURE(
+ mFakeListener->assertNotifyKeyWasCalled(WithSource(AINPUT_SOURCE_KEYBOARD)));
+
+ // Add a mapper with SOURCE_DPAD
+ KeyboardInputMapper& dpadMapper =
+ constructAndAddMapper<KeyboardInputMapper>(AINPUT_SOURCE_DPAD);
+ for (auto* mapper : {&keyboardMapper, &dpadMapper}) {
+ process(*mapper, ARBITRARY_TIME, 0, EV_KEY, KEY_HOME, 1);
+ ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyKeyWasCalled(
+ WithSource(AINPUT_SOURCE_KEYBOARD | AINPUT_SOURCE_DPAD)));
+ process(*mapper, ARBITRARY_TIME, 0, EV_KEY, KEY_HOME, 0);
+ ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyKeyWasCalled(
+ WithSource(AINPUT_SOURCE_KEYBOARD | AINPUT_SOURCE_DPAD)));
+ }
+
+ // Add a mapper with SOURCE_GAMEPAD
+ KeyboardInputMapper& gamepadMapper =
+ constructAndAddMapper<KeyboardInputMapper>(AINPUT_SOURCE_GAMEPAD);
+ for (auto* mapper : {&keyboardMapper, &dpadMapper, &gamepadMapper}) {
+ process(*mapper, ARBITRARY_TIME, 0, EV_KEY, KEY_HOME, 1);
+ ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyKeyWasCalled(
+ WithSource(AINPUT_SOURCE_KEYBOARD | AINPUT_SOURCE_DPAD | AINPUT_SOURCE_GAMEPAD)));
+ process(*mapper, ARBITRARY_TIME, 0, EV_KEY, KEY_HOME, 0);
+ ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyKeyWasCalled(
+ WithSource(AINPUT_SOURCE_KEYBOARD | AINPUT_SOURCE_DPAD | AINPUT_SOURCE_GAMEPAD)));
+ }
+}
+
+// --- KeyboardInputMapperTest_ExternalAlphabeticDevice ---
+
+class KeyboardInputMapperTest_ExternalAlphabeticDevice : public KeyboardInputMapperUnitTest {
+protected:
+ void SetUp() override {
+ InputMapperUnitTest::SetUp();
+ ON_CALL((*mDevice), getSources).WillByDefault(Return(AINPUT_SOURCE_KEYBOARD));
+ ON_CALL((*mDevice), getKeyboardType).WillByDefault(Return(KeyboardType::ALPHABETIC));
+ ON_CALL((*mDevice), isExternal).WillByDefault(Return(true));
+ EXPECT_CALL(mMockEventHub, getDeviceClasses(EVENTHUB_ID))
+ .WillRepeatedly(Return(InputDeviceClass::KEYBOARD | InputDeviceClass::ALPHAKEY |
+ InputDeviceClass::EXTERNAL));
+ mMapper = createInputMapper<KeyboardInputMapper>(*mDeviceContext, mReaderConfiguration,
+ AINPUT_SOURCE_KEYBOARD);
+ }
+};
+
+// --- KeyboardInputMapperTest_ExternalNonAlphabeticDevice ---
+
+class KeyboardInputMapperTest_ExternalNonAlphabeticDevice : public KeyboardInputMapperUnitTest {
+protected:
+ void SetUp() override {
+ InputMapperUnitTest::SetUp();
+ ON_CALL((*mDevice), getSources).WillByDefault(Return(AINPUT_SOURCE_KEYBOARD));
+ ON_CALL((*mDevice), getKeyboardType).WillByDefault(Return(KeyboardType::NON_ALPHABETIC));
+ ON_CALL((*mDevice), isExternal).WillByDefault(Return(true));
+ EXPECT_CALL(mMockEventHub, getDeviceClasses(EVENTHUB_ID))
+ .WillRepeatedly(Return(InputDeviceClass::KEYBOARD | InputDeviceClass::EXTERNAL));
+ mMapper = createInputMapper<KeyboardInputMapper>(*mDeviceContext, mReaderConfiguration,
+ AINPUT_SOURCE_KEYBOARD);
+ }
+};
+
+TEST_F(KeyboardInputMapperTest_ExternalAlphabeticDevice, WakeBehavior_AlphabeticKeyboard) {
+ // For external devices, keys will trigger wake on key down. Media keys should also trigger
+ // wake if triggered from external devices.
+
+ addKeyByEvdevCode(KEY_HOME, AKEYCODE_HOME);
+ addKeyByEvdevCode(KEY_PLAY, AKEYCODE_MEDIA_PLAY);
+ addKeyByEvdevCode(KEY_PLAYPAUSE, AKEYCODE_MEDIA_PLAY_PAUSE, POLICY_FLAG_WAKE);
+
+ std::list<NotifyArgs> argsList = process(ARBITRARY_TIME, EV_KEY, KEY_HOME, 1);
+ ASSERT_EQ(POLICY_FLAG_WAKE, expectSingleKeyArg(argsList).policyFlags);
+
+ argsList = process(ARBITRARY_TIME + 1, EV_KEY, KEY_HOME, 0);
+ ASSERT_EQ(uint32_t(0), expectSingleKeyArg(argsList).policyFlags);
+
+ argsList = process(ARBITRARY_TIME, EV_KEY, KEY_PLAY, 1);
+ ASSERT_EQ(POLICY_FLAG_WAKE, expectSingleKeyArg(argsList).policyFlags);
+
+ argsList = process(ARBITRARY_TIME + 1, EV_KEY, KEY_PLAY, 0);
+ ASSERT_EQ(uint32_t(0), expectSingleKeyArg(argsList).policyFlags);
+
+ argsList = process(ARBITRARY_TIME, EV_KEY, KEY_PLAYPAUSE, 1);
+ ASSERT_EQ(POLICY_FLAG_WAKE, expectSingleKeyArg(argsList).policyFlags);
+
+ argsList = process(ARBITRARY_TIME + 1, EV_KEY, KEY_PLAYPAUSE, 0);
+ ASSERT_EQ(POLICY_FLAG_WAKE, expectSingleKeyArg(argsList).policyFlags);
+}
+
+TEST_F(KeyboardInputMapperTest_ExternalNonAlphabeticDevice, WakeBehavior_NonAlphabeticKeyboard) {
+ // For external devices, keys will trigger wake on key down. Media keys should not trigger
+ // wake if triggered from external non-alphaebtic keyboard (e.g. headsets).
+
+ addKeyByEvdevCode(KEY_PLAY, AKEYCODE_MEDIA_PLAY);
+ addKeyByEvdevCode(KEY_PLAYPAUSE, AKEYCODE_MEDIA_PLAY_PAUSE, POLICY_FLAG_WAKE);
+
+ std::list<NotifyArgs> argsList = process(ARBITRARY_TIME, EV_KEY, KEY_PLAY, 1);
+ ASSERT_EQ(uint32_t(0), expectSingleKeyArg(argsList).policyFlags);
+
+ argsList = process(ARBITRARY_TIME + 1, EV_KEY, KEY_PLAY, 0);
+ ASSERT_EQ(uint32_t(0), expectSingleKeyArg(argsList).policyFlags);
+
+ argsList = process(ARBITRARY_TIME, EV_KEY, KEY_PLAYPAUSE, 1);
+ ASSERT_EQ(POLICY_FLAG_WAKE, expectSingleKeyArg(argsList).policyFlags);
+
+ argsList = process(ARBITRARY_TIME + 1, EV_KEY, KEY_PLAYPAUSE, 0);
+ ASSERT_EQ(POLICY_FLAG_WAKE, expectSingleKeyArg(argsList).policyFlags);
+}
+
+TEST_F(KeyboardInputMapperTest_ExternalAlphabeticDevice, DoNotWakeByDefaultBehavior) {
+ // Tv Remote key's wake behavior is prescribed by the keylayout file.
+
+ addKeyByEvdevCode(KEY_HOME, AKEYCODE_HOME, POLICY_FLAG_WAKE);
+ addKeyByEvdevCode(KEY_DOWN, AKEYCODE_DPAD_DOWN);
+ addKeyByEvdevCode(KEY_PLAY, AKEYCODE_MEDIA_PLAY, POLICY_FLAG_WAKE);
+
+ mPropertyMap.addProperty("keyboard.doNotWakeByDefault", "1");
+ mMapper = createInputMapper<KeyboardInputMapper>(*mDeviceContext, mReaderConfiguration,
+ AINPUT_SOURCE_KEYBOARD);
+
+ std::list<NotifyArgs> argsList = process(ARBITRARY_TIME, EV_KEY, KEY_HOME, 1);
+ ASSERT_EQ(POLICY_FLAG_WAKE, expectSingleKeyArg(argsList).policyFlags);
+
+ argsList = process(ARBITRARY_TIME + 1, EV_KEY, KEY_HOME, 0);
+ ASSERT_EQ(POLICY_FLAG_WAKE, expectSingleKeyArg(argsList).policyFlags);
+
+ argsList = process(ARBITRARY_TIME, EV_KEY, KEY_DOWN, 1);
+ ASSERT_EQ(uint32_t(0), expectSingleKeyArg(argsList).policyFlags);
+
+ argsList = process(ARBITRARY_TIME + 1, EV_KEY, KEY_DOWN, 0);
+ ASSERT_EQ(uint32_t(0), expectSingleKeyArg(argsList).policyFlags);
+
+ argsList = process(ARBITRARY_TIME, EV_KEY, KEY_PLAY, 1);
+ ASSERT_EQ(POLICY_FLAG_WAKE, expectSingleKeyArg(argsList).policyFlags);
+
+ argsList = process(ARBITRARY_TIME + 1, EV_KEY, KEY_PLAY, 0);
+ ASSERT_EQ(POLICY_FLAG_WAKE, expectSingleKeyArg(argsList).policyFlags);
+}
+
} // namespace android
diff --git a/services/inputflinger/tests/SensorInputMapper_test.cpp b/services/inputflinger/tests/SensorInputMapper_test.cpp
new file mode 100644
index 0000000..ac2e99e
--- /dev/null
+++ b/services/inputflinger/tests/SensorInputMapper_test.cpp
@@ -0,0 +1,179 @@
+/*
+ * Copyright 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "SensorInputMapper.h"
+
+#include <cstdint>
+#include <list>
+#include <optional>
+#include <utility>
+#include <vector>
+
+#include <EventHub.h>
+#include <NotifyArgs.h>
+#include <ftl/enum.h>
+#include <gmock/gmock.h>
+#include <gtest/gtest.h>
+#include <input/Input.h>
+#include <input/InputDevice.h>
+#include <input/PrintTools.h>
+#include <linux/input-event-codes.h>
+
+#include "InputMapperTest.h"
+#include "TestEventMatchers.h"
+
+namespace android {
+
+using testing::AllOf;
+using testing::ElementsAre;
+using testing::Return;
+using testing::VariantWith;
+
+namespace {
+
+constexpr int32_t ACCEL_RAW_MIN = -32768;
+constexpr int32_t ACCEL_RAW_MAX = 32768;
+constexpr int32_t ACCEL_RAW_FUZZ = 16;
+constexpr int32_t ACCEL_RAW_FLAT = 0;
+constexpr int32_t ACCEL_RAW_RESOLUTION = 8192;
+
+constexpr int32_t GYRO_RAW_MIN = -2097152;
+constexpr int32_t GYRO_RAW_MAX = 2097152;
+constexpr int32_t GYRO_RAW_FUZZ = 16;
+constexpr int32_t GYRO_RAW_FLAT = 0;
+constexpr int32_t GYRO_RAW_RESOLUTION = 1024;
+
+constexpr float GRAVITY_MS2_UNIT = 9.80665f;
+constexpr float DEGREE_RADIAN_UNIT = 0.0174533f;
+
+} // namespace
+
+class SensorInputMapperTest : public InputMapperUnitTest {
+protected:
+ void SetUp() override {
+ InputMapperUnitTest::SetUp();
+ EXPECT_CALL(mMockEventHub, getDeviceClasses(EVENTHUB_ID))
+ .WillRepeatedly(Return(InputDeviceClass::SENSOR));
+ // The mapper requests info on all ABS axes, including ones which aren't actually used, so
+ // just return nullopt for all axes we don't explicitly set up.
+ EXPECT_CALL(mMockEventHub, getAbsoluteAxisInfo(EVENTHUB_ID, testing::_))
+ .WillRepeatedly(Return(std::nullopt));
+ }
+
+ void setupSensor(int32_t absCode, InputDeviceSensorType type, int32_t sensorDataIndex) {
+ EXPECT_CALL(mMockEventHub, mapSensor(EVENTHUB_ID, absCode))
+ .WillRepeatedly(Return(std::make_pair(type, sensorDataIndex)));
+ }
+};
+
+TEST_F(SensorInputMapperTest, GetSources) {
+ mMapper = createInputMapper<SensorInputMapper>(*mDeviceContext,
+ mFakePolicy->getReaderConfiguration());
+
+ ASSERT_EQ(static_cast<uint32_t>(AINPUT_SOURCE_SENSOR), mMapper->getSources());
+}
+
+TEST_F(SensorInputMapperTest, ProcessAccelerometerSensor) {
+ EXPECT_CALL(mMockEventHub, hasMscEvent(EVENTHUB_ID, MSC_TIMESTAMP))
+ .WillRepeatedly(Return(true));
+ setupSensor(ABS_X, InputDeviceSensorType::ACCELEROMETER, /*sensorDataIndex=*/0);
+ setupSensor(ABS_Y, InputDeviceSensorType::ACCELEROMETER, /*sensorDataIndex=*/1);
+ setupSensor(ABS_Z, InputDeviceSensorType::ACCELEROMETER, /*sensorDataIndex=*/2);
+ setupAxis(ABS_X, /*valid=*/true, ACCEL_RAW_MIN, ACCEL_RAW_MAX, ACCEL_RAW_RESOLUTION,
+ ACCEL_RAW_FLAT, ACCEL_RAW_FUZZ);
+ setupAxis(ABS_Y, /*valid=*/true, ACCEL_RAW_MIN, ACCEL_RAW_MAX, ACCEL_RAW_RESOLUTION,
+ ACCEL_RAW_FLAT, ACCEL_RAW_FUZZ);
+ setupAxis(ABS_Z, /*valid=*/true, ACCEL_RAW_MIN, ACCEL_RAW_MAX, ACCEL_RAW_RESOLUTION,
+ ACCEL_RAW_FLAT, ACCEL_RAW_FUZZ);
+ mPropertyMap.addProperty("sensor.accelerometer.reportingMode", "0");
+ mPropertyMap.addProperty("sensor.accelerometer.maxDelay", "100000");
+ mPropertyMap.addProperty("sensor.accelerometer.minDelay", "5000");
+ mPropertyMap.addProperty("sensor.accelerometer.power", "1.5");
+ mMapper = createInputMapper<SensorInputMapper>(*mDeviceContext,
+ mFakePolicy->getReaderConfiguration());
+
+ EXPECT_CALL(mMockEventHub, enableDevice(EVENTHUB_ID));
+ ASSERT_TRUE(mMapper->enableSensor(InputDeviceSensorType::ACCELEROMETER,
+ std::chrono::microseconds(10000),
+ std::chrono::microseconds(0)));
+ std::list<NotifyArgs> args;
+ args += process(ARBITRARY_TIME, EV_ABS, ABS_X, 20000);
+ args += process(ARBITRARY_TIME, EV_ABS, ABS_Y, -20000);
+ args += process(ARBITRARY_TIME, EV_ABS, ABS_Z, 40000);
+ args += process(ARBITRARY_TIME, EV_MSC, MSC_TIMESTAMP, 1000);
+ args += process(ARBITRARY_TIME, EV_SYN, SYN_REPORT, 0);
+
+ std::vector<float> values = {20000.0f / ACCEL_RAW_RESOLUTION * GRAVITY_MS2_UNIT,
+ -20000.0f / ACCEL_RAW_RESOLUTION * GRAVITY_MS2_UNIT,
+ 40000.0f / ACCEL_RAW_RESOLUTION * GRAVITY_MS2_UNIT};
+
+ ASSERT_EQ(args.size(), 1u);
+ const NotifySensorArgs& arg = std::get<NotifySensorArgs>(args.front());
+ ASSERT_EQ(arg.source, AINPUT_SOURCE_SENSOR);
+ ASSERT_EQ(arg.deviceId, DEVICE_ID);
+ ASSERT_EQ(arg.sensorType, InputDeviceSensorType::ACCELEROMETER);
+ ASSERT_EQ(arg.accuracy, InputDeviceSensorAccuracy::HIGH);
+ ASSERT_EQ(arg.hwTimestamp, ARBITRARY_TIME);
+ ASSERT_EQ(arg.values, values);
+ mMapper->flushSensor(InputDeviceSensorType::ACCELEROMETER);
+}
+
+TEST_F(SensorInputMapperTest, ProcessGyroscopeSensor) {
+ EXPECT_CALL(mMockEventHub, hasMscEvent(EVENTHUB_ID, MSC_TIMESTAMP))
+ .WillRepeatedly(Return(true));
+ setupSensor(ABS_RX, InputDeviceSensorType::GYROSCOPE, /*sensorDataIndex=*/0);
+ setupSensor(ABS_RY, InputDeviceSensorType::GYROSCOPE, /*sensorDataIndex=*/1);
+ setupSensor(ABS_RZ, InputDeviceSensorType::GYROSCOPE, /*sensorDataIndex=*/2);
+ setupAxis(ABS_RX, /*valid=*/true, GYRO_RAW_MIN, GYRO_RAW_MAX, GYRO_RAW_RESOLUTION,
+ GYRO_RAW_FLAT, GYRO_RAW_FUZZ);
+ setupAxis(ABS_RY, /*valid=*/true, GYRO_RAW_MIN, GYRO_RAW_MAX, GYRO_RAW_RESOLUTION,
+ GYRO_RAW_FLAT, GYRO_RAW_FUZZ);
+ setupAxis(ABS_RZ, /*valid=*/true, GYRO_RAW_MIN, GYRO_RAW_MAX, GYRO_RAW_RESOLUTION,
+ GYRO_RAW_FLAT, GYRO_RAW_FUZZ);
+ mPropertyMap.addProperty("sensor.gyroscope.reportingMode", "0");
+ mPropertyMap.addProperty("sensor.gyroscope.maxDelay", "100000");
+ mPropertyMap.addProperty("sensor.gyroscope.minDelay", "5000");
+ mPropertyMap.addProperty("sensor.gyroscope.power", "0.8");
+ mMapper = createInputMapper<SensorInputMapper>(*mDeviceContext,
+ mFakePolicy->getReaderConfiguration());
+
+ EXPECT_CALL(mMockEventHub, enableDevice(EVENTHUB_ID));
+ ASSERT_TRUE(mMapper->enableSensor(InputDeviceSensorType::GYROSCOPE,
+ std::chrono::microseconds(10000),
+ std::chrono::microseconds(0)));
+ std::list<NotifyArgs> args;
+ args += process(ARBITRARY_TIME, EV_ABS, ABS_RX, 20000);
+ args += process(ARBITRARY_TIME, EV_ABS, ABS_RY, -20000);
+ args += process(ARBITRARY_TIME, EV_ABS, ABS_RZ, 40000);
+ args += process(ARBITRARY_TIME, EV_MSC, MSC_TIMESTAMP, 1000);
+ args += process(ARBITRARY_TIME, EV_SYN, SYN_REPORT, 0);
+
+ std::vector<float> values = {20000.0f / GYRO_RAW_RESOLUTION * DEGREE_RADIAN_UNIT,
+ -20000.0f / GYRO_RAW_RESOLUTION * DEGREE_RADIAN_UNIT,
+ 40000.0f / GYRO_RAW_RESOLUTION * DEGREE_RADIAN_UNIT};
+
+ ASSERT_EQ(args.size(), 1u);
+ const NotifySensorArgs& arg = std::get<NotifySensorArgs>(args.front());
+ ASSERT_EQ(arg.source, AINPUT_SOURCE_SENSOR);
+ ASSERT_EQ(arg.deviceId, DEVICE_ID);
+ ASSERT_EQ(arg.sensorType, InputDeviceSensorType::GYROSCOPE);
+ ASSERT_EQ(arg.accuracy, InputDeviceSensorAccuracy::HIGH);
+ ASSERT_EQ(arg.hwTimestamp, ARBITRARY_TIME);
+ ASSERT_EQ(arg.values, values);
+ mMapper->flushSensor(InputDeviceSensorType::GYROSCOPE);
+}
+
+} // namespace android
\ No newline at end of file
diff --git a/services/inputflinger/tests/TestEventMatchers.h b/services/inputflinger/tests/TestEventMatchers.h
index f58d8fd..7078e49 100644
--- a/services/inputflinger/tests/TestEventMatchers.h
+++ b/services/inputflinger/tests/TestEventMatchers.h
@@ -108,20 +108,33 @@
using is_gtest_matcher = void;
explicit WithMotionActionMatcher(int32_t action) : mAction(action) {}
- bool MatchAndExplain(const NotifyMotionArgs& args, std::ostream*) const {
- bool matches = mAction == args.action;
- if (args.action == AMOTION_EVENT_ACTION_CANCEL) {
- matches &= (args.flags & AMOTION_EVENT_FLAG_CANCELED) != 0;
+ bool MatchAndExplain(const NotifyMotionArgs& args,
+ testing::MatchResultListener* listener) const {
+ if (mAction != args.action) {
+ *listener << "expected " << MotionEvent::actionToString(mAction) << ", but got "
+ << MotionEvent::actionToString(args.action);
+ return false;
}
- return matches;
+ if (args.action == AMOTION_EVENT_ACTION_CANCEL &&
+ (args.flags & AMOTION_EVENT_FLAG_CANCELED) == 0) {
+ *listener << "event with CANCEL action is missing FLAG_CANCELED";
+ return false;
+ }
+ return true;
}
- bool MatchAndExplain(const MotionEvent& event, std::ostream*) const {
- bool matches = mAction == event.getAction();
- if (event.getAction() == AMOTION_EVENT_ACTION_CANCEL) {
- matches &= (event.getFlags() & AMOTION_EVENT_FLAG_CANCELED) != 0;
+ bool MatchAndExplain(const MotionEvent& event, testing::MatchResultListener* listener) const {
+ if (mAction != event.getAction()) {
+ *listener << "expected " << MotionEvent::actionToString(mAction) << ", but got "
+ << MotionEvent::actionToString(event.getAction());
+ return false;
}
- return matches;
+ if (event.getAction() == AMOTION_EVENT_ACTION_CANCEL &&
+ (event.getFlags() & AMOTION_EVENT_FLAG_CANCELED) == 0) {
+ *listener << "event with CANCEL action is missing FLAG_CANCELED";
+ return false;
+ }
+ return true;
}
void DescribeTo(std::ostream* os) const {
diff --git a/services/inputflinger/tests/fuzzers/InputReaderFuzzer.cpp b/services/inputflinger/tests/fuzzers/InputReaderFuzzer.cpp
index 64f3c27..6be922d 100644
--- a/services/inputflinger/tests/fuzzers/InputReaderFuzzer.cpp
+++ b/services/inputflinger/tests/fuzzers/InputReaderFuzzer.cpp
@@ -75,6 +75,8 @@
void toggleCapsLockState(int32_t deviceId) { reader->toggleCapsLockState(deviceId); }
+ void resetLockedModifierState() { reader->resetLockedModifierState(); }
+
bool hasKeys(int32_t deviceId, uint32_t sourceMask, const std::vector<int32_t>& keyCodes,
uint8_t* outFlags) {
return reader->hasKeys(deviceId, sourceMask, keyCodes, outFlags);
@@ -226,6 +228,7 @@
fdp->ConsumeIntegral<int32_t>());
},
[&]() -> void { reader->toggleCapsLockState(fdp->ConsumeIntegral<int32_t>()); },
+ [&]() -> void { reader->resetLockedModifierState(); },
[&]() -> void {
size_t count = fdp->ConsumeIntegralInRange<size_t>(1, 1024);
std::vector<uint8_t> outFlags(count);
diff --git a/services/inputflinger/tests/fuzzers/MapperHelpers.h b/services/inputflinger/tests/fuzzers/MapperHelpers.h
index 60c676d..a1da39a 100644
--- a/services/inputflinger/tests/fuzzers/MapperHelpers.h
+++ b/services/inputflinger/tests/fuzzers/MapperHelpers.h
@@ -288,6 +288,7 @@
void notifyTouchpadHardwareState(const SelfContainedHardwareState& schs,
int32_t deviceId) override {}
void notifyTouchpadGestureInfo(GestureType type, int32_t deviceId) override {}
+ void notifyTouchpadThreeFingerTap() override {}
std::shared_ptr<KeyCharacterMap> getKeyboardLayoutOverlay(
const InputDeviceIdentifier& identifier,
const std::optional<KeyboardLayoutInfo> layoutInfo) override {
diff --git a/services/powermanager/PowerHalController.cpp b/services/powermanager/PowerHalController.cpp
index 40fd097..0ba1909 100644
--- a/services/powermanager/PowerHalController.cpp
+++ b/services/powermanager/PowerHalController.cpp
@@ -168,6 +168,11 @@
"closeSessionChannel"));
}
+HalResult<aidl::android::hardware::power::SupportInfo> PowerHalController::getSupportInfo() {
+ std::shared_ptr<HalWrapper> handle = initHal();
+ return CACHE_SUPPORT(6, processHalResult(handle->getSupportInfo(), "getSupportInfo"));
+}
+
} // namespace power
} // namespace android
diff --git a/services/powermanager/PowerHalWrapper.cpp b/services/powermanager/PowerHalWrapper.cpp
index bd6685c..068c23f 100644
--- a/services/powermanager/PowerHalWrapper.cpp
+++ b/services/powermanager/PowerHalWrapper.cpp
@@ -18,6 +18,7 @@
#include <aidl/android/hardware/power/Boost.h>
#include <aidl/android/hardware/power/IPowerHintSession.h>
#include <aidl/android/hardware/power/Mode.h>
+#include <aidl/android/hardware/power/SupportInfo.h>
#include <powermanager/HalResult.h>
#include <powermanager/PowerHalWrapper.h>
#include <utils/Log.h>
@@ -73,6 +74,11 @@
return HalResult<void>::unsupported();
}
+HalResult<Aidl::SupportInfo> EmptyHalWrapper::getSupportInfo() {
+ ALOGV("Skipped getSupportInfo because %s", getUnsupportedMessage());
+ return HalResult<Aidl::SupportInfo>::unsupported();
+}
+
const char* EmptyHalWrapper::getUnsupportedMessage() {
return "Power HAL is not supported";
}
@@ -280,6 +286,12 @@
return HalResult<void>::fromStatus(mHandle->closeSessionChannel(tgid, uid));
}
+HalResult<Aidl::SupportInfo> AidlHalWrapper::getSupportInfo() {
+ Aidl::SupportInfo support;
+ auto result = mHandle->getSupportInfo(&support);
+ return HalResult<Aidl::SupportInfo>::fromStatus(result, std::move(support));
+}
+
const char* AidlHalWrapper::getUnsupportedMessage() {
return "Power HAL doesn't support it";
}
diff --git a/services/powermanager/tests/PowerHalWrapperAidlTest.cpp b/services/powermanager/tests/PowerHalWrapperAidlTest.cpp
index 1589c99..71d3d1f 100644
--- a/services/powermanager/tests/PowerHalWrapperAidlTest.cpp
+++ b/services/powermanager/tests/PowerHalWrapperAidlTest.cpp
@@ -28,15 +28,10 @@
#include <unistd.h>
#include <thread>
-using aidl::android::hardware::power::Boost;
-using aidl::android::hardware::power::ChannelConfig;
-using aidl::android::hardware::power::IPower;
-using aidl::android::hardware::power::IPowerHintSession;
-using aidl::android::hardware::power::Mode;
-using aidl::android::hardware::power::SessionConfig;
-using aidl::android::hardware::power::SessionTag;
+
using android::binder::Status;
+using namespace aidl::android::hardware::power;
using namespace android;
using namespace android::power;
using namespace std::chrono_literals;
@@ -65,10 +60,23 @@
(int32_t tgid, int32_t uid, ChannelConfig* _aidl_return), (override));
MOCK_METHOD(ndk::ScopedAStatus, closeSessionChannel, (int32_t tgid, int32_t uid), (override));
MOCK_METHOD(ndk::ScopedAStatus, getHintSessionPreferredRate, (int64_t * rate), (override));
+ MOCK_METHOD(ndk::ScopedAStatus, getSupportInfo, (SupportInfo * _aidl_return), (override));
MOCK_METHOD(ndk::ScopedAStatus, getInterfaceVersion, (int32_t * version), (override));
MOCK_METHOD(ndk::ScopedAStatus, getInterfaceHash, (std::string * hash), (override));
MOCK_METHOD(ndk::SpAIBinder, asBinder, (), (override));
MOCK_METHOD(bool, isRemote, (), (override));
+ MOCK_METHOD(ndk::ScopedAStatus, getCpuHeadroom,
+ (const CpuHeadroomParams& params, CpuHeadroomResult* headroom), (override));
+ MOCK_METHOD(ndk::ScopedAStatus, getGpuHeadroom,
+ (const GpuHeadroomParams& params, GpuHeadroomResult* headroom), (override));
+ MOCK_METHOD(ndk::ScopedAStatus, getCpuHeadroomMinIntervalMillis, (int64_t* interval),
+ (override));
+ MOCK_METHOD(ndk::ScopedAStatus, getGpuHeadroomMinIntervalMillis, (int64_t* interval),
+ (override));
+ MOCK_METHOD(ndk::ScopedAStatus, sendCompositionData,
+ (const std::vector<CompositionData>& in_data), (override));
+ MOCK_METHOD(ndk::ScopedAStatus, sendCompositionUpdate,
+ (const CompositionUpdate& in_update), (override));
};
// -------------------------------------------------------------------------------------------------
diff --git a/services/surfaceflinger/ActivePictureUpdater.cpp b/services/surfaceflinger/ActivePictureUpdater.cpp
new file mode 100644
index 0000000..210e948
--- /dev/null
+++ b/services/surfaceflinger/ActivePictureUpdater.cpp
@@ -0,0 +1,66 @@
+/*
+ * Copyright 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "ActivePictureUpdater.h"
+
+#include <algorithm>
+
+#include "Layer.h"
+#include "LayerFE.h"
+
+namespace android {
+
+void ActivePictureUpdater::onLayerComposed(const Layer& layer, const LayerFE& layerFE,
+ const CompositionResult& result) {
+ if (result.wasPictureProfileCommitted) {
+ gui::ActivePicture picture;
+ picture.layerId = int32_t(layer.sequence);
+ picture.ownerUid = int32_t(layer.getOwnerUid());
+ // TODO(b/337330263): Why does LayerFE coming from SF have a null composition state?
+ if (layerFE.getCompositionState()) {
+ picture.pictureProfileId = layerFE.getCompositionState()->pictureProfileHandle.getId();
+ } else {
+ picture.pictureProfileId = result.pictureProfileHandle.getId();
+ }
+ mNewActivePictures.push_back(picture);
+ }
+}
+
+bool ActivePictureUpdater::updateAndHasChanged() {
+ bool hasChanged = true;
+ if (mNewActivePictures.size() == mOldActivePictures.size()) {
+ auto compare = [](const gui::ActivePicture& lhs, const gui::ActivePicture& rhs) -> int {
+ if (lhs.layerId == rhs.layerId) {
+ return lhs.pictureProfileId < rhs.pictureProfileId;
+ }
+ return lhs.layerId < rhs.layerId;
+ };
+ std::sort(mNewActivePictures.begin(), mNewActivePictures.end(), compare);
+ if (std::equal(mNewActivePictures.begin(), mNewActivePictures.end(),
+ mOldActivePictures.begin())) {
+ hasChanged = false;
+ }
+ }
+ std::swap(mOldActivePictures, mNewActivePictures);
+ mNewActivePictures.resize(0);
+ return hasChanged;
+}
+
+const std::vector<gui::ActivePicture>& ActivePictureUpdater::getActivePictures() const {
+ return mOldActivePictures;
+}
+
+} // namespace android
diff --git a/services/surfaceflinger/ActivePictureUpdater.h b/services/surfaceflinger/ActivePictureUpdater.h
new file mode 100644
index 0000000..20779bb
--- /dev/null
+++ b/services/surfaceflinger/ActivePictureUpdater.h
@@ -0,0 +1,47 @@
+/*
+ * Copyright 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include <vector>
+
+#include <android/gui/ActivePicture.h>
+
+namespace android {
+
+class Layer;
+class LayerFE;
+struct CompositionResult;
+
+// Keeps track of active pictures - layers that are undergoing picture processing.
+class ActivePictureUpdater {
+public:
+ // Called for each visible layer when SurfaceFlinger finishes composing.
+ void onLayerComposed(const Layer& layer, const LayerFE& layerFE,
+ const CompositionResult& result);
+
+ // Update internals and return whether the set of active pictures have changed.
+ bool updateAndHasChanged();
+
+ // The current set of active pictures.
+ const std::vector<gui::ActivePicture>& getActivePictures() const;
+
+private:
+ std::vector<gui::ActivePicture> mOldActivePictures;
+ std::vector<gui::ActivePicture> mNewActivePictures;
+};
+
+} // namespace android
diff --git a/services/surfaceflinger/Android.bp b/services/surfaceflinger/Android.bp
index d500ae8..3f3d2c6 100644
--- a/services/surfaceflinger/Android.bp
+++ b/services/surfaceflinger/Android.bp
@@ -148,6 +148,46 @@
},
}
+// libsurfaceflinger_backend_{headers|sources} are a step towards pulling out
+// the "backend" sources to clean up the dependency graph between
+// CompositionEngine and SurfaceFlinger. Completing the cleanup would require
+// moving the headers in particular so that the dependency can strictly be a
+// DAG. There would certainly be additional cleanups: VirtualDisplaySurface.cpp
+// and FrameBufferSurface.cpp likely belong in CompositionEngine for example.
+cc_library_headers {
+ name: "libsurfaceflinger_backend_headers",
+ export_include_dirs: ["."],
+ static_libs: ["libserviceutils"],
+ export_static_lib_headers: ["libserviceutils"],
+
+ shared_libs: [
+ "android.hardware.configstore-utils",
+ "android.hardware.configstore@1.0",
+ "android.hardware.configstore@1.1",
+ "libbinder_ndk",
+ ],
+ export_shared_lib_headers: [
+ "android.hardware.configstore-utils",
+ "android.hardware.configstore@1.0",
+ "android.hardware.configstore@1.1",
+ "libbinder_ndk",
+ ],
+}
+
+filegroup {
+ name: "libsurfaceflinger_backend_sources",
+ srcs: [
+ "PowerAdvisor/*.cpp",
+ "DisplayHardware/AidlComposerHal.cpp",
+ "DisplayHardware/ComposerHal.cpp",
+ "DisplayHardware/FramebufferSurface.cpp",
+ "DisplayHardware/HWC2.cpp",
+ "DisplayHardware/HWComposer.cpp",
+ "DisplayHardware/HidlComposerHal.cpp",
+ "DisplayHardware/VirtualDisplaySurface.cpp",
+ ],
+}
+
cc_library_headers {
name: "libsurfaceflinger_headers",
export_include_dirs: ["."],
@@ -158,20 +198,14 @@
filegroup {
name: "libsurfaceflinger_sources",
srcs: [
+ ":libsurfaceflinger_backend_sources",
+ "ActivePictureUpdater.cpp",
"BackgroundExecutor.cpp",
"Client.cpp",
"ClientCache.cpp",
"Display/DisplayModeController.cpp",
"Display/DisplaySnapshot.cpp",
"DisplayDevice.cpp",
- "DisplayHardware/AidlComposerHal.cpp",
- "DisplayHardware/ComposerHal.cpp",
- "DisplayHardware/FramebufferSurface.cpp",
- "DisplayHardware/HWC2.cpp",
- "DisplayHardware/HWComposer.cpp",
- "DisplayHardware/HidlComposerHal.cpp",
- "DisplayHardware/PowerAdvisor.cpp",
- "DisplayHardware/VirtualDisplaySurface.cpp",
"DisplayRenderArea.cpp",
"Effects/Daltonizer.cpp",
"FrontEnd/LayerCreationArgs.cpp",
@@ -251,7 +285,6 @@
],
static_libs: [
"android.frameworks.displayservice@1.0",
- "libc++fs",
"libdisplayservicehidl",
"libserviceutils",
],
diff --git a/services/surfaceflinger/CompositionEngine/Android.bp b/services/surfaceflinger/CompositionEngine/Android.bp
index b4ac9ba..82eafd4 100644
--- a/services/surfaceflinger/CompositionEngine/Android.bp
+++ b/services/surfaceflinger/CompositionEngine/Android.bp
@@ -42,6 +42,7 @@
"libutils",
],
static_libs: [
+ "libguiflags",
"libmath",
"librenderengine",
"libtimestats",
@@ -49,9 +50,7 @@
"libaidlcommonsupport",
"libprocessgroup",
"libprocessgroup_util",
- "libcgrouprc",
"libjsoncpp",
- "libcgrouprc_format",
],
header_libs: [
"android.hardware.graphics.composer@2.1-command-buffer",
@@ -59,7 +58,7 @@
"android.hardware.graphics.composer@2.3-command-buffer",
"android.hardware.graphics.composer@2.4-command-buffer",
"android.hardware.graphics.composer3-command-buffer",
- "libsurfaceflinger_headers",
+ "libsurfaceflinger_backend_headers",
],
}
@@ -141,6 +140,8 @@
],
srcs: [
":libcompositionengine_sources",
+ ":libsurfaceflinger_backend_mock_sources",
+ ":libsurfaceflinger_backend_sources",
"tests/planner/CachedSetTest.cpp",
"tests/planner/FlattenerTest.cpp",
"tests/planner/LayerStateTest.cpp",
@@ -151,14 +152,14 @@
"tests/DisplayTest.cpp",
"tests/HwcAsyncWorkerTest.cpp",
"tests/HwcBufferCacheTest.cpp",
- "tests/MockHWC2.cpp",
- "tests/MockHWComposer.cpp",
- "tests/MockPowerAdvisor.cpp",
"tests/OutputLayerTest.cpp",
"tests/OutputTest.cpp",
"tests/ProjectionSpaceTest.cpp",
"tests/RenderSurfaceTest.cpp",
],
+ header_libs: [
+ "libsurfaceflinger_backend_mock_headers",
+ ],
static_libs: [
"libcompositionengine_mocks",
"libgui_mocks",
@@ -167,6 +168,7 @@
"libgtest",
],
shared_libs: [
+ "libbinder_ndk",
// For some reason, libvulkan isn't picked up from librenderengine
// Probably ASAN related?
"libvulkan",
diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/DisplayCreationArgs.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/DisplayCreationArgs.h
index 6e60839..252adaa 100644
--- a/services/surfaceflinger/CompositionEngine/include/compositionengine/DisplayCreationArgs.h
+++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/DisplayCreationArgs.h
@@ -24,7 +24,7 @@
#include <ui/Size.h>
#include <ui/StaticDisplayInfo.h>
-#include "DisplayHardware/PowerAdvisor.h"
+#include "PowerAdvisor/PowerAdvisor.h"
namespace android::compositionengine {
@@ -46,9 +46,15 @@
// content.
bool isProtected = false;
+ // True if this display has picture processing hardware and pipelines.
+ bool hasPictureProcessing = false;
+
+ // The number of layer-specific picture-processing pipelines.
+ int32_t maxLayerPictureProfiles = 0;
+
// Optional pointer to the power advisor interface, if one is needed for
// this display.
- Hwc2::PowerAdvisor* powerAdvisor = nullptr;
+ adpf::PowerAdvisor* powerAdvisor = nullptr;
// Debugging. Human readable name for the display.
std::string name;
@@ -82,7 +88,17 @@
return *this;
}
- DisplayCreationArgsBuilder& setPowerAdvisor(Hwc2::PowerAdvisor* powerAdvisor) {
+ DisplayCreationArgsBuilder& setHasPictureProcessing(bool hasPictureProcessing) {
+ mArgs.hasPictureProcessing = hasPictureProcessing;
+ return *this;
+ }
+
+ DisplayCreationArgsBuilder& setMaxLayerPictureProfiles(int32_t maxLayerPictureProfiles) {
+ mArgs.maxLayerPictureProfiles = maxLayerPictureProfiles;
+ return *this;
+ }
+
+ DisplayCreationArgsBuilder& setPowerAdvisor(adpf::PowerAdvisor* powerAdvisor) {
mArgs.powerAdvisor = powerAdvisor;
return *this;
}
diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/LayerFE.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/LayerFE.h
index 4e080b3..cda4edc 100644
--- a/services/surfaceflinger/CompositionEngine/include/compositionengine/LayerFE.h
+++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/LayerFE.h
@@ -148,9 +148,6 @@
virtual std::optional<LayerSettings> prepareClientComposition(
ClientCompositionTargetSettings&) const = 0;
- // Called after the layer is displayed to update the presentation fence
- virtual void onLayerDisplayed(ftl::SharedFuture<FenceResult>, ui::LayerStack layerStack) = 0;
-
// Initializes a promise for a buffer release fence and provides the future for that
// fence. This should only be called when a promise has not yet been created, or
// after the previous promise has already been fulfilled. Attempting to call this
@@ -164,6 +161,9 @@
// Checks if the buffer's release fence has been set
virtual LayerFE::ReleaseFencePromiseStatus getReleaseFencePromiseStatus() = 0;
+ // Indicates that the picture profile request was applied to this layer.
+ virtual void onPictureProfileCommitted() = 0;
+
// Gets some kind of identifier for the layer for debug purposes.
virtual const char* getDebugName() const = 0;
diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/LayerFECompositionState.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/LayerFECompositionState.h
index 8a91a07..fb8fed0 100644
--- a/services/surfaceflinger/CompositionEngine/include/compositionengine/LayerFECompositionState.h
+++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/LayerFECompositionState.h
@@ -25,6 +25,7 @@
#include <ui/BlurRegion.h>
#include <ui/FloatRect.h>
#include <ui/LayerStack.h>
+#include <ui/PictureProfileHandle.h>
#include <ui/Rect.h>
#include <ui/Region.h>
#include <ui/ShadowSettings.h>
@@ -219,6 +220,14 @@
float currentHdrSdrRatio = 1.f;
float desiredHdrSdrRatio = 1.f;
+ // A picture profile handle refers to a PictureProfile configured on the display, which is a
+ // set of parameters that configures the picture processing hardware that is used to enhance
+ // the quality of buffer contents.
+ PictureProfileHandle pictureProfileHandle{PictureProfileHandle::NONE};
+
+ // A layer's priority in terms of limited picture processing pipeline utilization.
+ int64_t pictureProfilePriority;
+
gui::CachingHint cachingHint = gui::CachingHint::Enabled;
std::shared_ptr<gui::DisplayLuts> luts;
diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/Output.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/Output.h
index 556aa24..bda7856 100644
--- a/services/surfaceflinger/CompositionEngine/include/compositionengine/Output.h
+++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/Output.h
@@ -16,6 +16,8 @@
#pragma once
+#include <ftl/future.h>
+#include <ftl/optional.h>
#include <cstdint>
#include <iterator>
#include <optional>
@@ -26,18 +28,18 @@
#include <vector>
#include <compositionengine/LayerFE.h>
-#include <ftl/future.h>
#include <renderengine/LayerSettings.h>
+#include <ui/DisplayIdentification.h>
#include <ui/Fence.h>
#include <ui/FenceTime.h>
#include <ui/GraphicTypes.h>
#include <ui/LayerStack.h>
+#include <ui/PictureProfileHandle.h>
#include <ui/Region.h>
#include <ui/Transform.h>
#include <utils/StrongPointer.h>
#include <utils/Vector.h>
-#include <ui/DisplayIdentification.h>
#include "DisplayHardware/HWComposer.h"
namespace android {
@@ -167,7 +169,7 @@
virtual bool isValid() const = 0;
// Returns the DisplayId the output represents, if it has one
- virtual std::optional<DisplayId> getDisplayId() const = 0;
+ virtual ftl::Optional<DisplayId> getDisplayId() const = 0;
// Enables (or disables) composition on this output
virtual void setCompositionEnabled(bool) = 0;
@@ -331,6 +333,9 @@
virtual bool canPredictCompositionStrategy(const CompositionRefreshArgs&) = 0;
virtual const aidl::android::hardware::graphics::composer3::OverlayProperties*
getOverlaySupport() = 0;
+ virtual bool hasPictureProcessing() const = 0;
+ virtual int32_t getMaxLayerPictureProfiles() const = 0;
+ virtual void applyPictureProfile() = 0;
};
} // namespace compositionengine
diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/OutputLayer.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/OutputLayer.h
index 80c5124..2e7a7d9 100644
--- a/services/surfaceflinger/CompositionEngine/include/compositionengine/OutputLayer.h
+++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/OutputLayer.h
@@ -21,6 +21,7 @@
#include <string>
#include <vector>
+#include <ui/PictureProfileHandle.h>
#include <ui/Transform.h>
#include <utils/StrongPointer.h>
@@ -86,6 +87,16 @@
// longer cares about.
virtual void uncacheBuffers(const std::vector<uint64_t>& bufferIdsToUncache) = 0;
+ // Get the relative priority of the layer's picture profile with respect to the importance of
+ // the visual content to the user experience. Lower is higher priority.
+ virtual int64_t getPictureProfilePriority() const = 0;
+
+ // The picture profile handle for the layer.
+ virtual const PictureProfileHandle& getPictureProfileHandle() const = 0;
+
+ // Commit the picture profile to the composition state.
+ virtual void commitPictureProfileToCompositionState() = 0;
+
// Recalculates the state of the output layer from the output-independent
// layer. If includeGeometry is false, the geometry state can be skipped.
// internalDisplayRotationFlags must be set to the rotation flags for the
diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/Display.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/Display.h
index d8466ff..5519aaf 100644
--- a/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/Display.h
+++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/Display.h
@@ -30,7 +30,7 @@
#include <ui/DisplayIdentification.h>
#include "DisplayHardware/HWComposer.h"
-#include "DisplayHardware/PowerAdvisor.h"
+#include "PowerAdvisor/PowerAdvisor.h"
namespace android::compositionengine {
@@ -45,7 +45,7 @@
virtual ~Display();
// compositionengine::Output overrides
- std::optional<DisplayId> getDisplayId() const override;
+ ftl::Optional<DisplayId> getDisplayId() const override;
bool isValid() const override;
void dump(std::string&) const override;
using compositionengine::impl::Output::setReleasedLayers;
@@ -100,11 +100,16 @@
void setHintSessionGpuStart(TimePoint startTime) override;
void setHintSessionGpuFence(std::unique_ptr<FenceTime>&& gpuFence) override;
void setHintSessionRequiresRenderEngine(bool requiresRenderEngine) override;
- DisplayId mId;
- bool mIsDisconnected = false;
- Hwc2::PowerAdvisor* mPowerAdvisor = nullptr;
const aidl::android::hardware::graphics::composer3::OverlayProperties* getOverlaySupport()
override;
+ bool hasPictureProcessing() const override;
+ int32_t getMaxLayerPictureProfiles() const override;
+
+ DisplayId mId;
+ bool mIsDisconnected = false;
+ adpf::PowerAdvisor* mPowerAdvisor = nullptr;
+ bool mHasPictureProcessing = false;
+ int32_t mMaxLayerPictureProfiles = 0;
};
// This template factory function standardizes the implementation details of the
diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/Output.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/Output.h
index 69e1efc..0ccdd22 100644
--- a/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/Output.h
+++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/Output.h
@@ -16,6 +16,11 @@
#pragma once
+#include <ftl/optional.h>
+#include <memory>
+#include <utility>
+#include <vector>
+
#include <compositionengine/CompositionEngine.h>
#include <compositionengine/LayerFECompositionState.h>
#include <compositionengine/Output.h>
@@ -28,10 +33,6 @@
#include <renderengine/DisplaySettings.h>
#include <renderengine/LayerSettings.h>
-#include <memory>
-#include <utility>
-#include <vector>
-
namespace android::compositionengine::impl {
// The implementation class contains the common implementation, but does not
@@ -43,7 +44,7 @@
// compositionengine::Output overrides
bool isValid() const override;
- std::optional<DisplayId> getDisplayId() const override;
+ ftl::Optional<DisplayId> getDisplayId() const override;
void setCompositionEnabled(bool) override;
void setLayerCachingEnabled(bool) override;
void setLayerCachingTexturePoolEnabled(bool) override;
@@ -84,13 +85,14 @@
bool supportsOffloadPresent() const override { return false; }
void offloadPresentNextFrame() override;
- void uncacheBuffers(const std::vector<uint64_t>& bufferIdsToUncache) override;
void rebuildLayerStacks(const CompositionRefreshArgs&, LayerFESet&) override;
void collectVisibleLayers(const CompositionRefreshArgs&,
compositionengine::Output::CoverageState&) override;
void ensureOutputLayerIfVisible(sp<compositionengine::LayerFE>&,
compositionengine::Output::CoverageState&) override;
void setReleasedLayers(const compositionengine::CompositionRefreshArgs&) override;
+ void uncacheBuffers(const std::vector<uint64_t>& bufferIdsToUncache) override;
+ void commitPictureProfilesToCompositionState();
void updateCompositionState(const compositionengine::CompositionRefreshArgs&) override;
void planComposition() override;
@@ -151,6 +153,9 @@
void setHintSessionRequiresRenderEngine(bool requiresRenderEngine) override;
bool isPowerHintSessionEnabled() override;
bool isPowerHintSessionGpuReportingEnabled() override;
+ bool hasPictureProcessing() const override;
+ int32_t getMaxLayerPictureProfiles() const override;
+ void applyPictureProfile() override;
void dumpBase(std::string&) const;
// Implemented by the final implementation for the final state it uses.
diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/OutputCompositionState.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/OutputCompositionState.h
index f8ffde1..c76b344 100644
--- a/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/OutputCompositionState.h
+++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/OutputCompositionState.h
@@ -35,6 +35,7 @@
#include <compositionengine/CompositionRefreshArgs.h>
#include <compositionengine/ProjectionSpace.h>
#include <ui/LayerStack.h>
+#include <ui/PictureProfileHandle.h>
#include <ui/Rect.h>
#include <ui/Region.h>
#include <ui/Transform.h>
@@ -170,6 +171,8 @@
ICEPowerCallback* powerCallback = nullptr;
+ PictureProfileHandle pictureProfileHandle;
+
// Debugging
void dump(std::string& result) const;
};
diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/OutputLayer.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/OutputLayer.h
index 0c7e4dd..712b551 100644
--- a/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/OutputLayer.h
+++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/OutputLayer.h
@@ -25,6 +25,7 @@
#include <compositionengine/LayerFE.h>
#include <compositionengine/OutputLayer.h>
#include <ui/FloatRect.h>
+#include <ui/PictureProfileHandle.h>
#include <ui/Rect.h>
#include <ui/DisplayIdentification.h>
@@ -48,6 +49,9 @@
void setHwcLayer(std::shared_ptr<HWC2::Layer>) override;
void uncacheBuffers(const std::vector<uint64_t>& bufferIdsToUncache) override;
+ int64_t getPictureProfilePriority() const override;
+ const PictureProfileHandle& getPictureProfileHandle() const override;
+ void commitPictureProfileToCompositionState() override;
void updateCompositionState(bool includeGeometry, bool forceClientComposition,
ui::Transform::RotationFlags,
@@ -101,7 +105,7 @@
aidl::android::hardware::graphics::composer3::Composition from,
aidl::android::hardware::graphics::composer3::Composition to) const;
bool isClientCompositionForced(bool isPeekingThrough) const;
- void updateLuts(std::shared_ptr<gui::DisplayLuts> luts,
+ void updateLuts(const LayerFECompositionState&,
const std::optional<std::vector<std::optional<LutProperties>>>& properties);
};
diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/OutputLayerCompositionState.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/OutputLayerCompositionState.h
index 28216a4..c558739 100644
--- a/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/OutputLayerCompositionState.h
+++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/OutputLayerCompositionState.h
@@ -22,6 +22,7 @@
#include <renderengine/ExternalTexture.h>
#include <ui/FloatRect.h>
#include <ui/GraphicTypes.h>
+#include <ui/PictureProfileHandle.h>
#include <ui/Rect.h>
#include <ui/Region.h>
@@ -101,6 +102,9 @@
// order to save power.
Region outputSpaceBlockingRegionHint;
+ // The picture profile for this layer.
+ PictureProfileHandle pictureProfileHandle;
+
// Overrides the buffer, acquire fence, and display frame stored in LayerFECompositionState
struct {
std::shared_ptr<renderengine::ExternalTexture> buffer = nullptr;
diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/mock/LayerFE.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/mock/LayerFE.h
index 05a5d38..272fa3e 100644
--- a/services/surfaceflinger/CompositionEngine/include/compositionengine/mock/LayerFE.h
+++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/mock/LayerFE.h
@@ -50,9 +50,6 @@
std::optional<compositionengine::LayerFE::LayerSettings>(
compositionengine::LayerFE::ClientCompositionTargetSettings&));
- MOCK_METHOD(void, onLayerDisplayed, (ftl::SharedFuture<FenceResult>, ui::LayerStack),
- (override));
-
MOCK_METHOD0(createReleaseFenceFuture, ftl::Future<FenceResult>());
MOCK_METHOD1(setReleaseFence, void(const FenceResult&));
MOCK_METHOD0(getReleaseFencePromiseStatus, LayerFE::ReleaseFencePromiseStatus());
@@ -61,6 +58,7 @@
MOCK_CONST_METHOD0(hasRoundedCorners, bool());
MOCK_CONST_METHOD0(getMetadata, gui::LayerMetadata*());
MOCK_CONST_METHOD0(getRelativeMetadata, gui::LayerMetadata*());
+ MOCK_METHOD0(onPictureProfileCommitted, void());
};
} // namespace android::compositionengine::mock
diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/mock/Output.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/mock/Output.h
index 33cdc54..f2c265a 100644
--- a/services/surfaceflinger/CompositionEngine/include/compositionengine/mock/Output.h
+++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/mock/Output.h
@@ -34,7 +34,7 @@
virtual ~Output();
MOCK_CONST_METHOD0(isValid, bool());
- MOCK_CONST_METHOD0(getDisplayId, std::optional<DisplayId>());
+ MOCK_CONST_METHOD0(getDisplayId, ftl::Optional<DisplayId>());
MOCK_METHOD1(setCompositionEnabled, void(bool));
MOCK_METHOD1(setLayerCachingEnabled, void(bool));
@@ -142,6 +142,9 @@
MOCK_METHOD(bool, isPowerHintSessionGpuReportingEnabled, ());
MOCK_METHOD((const aidl::android::hardware::graphics::composer3::OverlayProperties*),
getOverlaySupport, ());
+ MOCK_METHOD(bool, hasPictureProcessing, (), (const));
+ MOCK_METHOD(int32_t, getMaxLayerPictureProfiles, (), (const));
+ MOCK_METHOD(void, applyPictureProfile, ());
};
} // namespace android::compositionengine::mock
diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/mock/OutputLayer.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/mock/OutputLayer.h
index 12f2094..9333ebb 100644
--- a/services/surfaceflinger/CompositionEngine/include/compositionengine/mock/OutputLayer.h
+++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/mock/OutputLayer.h
@@ -63,7 +63,9 @@
(ndk::ScopedFileDescriptor,
(std::vector<std::pair<
int, aidl::android::hardware::graphics::composer3::LutProperties>>)));
-
+ MOCK_METHOD(int64_t, getPictureProfilePriority, (), (const));
+ MOCK_METHOD(const PictureProfileHandle&, getPictureProfileHandle, (), (const));
+ MOCK_METHOD(void, commitPictureProfileToCompositionState, ());
MOCK_CONST_METHOD1(dump, void(std::string&));
};
diff --git a/services/surfaceflinger/CompositionEngine/src/Display.cpp b/services/surfaceflinger/CompositionEngine/src/Display.cpp
index 1825065..e37ce0a 100644
--- a/services/surfaceflinger/CompositionEngine/src/Display.cpp
+++ b/services/surfaceflinger/CompositionEngine/src/Display.cpp
@@ -36,7 +36,7 @@
// TODO(b/129481165): remove the #pragma below and fix conversion issues
#pragma clang diagnostic pop // ignored "-Wconversion"
-#include "DisplayHardware/PowerAdvisor.h"
+#include "PowerAdvisor/PowerAdvisor.h"
using aidl::android::hardware::graphics::composer3::Capability;
using aidl::android::hardware::graphics::composer3::DisplayCapability;
@@ -54,6 +54,8 @@
void Display::setConfiguration(const compositionengine::DisplayCreationArgs& args) {
mId = args.id;
mPowerAdvisor = args.powerAdvisor;
+ mHasPictureProcessing = args.hasPictureProcessing;
+ mMaxLayerPictureProfiles = args.maxLayerPictureProfiles;
editState().isSecure = args.isSecure;
editState().isProtected = args.isProtected;
editState().displaySpace.setBounds(args.pixels);
@@ -80,7 +82,7 @@
return mId.isVirtual();
}
-std::optional<DisplayId> Display::getDisplayId() const {
+ftl::Optional<DisplayId> Display::getDisplayId() const {
return mId;
}
@@ -203,15 +205,16 @@
}
void Display::applyDisplayBrightness(bool applyImmediately) {
- if (const auto displayId = ftl::Optional(getDisplayId()).and_then(PhysicalDisplayId::tryCast);
- displayId && getState().displayBrightness) {
+ if (!getState().displayBrightness) {
+ return;
+ }
+ if (auto displayId = PhysicalDisplayId::tryCast(mId)) {
auto& hwc = getCompositionEngine().getHwComposer();
- const status_t result =
- hwc.setDisplayBrightness(*displayId, *getState().displayBrightness,
- getState().displayBrightnessNits,
- Hwc2::Composer::DisplayBrightnessOptions{
- .applyImmediately = applyImmediately})
- .get();
+ status_t result = hwc.setDisplayBrightness(*displayId, *getState().displayBrightness,
+ getState().displayBrightnessNits,
+ Hwc2::Composer::DisplayBrightnessOptions{
+ .applyImmediately = applyImmediately})
+ .get();
ALOGE_IF(result != NO_ERROR, "setDisplayBrightness failed for %s: %d, (%s)",
getName().c_str(), result, strerror(-result));
}
@@ -288,8 +291,8 @@
}
bool Display::getSkipColorTransform() const {
- const auto& hwc = getCompositionEngine().getHwComposer();
- if (const auto halDisplayId = HalDisplayId::tryCast(mId)) {
+ auto& hwc = getCompositionEngine().getHwComposer();
+ if (auto halDisplayId = HalDisplayId::tryCast(mId)) {
return hwc.hasDisplayCapability(*halDisplayId,
DisplayCapability::SKIP_CLIENT_COLOR_TRANSFORM);
}
@@ -462,6 +465,14 @@
return &getCompositionEngine().getHwComposer().getOverlaySupport();
}
+bool Display::hasPictureProcessing() const {
+ return mHasPictureProcessing;
+}
+
+int32_t Display::getMaxLayerPictureProfiles() const {
+ return mMaxLayerPictureProfiles;
+}
+
void Display::finishFrame(GpuCompositionResult&& result) {
// We only need to actually compose the display if:
// 1) It is being handled by hardware composer, which may need this to
@@ -476,8 +487,8 @@
}
bool Display::supportsOffloadPresent() const {
- if (const auto halDisplayId = HalDisplayId::tryCast(mId)) {
- const auto& hwc = getCompositionEngine().getHwComposer();
+ if (auto halDisplayId = HalDisplayId::tryCast(mId)) {
+ auto& hwc = getCompositionEngine().getHwComposer();
return hwc.hasDisplayCapability(*halDisplayId, DisplayCapability::MULTI_THREADED_PRESENT);
}
diff --git a/services/surfaceflinger/CompositionEngine/src/LayerFECompositionState.cpp b/services/surfaceflinger/CompositionEngine/src/LayerFECompositionState.cpp
index 2d10866..348111d 100644
--- a/services/surfaceflinger/CompositionEngine/src/LayerFECompositionState.cpp
+++ b/services/surfaceflinger/CompositionEngine/src/LayerFECompositionState.cpp
@@ -127,6 +127,9 @@
}
dumpVal(out, "colorTransform", colorTransform);
dumpVal(out, "caching hint", toString(cachingHint));
+ if (pictureProfileHandle) {
+ dumpVal(out, "pictureProfile", toString(pictureProfileHandle));
+ }
out.append("\n");
}
diff --git a/services/surfaceflinger/CompositionEngine/src/Output.cpp b/services/surfaceflinger/CompositionEngine/src/Output.cpp
index bb45613..f9ed92d 100644
--- a/services/surfaceflinger/CompositionEngine/src/Output.cpp
+++ b/services/surfaceflinger/CompositionEngine/src/Output.cpp
@@ -32,9 +32,12 @@
#include <compositionengine/impl/planner/Planner.h>
#include <ftl/algorithm.h>
#include <ftl/future.h>
+#include <ftl/optional.h>
#include <scheduler/FrameTargeter.h>
#include <scheduler/Time.h>
+#include <com_android_graphics_libgui_flags.h>
+
#include <optional>
#include <thread>
@@ -111,7 +114,7 @@
mRenderSurface->isValid();
}
-std::optional<DisplayId> Output::getDisplayId() const {
+ftl::Optional<DisplayId> Output::getDisplayId() const {
return {};
}
@@ -433,7 +436,7 @@
ftl::Future<std::monostate> Output::present(
const compositionengine::CompositionRefreshArgs& refreshArgs) {
const auto stringifyExpectedPresentTime = [this, &refreshArgs]() -> std::string {
- return ftl::Optional(getDisplayId())
+ return getDisplayId()
.and_then(PhysicalDisplayId::tryCast)
.and_then([&refreshArgs](PhysicalDisplayId id) {
return refreshArgs.frameTargets.get(id);
@@ -500,15 +503,6 @@
updateHwcAsyncWorker();
}
-void Output::uncacheBuffers(std::vector<uint64_t> const& bufferIdsToUncache) {
- if (bufferIdsToUncache.empty()) {
- return;
- }
- for (auto outputLayer : getOutputLayersOrderedByZ()) {
- outputLayer->uncacheBuffers(bufferIdsToUncache);
- }
-}
-
void Output::rebuildLayerStacks(const compositionengine::CompositionRefreshArgs& refreshArgs,
LayerFESet& layerFESet) {
auto& outputState = editState();
@@ -776,11 +770,11 @@
// The layer is visible. Either reuse the existing outputLayer if we have
// one, or create a new one if we do not.
- auto result = ensureOutputLayer(prevOutputLayerIndex, layerFE);
+ auto outputLayer = ensureOutputLayer(prevOutputLayerIndex, layerFE);
// Store the layer coverage information into the layer state as some of it
// is useful later.
- auto& outputLayerState = result->editState();
+ auto& outputLayerState = outputLayer->editState();
outputLayerState.visibleRegion = visibleRegion;
outputLayerState.visibleNonTransparentRegion = visibleNonTransparentRegion;
outputLayerState.coveredRegion = coveredRegion;
@@ -798,6 +792,54 @@
}
}
+void Output::uncacheBuffers(std::vector<uint64_t> const& bufferIdsToUncache) {
+ if (bufferIdsToUncache.empty()) {
+ return;
+ }
+ for (auto outputLayer : getOutputLayersOrderedByZ()) {
+ outputLayer->uncacheBuffers(bufferIdsToUncache);
+ }
+}
+
+void Output::commitPictureProfilesToCompositionState() {
+ if (!com_android_graphics_libgui_flags_apply_picture_profiles()) {
+ return;
+ }
+ if (!hasPictureProcessing()) {
+ return;
+ }
+ auto compare = [](const ::android::compositionengine::OutputLayer* lhs,
+ const ::android::compositionengine::OutputLayer* rhs) {
+ return lhs->getPictureProfilePriority() > rhs->getPictureProfilePriority();
+ };
+ std::priority_queue<::android::compositionengine::OutputLayer*,
+ std::vector<::android::compositionengine::OutputLayer*>, decltype(compare)>
+ layersWithProfiles;
+ for (auto outputLayer : getOutputLayersOrderedByZ()) {
+ if (outputLayer->getPictureProfileHandle()) {
+ layersWithProfiles.push(outputLayer);
+ }
+ }
+
+ // TODO(b/337330263): Use the default display picture profile from SurfaceFlinger
+ editState().pictureProfileHandle = PictureProfileHandle::NONE;
+
+ // When layer-specific picture processing is supported, apply as many high priority profiles as
+ // possible to the layers, and ignore the low priority layers.
+ if (getMaxLayerPictureProfiles() > 0) {
+ for (int i = 0; i < getMaxLayerPictureProfiles() && !layersWithProfiles.empty();
+ layersWithProfiles.pop(), ++i) {
+ layersWithProfiles.top()->commitPictureProfileToCompositionState();
+ layersWithProfiles.top()->getLayerFE().onPictureProfileCommitted();
+ }
+ // No layer-specific picture processing, so apply the highest priority picture profile to
+ // the entire display.
+ } else if (!layersWithProfiles.empty()) {
+ editState().pictureProfileHandle = layersWithProfiles.top()->getPictureProfileHandle();
+ layersWithProfiles.top()->getLayerFE().onPictureProfileCommitted();
+ }
+}
+
void Output::setReleasedLayers(const compositionengine::CompositionRefreshArgs&) {
// The base class does nothing with this call.
}
@@ -826,6 +868,7 @@
forceClientComposition = false;
}
}
+ commitPictureProfilesToCompositionState();
}
void Output::planComposition() {
@@ -847,17 +890,25 @@
return;
}
- if (auto frameTargetPtrOpt = ftl::Optional(getDisplayId())
+ if (auto frameTargetPtrOpt = getDisplayId()
.and_then(PhysicalDisplayId::tryCast)
.and_then([&refreshArgs](PhysicalDisplayId id) {
return refreshArgs.frameTargets.get(id);
})) {
editState().earliestPresentTime = frameTargetPtrOpt->get()->earliestPresentTime();
editState().expectedPresentTime = frameTargetPtrOpt->get()->expectedPresentTime().ns();
+ const auto debugPresentDelay = frameTargetPtrOpt->get()->debugPresentDelay();
+ if (debugPresentDelay) {
+ SFTRACE_FORMAT_INSTANT("DEBUG delaying presentation by %.2fms",
+ debugPresentDelay->ns() / 1e6f);
+ editState().expectedPresentTime += debugPresentDelay->ns();
+ }
}
editState().frameInterval = refreshArgs.frameInterval;
editState().powerCallback = refreshArgs.powerCallback;
+ applyPictureProfile();
+
compositionengine::OutputLayer* peekThroughLayer = nullptr;
sp<GraphicBuffer> previousOverride = nullptr;
bool includeGeometry = refreshArgs.updatingGeometryThisFrame;
@@ -1763,5 +1814,34 @@
return getState().displayBrightnessNits / getState().sdrWhitePointNits;
}
+bool Output::hasPictureProcessing() const {
+ return false;
+}
+
+int32_t Output::getMaxLayerPictureProfiles() const {
+ return 0;
+}
+
+void Output::applyPictureProfile() {
+ if (!com_android_graphics_libgui_flags_apply_picture_profiles()) {
+ return;
+ }
+
+ // TODO(b/337330263): Move this into the Display class and add a Display unit test.
+ if (!getState().pictureProfileHandle) {
+ return;
+ }
+ if (!getDisplayId()) {
+ return;
+ }
+ if (auto displayId = PhysicalDisplayId::tryCast(*getDisplayId())) {
+ auto& hwc = getCompositionEngine().getHwComposer();
+ const status_t error =
+ hwc.setDisplayPictureProfileHandle(*displayId, getState().pictureProfileHandle);
+ ALOGE_IF(error, "setDisplayPictureProfileHandle failed for %s: %d, (%s)", getName().c_str(),
+ error, strerror(-error));
+ }
+}
+
} // namespace impl
} // namespace android::compositionengine
diff --git a/services/surfaceflinger/CompositionEngine/src/OutputLayer.cpp b/services/surfaceflinger/CompositionEngine/src/OutputLayer.cpp
index 934909d..a040c88 100644
--- a/services/surfaceflinger/CompositionEngine/src/OutputLayer.cpp
+++ b/services/surfaceflinger/CompositionEngine/src/OutputLayer.cpp
@@ -13,6 +13,7 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
+
#include <DisplayHardware/Hal.h>
#include <android-base/stringprintf.h>
#include <compositionengine/DisplayColorProfile.h>
@@ -22,11 +23,13 @@
#include <compositionengine/impl/OutputCompositionState.h>
#include <compositionengine/impl/OutputLayer.h>
#include <compositionengine/impl/OutputLayerCompositionState.h>
-#include <cstdint>
-#include "system/graphics-base-v1.0.h"
-#include "ui/FloatRect.h"
-
+#include <ui/FloatRect.h>
#include <ui/HdrRenderTypeUtils.h>
+#include <cstdint>
+#include <limits>
+#include "system/graphics-base-v1.0.h"
+
+#include <com_android_graphics_libgui_flags.h>
// TODO(b/129481165): remove the #pragma below and fix conversion issues
#pragma clang diagnostic push
@@ -286,8 +289,13 @@
}
void OutputLayer::updateLuts(
- std::shared_ptr<gui::DisplayLuts> layerFEStateLut,
+ const LayerFECompositionState& layerFEState,
const std::optional<std::vector<std::optional<LutProperties>>>& properties) {
+ auto& luts = layerFEState.luts;
+ if (!luts) {
+ return;
+ }
+
auto& state = editState();
if (!properties) {
@@ -303,7 +311,7 @@
}
}
- for (const auto& inputLut : layerFEStateLut->lutProperties) {
+ for (const auto& inputLut : luts->lutProperties) {
bool foundInHwcLuts = false;
for (const auto& hwcLut : hwcLutProperties) {
if (static_cast<int32_t>(hwcLut.dimension) ==
@@ -316,7 +324,7 @@
break;
}
}
- // if any lut properties of layerFEStateLut can not be found in hwcLutProperties,
+ // if any lut properties of luts can not be found in hwcLutProperties,
// GPU composition instead
if (!foundInHwcLuts) {
state.forceClientComposition = true;
@@ -391,11 +399,22 @@
// For hdr content, treat the white point as the display brightness - HDR content should not be
// boosted or dimmed.
// If the layer explicitly requests to disable dimming, then don't dim either.
- if (hdrRenderType == HdrRenderType::GENERIC_HDR ||
- getOutput().getState().displayBrightnessNits == getOutput().getState().sdrWhitePointNits ||
- getOutput().getState().displayBrightnessNits == 0.f || !layerFEState->dimmingEnabled) {
+ if (getOutput().getState().displayBrightnessNits == getOutput().getState().sdrWhitePointNits ||
+ getOutput().getState().displayBrightnessNits <= 0.f || !layerFEState->dimmingEnabled) {
state.dimmingRatio = 1.f;
state.whitePointNits = getOutput().getState().displayBrightnessNits;
+ } else if (hdrRenderType == HdrRenderType::GENERIC_HDR) {
+ float deviceHeadroom = getOutput().getState().displayBrightnessNits /
+ getOutput().getState().sdrWhitePointNits;
+ float idealizedMaxHeadroom = deviceHeadroom;
+
+ if (FlagManager::getInstance().begone_bright_hlg()) {
+ idealizedMaxHeadroom =
+ std::min(idealizedMaxHeadroom, getIdealizedMaxHeadroom(state.dataspace));
+ }
+
+ state.dimmingRatio = std::min(idealizedMaxHeadroom / deviceHeadroom, 1.0f);
+ state.whitePointNits = getOutput().getState().displayBrightnessNits * state.dimmingRatio;
} else {
float layerBrightnessNits = getOutput().getState().sdrWhitePointNits;
// RANGE_EXTENDED can "self-promote" to HDR, but is still rendered for a particular
@@ -409,10 +428,7 @@
state.whitePointNits = layerBrightnessNits;
}
- const auto& layerFEStateLut = layerFEState->luts;
- if (layerFEStateLut) {
- updateLuts(layerFEStateLut, properties);
- }
+ updateLuts(*layerFEState, properties);
// These are evaluated every frame as they can potentially change at any
// time.
@@ -422,6 +438,16 @@
}
}
+void OutputLayer::commitPictureProfileToCompositionState() {
+ if (!com_android_graphics_libgui_flags_apply_picture_profiles()) {
+ return;
+ }
+ const auto* layerState = getLayerFE().getCompositionState();
+ if (layerState) {
+ editState().pictureProfileHandle = layerState->pictureProfileHandle;
+ }
+}
+
void OutputLayer::writeStateToHWC(bool includeGeometry, bool skipLayer, uint32_t z,
bool zIsOverridden, bool isPeekingThrough) {
const auto& state = getState();
@@ -643,6 +669,21 @@
ALOGE("[%s] Failed to set brightness %f: %s (%d)", getLayerFE().getDebugName(),
dimmingRatio, to_string(error).c_str(), static_cast<int32_t>(error));
}
+
+ if (com_android_graphics_libgui_flags_apply_picture_profiles() &&
+ outputDependentState.pictureProfileHandle) {
+ if (auto error =
+ hwcLayer->setPictureProfileHandle(outputDependentState.pictureProfileHandle);
+ error != hal::Error::NONE) {
+ ALOGE("[%s] Failed to set picture profile handle: %s (%d)", getLayerFE().getDebugName(),
+ toString(outputDependentState.pictureProfileHandle).c_str(),
+ static_cast<int32_t>(error));
+ }
+ // Reset the picture profile state, as it needs to be re-committed on each present cycle
+ // when Output decides that the limited picture-processing hardware should be used by this
+ // layer.
+ editState().pictureProfileHandle = PictureProfileHandle::NONE;
+ }
}
void OutputLayer::writeOutputIndependentPerFrameStateToHWC(
@@ -748,6 +789,16 @@
}
}
+int64_t OutputLayer::getPictureProfilePriority() const {
+ const auto* layerState = getLayerFE().getCompositionState();
+ return layerState ? layerState->pictureProfilePriority : 0;
+}
+
+const PictureProfileHandle& OutputLayer::getPictureProfileHandle() const {
+ const auto* layerState = getLayerFE().getCompositionState();
+ return layerState ? layerState->pictureProfileHandle : PictureProfileHandle::NONE;
+}
+
void OutputLayer::writeBufferStateToHWC(HWC2::Layer* hwcLayer,
const LayerFECompositionState& outputIndependentState,
bool skipLayer) {
@@ -830,14 +881,14 @@
return;
}
- const auto* layerFEState = getLayerFE().getCompositionState();
- if (!layerFEState) {
+ const auto* layerState = getLayerFE().getCompositionState();
+ if (!layerState) {
return;
}
const auto& outputState = getOutput().getState();
- Rect frame = layerFEState->cursorFrame;
+ Rect frame = layerState->cursorFrame;
frame.intersect(outputState.layerStackSpace.getContent(), &frame);
Rect position = outputState.transform.transform(frame);
diff --git a/services/surfaceflinger/CompositionEngine/src/OutputLayerCompositionState.cpp b/services/surfaceflinger/CompositionEngine/src/OutputLayerCompositionState.cpp
index da1f7e4..deef747 100644
--- a/services/surfaceflinger/CompositionEngine/src/OutputLayerCompositionState.cpp
+++ b/services/surfaceflinger/CompositionEngine/src/OutputLayerCompositionState.cpp
@@ -72,6 +72,9 @@
dumpVal(out, "dataspace", toString(dataspace), dataspace);
dumpVal(out, "whitePointNits", whitePointNits);
dumpVal(out, "dimmingRatio", dimmingRatio);
+ if (pictureProfileHandle) {
+ dumpVal(out, "pictureProfile", toString(pictureProfileHandle));
+ }
dumpVal(out, "override buffer", overrideInfo.buffer.get());
dumpVal(out, "override acquire fence", overrideInfo.acquireFence.get());
dumpVal(out, "override display frame", overrideInfo.displayFrame);
diff --git a/services/surfaceflinger/CompositionEngine/tests/CompositionEngineTest.cpp b/services/surfaceflinger/CompositionEngine/tests/CompositionEngineTest.cpp
index 48ebc32..3e0c390 100644
--- a/services/surfaceflinger/CompositionEngine/tests/CompositionEngineTest.cpp
+++ b/services/surfaceflinger/CompositionEngine/tests/CompositionEngineTest.cpp
@@ -26,9 +26,8 @@
#include <gtest/gtest.h>
#include <renderengine/mock/RenderEngine.h>
-#include "MockHWComposer.h"
#include "TimeStats/TimeStats.h"
-#include "gmock/gmock.h"
+#include "mock/DisplayHardware/MockHWComposer.h"
using namespace com::android::graphics::surfaceflinger;
diff --git a/services/surfaceflinger/CompositionEngine/tests/DisplayTest.cpp b/services/surfaceflinger/CompositionEngine/tests/DisplayTest.cpp
index 9c0e62c..c1e59d0 100644
--- a/services/surfaceflinger/CompositionEngine/tests/DisplayTest.cpp
+++ b/services/surfaceflinger/CompositionEngine/tests/DisplayTest.cpp
@@ -36,10 +36,10 @@
#include <ui/Rect.h>
#include <ui/StaticDisplayInfo.h>
-#include "MockHWC2.h"
-#include "MockHWComposer.h"
-#include "MockPowerAdvisor.h"
#include "ftl/future.h"
+#include "mock/DisplayHardware/MockHWC2.h"
+#include "mock/DisplayHardware/MockHWComposer.h"
+#include "mock/PowerAdvisor/MockPowerAdvisor.h"
#include <aidl/android/hardware/graphics/composer3/Composition.h>
@@ -192,7 +192,7 @@
}
StrictMock<android::mock::HWComposer> mHwComposer;
- StrictMock<Hwc2::mock::PowerAdvisor> mPowerAdvisor;
+ StrictMock<adpf::mock::PowerAdvisor> mPowerAdvisor;
StrictMock<renderengine::mock::RenderEngine> mRenderEngine;
StrictMock<mock::CompositionEngine> mCompositionEngine;
sp<mock::NativeWindow> mNativeWindow = sp<StrictMock<mock::NativeWindow>>::make();
@@ -1035,7 +1035,7 @@
}
NiceMock<android::mock::HWComposer> mHwComposer;
- NiceMock<Hwc2::mock::PowerAdvisor> mPowerAdvisor;
+ NiceMock<adpf::mock::PowerAdvisor> mPowerAdvisor;
NiceMock<mock::CompositionEngine> mCompositionEngine;
sp<mock::NativeWindow> mNativeWindow = sp<NiceMock<mock::NativeWindow>>::make();
sp<mock::DisplaySurface> mDisplaySurface = sp<NiceMock<mock::DisplaySurface>>::make();
diff --git a/services/surfaceflinger/CompositionEngine/tests/MockHWC2.cpp b/services/surfaceflinger/CompositionEngine/tests/MockHWC2.cpp
deleted file mode 100644
index 0baa79d..0000000
--- a/services/surfaceflinger/CompositionEngine/tests/MockHWC2.cpp
+++ /dev/null
@@ -1,32 +0,0 @@
-/*
- * Copyright 2019 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.
- */
-
-#include "MockHWC2.h"
-
-namespace android::HWC2 {
-
-// This will go away once HWC2::Layer is moved into the "backend" library
-Layer::~Layer() = default;
-
-namespace mock {
-
-// The Google Mock documentation recommends explicit non-header instantiations
-// for better compile time performance.
-Layer::Layer() = default;
-Layer::~Layer() = default;
-
-} // namespace mock
-} // namespace android::HWC2
diff --git a/services/surfaceflinger/CompositionEngine/tests/MockHWC2.h b/services/surfaceflinger/CompositionEngine/tests/MockHWC2.h
deleted file mode 100644
index 26b5f4a..0000000
--- a/services/surfaceflinger/CompositionEngine/tests/MockHWC2.h
+++ /dev/null
@@ -1,86 +0,0 @@
-/*
- * Copyright 2019 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.
- */
-
-#pragma once
-
-#include <gmock/gmock.h>
-#include <ui/Fence.h>
-#include <ui/FloatRect.h>
-#include <ui/GraphicBuffer.h>
-#include <ui/Rect.h>
-#include <ui/Region.h>
-#include <ui/Transform.h>
-
-// TODO(b/129481165): remove the #pragma below and fix conversion issues
-#pragma clang diagnostic push
-#pragma clang diagnostic ignored "-Wconversion"
-#pragma clang diagnostic ignored "-Wextra"
-
-#include <ui/GraphicTypes.h>
-#include "DisplayHardware/HWC2.h"
-
-#include <aidl/android/hardware/graphics/composer3/Composition.h>
-#include <aidl/android/hardware/graphics/composer3/Luts.h>
-
-// TODO(b/129481165): remove the #pragma below and fix conversion issues
-#pragma clang diagnostic pop // ignored "-Wconversion -Wextra"
-
-namespace android {
-namespace HWC2 {
-namespace mock {
-
-namespace hal = android::hardware::graphics::composer::hal;
-
-using Error = hal::Error;
-
-class Layer : public HWC2::Layer {
-public:
- Layer();
- ~Layer() override;
-
- MOCK_CONST_METHOD0(getId, hal::HWLayerId());
-
- MOCK_METHOD2(setCursorPosition, Error(int32_t, int32_t));
- MOCK_METHOD3(setBuffer,
- Error(uint32_t, const android::sp<android::GraphicBuffer>&,
- const android::sp<android::Fence>&));
- MOCK_METHOD2(setBufferSlotsToClear, Error(const std::vector<uint32_t>&, uint32_t));
- MOCK_METHOD1(setSurfaceDamage, Error(const android::Region&));
- MOCK_METHOD1(setBlendMode, Error(hal::BlendMode));
- MOCK_METHOD1(setColor, Error(aidl::android::hardware::graphics::composer3::Color));
- MOCK_METHOD1(setCompositionType,
- Error(aidl::android::hardware::graphics::composer3::Composition));
- MOCK_METHOD1(setDataspace, Error(android::ui::Dataspace));
- MOCK_METHOD2(setPerFrameMetadata, Error(const int32_t, const android::HdrMetadata&));
- MOCK_METHOD1(setDisplayFrame, Error(const android::Rect&));
- MOCK_METHOD1(setPlaneAlpha, Error(float));
- MOCK_METHOD1(setSidebandStream, Error(const native_handle_t*));
- MOCK_METHOD1(setSourceCrop, Error(const android::FloatRect&));
- MOCK_METHOD1(setTransform, Error(hal::Transform));
- MOCK_METHOD1(setVisibleRegion, Error(const android::Region&));
- MOCK_METHOD1(setZOrder, Error(uint32_t));
-
- MOCK_METHOD1(setColorTransform, Error(const android::mat4&));
- MOCK_METHOD3(setLayerGenericMetadata,
- Error(const std::string&, bool, const std::vector<uint8_t>&));
- MOCK_METHOD1(setBrightness, Error(float));
- MOCK_METHOD1(setBlockingRegion, Error(const android::Region&));
- MOCK_METHOD(Error, setLuts, (aidl::android::hardware::graphics::composer3::Luts&));
-};
-
-} // namespace mock
-} // namespace HWC2
-} // namespace android
diff --git a/services/surfaceflinger/CompositionEngine/tests/MockHWComposer.h b/services/surfaceflinger/CompositionEngine/tests/MockHWComposer.h
deleted file mode 100644
index 5c55ce7..0000000
--- a/services/surfaceflinger/CompositionEngine/tests/MockHWComposer.h
+++ /dev/null
@@ -1,159 +0,0 @@
-/*
- * Copyright 2018 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.
- */
-
-#pragma once
-
-#include <compositionengine/Output.h>
-#include <gmock/gmock.h>
-
-// TODO(b/129481165): remove the #pragma below and fix conversion issues
-#pragma clang diagnostic push
-#pragma clang diagnostic ignored "-Wconversion"
-#pragma clang diagnostic ignored "-Wextra"
-
-#include "DisplayHardware/HWComposer.h"
-
-// TODO(b/129481165): remove the #pragma below and fix conversion issues
-#pragma clang diagnostic pop // ignored "-Wconversion -Wextra"
-
-namespace android {
-namespace mock {
-
-namespace hal = android::hardware::graphics::composer::hal;
-
-class HWComposer : public android::HWComposer {
-public:
- HWComposer();
- ~HWComposer() override;
-
- MOCK_METHOD1(setCallback, void(HWC2::ComposerCallback&));
- MOCK_CONST_METHOD3(getDisplayIdentificationData,
- bool(hal::HWDisplayId, uint8_t*, DisplayIdentificationData*));
- MOCK_CONST_METHOD1(hasCapability,
- bool(aidl::android::hardware::graphics::composer3::Capability));
- MOCK_CONST_METHOD2(hasDisplayCapability,
- bool(HalDisplayId,
- aidl::android::hardware::graphics::composer3::DisplayCapability));
-
- MOCK_CONST_METHOD0(getMaxVirtualDisplayCount, size_t());
- MOCK_CONST_METHOD0(getMaxVirtualDisplayDimension, size_t());
- MOCK_METHOD3(allocateVirtualDisplay, bool(HalVirtualDisplayId, ui::Size, ui::PixelFormat*));
- MOCK_METHOD3(allocatePhysicalDisplay,
- void(hal::HWDisplayId, PhysicalDisplayId, std::optional<ui::Size>));
-
- MOCK_METHOD1(createLayer, std::shared_ptr<HWC2::Layer>(HalDisplayId));
- MOCK_METHOD(status_t, getDeviceCompositionChanges,
- (HalDisplayId, bool, std::optional<std::chrono::steady_clock::time_point>, nsecs_t,
- Fps, std::optional<android::HWComposer::DeviceRequestedChanges>*));
- MOCK_METHOD(status_t, setClientTarget,
- (HalDisplayId, uint32_t, const sp<Fence>&, const sp<GraphicBuffer>&, ui::Dataspace,
- float),
- (override));
- MOCK_METHOD2(presentAndGetReleaseFences,
- status_t(HalDisplayId, std::optional<std::chrono::steady_clock::time_point>));
- MOCK_METHOD(status_t, executeCommands, (HalDisplayId));
- MOCK_METHOD2(setPowerMode, status_t(PhysicalDisplayId, hal::PowerMode));
- MOCK_METHOD2(setActiveConfig, status_t(HalDisplayId, size_t));
- MOCK_METHOD2(setColorTransform, status_t(HalDisplayId, const mat4&));
- MOCK_METHOD1(disconnectDisplay, void(HalDisplayId));
- MOCK_CONST_METHOD1(hasDeviceComposition, bool(const std::optional<DisplayId>&));
- MOCK_CONST_METHOD1(getPresentFence, sp<Fence>(HalDisplayId));
- MOCK_METHOD(nsecs_t, getPresentTimestamp, (PhysicalDisplayId), (const, override));
- MOCK_CONST_METHOD2(getLayerReleaseFence, sp<Fence>(HalDisplayId, HWC2::Layer*));
- MOCK_METHOD3(setOutputBuffer,
- status_t(HalVirtualDisplayId, const sp<Fence>&, const sp<GraphicBuffer>&));
- MOCK_METHOD1(clearReleaseFences, void(HalDisplayId));
- MOCK_METHOD2(getHdrCapabilities, status_t(HalDisplayId, HdrCapabilities*));
- MOCK_CONST_METHOD1(getSupportedPerFrameMetadata, int32_t(HalDisplayId));
- MOCK_CONST_METHOD2(getRenderIntents,
- std::vector<ui::RenderIntent>(HalDisplayId, ui::ColorMode));
- MOCK_METHOD2(getDataspaceSaturationMatrix, mat4(HalDisplayId, ui::Dataspace));
- MOCK_METHOD4(getDisplayedContentSamplingAttributes,
- status_t(HalDisplayId, ui::PixelFormat*, ui::Dataspace*, uint8_t*));
- MOCK_METHOD4(setDisplayContentSamplingEnabled, status_t(HalDisplayId, bool, uint8_t, uint64_t));
- MOCK_METHOD4(getDisplayedContentSample,
- status_t(HalDisplayId, uint64_t, uint64_t, DisplayedFrameStats*));
- MOCK_METHOD(ftl::Future<status_t>, setDisplayBrightness,
- (PhysicalDisplayId, float, float, const Hwc2::Composer::DisplayBrightnessOptions&),
- (override));
- MOCK_METHOD2(getDisplayBrightnessSupport, status_t(PhysicalDisplayId, bool*));
-
- MOCK_METHOD2(onHotplug,
- std::optional<DisplayIdentificationInfo>(hal::HWDisplayId, hal::Connection));
- MOCK_CONST_METHOD0(updatesDeviceProductInfoOnHotplugReconnect, bool());
- MOCK_METHOD(std::optional<PhysicalDisplayId>, onVsync, (hal::HWDisplayId, int64_t));
- MOCK_METHOD2(setVsyncEnabled, void(PhysicalDisplayId, hal::Vsync));
- MOCK_CONST_METHOD1(isConnected, bool(PhysicalDisplayId));
- MOCK_CONST_METHOD2(getModes,
- std::vector<HWComposer::HWCDisplayMode>(PhysicalDisplayId, int32_t));
- MOCK_CONST_METHOD1(getActiveMode, ftl::Expected<hal::HWConfigId, status_t>(PhysicalDisplayId));
- MOCK_CONST_METHOD1(getColorModes, std::vector<ui::ColorMode>(PhysicalDisplayId));
- MOCK_METHOD3(setActiveColorMode, status_t(PhysicalDisplayId, ui::ColorMode, ui::RenderIntent));
- MOCK_CONST_METHOD0(isUsingVrComposer, bool());
- MOCK_CONST_METHOD1(getDisplayConnectionType, ui::DisplayConnectionType(PhysicalDisplayId));
- MOCK_CONST_METHOD1(isVsyncPeriodSwitchSupported, bool(PhysicalDisplayId));
- MOCK_CONST_METHOD1(getDisplayVsyncPeriod, ftl::Expected<nsecs_t, status_t>(PhysicalDisplayId));
- MOCK_METHOD4(setActiveModeWithConstraints,
- status_t(PhysicalDisplayId, hal::HWConfigId,
- const hal::VsyncPeriodChangeConstraints&,
- hal::VsyncPeriodChangeTimeline*));
- MOCK_METHOD2(setBootDisplayMode, status_t(PhysicalDisplayId, hal::HWConfigId));
- MOCK_METHOD1(clearBootDisplayMode, status_t(PhysicalDisplayId));
- MOCK_METHOD1(getPreferredBootDisplayMode, std::optional<hal::HWConfigId>(PhysicalDisplayId));
- MOCK_METHOD0(getBootDisplayModeSupport, bool());
- MOCK_CONST_METHOD0(
- getHdrConversionCapabilities,
- std::vector<aidl::android::hardware::graphics::common::HdrConversionCapability>());
- MOCK_METHOD2(setHdrConversionStrategy,
- status_t(aidl::android::hardware::graphics::common::HdrConversionStrategy,
- aidl::android::hardware::graphics::common::Hdr*));
- MOCK_METHOD2(setAutoLowLatencyMode, status_t(PhysicalDisplayId, bool));
- MOCK_METHOD(status_t, getSupportedContentTypes,
- (PhysicalDisplayId, std::vector<hal::ContentType>*), (const, override));
- MOCK_METHOD2(setContentType, status_t(PhysicalDisplayId, hal::ContentType));
- MOCK_CONST_METHOD0(getSupportedLayerGenericMetadata,
- const std::unordered_map<std::string, bool>&());
-
- MOCK_CONST_METHOD1(dump, void(std::string&));
- MOCK_CONST_METHOD1(dumpOverlayProperties, void(std::string&));
- MOCK_CONST_METHOD0(getComposer, android::Hwc2::Composer*());
-
- MOCK_METHOD(hal::HWDisplayId, getPrimaryHwcDisplayId, (), (const, override));
- MOCK_METHOD(PhysicalDisplayId, getPrimaryDisplayId, (), (const, override));
- MOCK_METHOD(bool, isHeadless, (), (const, override));
-
- MOCK_METHOD(std::optional<PhysicalDisplayId>, toPhysicalDisplayId, (hal::HWDisplayId),
- (const, override));
- MOCK_METHOD(std::optional<hal::HWDisplayId>, fromPhysicalDisplayId, (PhysicalDisplayId),
- (const, override));
- MOCK_METHOD2(getDisplayDecorationSupport,
- status_t(PhysicalDisplayId,
- std::optional<aidl::android::hardware::graphics::common::
- DisplayDecorationSupport>* support));
- MOCK_METHOD2(setIdleTimerEnabled, status_t(PhysicalDisplayId, std::chrono::milliseconds));
- MOCK_METHOD(bool, hasDisplayIdleTimerCapability, (PhysicalDisplayId), (const, override));
- MOCK_METHOD(Hwc2::AidlTransform, getPhysicalDisplayOrientation, (PhysicalDisplayId),
- (const, override));
- MOCK_METHOD(bool, getValidateSkipped, (HalDisplayId), (const, override));
- MOCK_METHOD(const aidl::android::hardware::graphics::composer3::OverlayProperties&,
- getOverlaySupport, (), (const, override));
- MOCK_METHOD(status_t, setRefreshRateChangedCallbackDebugEnabled, (PhysicalDisplayId, bool));
- MOCK_METHOD(status_t, notifyExpectedPresent, (PhysicalDisplayId, TimePoint, Fps));
- MOCK_METHOD((HWC2::Display::LutFileDescriptorMapper&), getLutFileDescriptorMapper, (), ());
-};
-
-} // namespace mock
-} // namespace android
diff --git a/services/surfaceflinger/CompositionEngine/tests/MockPowerAdvisor.cpp b/services/surfaceflinger/CompositionEngine/tests/MockPowerAdvisor.cpp
deleted file mode 100644
index 85b9403..0000000
--- a/services/surfaceflinger/CompositionEngine/tests/MockPowerAdvisor.cpp
+++ /dev/null
@@ -1,34 +0,0 @@
-/*
- * Copyright 2019 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.
- */
-
-#include "MockPowerAdvisor.h"
-
-namespace android {
-namespace Hwc2 {
-
-// This will go away once PowerAdvisor is moved into the "backend" library
-PowerAdvisor::~PowerAdvisor() = default;
-
-namespace mock {
-
-// The Google Mock documentation recommends explicit non-header instantiations
-// for better compile time performance.
-PowerAdvisor::PowerAdvisor() = default;
-PowerAdvisor::~PowerAdvisor() = default;
-
-} // namespace mock
-} // namespace Hwc2
-} // namespace android
diff --git a/services/surfaceflinger/CompositionEngine/tests/MockPowerAdvisor.h b/services/surfaceflinger/CompositionEngine/tests/MockPowerAdvisor.h
deleted file mode 100644
index ed2ffa9..0000000
--- a/services/surfaceflinger/CompositionEngine/tests/MockPowerAdvisor.h
+++ /dev/null
@@ -1,72 +0,0 @@
-/*
- * Copyright 2019 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.
- */
-
-#pragma once
-
-#include <gmock/gmock.h>
-
-#include "DisplayHardware/PowerAdvisor.h"
-
-namespace android {
-namespace Hwc2 {
-namespace mock {
-
-class PowerAdvisor : public android::Hwc2::PowerAdvisor {
-public:
- PowerAdvisor();
- ~PowerAdvisor() override;
-
- MOCK_METHOD(void, init, (), (override));
- MOCK_METHOD(void, onBootFinished, (), (override));
- MOCK_METHOD(void, setExpensiveRenderingExpected, (DisplayId displayId, bool expected),
- (override));
- MOCK_METHOD(bool, isUsingExpensiveRendering, (), (override));
- MOCK_METHOD(void, notifyCpuLoadUp, (), (override));
- MOCK_METHOD(void, notifyDisplayUpdateImminentAndCpuReset, (), (override));
- MOCK_METHOD(bool, usePowerHintSession, (), (override));
- MOCK_METHOD(bool, supportsPowerHintSession, (), (override));
- MOCK_METHOD(bool, supportsGpuReporting, (), (override));
- MOCK_METHOD(void, updateTargetWorkDuration, (Duration targetDuration), (override));
- MOCK_METHOD(void, reportActualWorkDuration, (), (override));
- MOCK_METHOD(void, enablePowerHintSession, (bool enabled), (override));
- MOCK_METHOD(bool, startPowerHintSession, (std::vector<int32_t> && threadIds), (override));
- MOCK_METHOD(void, setGpuStartTime, (DisplayId displayId, TimePoint startTime), (override));
- MOCK_METHOD(void, setGpuFenceTime,
- (DisplayId displayId, std::unique_ptr<FenceTime>&& fenceTime), (override));
- MOCK_METHOD(void, setHwcValidateTiming,
- (DisplayId displayId, TimePoint validateStartTime, TimePoint validateEndTime),
- (override));
- MOCK_METHOD(void, setHwcPresentTiming,
- (DisplayId displayId, TimePoint presentStartTime, TimePoint presentEndTime),
- (override));
- MOCK_METHOD(void, setSkippedValidate, (DisplayId displayId, bool skipped), (override));
- MOCK_METHOD(void, setRequiresRenderEngine, (DisplayId displayId, bool requiresRenderEngine),
- (override));
- MOCK_METHOD(void, setExpectedPresentTime, (TimePoint expectedPresentTime), (override));
- MOCK_METHOD(void, setSfPresentTiming, (TimePoint presentFenceTime, TimePoint presentEndTime),
- (override));
- MOCK_METHOD(void, setHwcPresentDelayedTime,
- (DisplayId displayId, TimePoint earliestFrameStartTime));
- MOCK_METHOD(void, setFrameDelay, (Duration frameDelayDuration), (override));
- MOCK_METHOD(void, setCommitStart, (TimePoint commitStartTime), (override));
- MOCK_METHOD(void, setCompositeEnd, (TimePoint compositeEndTime), (override));
- MOCK_METHOD(void, setDisplays, (std::vector<DisplayId> & displayIds), (override));
- MOCK_METHOD(void, setTotalFrameTargetWorkDuration, (Duration targetDuration), (override));
-};
-
-} // namespace mock
-} // namespace Hwc2
-} // namespace android
diff --git a/services/surfaceflinger/CompositionEngine/tests/OutputLayerTest.cpp b/services/surfaceflinger/CompositionEngine/tests/OutputLayerTest.cpp
index b21533a..dbffe80 100644
--- a/services/surfaceflinger/CompositionEngine/tests/OutputLayerTest.cpp
+++ b/services/surfaceflinger/CompositionEngine/tests/OutputLayerTest.cpp
@@ -14,6 +14,7 @@
* limitations under the License.
*/
+#include <com_android_graphics_libgui_flags.h>
#include <compositionengine/impl/HwcBufferCache.h>
#include <compositionengine/impl/OutputLayer.h>
#include <compositionengine/impl/OutputLayerCompositionState.h>
@@ -23,14 +24,14 @@
#include <compositionengine/mock/Output.h>
#include <gtest/gtest.h>
#include <log/log.h>
-
#include <renderengine/impl/ExternalTexture.h>
#include <renderengine/mock/RenderEngine.h>
+#include <ui/FloatRect.h>
#include <ui/PixelFormat.h>
-#include "MockHWC2.h"
-#include "MockHWComposer.h"
+
#include "RegionMatcher.h"
-#include "ui/FloatRect.h"
+#include "mock/DisplayHardware/MockHWC2.h"
+#include "mock/DisplayHardware/MockHWComposer.h"
#include <aidl/android/hardware/graphics/composer3/Composition.h>
@@ -1332,6 +1333,71 @@
/*zIsOverridden*/ false, /*isPeekingThrough*/ false);
}
+TEST_F(OutputLayerWriteStateToHWCTest, setsPictureProfileWhenCommitted) {
+ if (!com_android_graphics_libgui_flags_apply_picture_profiles()) {
+ GTEST_SKIP() << "Feature flag disabled, skipping";
+ }
+ mLayerFEState.compositionType = Composition::DEVICE;
+ mLayerFEState.pictureProfileHandle = PictureProfileHandle(1);
+
+ expectGeometryCommonCalls();
+ expectPerFrameCommonCalls();
+ expectSetHdrMetadataAndBufferCalls();
+ expectSetCompositionTypeCall(Composition::DEVICE);
+
+ EXPECT_CALL(*mHwcLayer, setPictureProfileHandle(PictureProfileHandle(1)));
+
+ mOutputLayer.commitPictureProfileToCompositionState();
+ mOutputLayer.writeStateToHWC(/*includeGeometry*/ true, /*skipLayer*/ false, 0,
+ /*zIsOverridden*/ false, /*isPeekingThrough*/ false);
+}
+
+TEST_F(OutputLayerWriteStateToHWCTest, doesNotSetPictureProfileWhenNotCommitted) {
+ if (!com_android_graphics_libgui_flags_apply_picture_profiles()) {
+ GTEST_SKIP() << "Feature flag disabled, skipping";
+ }
+ mLayerFEState.compositionType = Composition::DEVICE;
+ mLayerFEState.pictureProfileHandle = PictureProfileHandle(1);
+
+ expectGeometryCommonCalls();
+ expectPerFrameCommonCalls();
+ expectSetHdrMetadataAndBufferCalls();
+ expectSetCompositionTypeCall(Composition::DEVICE);
+
+ EXPECT_CALL(*mHwcLayer, setPictureProfileHandle(_)).Times(0);
+
+ mOutputLayer.writeStateToHWC(/*includeGeometry*/ true, /*skipLayer*/ false, 0,
+ /*zIsOverridden*/ false, /*isPeekingThrough*/ false);
+}
+
+TEST_F(OutputLayerWriteStateToHWCTest, doesNotSetPictureProfileWhenNotCommittedLater) {
+ if (!com_android_graphics_libgui_flags_apply_picture_profiles()) {
+ GTEST_SKIP() << "Feature flag disabled, skipping";
+ }
+ mLayerFEState.compositionType = Composition::DEVICE;
+ mLayerFEState.pictureProfileHandle = PictureProfileHandle(1);
+
+ expectGeometryCommonCalls();
+ expectPerFrameCommonCalls();
+ expectSetHdrMetadataAndBufferCalls();
+ expectSetCompositionTypeCall(Composition::DEVICE);
+
+ EXPECT_CALL(*mHwcLayer, setPictureProfileHandle(PictureProfileHandle(1)));
+
+ mOutputLayer.commitPictureProfileToCompositionState();
+ mOutputLayer.writeStateToHWC(/*includeGeometry*/ true, /*skipLayer*/ false, 0,
+ /*zIsOverridden*/ false, /*isPeekingThrough*/ false);
+
+ expectGeometryCommonCalls();
+ expectPerFrameCommonCalls();
+ expectSetHdrMetadataAndBufferCalls(kExpectedHwcSlot, nullptr, kFence);
+
+ EXPECT_CALL(*mHwcLayer, setPictureProfileHandle(PictureProfileHandle(1))).Times(0);
+ // No committing of picture profile before writing the state
+ mOutputLayer.writeStateToHWC(/*includeGeometry*/ true, /*skipLayer*/ false, 0,
+ /*zIsOverridden*/ false, /*isPeekingThrough*/ false);
+}
+
/*
* OutputLayer::uncacheBuffers
*/
diff --git a/services/surfaceflinger/CompositionEngine/tests/OutputTest.cpp b/services/surfaceflinger/CompositionEngine/tests/OutputTest.cpp
index 74ff124..442b603 100644
--- a/services/surfaceflinger/CompositionEngine/tests/OutputTest.cpp
+++ b/services/surfaceflinger/CompositionEngine/tests/OutputTest.cpp
@@ -15,6 +15,7 @@
*/
#include <android-base/stringprintf.h>
+#include <com_android_graphics_libgui_flags.h>
#include <com_android_graphics_surfaceflinger_flags.h>
#include <compositionengine/LayerFECompositionState.h>
#include <compositionengine/impl/Output.h>
@@ -37,11 +38,14 @@
#include <cstdint>
#include <variant>
+#include <com_android_graphics_surfaceflinger_flags.h>
+
#include <common/FlagManager.h>
#include <common/test/FlagUtils.h>
#include "CallOrderStateMachineHelper.h"
-#include "MockHWC2.h"
#include "RegionMatcher.h"
+#include "mock/DisplayHardware/MockHWC2.h"
+#include "mock/DisplayHardware/MockHWComposer.h"
namespace android::compositionengine {
namespace {
@@ -142,6 +146,24 @@
public:
using impl::Output::injectOutputLayerForTest;
virtual void injectOutputLayerForTest(std::unique_ptr<compositionengine::OutputLayer>) = 0;
+
+ virtual ftl::Optional<DisplayId> getDisplayId() const override { return mId; }
+
+ virtual bool hasPictureProcessing() const override { return mHasPictureProcessing; }
+ virtual int32_t getMaxLayerPictureProfiles() const override {
+ return mMaxLayerPictureProfiles;
+ }
+
+ void setDisplayIdForTest(DisplayId value) { mId = value; }
+
+ void setHasPictureProcessingForTest(bool value) { mHasPictureProcessing = value; }
+
+ void setMaxLayerPictureProfilesForTest(int32_t value) { mMaxLayerPictureProfiles = value; }
+
+ private:
+ ftl::Optional<DisplayId> mId;
+ bool mHasPictureProcessing;
+ int32_t mMaxLayerPictureProfiles;
};
static std::shared_ptr<Output> createOutput(
@@ -157,6 +179,7 @@
mOutput->editState().displaySpace.setBounds(
ui::Size(kDefaultDisplaySize.getWidth(), kDefaultDisplaySize.getHeight()));
EXPECT_CALL(mCompositionEngine, getRenderEngine()).WillRepeatedly(ReturnRef(mRenderEngine));
+ EXPECT_CALL(mCompositionEngine, getHwComposer()).WillRepeatedly(ReturnRef(mHwComposer));
}
void injectOutputLayer(InjectedLayer& layer) {
@@ -169,6 +192,7 @@
static const Rect kDefaultDisplaySize;
+ StrictMock<::android::mock::HWComposer> mHwComposer;
StrictMock<mock::CompositionEngine> mCompositionEngine;
StrictMock<renderengine::mock::RenderEngine> mRenderEngine;
mock::DisplayColorProfile* mDisplayColorProfile = new StrictMock<mock::DisplayColorProfile>();
@@ -5043,6 +5067,133 @@
mOutput->writeCompositionState(args);
}
+TEST_F(OutputUpdateAndWriteCompositionStateTest, assignsDisplayProfileBasedOnLayerPriority) {
+ if (!com_android_graphics_libgui_flags_apply_picture_profiles()) {
+ GTEST_SKIP() << "Feature flag disabled, skipping";
+ }
+
+ mOutput->setDisplayIdForTest(PhysicalDisplayId::fromPort(1));
+ // Has only one display-global picture processing pipeline
+ mOutput->setHasPictureProcessingForTest(true);
+ mOutput->setMaxLayerPictureProfilesForTest(0);
+
+ InjectedLayer layer1;
+ injectOutputLayer(layer1);
+ PictureProfileHandle profileForLayer1(1);
+ EXPECT_CALL(*layer1.outputLayer, getPictureProfilePriority()).WillRepeatedly(Return(3));
+ EXPECT_CALL(*layer1.outputLayer, getPictureProfileHandle())
+ .WillRepeatedly(ReturnRef(profileForLayer1));
+
+ InjectedLayer layer2;
+ injectOutputLayer(layer2);
+ PictureProfileHandle profileForLayer2(2);
+ EXPECT_CALL(*layer2.outputLayer, getPictureProfilePriority()).WillRepeatedly(Return(1));
+ EXPECT_CALL(*layer2.outputLayer, getPictureProfileHandle())
+ .WillRepeatedly(ReturnRef(profileForLayer2));
+
+ InjectedLayer layer3;
+ injectOutputLayer(layer3);
+ PictureProfileHandle profileForLayer3(3);
+ EXPECT_CALL(*layer3.outputLayer, getPictureProfilePriority()).WillRepeatedly(Return(2));
+ EXPECT_CALL(*layer3.outputLayer, getPictureProfileHandle())
+ .WillRepeatedly(ReturnRef(profileForLayer3));
+
+ // Because StrictMock
+ EXPECT_CALL(*layer1.outputLayer, requiresClientComposition()).WillRepeatedly(Return(false));
+ EXPECT_CALL(*layer1.outputLayer, updateCompositionState(_, _, _, _));
+ EXPECT_CALL(*layer1.outputLayer, writeStateToHWC(_, _, _, _, _));
+ EXPECT_CALL(*layer2.outputLayer, requiresClientComposition()).WillRepeatedly(Return(false));
+ EXPECT_CALL(*layer2.outputLayer, updateCompositionState(_, _, _, _));
+ EXPECT_CALL(*layer2.outputLayer, writeStateToHWC(_, _, _, _, _));
+ EXPECT_CALL(*layer3.outputLayer, requiresClientComposition()).WillRepeatedly(Return(false));
+ EXPECT_CALL(*layer3.outputLayer, updateCompositionState(_, _, _, _));
+ EXPECT_CALL(*layer3.outputLayer, writeStateToHWC(_, _, _, _, _));
+
+ // No layer picture profiles should be committed
+ EXPECT_CALL(*layer1.outputLayer, commitPictureProfileToCompositionState).Times(0);
+ EXPECT_CALL(*layer2.outputLayer, commitPictureProfileToCompositionState).Times(0);
+ EXPECT_CALL(*layer3.outputLayer, commitPictureProfileToCompositionState).Times(0);
+
+ // Sets display picture profile to the highest priority layer's profile
+ EXPECT_CALL(mHwComposer, setDisplayPictureProfileHandle(_, Eq(profileForLayer2)));
+
+ // Marks only the highest priority layer as committed
+ EXPECT_CALL(*layer1.layerFE, onPictureProfileCommitted).Times(0);
+ EXPECT_CALL(*layer2.layerFE, onPictureProfileCommitted);
+ EXPECT_CALL(*layer3.layerFE, onPictureProfileCommitted).Times(0);
+
+ mOutput->editState().isEnabled = true;
+ CompositionRefreshArgs args;
+ args.updatingGeometryThisFrame = false;
+ args.devOptForceClientComposition = false;
+ mOutput->updateCompositionState(args);
+ mOutput->planComposition();
+ mOutput->writeCompositionState(args);
+}
+
+TEST_F(OutputUpdateAndWriteCompositionStateTest, assignsLayerProfileBasedOnLayerPriority) {
+ if (!com_android_graphics_libgui_flags_apply_picture_profiles()) {
+ GTEST_SKIP() << "Feature flag disabled, skipping";
+ }
+ mOutput->setDisplayIdForTest(PhysicalDisplayId::fromPort(1));
+ // Has 2 layer-specific picture processing pipelines
+ mOutput->setHasPictureProcessingForTest(true);
+ mOutput->setMaxLayerPictureProfilesForTest(2);
+
+ InjectedLayer layer1;
+ injectOutputLayer(layer1);
+ PictureProfileHandle profileForLayer1(1);
+ EXPECT_CALL(*layer1.outputLayer, getPictureProfilePriority()).WillRepeatedly(Return(3));
+ EXPECT_CALL(*layer1.outputLayer, getPictureProfileHandle())
+ .WillRepeatedly(ReturnRef(profileForLayer1));
+
+ InjectedLayer layer2;
+ injectOutputLayer(layer2);
+ PictureProfileHandle profileForLayer2(2);
+ EXPECT_CALL(*layer2.outputLayer, getPictureProfilePriority()).WillRepeatedly(Return(1));
+ EXPECT_CALL(*layer2.outputLayer, getPictureProfileHandle())
+ .WillRepeatedly(ReturnRef(profileForLayer2));
+
+ InjectedLayer layer3;
+ injectOutputLayer(layer3);
+ PictureProfileHandle profileForLayer3(3);
+ EXPECT_CALL(*layer3.outputLayer, getPictureProfilePriority()).WillRepeatedly(Return(2));
+ EXPECT_CALL(*layer3.outputLayer, getPictureProfileHandle())
+ .WillRepeatedly(ReturnRef(profileForLayer3));
+
+ // Because StrictMock
+ EXPECT_CALL(*layer1.outputLayer, requiresClientComposition()).WillRepeatedly(Return(false));
+ EXPECT_CALL(*layer1.outputLayer, updateCompositionState(_, _, _, _));
+ EXPECT_CALL(*layer1.outputLayer, writeStateToHWC(_, _, _, _, _));
+ EXPECT_CALL(*layer2.outputLayer, requiresClientComposition()).WillRepeatedly(Return(false));
+ EXPECT_CALL(*layer2.outputLayer, updateCompositionState(_, _, _, _));
+ EXPECT_CALL(*layer2.outputLayer, writeStateToHWC(_, _, _, _, _));
+ EXPECT_CALL(*layer3.outputLayer, requiresClientComposition()).WillRepeatedly(Return(false));
+ EXPECT_CALL(*layer3.outputLayer, updateCompositionState(_, _, _, _));
+ EXPECT_CALL(*layer3.outputLayer, writeStateToHWC(_, _, _, _, _));
+
+ // The two highest priority layers should have their picture profiles committed
+ EXPECT_CALL(*layer1.outputLayer, commitPictureProfileToCompositionState).Times(0);
+ EXPECT_CALL(*layer2.outputLayer, commitPictureProfileToCompositionState);
+ EXPECT_CALL(*layer3.outputLayer, commitPictureProfileToCompositionState);
+
+ // Marks only the highest priority layers as committed
+ EXPECT_CALL(*layer1.layerFE, onPictureProfileCommitted).Times(0);
+ EXPECT_CALL(*layer2.layerFE, onPictureProfileCommitted);
+ EXPECT_CALL(*layer3.layerFE, onPictureProfileCommitted);
+
+ // No display picture profile is sent
+ EXPECT_CALL(mHwComposer, setDisplayPictureProfileHandle).Times(0);
+
+ mOutput->editState().isEnabled = true;
+ CompositionRefreshArgs args;
+ args.updatingGeometryThisFrame = false;
+ args.devOptForceClientComposition = false;
+ mOutput->updateCompositionState(args);
+ mOutput->planComposition();
+ mOutput->writeCompositionState(args);
+}
+
TEST_F(GenerateClientCompositionRequestsTest, handlesLandscapeModeSplitScreenRequests) {
// In split-screen landscape mode, the screen is rotated 90 degrees, with
// one layer on the left covering the left side of the output, and one layer
diff --git a/services/surfaceflinger/Display/DisplayModeController.cpp b/services/surfaceflinger/Display/DisplayModeController.cpp
index f8b6c6e..a086aee 100644
--- a/services/surfaceflinger/Display/DisplayModeController.cpp
+++ b/services/surfaceflinger/Display/DisplayModeController.cpp
@@ -28,6 +28,7 @@
#include <ftl/concat.h>
#include <ftl/expected.h>
#include <log/log.h>
+#include <utils/Errors.h>
namespace android::display {
@@ -177,12 +178,13 @@
}
}
-bool DisplayModeController::initiateModeChange(PhysicalDisplayId displayId,
- DisplayModeRequest&& desiredMode,
- const hal::VsyncPeriodChangeConstraints& constraints,
- hal::VsyncPeriodChangeTimeline& outTimeline) {
+auto DisplayModeController::initiateModeChange(
+ PhysicalDisplayId displayId, DisplayModeRequest&& desiredMode,
+ const hal::VsyncPeriodChangeConstraints& constraints,
+ hal::VsyncPeriodChangeTimeline& outTimeline) -> ModeChangeResult {
std::lock_guard lock(mDisplayLock);
- const auto& displayPtr = FTL_EXPECT(mDisplays.get(displayId).ok_or(false)).get();
+ const auto& displayPtr =
+ FTL_EXPECT(mDisplays.get(displayId).ok_or(ModeChangeResult::Aborted)).get();
// TODO: b/255635711 - Flow the DisplayModeRequest through the desired/pending/active states.
// For now, `desiredMode` and `desiredModeOpt` are one and the same, but the latter is not
@@ -201,13 +203,17 @@
const auto& mode = *displayPtr->pendingModeOpt->mode.modePtr;
- if (mComposerPtr->setActiveModeWithConstraints(displayId, mode.getHwcId(), constraints,
- &outTimeline) != OK) {
- return false;
+ const auto error = mComposerPtr->setActiveModeWithConstraints(displayId, mode.getHwcId(),
+ constraints, &outTimeline);
+ switch (error) {
+ case FAILED_TRANSACTION:
+ return ModeChangeResult::Rejected;
+ case OK:
+ SFTRACE_INT(displayPtr->pendingModeFpsTrace.c_str(), mode.getVsyncRate().getIntValue());
+ return ModeChangeResult::Changed;
+ default:
+ return ModeChangeResult::Aborted;
}
-
- SFTRACE_INT(displayPtr->pendingModeFpsTrace.c_str(), mode.getVsyncRate().getIntValue());
- return true;
}
void DisplayModeController::finalizeModeChange(PhysicalDisplayId displayId, DisplayModeId modeId,
diff --git a/services/surfaceflinger/Display/DisplayModeController.h b/services/surfaceflinger/Display/DisplayModeController.h
index 9ec603d..af3e909 100644
--- a/services/surfaceflinger/Display/DisplayModeController.h
+++ b/services/surfaceflinger/Display/DisplayModeController.h
@@ -70,6 +70,7 @@
RefreshRateSelectorPtr selectorPtrFor(PhysicalDisplayId) const EXCLUDES(mDisplayLock);
enum class DesiredModeAction { None, InitiateDisplayModeSwitch, InitiateRenderRateSwitch };
+ enum class ModeChangeResult { Changed, Rejected, Aborted };
DesiredModeAction setDesiredMode(PhysicalDisplayId, DisplayModeRequest&&)
EXCLUDES(mDisplayLock);
@@ -86,9 +87,9 @@
scheduler::FrameRateMode getActiveMode(PhysicalDisplayId) const EXCLUDES(mDisplayLock);
- bool initiateModeChange(PhysicalDisplayId, DisplayModeRequest&&,
- const hal::VsyncPeriodChangeConstraints&,
- hal::VsyncPeriodChangeTimeline& outTimeline)
+ ModeChangeResult initiateModeChange(PhysicalDisplayId, DisplayModeRequest&&,
+ const hal::VsyncPeriodChangeConstraints&,
+ hal::VsyncPeriodChangeTimeline& outTimeline)
REQUIRES(kMainThreadContext) EXCLUDES(mDisplayLock);
void finalizeModeChange(PhysicalDisplayId, DisplayModeId, Fps vsyncRate, Fps renderFps)
diff --git a/services/surfaceflinger/DisplayDevice.cpp b/services/surfaceflinger/DisplayDevice.cpp
index 402a3d2..c743ea2 100644
--- a/services/surfaceflinger/DisplayDevice.cpp
+++ b/services/surfaceflinger/DisplayDevice.cpp
@@ -201,6 +201,10 @@
return mPowerMode != hal::PowerMode::OFF;
}
+bool DisplayDevice::isRefreshable() const {
+ return mPowerMode == hal::PowerMode::DOZE || mPowerMode == hal::PowerMode::ON;
+}
+
ui::Dataspace DisplayDevice::getCompositionDataSpace() const {
return mCompositionDisplay->getState().dataspace;
}
diff --git a/services/surfaceflinger/DisplayDevice.h b/services/surfaceflinger/DisplayDevice.h
index 3e3f558..af2b48f 100644
--- a/services/surfaceflinger/DisplayDevice.h
+++ b/services/surfaceflinger/DisplayDevice.h
@@ -42,7 +42,6 @@
#include "DisplayHardware/DisplayMode.h"
#include "DisplayHardware/Hal.h"
-#include "DisplayHardware/PowerAdvisor.h"
#include "FrontEnd/DisplayInfo.h"
#include "Scheduler/RefreshRateSelector.h"
#include "ThreadContext.h"
@@ -173,6 +172,7 @@
hardware::graphics::composer::hal::PowerMode getPowerMode() const;
void setPowerMode(hardware::graphics::composer::hal::PowerMode);
bool isPoweredOn() const;
+ bool isRefreshable() const;
void tracePowerMode();
// Enables layer caching on this DisplayDevice
@@ -285,6 +285,8 @@
bool isProtected = false;
// Refer to DisplayDevice::mRequestedRefreshRate, for virtual display only
Fps requestedRefreshRate;
+ int32_t maxLayerPictureProfiles = 0;
+ bool hasPictureProcessing = false;
private:
static std::atomic<int32_t> sNextSequenceId;
diff --git a/services/surfaceflinger/DisplayHardware/AidlComposerHal.cpp b/services/surfaceflinger/DisplayHardware/AidlComposerHal.cpp
index 5814aa4..b83f2ab 100644
--- a/services/surfaceflinger/DisplayHardware/AidlComposerHal.cpp
+++ b/services/surfaceflinger/DisplayHardware/AidlComposerHal.cpp
@@ -44,12 +44,11 @@
using aidl::android::hardware::graphics::composer3::BnComposerCallback;
using aidl::android::hardware::graphics::composer3::Capability;
using aidl::android::hardware::graphics::composer3::ClientTargetPropertyWithBrightness;
+using aidl::android::hardware::graphics::composer3::CommandResultPayload;
using aidl::android::hardware::graphics::composer3::Luts;
using aidl::android::hardware::graphics::composer3::PowerMode;
using aidl::android::hardware::graphics::composer3::VirtualDisplay;
-using aidl::android::hardware::graphics::composer3::CommandResultPayload;
-
using AidlColorMode = aidl::android::hardware::graphics::composer3::ColorMode;
using AidlContentType = aidl::android::hardware::graphics::composer3::ContentType;
using AidlDisplayIdentification =
@@ -1385,7 +1384,7 @@
return V2_4::Error::NONE;
}
-V2_4::Error AidlComposer::setActiveConfigWithConstraints(
+Error AidlComposer::setActiveConfigWithConstraints(
Display display, Config config,
const IComposerClient::VsyncPeriodChangeConstraints& vsyncPeriodChangeConstraints,
VsyncPeriodChangeTimeline* outTimeline) {
@@ -1399,10 +1398,10 @@
&timeline);
if (!status.isOk()) {
ALOGE("setActiveConfigWithConstraints failed %s", status.getDescription().c_str());
- return static_cast<V2_4::Error>(status.getServiceSpecificError());
+ return static_cast<Error>(status.getServiceSpecificError());
}
*outTimeline = translate<VsyncPeriodChangeTimeline>(timeline);
- return V2_4::Error::NONE;
+ return Error::NONE;
}
V2_4::Error AidlComposer::setAutoLowLatencyMode(Display display, bool on) {
@@ -1639,6 +1638,41 @@
return Error::NONE;
}
+Error AidlComposer::getMaxLayerPictureProfiles(Display display, int32_t* outMaxProfiles) {
+ const auto status = mAidlComposerClient->getMaxLayerPictureProfiles(translate<int64_t>(display),
+ outMaxProfiles);
+ if (!status.isOk()) {
+ ALOGE("getMaxLayerPictureProfiles failed %s", status.getDescription().c_str());
+ return static_cast<Error>(status.getServiceSpecificError());
+ }
+ return Error::NONE;
+}
+
+Error AidlComposer::setDisplayPictureProfileId(Display display, PictureProfileId id) {
+ Error error = Error::NONE;
+ mMutex.lock_shared();
+ if (auto writer = getWriter(display)) {
+ writer->get().setDisplayPictureProfileId(translate<int64_t>(display), id);
+ } else {
+ error = Error::BAD_DISPLAY;
+ }
+ mMutex.unlock_shared();
+ return error;
+}
+
+Error AidlComposer::setLayerPictureProfileId(Display display, Layer layer, PictureProfileId id) {
+ Error error = Error::NONE;
+ mMutex.lock_shared();
+ if (auto writer = getWriter(display)) {
+ writer->get().setLayerPictureProfileId(translate<int64_t>(display),
+ translate<int64_t>(layer), id);
+ } else {
+ error = Error::BAD_DISPLAY;
+ }
+ mMutex.unlock_shared();
+ return error;
+}
+
ftl::Optional<std::reference_wrapper<ComposerClientWriter>> AidlComposer::getWriter(Display display)
REQUIRES_SHARED(mMutex) {
return mWriters.get(display);
diff --git a/services/surfaceflinger/DisplayHardware/AidlComposerHal.h b/services/surfaceflinger/DisplayHardware/AidlComposerHal.h
index d724b21..db63d3e 100644
--- a/services/surfaceflinger/DisplayHardware/AidlComposerHal.h
+++ b/services/surfaceflinger/DisplayHardware/AidlComposerHal.h
@@ -206,7 +206,7 @@
V2_4::Error getDisplayConnectionType(Display display,
IComposerClient::DisplayConnectionType* outType) override;
V2_4::Error getDisplayVsyncPeriod(Display display, VsyncPeriodNanos* outVsyncPeriod) override;
- V2_4::Error setActiveConfigWithConstraints(
+ Error setActiveConfigWithConstraints(
Display display, Config config,
const IComposerClient::VsyncPeriodChangeConstraints& vsyncPeriodChangeConstraints,
VsyncPeriodChangeTimeline* outTimeline) override;
@@ -250,6 +250,9 @@
std::vector<aidl::android::hardware::graphics::composer3::DisplayLuts::LayerLut>*
outLuts) override;
Error setLayerLuts(Display display, Layer layer, Luts& luts) override;
+ Error getMaxLayerPictureProfiles(Display, int32_t* outMaxProfiles) override;
+ Error setDisplayPictureProfileId(Display, PictureProfileId id) override;
+ Error setLayerPictureProfileId(Display, Layer, PictureProfileId id) 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 42ddcd1..ff292fa 100644
--- a/services/surfaceflinger/DisplayHardware/ComposerHal.h
+++ b/services/surfaceflinger/DisplayHardware/ComposerHal.h
@@ -29,11 +29,15 @@
#include <math/mat4.h>
#include <ui/DisplayedFrameStats.h>
#include <ui/GraphicBuffer.h>
+#include <ui/PictureProfileHandle.h>
#include <utils/StrongPointer.h>
+#include "DisplayHardware/Hal.h"
+
#include <aidl/android/hardware/graphics/common/DisplayDecorationSupport.h>
#include <aidl/android/hardware/graphics/common/HdrConversionCapability.h>
#include <aidl/android/hardware/graphics/common/HdrConversionStrategy.h>
+#include <aidl/android/hardware/graphics/common/Transform.h>
#include <aidl/android/hardware/graphics/composer3/Capability.h>
#include <aidl/android/hardware/graphics/composer3/ClientTargetPropertyWithBrightness.h>
#include <aidl/android/hardware/graphics/composer3/Color.h>
@@ -44,7 +48,6 @@
#include <aidl/android/hardware/graphics/composer3/IComposerCallback.h>
#include <aidl/android/hardware/graphics/composer3/OverlayProperties.h>
-#include <aidl/android/hardware/graphics/common/Transform.h>
#include <optional>
// TODO(b/129481165): remove the #pragma below and fix conversion issues
@@ -72,9 +75,9 @@
using types::V1_2::Dataspace;
using types::V1_2::PixelFormat;
+using hardware::graphics::composer::hal::Error;
using V2_1::Config;
using V2_1::Display;
-using V2_1::Error;
using V2_1::Layer;
using V2_4::CommandReaderBase;
using V2_4::CommandWriterBase;
@@ -260,7 +263,7 @@
Display display, IComposerClient::DisplayConnectionType* outType) = 0;
virtual V2_4::Error getDisplayVsyncPeriod(Display display,
VsyncPeriodNanos* outVsyncPeriod) = 0;
- virtual V2_4::Error setActiveConfigWithConstraints(
+ virtual Error setActiveConfigWithConstraints(
Display display, Config config,
const IComposerClient::VsyncPeriodChangeConstraints& vsyncPeriodChangeConstraints,
VsyncPeriodChangeTimeline* outTimeline) = 0;
@@ -307,6 +310,9 @@
virtual Error getRequestedLuts(Display display, std::vector<Layer>* outLayers,
std::vector<V3_0::DisplayLuts::LayerLut>* outLuts) = 0;
virtual Error setLayerLuts(Display display, Layer layer, V3_0::Luts& luts) = 0;
+ virtual Error getMaxLayerPictureProfiles(Display display, int32_t* outMaxProfiles) = 0;
+ virtual Error setDisplayPictureProfileId(Display display, PictureProfileId id) = 0;
+ virtual Error setLayerPictureProfileId(Display display, Layer layer, PictureProfileId id) = 0;
};
} // namespace Hwc2
diff --git a/services/surfaceflinger/DisplayHardware/HWC2.cpp b/services/surfaceflinger/DisplayHardware/HWC2.cpp
index 5355a12..081f4aa 100644
--- a/services/surfaceflinger/DisplayHardware/HWC2.cpp
+++ b/services/surfaceflinger/DisplayHardware/HWC2.cpp
@@ -31,6 +31,9 @@
#include <ui/Fence.h>
#include <ui/FloatRect.h>
#include <ui/GraphicBuffer.h>
+#include <ui/PictureProfileHandle.h>
+
+#include "DisplayHardware/Hal.h"
#include <algorithm>
#include <cinttypes>
@@ -53,6 +56,7 @@
using android::GraphicBuffer;
using android::HdrCapabilities;
using android::HdrMetadata;
+using android::PictureProfileHandle;
using android::Rect;
using android::Region;
using android::sp;
@@ -655,6 +659,16 @@
return static_cast<Error>(error);
}
+Error Display::getMaxLayerPictureProfiles(int32_t* outMaxProfiles) {
+ const auto error = mComposer.getMaxLayerPictureProfiles(mId, outMaxProfiles);
+ return static_cast<Error>(error);
+}
+
+Error Display::setPictureProfileHandle(const PictureProfileHandle& handle) {
+ const auto error = mComposer.setDisplayPictureProfileId(mId, handle.getId());
+ return static_cast<Error>(error);
+}
+
// For use by Device
void Display::setConnected(bool connected) {
@@ -1086,6 +1100,15 @@
return static_cast<Error>(intError);
}
+Error Layer::setPictureProfileHandle(const PictureProfileHandle& handle) {
+ if (CC_UNLIKELY(!mDisplay)) {
+ return Error::BAD_DISPLAY;
+ }
+ const auto intError =
+ mComposer.setLayerPictureProfileId(mDisplay->getId(), mId, handle.getId());
+ return static_cast<Error>(intError);
+}
+
} // namespace impl
} // namespace HWC2
} // namespace android
diff --git a/services/surfaceflinger/DisplayHardware/HWC2.h b/services/surfaceflinger/DisplayHardware/HWC2.h
index 799fd02..6740d8a 100644
--- a/services/surfaceflinger/DisplayHardware/HWC2.h
+++ b/services/surfaceflinger/DisplayHardware/HWC2.h
@@ -24,6 +24,7 @@
#include <gui/HdrMetadata.h>
#include <math/mat4.h>
#include <ui/HdrCapabilities.h>
+#include <ui/PictureProfileHandle.h>
#include <ui/Region.h>
#include <ui/StaticDisplayInfo.h>
#include <utils/Log.h>
@@ -199,6 +200,9 @@
[[nodiscard]] virtual hal::Error setIdleTimerEnabled(std::chrono::milliseconds timeout) = 0;
[[nodiscard]] virtual hal::Error getPhysicalDisplayOrientation(
Hwc2::AidlTransform* outTransform) const = 0;
+ [[nodiscard]] virtual hal::Error getMaxLayerPictureProfiles(int32_t* maxProfiles) = 0;
+ [[nodiscard]] virtual hal::Error setPictureProfileHandle(
+ const PictureProfileHandle& handle) = 0;
};
namespace impl {
@@ -282,6 +286,8 @@
std::optional<aidl::android::hardware::graphics::common::DisplayDecorationSupport>*
support) override;
hal::Error setIdleTimerEnabled(std::chrono::milliseconds timeout) override;
+ hal::Error getMaxLayerPictureProfiles(int32_t* maxProfiles) override;
+ hal::Error setPictureProfileHandle(const android::PictureProfileHandle& handle) override;
// Other Display methods
hal::HWDisplayId getId() const override { return mId; }
@@ -377,6 +383,8 @@
[[nodiscard]] virtual hal::Error setBlockingRegion(const android::Region& region) = 0;
[[nodiscard]] virtual hal::Error setLuts(
aidl::android::hardware::graphics::composer3::Luts& luts) = 0;
+ [[nodiscard]] virtual hal::Error setPictureProfileHandle(
+ const PictureProfileHandle& handle) = 0;
};
namespace impl {
@@ -428,6 +436,7 @@
hal::Error setBrightness(float brightness) override;
hal::Error setBlockingRegion(const android::Region& region) override;
hal::Error setLuts(aidl::android::hardware::graphics::composer3::Luts&) override;
+ hal::Error setPictureProfileHandle(const PictureProfileHandle& handle) override;
private:
// These are references to data owned by HWComposer, which will outlive
@@ -449,6 +458,7 @@
android::HdrMetadata mHdrMetadata;
android::mat4 mColorMatrix;
uint32_t mBufferSlot;
+ android::PictureProfileHandle profile;
};
} // namespace impl
diff --git a/services/surfaceflinger/DisplayHardware/HWComposer.cpp b/services/surfaceflinger/DisplayHardware/HWComposer.cpp
index 7d77634..9943856 100644
--- a/services/surfaceflinger/DisplayHardware/HWComposer.cpp
+++ b/services/surfaceflinger/DisplayHardware/HWComposer.cpp
@@ -27,6 +27,7 @@
#include "HWComposer.h"
+#include <aidl/android/hardware/graphics/composer3/IComposerClient.h>
#include <android-base/properties.h>
#include <common/trace.h>
#include <compositionengine/Output.h>
@@ -733,7 +734,11 @@
auto error = mDisplayData[displayId].hwcDisplay->setActiveConfigWithConstraints(hwcModeId,
constraints,
outTimeline);
- RETURN_IF_HWC_ERROR(error, displayId, UNKNOWN_ERROR);
+ if (error == hal::Error::CONFIG_FAILED) {
+ RETURN_IF_HWC_ERROR_FOR("setActiveConfigWithConstraints", error, displayId,
+ FAILED_TRANSACTION);
+ }
+ RETURN_IF_HWC_ERROR_FOR("setActiveConfigWithConstraints", error, displayId, UNKNOWN_ERROR);
return NO_ERROR;
}
@@ -1022,6 +1027,24 @@
return NO_ERROR;
}
+int32_t HWComposer::getMaxLayerPictureProfiles(PhysicalDisplayId displayId) {
+ int32_t maxProfiles = 0;
+ RETURN_IF_INVALID_DISPLAY(displayId, 0);
+ const auto error = mDisplayData[displayId].hwcDisplay->getMaxLayerPictureProfiles(&maxProfiles);
+ RETURN_IF_HWC_ERROR(error, displayId, 0);
+ return maxProfiles;
+}
+
+status_t HWComposer::setDisplayPictureProfileHandle(PhysicalDisplayId displayId,
+ const PictureProfileHandle& handle) {
+ RETURN_IF_INVALID_DISPLAY(displayId, BAD_INDEX);
+ const auto error = mDisplayData[displayId].hwcDisplay->setPictureProfileHandle(handle);
+ if (error != hal::Error::UNSUPPORTED) {
+ RETURN_IF_HWC_ERROR(error, displayId, INVALID_OPERATION);
+ }
+ return NO_ERROR;
+}
+
const std::unordered_map<std::string, bool>& HWComposer::getSupportedLayerGenericMetadata() const {
return mSupportedLayerGenericMetadata;
}
diff --git a/services/surfaceflinger/DisplayHardware/HWComposer.h b/services/surfaceflinger/DisplayHardware/HWComposer.h
index 7b04d67..e21ce1d 100644
--- a/services/surfaceflinger/DisplayHardware/HWComposer.h
+++ b/services/surfaceflinger/DisplayHardware/HWComposer.h
@@ -29,6 +29,7 @@
#include <ftl/future.h>
#include <ui/DisplayIdentification.h>
#include <ui/FenceTime.h>
+#include <ui/PictureProfileHandle.h>
// TODO(b/129481165): remove the #pragma below and fix conversion issues
#pragma clang diagnostic push
@@ -65,6 +66,7 @@
class TestableSurfaceFlinger;
struct HWComposerTest;
struct CompositionInfo;
+class PictureProfileHandle;
namespace Hwc2 {
class Composer;
@@ -296,7 +298,7 @@
virtual std::optional<PhysicalDisplayId> toPhysicalDisplayId(hal::HWDisplayId) const = 0;
virtual std::optional<hal::HWDisplayId> fromPhysicalDisplayId(PhysicalDisplayId) const = 0;
- // Composer 3.0
+ // AIDL Composer
virtual status_t setBootDisplayMode(PhysicalDisplayId, hal::HWConfigId) = 0;
virtual status_t clearBootDisplayMode(PhysicalDisplayId) = 0;
virtual std::optional<hal::HWConfigId> getPreferredBootDisplayMode(PhysicalDisplayId) = 0;
@@ -315,8 +317,10 @@
virtual status_t setRefreshRateChangedCallbackDebugEnabled(PhysicalDisplayId, bool enabled) = 0;
virtual status_t notifyExpectedPresent(PhysicalDisplayId, TimePoint expectedPresentTime,
Fps frameInterval) = 0;
- // mapper
virtual HWC2::Display::LutFileDescriptorMapper& getLutFileDescriptorMapper() = 0;
+ virtual int32_t getMaxLayerPictureProfiles(PhysicalDisplayId) = 0;
+ virtual status_t setDisplayPictureProfileHandle(PhysicalDisplayId,
+ const PictureProfileHandle& handle) = 0;
};
static inline bool operator==(const android::HWComposer::DeviceRequestedChanges& lhs,
@@ -480,9 +484,10 @@
status_t setRefreshRateChangedCallbackDebugEnabled(PhysicalDisplayId, bool enabled) override;
status_t notifyExpectedPresent(PhysicalDisplayId, TimePoint expectedPresentTime,
Fps frameInterval) override;
-
- // get a mapper
HWC2::Display::LutFileDescriptorMapper& getLutFileDescriptorMapper() override;
+ int32_t getMaxLayerPictureProfiles(PhysicalDisplayId) override;
+ status_t setDisplayPictureProfileHandle(PhysicalDisplayId,
+ const android::PictureProfileHandle& profile) override;
// for debugging ----------------------------------------------------------
void dump(std::string& out) const override;
diff --git a/services/surfaceflinger/DisplayHardware/Hal.h b/services/surfaceflinger/DisplayHardware/Hal.h
index e3d9622..568d758 100644
--- a/services/surfaceflinger/DisplayHardware/Hal.h
+++ b/services/surfaceflinger/DisplayHardware/Hal.h
@@ -17,16 +17,21 @@
#pragma once
#include <android/hardware/graphics/common/1.1/types.h>
+#include <android/hardware/graphics/composer/2.1/types.h>
#include <android/hardware/graphics/composer/2.4/IComposer.h>
#include <android/hardware/graphics/composer/2.4/IComposerClient.h>
+#include <android/hardware/graphics/composer/2.4/types.h>
#include <aidl/android/hardware/graphics/common/DisplayHotplugEvent.h>
#include <aidl/android/hardware/graphics/common/Hdr.h>
#include <aidl/android/hardware/graphics/composer3/Composition.h>
#include <aidl/android/hardware/graphics/composer3/DisplayCapability.h>
#include <aidl/android/hardware/graphics/composer3/DisplayConfiguration.h>
+#include <aidl/android/hardware/graphics/composer3/IComposerClient.h>
#include <aidl/android/hardware/graphics/composer3/VrrConfig.h>
+#include <ftl/enum.h>
+
#define ERROR_HAS_CHANGES 5
namespace android {
@@ -46,7 +51,6 @@
using types::V1_2::Dataspace;
using types::V1_2::PixelFormat;
-using V2_1::Error;
using V2_4::IComposer;
using V2_4::IComposerCallback;
using V2_4::IComposerClient;
@@ -78,6 +82,22 @@
using DisplayConfiguration = V3_0::DisplayConfiguration;
using VrrConfig = V3_0::VrrConfig;
+enum class Error : int32_t {
+ NONE = static_cast<int32_t>(V2_1::Error::NONE),
+ BAD_CONFIG = static_cast<int32_t>(V2_1::Error::BAD_CONFIG),
+ BAD_DISPLAY = static_cast<int32_t>(V2_1::Error::BAD_DISPLAY),
+ BAD_LAYER = static_cast<int32_t>(V2_1::Error::BAD_LAYER),
+ BAD_PARAMETER = static_cast<int32_t>(V2_1::Error::BAD_PARAMETER),
+ NO_RESOURCES = static_cast<int32_t>(V2_1::Error::NO_RESOURCES),
+ NOT_VALIDATED = static_cast<int32_t>(V2_1::Error::NOT_VALIDATED),
+ UNSUPPORTED = static_cast<int32_t>(V2_1::Error::UNSUPPORTED),
+ SEAMLESS_NOT_ALLOWED = static_cast<int32_t>(V2_4::Error::SEAMLESS_NOT_ALLOWED),
+ SEAMLESS_NOT_POSSIBLE = static_cast<int32_t>(V2_4::Error::SEAMLESS_NOT_POSSIBLE),
+ CONFIG_FAILED = V3_0::IComposerClient::EX_CONFIG_FAILED,
+ PICTURE_PROFILE_MAX_EXCEEDED = V3_0::IComposerClient::EX_PICTURE_PROFILE_MAX_EXCEEDED,
+ ftl_last = PICTURE_PROFILE_MAX_EXCEEDED
+};
+
} // namespace hardware::graphics::composer::hal
inline bool hasChangesError(hardware::graphics::composer::hal::Error error) {
@@ -210,7 +230,11 @@
}
inline std::string to_string(hardware::graphics::composer::hal::Error error) {
- return to_string(static_cast<hardware::graphics::composer::hal::V2_4::Error>(error));
+ // 5 is reserved for historical reason, during validation 5 means has changes.
+ if (hasChangesError(error)) {
+ return "HAS_CHANGES";
+ }
+ return ftl::enum_string(error);
}
// For utils::Dumper ADL.
diff --git a/services/surfaceflinger/DisplayHardware/HidlComposerHal.cpp b/services/surfaceflinger/DisplayHardware/HidlComposerHal.cpp
index 6a7a09b..5703a2d 100644
--- a/services/surfaceflinger/DisplayHardware/HidlComposerHal.cpp
+++ b/services/surfaceflinger/DisplayHardware/HidlComposerHal.cpp
@@ -27,6 +27,7 @@
#include <SurfaceFlingerProperties.h>
#include <aidl/android/hardware/graphics/common/DisplayHotplugEvent.h>
#include <android/binder_manager.h>
+#include <android/hardware/graphics/composer/2.1/types.h>
#include <common/trace.h>
#include <composer-command-buffer/2.2/ComposerCommandBuffer.h>
#include <hidl/HidlTransportSupport.h>
@@ -173,7 +174,7 @@
};
// assume NO_RESOURCES when Status::isOk returns false
-constexpr Error kDefaultError = Error::NO_RESOURCES;
+constexpr V2_1::Error kDefaultError = V2_1::Error::NO_RESOURCES;
constexpr V2_4::Error kDefaultError_2_4 = static_cast<V2_4::Error>(kDefaultError);
template <typename T, typename U>
@@ -181,7 +182,7 @@
return (ret.isOk()) ? static_cast<T>(ret) : static_cast<T>(default_val);
}
-Error unwrapRet(Return<Error>& ret) {
+V2_1::Error unwrapRet(Return<V2_1::Error>& ret) {
return unwrapRet(ret, kDefaultError);
}
@@ -235,7 +236,7 @@
});
} else if (sp<V2_3::IComposer> composer_2_3 = V2_3::IComposer::castFrom(mComposer)) {
composer_2_3->createClient_2_3([&](const auto& tmpError, const auto& tmpClient) {
- if (tmpError == Error::NONE) {
+ if (tmpError == V2_1::Error::NONE) {
mClient = tmpClient;
mClient_2_2 = tmpClient;
mClient_2_3 = tmpClient;
@@ -243,7 +244,7 @@
});
} else {
mComposer->createClient([&](const auto& tmpError, const auto& tmpClient) {
- if (tmpError != Error::NONE) {
+ if (tmpError != V2_1::Error::NONE) {
return;
}
@@ -325,14 +326,14 @@
Error HidlComposer::createVirtualDisplay(uint32_t width, uint32_t height, PixelFormat* format,
Display* outDisplay) {
const uint32_t bufferSlotCount = 1;
- Error error = kDefaultError;
+ Error error = static_cast<Error>(kDefaultError);
if (mClient_2_2) {
mClient_2_2->createVirtualDisplay_2_2(width, height,
static_cast<types::V1_1::PixelFormat>(*format),
bufferSlotCount,
[&](const auto& tmpError, const auto& tmpDisplay,
const auto& tmpFormat) {
- error = tmpError;
+ error = static_cast<Error>(tmpError);
if (error != Error::NONE) {
return;
}
@@ -346,7 +347,7 @@
bufferSlotCount,
[&](const auto& tmpError, const auto& tmpDisplay,
const auto& tmpFormat) {
- error = tmpError;
+ error = static_cast<Error>(tmpError);
if (error != Error::NONE) {
return;
}
@@ -361,7 +362,7 @@
Error HidlComposer::destroyVirtualDisplay(Display display) {
auto ret = mClient->destroyVirtualDisplay(display);
- return unwrapRet(ret);
+ return static_cast<Error>(unwrapRet(ret));
}
Error HidlComposer::acceptDisplayChanges(Display display) {
@@ -371,10 +372,10 @@
}
Error HidlComposer::createLayer(Display display, Layer* outLayer) {
- Error error = kDefaultError;
+ Error error = static_cast<Error>(kDefaultError);
mClient->createLayer(display, kMaxLayerBufferCount,
[&](const auto& tmpError, const auto& tmpLayer) {
- error = tmpError;
+ error = static_cast<Error>(tmpError);
if (error != Error::NONE) {
return;
}
@@ -387,13 +388,13 @@
Error HidlComposer::destroyLayer(Display display, Layer layer) {
auto ret = mClient->destroyLayer(display, layer);
- return unwrapRet(ret);
+ return static_cast<Error>(unwrapRet(ret));
}
Error HidlComposer::getActiveConfig(Display display, Config* outConfig) {
- Error error = kDefaultError;
+ Error error = static_cast<Error>(kDefaultError);
mClient->getActiveConfig(display, [&](const auto& tmpError, const auto& tmpConfig) {
- error = tmpError;
+ error = static_cast<Error>(tmpError);
if (error != Error::NONE) {
return;
}
@@ -412,11 +413,11 @@
}
Error HidlComposer::getColorModes(Display display, std::vector<ColorMode>* outModes) {
- Error error = kDefaultError;
+ Error error = static_cast<Error>(kDefaultError);
if (mClient_2_3) {
mClient_2_3->getColorModes_2_3(display, [&](const auto& tmpError, const auto& tmpModes) {
- error = tmpError;
+ error = static_cast<Error>(tmpError);
if (error != Error::NONE) {
return;
}
@@ -425,7 +426,7 @@
});
} else if (mClient_2_2) {
mClient_2_2->getColorModes_2_2(display, [&](const auto& tmpError, const auto& tmpModes) {
- error = tmpError;
+ error = static_cast<Error>(tmpError);
if (error != Error::NONE) {
return;
}
@@ -436,7 +437,7 @@
});
} else {
mClient->getColorModes(display, [&](const auto& tmpError, const auto& tmpModes) {
- error = tmpError;
+ error = static_cast<Error>(tmpError);
if (error != Error::NONE) {
return;
}
@@ -451,7 +452,7 @@
Error HidlComposer::getDisplayAttribute(Display display, Config config,
IComposerClient::Attribute attribute, int32_t* outValue) {
- Error error = kDefaultError;
+ Error error = static_cast<Error>(kDefaultError);
if (mClient_2_4) {
mClient_2_4->getDisplayAttribute_2_4(display, config, attribute,
[&](const auto& tmpError, const auto& tmpValue) {
@@ -466,7 +467,7 @@
mClient->getDisplayAttribute(display, config,
static_cast<V2_1::IComposerClient::Attribute>(attribute),
[&](const auto& tmpError, const auto& tmpValue) {
- error = tmpError;
+ error = static_cast<Error>(tmpError);
if (error != Error::NONE) {
return;
}
@@ -479,9 +480,9 @@
}
Error HidlComposer::getDisplayConfigs(Display display, std::vector<Config>* outConfigs) {
- Error error = kDefaultError;
+ Error error = static_cast<Error>(kDefaultError);
mClient->getDisplayConfigs(display, [&](const auto& tmpError, const auto& tmpConfigs) {
- error = tmpError;
+ error = static_cast<Error>(tmpError);
if (error != Error::NONE) {
return;
}
@@ -499,9 +500,9 @@
}
Error HidlComposer::getDisplayName(Display display, std::string* outName) {
- Error error = kDefaultError;
+ Error error = static_cast<Error>(kDefaultError);
mClient->getDisplayName(display, [&](const auto& tmpError, const auto& tmpName) {
- error = tmpError;
+ error = static_cast<Error>(tmpError);
if (error != Error::NONE) {
return;
}
@@ -520,9 +521,9 @@
}
Error HidlComposer::getDozeSupport(Display display, bool* outSupport) {
- Error error = kDefaultError;
+ Error error = static_cast<Error>(kDefaultError);
mClient->getDozeSupport(display, [&](const auto& tmpError, const auto& tmpSupport) {
- error = tmpError;
+ error = static_cast<Error>(tmpError);
if (error != Error::NONE) {
return;
}
@@ -541,14 +542,14 @@
Error HidlComposer::getHdrCapabilities(Display display, std::vector<Hdr>* outHdrTypes,
float* outMaxLuminance, float* outMaxAverageLuminance,
float* outMinLuminance) {
- Error error = kDefaultError;
+ Error error = static_cast<Error>(kDefaultError);
if (mClient_2_3) {
mClient_2_3->getHdrCapabilities_2_3(display,
[&](const auto& tmpError, const auto& tmpHdrTypes,
const auto& tmpMaxLuminance,
const auto& tmpMaxAverageLuminance,
const auto& tmpMinLuminance) {
- error = tmpError;
+ error = static_cast<Error>(tmpError);
if (error != Error::NONE) {
return;
}
@@ -564,7 +565,7 @@
const auto& tmpMaxLuminance,
const auto& tmpMaxAverageLuminance,
const auto& tmpMinLuminance) {
- error = tmpError;
+ error = static_cast<Error>(tmpError);
if (error != Error::NONE) {
return;
}
@@ -606,7 +607,7 @@
Error HidlComposer::setActiveConfig(Display display, Config config) {
auto ret = mClient->setActiveConfig(display, config);
- return unwrapRet(ret);
+ return static_cast<Error>(unwrapRet(ret));
}
Error HidlComposer::setClientTarget(Display display, uint32_t slot, const sp<GraphicBuffer>& target,
@@ -625,7 +626,7 @@
}
Error HidlComposer::setColorMode(Display display, ColorMode mode, RenderIntent renderIntent) {
- hardware::Return<Error> ret(kDefaultError);
+ hardware::Return<V2_1::Error> ret(kDefaultError);
if (mClient_2_3) {
ret = mClient_2_3->setColorMode_2_3(display, mode, renderIntent);
} else if (mClient_2_2) {
@@ -634,7 +635,7 @@
} else {
ret = mClient->setColorMode(display, static_cast<types::V1_0::ColorMode>(mode));
}
- return unwrapRet(ret);
+ return static_cast<Error>(unwrapRet(ret));
}
Error HidlComposer::setColorTransform(Display display, const float* matrix) {
@@ -654,25 +655,25 @@
}
Error HidlComposer::setPowerMode(Display display, IComposerClient::PowerMode mode) {
- Return<Error> ret(Error::UNSUPPORTED);
+ Return<V2_1::Error> ret(V2_1::Error::UNSUPPORTED);
if (mClient_2_2) {
ret = mClient_2_2->setPowerMode_2_2(display, mode);
} else if (mode != IComposerClient::PowerMode::ON_SUSPEND) {
ret = mClient->setPowerMode(display, static_cast<V2_1::IComposerClient::PowerMode>(mode));
}
- return unwrapRet(ret);
+ return static_cast<Error>(unwrapRet(ret));
}
Error HidlComposer::setVsyncEnabled(Display display, IComposerClient::Vsync enabled) {
auto ret = mClient->setVsyncEnabled(display, enabled);
- return unwrapRet(ret);
+ return static_cast<Error>(unwrapRet(ret));
}
Error HidlComposer::setClientTargetSlotCount(Display display) {
const uint32_t bufferSlotCount = BufferQueue::NUM_BUFFER_SLOTS;
auto ret = mClient->setClientTargetSlotCount(display, bufferSlotCount);
- return unwrapRet(ret);
+ return static_cast<Error>(unwrapRet(ret));
}
Error HidlComposer::validateDisplay(Display display, nsecs_t /*expectedPresentTime*/,
@@ -903,7 +904,7 @@
// set up new input command queue if necessary
if (queueChanged) {
auto ret = mClient->setInputCommandQueue(*mWriter.getMQDescriptor());
- auto error = unwrapRet(ret);
+ auto error = static_cast<Error>(unwrapRet(ret));
if (error != Error::NONE) {
mWriter.reset();
return error;
@@ -915,17 +916,17 @@
return Error::NONE;
}
- Error error = kDefaultError;
+ Error error = static_cast<Error>(kDefaultError);
hardware::Return<void> ret;
auto hidl_callback = [&](const auto& tmpError, const auto& tmpOutChanged,
const auto& tmpOutLength, const auto& tmpOutHandles) {
- error = tmpError;
+ error = static_cast<Error>(tmpError);
// set up new output command queue if necessary
if (error == Error::NONE && tmpOutChanged) {
- error = kDefaultError;
+ error = static_cast<Error>(kDefaultError);
mClient->getOutputCommandQueue([&](const auto& tmpError, const auto& tmpDescriptor) {
- error = tmpError;
+ error = static_cast<Error>(tmpError);
if (error != Error::NONE) {
return;
}
@@ -1000,11 +1001,11 @@
return keys;
}
- Error error = kDefaultError;
+ Error error = static_cast<Error>(kDefaultError);
if (mClient_2_3) {
mClient_2_3->getPerFrameMetadataKeys_2_3(display,
[&](const auto& tmpError, const auto& tmpKeys) {
- error = tmpError;
+ error = static_cast<Error>(tmpError);
if (error != Error::NONE) {
ALOGW("getPerFrameMetadataKeys failed "
"with %d",
@@ -1016,7 +1017,7 @@
} else {
mClient_2_2
->getPerFrameMetadataKeys(display, [&](const auto& tmpError, const auto& tmpKeys) {
- error = tmpError;
+ error = static_cast<Error>(tmpError);
if (error != Error::NONE) {
ALOGW("getPerFrameMetadataKeys failed with %d", tmpError);
return;
@@ -1039,10 +1040,10 @@
return Error::NONE;
}
- Error error = kDefaultError;
+ Error error = static_cast<Error>(kDefaultError);
auto getRenderIntentsLambda = [&](const auto& tmpError, const auto& tmpKeys) {
- error = tmpError;
+ error = static_cast<Error>(tmpError);
if (error != Error::NONE) {
return;
}
@@ -1066,10 +1067,10 @@
return Error::NONE;
}
- Error error = kDefaultError;
+ Error error = static_cast<Error>(kDefaultError);
mClient_2_2->getDataspaceSaturationMatrix(static_cast<types::V1_1::Dataspace>(dataspace),
[&](const auto& tmpError, const auto& tmpMatrix) {
- error = tmpError;
+ error = static_cast<Error>(tmpError);
if (error != Error::NONE) {
return;
}
@@ -1087,11 +1088,11 @@
return Error::UNSUPPORTED;
}
- Error error = kDefaultError;
+ Error error = static_cast<Error>(kDefaultError);
mClient_2_3->getDisplayIdentificationData(display,
[&](const auto& tmpError, const auto& tmpPort,
const auto& tmpData) {
- error = tmpError;
+ error = static_cast<Error>(tmpError);
if (error != Error::NONE) {
return;
}
@@ -1123,13 +1124,13 @@
if (!mClient_2_3) {
return Error::UNSUPPORTED;
}
- Error error = kDefaultError;
+ Error error = static_cast<Error>(kDefaultError);
mClient_2_3->getDisplayedContentSamplingAttributes(display,
[&](const auto tmpError,
const auto& tmpFormat,
const auto& tmpDataspace,
const auto& tmpComponentMask) {
- error = tmpError;
+ error = static_cast<Error>(tmpError);
if (error == Error::NONE) {
*outFormat = tmpFormat;
*outDataspace = tmpDataspace;
@@ -1149,8 +1150,9 @@
auto enable = enabled ? V2_3::IComposerClient::DisplayedContentSampling::ENABLE
: V2_3::IComposerClient::DisplayedContentSampling::DISABLE;
- return mClient_2_3->setDisplayedContentSamplingEnabled(display, enable, componentMask,
- maxFrames);
+ auto ret = mClient_2_3->setDisplayedContentSamplingEnabled(display, enable, componentMask,
+ maxFrames);
+ return static_cast<Error>(unwrapRet(ret));
}
Error HidlComposer::getDisplayedContentSample(Display display, uint64_t maxFrames,
@@ -1161,12 +1163,12 @@
if (!mClient_2_3) {
return Error::UNSUPPORTED;
}
- Error error = kDefaultError;
+ Error error = static_cast<Error>(kDefaultError);
mClient_2_3->getDisplayedContentSample(display, maxFrames, timestamp,
[&](const auto tmpError, auto tmpNumFrames,
const auto& tmpSamples0, const auto& tmpSamples1,
const auto& tmpSamples2, const auto& tmpSamples3) {
- error = tmpError;
+ error = static_cast<Error>(tmpError);
if (error == Error::NONE) {
outStats->numFrames = tmpNumFrames;
outStats->component_0_sample = tmpSamples0;
@@ -1196,7 +1198,8 @@
if (!mClient_2_3) {
return Error::UNSUPPORTED;
}
- return mClient_2_3->setDisplayBrightness(display, brightness);
+ auto ret = mClient_2_3->setDisplayBrightness(display, brightness);
+ return static_cast<Error>(unwrapRet(ret));
}
// Composer HAL 2.4
@@ -1273,19 +1276,18 @@
return error;
}
-V2_4::Error HidlComposer::setActiveConfigWithConstraints(
+Error HidlComposer::setActiveConfigWithConstraints(
Display display, Config config,
const IComposerClient::VsyncPeriodChangeConstraints& vsyncPeriodChangeConstraints,
VsyncPeriodChangeTimeline* outTimeline) {
- using Error = V2_4::Error;
if (!mClient_2_4) {
return Error::UNSUPPORTED;
}
- Error error = kDefaultError_2_4;
+ Error error = static_cast<Error>(kDefaultError_2_4);
mClient_2_4->setActiveConfigWithConstraints(display, config, vsyncPeriodChangeConstraints,
[&](const auto& tmpError, const auto& tmpTimeline) {
- error = tmpError;
+ error = static_cast<Error>(tmpError);
if (error != Error::NONE) {
return;
}
@@ -1446,6 +1448,18 @@
"OptionalFeature::PhysicalDisplayOrientation is not supported on HIDL");
}
+Error HidlComposer::getMaxLayerPictureProfiles(Display, int32_t*) {
+ return Error::UNSUPPORTED;
+}
+
+Error HidlComposer::setDisplayPictureProfileId(Display, PictureProfileId) {
+ return Error::UNSUPPORTED;
+}
+
+Error HidlComposer::setLayerPictureProfileId(Display, Layer, PictureProfileId) {
+ return Error::UNSUPPORTED;
+}
+
void HidlComposer::registerCallback(ComposerCallback& callback) {
const bool vsyncSwitchingSupported =
isSupported(Hwc2::Composer::OptionalFeature::RefreshRateSwitching);
diff --git a/services/surfaceflinger/DisplayHardware/HidlComposerHal.h b/services/surfaceflinger/DisplayHardware/HidlComposerHal.h
index a3d1f7f..42ba9a9 100644
--- a/services/surfaceflinger/DisplayHardware/HidlComposerHal.h
+++ b/services/surfaceflinger/DisplayHardware/HidlComposerHal.h
@@ -60,7 +60,6 @@
using V2_1::Config;
using V2_1::Display;
-using V2_1::Error;
using V2_1::Layer;
using V2_4::CommandReaderBase;
using V2_4::CommandWriterBase;
@@ -308,7 +307,7 @@
V2_4::Error getDisplayConnectionType(Display display,
IComposerClient::DisplayConnectionType* outType) override;
V2_4::Error getDisplayVsyncPeriod(Display display, VsyncPeriodNanos* outVsyncPeriod) override;
- V2_4::Error setActiveConfigWithConstraints(
+ Error setActiveConfigWithConstraints(
Display display, Config config,
const IComposerClient::VsyncPeriodChangeConstraints& vsyncPeriodChangeConstraints,
VsyncPeriodChangeTimeline* outTimeline) override;
@@ -357,6 +356,9 @@
override;
Error setLayerLuts(Display, Layer,
aidl::android::hardware::graphics::composer3::Luts&) override;
+ Error getMaxLayerPictureProfiles(Display, int32_t* outMaxProfiles) override;
+ Error setDisplayPictureProfileId(Display, PictureProfileId) override;
+ Error setLayerPictureProfileId(Display, Layer, PictureProfileId) override;
private:
class CommandWriter : public CommandWriterBase {
diff --git a/services/surfaceflinger/FrameTimeline/FrameTimeline.cpp b/services/surfaceflinger/FrameTimeline/FrameTimeline.cpp
index 47b811b..86d7388 100644
--- a/services/surfaceflinger/FrameTimeline/FrameTimeline.cpp
+++ b/services/surfaceflinger/FrameTimeline/FrameTimeline.cpp
@@ -29,6 +29,7 @@
#include <cinttypes>
#include <numeric>
#include <unordered_set>
+#include <vector>
#include "../Jank/JankTracker.h"
@@ -378,6 +379,11 @@
}
}
+void SurfaceFrame::setDesiredPresentTime(nsecs_t desiredPresentTime) {
+ std::scoped_lock lock(mMutex);
+ mActuals.desiredPresentTime = desiredPresentTime;
+}
+
void SurfaceFrame::setDropTime(nsecs_t dropTime) {
std::scoped_lock lock(mMutex);
mDropTime = dropTime;
@@ -997,6 +1003,11 @@
finalizeCurrentDisplayFrame();
}
+const std::vector<std::shared_ptr<frametimeline::SurfaceFrame>>& FrameTimeline::getPresentFrames()
+ const {
+ return mPresentFrames;
+}
+
void FrameTimeline::onCommitNotComposited() {
SFTRACE_CALL();
std::scoped_lock lock(mMutex);
@@ -1456,6 +1467,30 @@
static_cast<float>(totalPresentToPresentWalls);
}
+void FrameTimeline::generateFrameStats(int32_t layer, size_t count, FrameStats* outStats) const {
+ std::scoped_lock lock(mMutex);
+
+ // TODO: Include FPS calculation here
+ for (auto displayFrame : mDisplayFrames) {
+ if (!count--) {
+ break;
+ }
+
+ if (displayFrame->getActuals().presentTime <= 0) {
+ continue;
+ }
+
+ for (const auto& surfaceFrame : displayFrame->getSurfaceFrames()) {
+ if (surfaceFrame->getLayerId() == layer) {
+ outStats->actualPresentTimesNano.push_back(surfaceFrame->getActuals().presentTime);
+ outStats->desiredPresentTimesNano.push_back(
+ surfaceFrame->getActuals().desiredPresentTime);
+ outStats->frameReadyTimesNano.push_back(surfaceFrame->getActuals().endTime);
+ }
+ }
+ }
+}
+
std::optional<size_t> FrameTimeline::getFirstSignalFenceIndex() const {
for (size_t i = 0; i < mPendingPresentFences.size(); i++) {
const auto& [fence, _] = mPendingPresentFences[i];
@@ -1492,6 +1527,7 @@
mPendingPresentFences.erase(mPendingPresentFences.begin());
}
+ mPresentFrames.clear();
for (size_t i = 0; i < mPendingPresentFences.size(); i++) {
const auto& pendingPresentFence = mPendingPresentFences[i];
nsecs_t signalTime = Fence::SIGNAL_TIME_INVALID;
@@ -1504,6 +1540,13 @@
auto& displayFrame = pendingPresentFence.second;
displayFrame->onPresent(signalTime, mPreviousActualPresentTime);
+
+ // Surface frames have been jank classified and can be provided to caller
+ // to detect if buffer stuffing is occurring.
+ for (const auto& frame : displayFrame->getSurfaceFrames()) {
+ mPresentFrames.push_back(frame);
+ }
+
mPreviousPredictionPresentTime =
displayFrame->trace(mSurfaceFlingerPid, monoBootOffset,
mPreviousPredictionPresentTime, mFilterFramesBeforeTraceStarts);
diff --git a/services/surfaceflinger/FrameTimeline/FrameTimeline.h b/services/surfaceflinger/FrameTimeline/FrameTimeline.h
index cffb61e..a47bd57 100644
--- a/services/surfaceflinger/FrameTimeline/FrameTimeline.h
+++ b/services/surfaceflinger/FrameTimeline/FrameTimeline.h
@@ -85,16 +85,20 @@
*/
struct TimelineItem {
TimelineItem(const nsecs_t startTime = 0, const nsecs_t endTime = 0,
- const nsecs_t presentTime = 0)
- : startTime(startTime), endTime(endTime), presentTime(presentTime) {}
+ const nsecs_t presentTime = 0, const nsecs_t desiredPresentTime = 0)
+ : startTime(startTime),
+ endTime(endTime),
+ presentTime(presentTime),
+ desiredPresentTime(desiredPresentTime) {}
nsecs_t startTime;
nsecs_t endTime;
nsecs_t presentTime;
+ nsecs_t desiredPresentTime;
bool operator==(const TimelineItem& other) const {
return startTime == other.startTime && endTime == other.endTime &&
- presentTime == other.presentTime;
+ presentTime == other.presentTime && desiredPresentTime != other.desiredPresentTime;
}
bool operator!=(const TimelineItem& other) const { return !(*this == other); }
@@ -183,6 +187,7 @@
void setActualStartTime(nsecs_t actualStartTime);
void setActualQueueTime(nsecs_t actualQueueTime);
void setAcquireFenceTime(nsecs_t acquireFenceTime);
+ void setDesiredPresentTime(nsecs_t desiredPresentTime);
void setDropTime(nsecs_t dropTime);
void setPresentState(PresentState presentState, nsecs_t lastLatchTime = 0);
void setRenderRate(Fps renderRate);
@@ -323,6 +328,11 @@
virtual void setSfPresent(nsecs_t sfPresentTime, const std::shared_ptr<FenceTime>& presentFence,
const std::shared_ptr<FenceTime>& gpuFence) = 0;
+ // Provides surface frames that have already been jank classified in the most recent
+ // flush of pending present fences. This allows buffer stuffing detection from SF.
+ virtual const std::vector<std::shared_ptr<frametimeline::SurfaceFrame>>& getPresentFrames()
+ const = 0;
+
// Tells FrameTimeline that a frame was committed but not composited. This is used to flush
// all the associated surface frames.
virtual void onCommitNotComposited() = 0;
@@ -341,6 +351,9 @@
// containing at least one layer ID.
virtual float computeFps(const std::unordered_set<int32_t>& layerIds) = 0;
+ // Supports the legacy FrameStats interface
+ virtual void generateFrameStats(int32_t layer, size_t count, FrameStats* outStats) const = 0;
+
// Restores the max number of display frames to default. Called by SF backdoor.
virtual void reset() = 0;
};
@@ -497,10 +510,13 @@
void setSfWakeUp(int64_t token, nsecs_t wakeupTime, Fps refreshRate, Fps renderRate) override;
void setSfPresent(nsecs_t sfPresentTime, const std::shared_ptr<FenceTime>& presentFence,
const std::shared_ptr<FenceTime>& gpuFence = FenceTime::NO_FENCE) override;
+ const std::vector<std::shared_ptr<frametimeline::SurfaceFrame>>& getPresentFrames()
+ const override;
void onCommitNotComposited() override;
void parseArgs(const Vector<String16>& args, std::string& result) override;
void setMaxDisplayFrames(uint32_t size) override;
float computeFps(const std::unordered_set<int32_t>& layerIds) override;
+ void generateFrameStats(int32_t layer, size_t count, FrameStats* outStats) const override;
void reset() override;
// Sets up the perfetto tracing backend and data source.
@@ -543,6 +559,9 @@
// display frame, this is a good starting size for the vector so that we can avoid the
// internal vector resizing that happens with push_back.
static constexpr uint32_t kNumSurfaceFramesInitial = 10;
+ // Presented surface frames that have been jank classified and can
+ // indicate of potential buffer stuffing.
+ std::vector<std::shared_ptr<frametimeline::SurfaceFrame>> mPresentFrames;
};
} // namespace impl
diff --git a/services/surfaceflinger/FrontEnd/LayerHierarchy.cpp b/services/surfaceflinger/FrontEnd/LayerHierarchy.cpp
index d709530..da536b6 100644
--- a/services/surfaceflinger/FrontEnd/LayerHierarchy.cpp
+++ b/services/surfaceflinger/FrontEnd/LayerHierarchy.cpp
@@ -166,7 +166,8 @@
}
out << "(Mirroring) ";
}
- out << *mLayer;
+
+ out << *mLayer << " pid=" << mLayer->ownerPid.val() << " uid=" << mLayer->ownerUid.val();
}
for (size_t i = 0; i < mChildren.size(); i++) {
diff --git a/services/surfaceflinger/FrontEnd/LayerSnapshot.cpp b/services/surfaceflinger/FrontEnd/LayerSnapshot.cpp
index 11b674b..58f6b96 100644
--- a/services/surfaceflinger/FrontEnd/LayerSnapshot.cpp
+++ b/services/surfaceflinger/FrontEnd/LayerSnapshot.cpp
@@ -250,6 +250,7 @@
if (drawShadows()) reason << " shadowSettings.length=" << shadowSettings.length;
if (backgroundBlurRadius > 0) reason << " backgroundBlurRadius=" << backgroundBlurRadius;
if (blurRegions.size() > 0) reason << " blurRegions.size()=" << blurRegions.size();
+ if (contentDirty) reason << " contentDirty";
return reason.str();
}
@@ -359,8 +360,9 @@
uint32_t displayRotationFlags) {
clientChanges = requested.what;
changes = requested.changes;
- contentDirty = requested.what & layer_state_t::CONTENT_DIRTY;
- hasReadyFrame = requested.autoRefresh;
+ autoRefresh = requested.autoRefresh;
+ contentDirty = requested.what & layer_state_t::CONTENT_DIRTY || autoRefresh;
+ hasReadyFrame = autoRefresh;
sidebandStreamHasFrame = requested.hasSidebandStreamFrame();
updateSurfaceDamage(requested, requested.hasReadyFrame(), forceFullDamage, surfaceDamage);
@@ -411,6 +413,13 @@
if (forceUpdate || requested.what & layer_state_t::eCropChanged) {
geomCrop = requested.crop;
}
+ if (forceUpdate || requested.what & layer_state_t::ePictureProfileHandleChanged) {
+ pictureProfileHandle = requested.pictureProfileHandle;
+ }
+ if (forceUpdate || requested.what & layer_state_t::eAppContentPriorityChanged) {
+ // TODO(b/337330263): Also consider the system-determined priority of the app
+ pictureProfilePriority = requested.appContentPriority;
+ }
if (forceUpdate || requested.what & layer_state_t::eDefaultFrameRateCompatibilityChanged) {
const auto compatibility =
diff --git a/services/surfaceflinger/FrontEnd/LayerSnapshot.h b/services/surfaceflinger/FrontEnd/LayerSnapshot.h
index b7d4cc5..b8df3ed 100644
--- a/services/surfaceflinger/FrontEnd/LayerSnapshot.h
+++ b/services/surfaceflinger/FrontEnd/LayerSnapshot.h
@@ -77,6 +77,7 @@
gui::LayerMetadata layerMetadata;
gui::LayerMetadata relativeLayerMetadata;
bool hasReadyFrame; // used in post composition to check if there is another frame ready
+ bool autoRefresh;
ui::Transform localTransformInverse;
gui::WindowInfo inputInfo;
ui::Transform localTransform;
diff --git a/services/surfaceflinger/FrontEnd/LayerSnapshotBuilder.cpp b/services/surfaceflinger/FrontEnd/LayerSnapshotBuilder.cpp
index 10e212e..4d9a9ca 100644
--- a/services/surfaceflinger/FrontEnd/LayerSnapshotBuilder.cpp
+++ b/services/surfaceflinger/FrontEnd/LayerSnapshotBuilder.cpp
@@ -261,20 +261,25 @@
}
snapshot.isVisible = visible;
- // TODO(b/238781169) we are ignoring this compat for now, since we will have
- // to remove any optimization based on visibility.
+ if (FlagManager::getInstance().skip_invisible_windows_in_input()) {
+ snapshot.inputInfo.setInputConfig(gui::WindowInfo::InputConfig::NOT_VISIBLE, !visible);
+ } else {
+ // TODO(b/238781169) we are ignoring this compat for now, since we will have
+ // to remove any optimization based on visibility.
- // For compatibility reasons we let layers which can receive input
- // receive input before they have actually submitted a buffer. Because
- // of this we use canReceiveInput instead of isVisible to check the
- // policy-visibility, ignoring the buffer state. However for layers with
- // hasInputInfo()==false we can use the real visibility state.
- // We are just using these layers for occlusion detection in
- // InputDispatcher, and obviously if they aren't visible they can't occlude
- // anything.
- const bool visibleForInput =
- snapshot.hasInputInfo() ? snapshot.canReceiveInput() : snapshot.isVisible;
- snapshot.inputInfo.setInputConfig(gui::WindowInfo::InputConfig::NOT_VISIBLE, !visibleForInput);
+ // For compatibility reasons we let layers which can receive input
+ // receive input before they have actually submitted a buffer. Because
+ // of this we use canReceiveInput instead of isVisible to check the
+ // policy-visibility, ignoring the buffer state. However for layers with
+ // hasInputInfo()==false we can use the real visibility state.
+ // We are just using these layers for occlusion detection in
+ // InputDispatcher, and obviously if they aren't visible they can't occlude
+ // anything.
+ const bool visibleForInput =
+ snapshot.hasInputInfo() ? snapshot.canReceiveInput() : snapshot.isVisible;
+ snapshot.inputInfo.setInputConfig(gui::WindowInfo::InputConfig::NOT_VISIBLE,
+ !visibleForInput);
+ }
LLOGV(snapshot.sequence, "updating visibility %s %s", visible ? "true" : "false",
snapshot.getDebugString().c_str());
}
@@ -314,8 +319,8 @@
void clearChanges(LayerSnapshot& snapshot) {
snapshot.changes.clear();
snapshot.clientChanges = 0;
- snapshot.contentDirty = false;
- snapshot.hasReadyFrame = false;
+ snapshot.contentDirty = snapshot.autoRefresh;
+ snapshot.hasReadyFrame = snapshot.autoRefresh;
snapshot.sidebandStreamHasFrame = false;
snapshot.surfaceDamage.clear();
}
@@ -724,10 +729,12 @@
if (args.displayChanges) snapshot.changes |= RequestedLayerState::Changes::Geometry;
snapshot.reachablilty = LayerSnapshot::Reachablilty::Reachable;
snapshot.clientChanges |= (parentSnapshot.clientChanges & layer_state_t::AFFECTS_CHILDREN);
+ // mark the content as dirty if the parent state changes can dirty the child's content (for
+ // example alpha)
+ snapshot.contentDirty |= (snapshot.clientChanges & layer_state_t::CONTENT_DIRTY) != 0;
snapshot.isHiddenByPolicyFromParent = parentSnapshot.isHiddenByPolicyFromParent ||
parentSnapshot.invalidTransform || requested.isHiddenByPolicy() ||
(args.excludeLayerIds.find(path.id) != args.excludeLayerIds.end());
-
const bool forceUpdate = args.forceUpdate == ForceUpdateFlags::ALL ||
snapshot.clientChanges & layer_state_t::eReparent ||
snapshot.changes.any(RequestedLayerState::Changes::Visibility |
@@ -1258,6 +1265,10 @@
for (int i = mNumInterestingSnapshots - 1; i >= 0; i--) {
LayerSnapshot& snapshot = *mSnapshots[(size_t)i];
if (!snapshot.hasInputInfo()) continue;
+ if (FlagManager::getInstance().skip_invisible_windows_in_input() &&
+ snapshot.inputInfo.inputConfig.test(gui::WindowInfo::InputConfig::NOT_VISIBLE)) {
+ continue;
+ }
visitor(snapshot);
}
}
diff --git a/services/surfaceflinger/FrontEnd/RequestedLayerState.cpp b/services/surfaceflinger/FrontEnd/RequestedLayerState.cpp
index 713a5c5..ee9302b 100644
--- a/services/surfaceflinger/FrontEnd/RequestedLayerState.cpp
+++ b/services/surfaceflinger/FrontEnd/RequestedLayerState.cpp
@@ -163,7 +163,7 @@
uint64_t clientChanges = what | layer_state_t::diff(clientState);
layer_state_t::merge(clientState);
what = clientChanges;
- LLOGV(layerId, "requested=%" PRIu64 "flags=%" PRIu64, clientState.what, clientChanges);
+ LLOGV(layerId, "requested=%" PRIu64 " flags=%" PRIu64 " ", clientState.what, clientChanges);
if (clientState.what & layer_state_t::eFlagsChanged) {
if ((oldFlags ^ flags) &
@@ -633,7 +633,7 @@
layer_state_t::eEdgeExtensionChanged | layer_state_t::eBufferCropChanged |
layer_state_t::eDestinationFrameChanged | layer_state_t::eDimmingEnabledChanged |
layer_state_t::eExtendedRangeBrightnessChanged |
- layer_state_t::eDesiredHdrHeadroomChanged |
+ layer_state_t::eDesiredHdrHeadroomChanged | layer_state_t::eLutsChanged |
(FlagManager::getInstance().latch_unsignaled_with_auto_refresh_changed()
? layer_state_t::eFlagsChanged
: 0);
diff --git a/services/surfaceflinger/FrontEnd/readme.md b/services/surfaceflinger/FrontEnd/readme.md
index e5f51a5..6258f7e 100644
--- a/services/surfaceflinger/FrontEnd/readme.md
+++ b/services/surfaceflinger/FrontEnd/readme.md
@@ -17,6 +17,29 @@
This allows control to be delegated to different parts of the system - such as SystemServer,
SysUI and Apps.
+### Layer Drawing Order
+Layers are drawn based on an inorder traversal, treating relative parents as
+direct parents. Negative z-values place layers below their parent, while
+non-negative values place them above. Layers with the same z-value are drawn
+in creation order (newer on top). However, relying on creation order for
+z-ordering is discouraged; use unique z-values whenever possible for better
+control.
+
+Traversal pseudo code:
+```
+fn traverseBottomToTop(root):
+ for each child node including relative children,
+ sorted by z then layer id, with z less than 0:
+ traverseBottomToTop(childNode)
+
+ visit(root)
+
+ for each child node including relative children,
+ sorted by z then layer id, with z greater than
+ or equal to 0:
+ traverseBottomToTop(childNode)
+```
+
### Layer Lifecycle
Layer is created by a client. The client receives a strong binder reference to the layer
handle, which will keep the layer alive as long as the client holds the reference. The
diff --git a/services/surfaceflinger/Layer.cpp b/services/surfaceflinger/Layer.cpp
index c88092b..195461f 100644
--- a/services/surfaceflinger/Layer.cpp
+++ b/services/surfaceflinger/Layer.cpp
@@ -154,7 +154,7 @@
mDrawingState.metadata = args.metadata;
mDrawingState.frameTimelineInfo = {};
mDrawingState.postTime = -1;
- mFrameTracker.setDisplayRefreshPeriod(
+ mDeprecatedFrameTracker.setDisplayRefreshPeriod(
args.flinger->mScheduler->getPacesetterVsyncPeriod().ns());
mOwnerUid = args.ownerUid;
@@ -472,6 +472,9 @@
getSequence(), mName,
mTransactionName,
/*isBuffer*/ false, gameMode);
+ // Buffer hasn't yet been latched, so use mDrawingState
+ surfaceFrame->setDesiredPresentTime(mDrawingState.desiredPresentTime);
+
surfaceFrame->setActualStartTime(info.startTimeNanos);
// For Transactions, the post time is considered to be both queue and acquire fence time.
surfaceFrame->setActualQueueTime(postTime);
@@ -490,6 +493,8 @@
mFlinger->mFrameTimeline->createSurfaceFrameForToken(info, mOwnerPid, mOwnerUid,
getSequence(), mName, debugName,
/*isBuffer*/ true, gameMode);
+ // Buffer hasn't yet been latched, so use mDrawingState
+ surfaceFrame->setDesiredPresentTime(mDrawingState.desiredPresentTime);
surfaceFrame->setActualStartTime(info.startTimeNanos);
// For buffers, acquire fence time will set during latch.
surfaceFrame->setActualQueueTime(queueTime);
@@ -514,6 +519,8 @@
mOwnerPid, mOwnerUid,
getSequence(), mName, debugName,
/*isBuffer*/ false, gameMode);
+ // Buffer hasn't yet been latched, so use mDrawingState
+ surfaceFrame->setDesiredPresentTime(mDrawingState.desiredPresentTime);
surfaceFrame->setActualStartTime(skippedFrameTimelineInfo.skippedFrameStartTimeNanos);
// For Transactions, the post time is considered to be both queue and acquire fence time.
surfaceFrame->setActualQueueTime(postTime);
@@ -605,15 +612,42 @@
}
void Layer::dumpFrameStats(std::string& result) const {
- mFrameTracker.dumpStats(result);
+ if (FlagManager::getInstance().deprecate_frame_tracker()) {
+ FrameStats fs = FrameStats();
+ getFrameStats(&fs);
+ for (auto desired = fs.desiredPresentTimesNano.begin(),
+ actual = fs.actualPresentTimesNano.begin(),
+ ready = fs.frameReadyTimesNano.begin();
+ desired != fs.desiredPresentTimesNano.end() &&
+ actual != fs.actualPresentTimesNano.end() && ready != fs.frameReadyTimesNano.end();
+ ++desired, ++actual, ++ready) {
+ result.append(std::format("{}\t{}\t{}\n", *desired, *actual, *ready));
+ }
+
+ result.push_back('\n');
+ } else {
+ mDeprecatedFrameTracker.dumpStats(result);
+ }
}
void Layer::clearFrameStats() {
- mFrameTracker.clearStats();
+ if (FlagManager::getInstance().deprecate_frame_tracker()) {
+ mFrameStatsHistorySize = 0;
+ } else {
+ mDeprecatedFrameTracker.clearStats();
+ }
}
void Layer::getFrameStats(FrameStats* outStats) const {
- mFrameTracker.getStats(outStats);
+ if (FlagManager::getInstance().deprecate_frame_tracker()) {
+ if (auto ftl = getTimeline()) {
+ float fps = ftl->get().computeFps({getSequence()});
+ ftl->get().generateFrameStats(getSequence(), mFrameStatsHistorySize, outStats);
+ outStats->refreshPeriodNano = Fps::fromValue(fps).getPeriodNsecs();
+ }
+ } else {
+ mDeprecatedFrameTracker.getStats(outStats);
+ }
}
void Layer::onDisconnect() {
@@ -689,8 +723,20 @@
listener->onReleaseBuffer(callbackId, fence, currentMaxAcquiredBufferCount);
}
- if (mBufferReleaseChannel) {
- mBufferReleaseChannel->writeReleaseFence(callbackId, fence, currentMaxAcquiredBufferCount);
+ if (!mBufferReleaseChannel) {
+ return;
+ }
+
+ status_t status = mBufferReleaseChannel->writeReleaseFence(callbackId, fence,
+ currentMaxAcquiredBufferCount);
+ if (status != OK) {
+ int error = -status;
+ // callReleaseBufferCallback is called during Layer's destructor. In this case, it's
+ // expected to receive connection errors.
+ if (error != EPIPE && error != ECONNRESET) {
+ ALOGD("[%s] writeReleaseFence failed. error %d (%s)", getDebugName(), error,
+ strerror(error));
+ }
}
}
@@ -1336,9 +1382,9 @@
handle->compositorTiming = compositorTiming;
}
- // Update mFrameTracker.
+ // Update mDeprecatedFrameTracker.
nsecs_t desiredPresentTime = mBufferInfo.mDesiredPresentTime;
- mFrameTracker.setDesiredPresentTime(desiredPresentTime);
+ mDeprecatedFrameTracker.setDesiredPresentTime(desiredPresentTime);
const int32_t layerId = getSequence();
mFlinger->mTimeStats->setDesiredTime(layerId, mCurrentFrameNumber, desiredPresentTime);
@@ -1358,15 +1404,15 @@
}
}
+ // The SurfaceFrame's AcquireFence is the same as this.
std::shared_ptr<FenceTime> frameReadyFence = mBufferInfo.mFenceTime;
if (frameReadyFence->isValid()) {
- mFrameTracker.setFrameReadyFence(std::move(frameReadyFence));
+ mDeprecatedFrameTracker.setFrameReadyFence(std::move(frameReadyFence));
} else {
// There was no fence for this frame, so assume that it was ready
// to be presented at the desired present time.
- mFrameTracker.setFrameReadyTime(desiredPresentTime);
+ mDeprecatedFrameTracker.setFrameReadyTime(desiredPresentTime);
}
-
if (display) {
const auto activeMode = display->refreshRateSelector().getActiveMode();
const Fps refreshRate = activeMode.fps;
@@ -1381,7 +1427,7 @@
mFlinger->mFrameTracer->traceFence(layerId, getCurrentBufferId(), mCurrentFrameNumber,
presentFence,
FrameTracer::FrameEvent::PRESENT_FENCE);
- mFrameTracker.setActualPresentFence(std::shared_ptr<FenceTime>(presentFence));
+ mDeprecatedFrameTracker.setActualPresentFence(std::shared_ptr<FenceTime>(presentFence));
} else if (const auto displayId = PhysicalDisplayId::tryCast(display->getId());
displayId && mFlinger->getHwComposer().isConnected(*displayId)) {
// The HWC doesn't support present fences, so use the present timestamp instead.
@@ -1402,11 +1448,12 @@
mFlinger->mFrameTracer->traceTimestamp(layerId, getCurrentBufferId(),
mCurrentFrameNumber, actualPresentTime,
FrameTracer::FrameEvent::PRESENT_FENCE);
- mFrameTracker.setActualPresentTime(actualPresentTime);
+ mDeprecatedFrameTracker.setActualPresentTime(actualPresentTime);
}
}
- mFrameTracker.advanceFrame();
+ mFrameStatsHistorySize++;
+ mDeprecatedFrameTracker.advanceFrame();
mBufferInfo.mFrameLatencyNeeded = false;
}
diff --git a/services/surfaceflinger/Layer.h b/services/surfaceflinger/Layer.h
index a2716c6..c234a75 100644
--- a/services/surfaceflinger/Layer.h
+++ b/services/surfaceflinger/Layer.h
@@ -18,6 +18,7 @@
#include <android/gui/DropInputMode.h>
#include <android/gui/ISurfaceComposerClient.h>
+#include <com_android_graphics_surfaceflinger_flags.h>
#include <ftl/small_map.h>
#include <gui/BufferQueue.h>
#include <gui/LayerState.h>
@@ -44,6 +45,7 @@
#include <scheduler/Seamlessness.h>
#include <cstdint>
+#include <functional>
#include <optional>
#include <vector>
@@ -433,8 +435,12 @@
uint32_t mTransactionFlags{0};
+ // Leverages FrameTimeline to generate FrameStats. Since FrameTimeline already has the data,
+ // statistical history needs to only be tracked by count of frames.
+ // TODO: Deprecate the '--latency-clear' and get rid of this.
+ std::atomic<uint16_t> mFrameStatsHistorySize;
// Timestamp history for UIAutomation. Thread safe.
- FrameTracker mFrameTracker;
+ FrameTracker mDeprecatedFrameTracker;
// main thread
sp<NativeHandle> mSidebandStream;
@@ -556,6 +562,9 @@
std::vector<std::pair<frontend::LayerHierarchy::TraversalPath, sp<LayerFE>>> mLayerFEs;
bool mHandleAlive = false;
+ std::optional<std::reference_wrapper<frametimeline::FrameTimeline>> getTimeline() const {
+ return *mFlinger->mFrameTimeline;
+ }
};
std::ostream& operator<<(std::ostream& stream, const Layer::FrameRate& rate);
diff --git a/services/surfaceflinger/LayerFE.cpp b/services/surfaceflinger/LayerFE.cpp
index a346981..fea7671 100644
--- a/services/surfaceflinger/LayerFE.cpp
+++ b/services/surfaceflinger/LayerFE.cpp
@@ -27,7 +27,6 @@
#include "LayerFE.h"
#include "SurfaceFlinger.h"
#include "ui/FenceResult.h"
-#include "ui/LayerStack.h"
namespace android {
@@ -343,13 +342,15 @@
caster.shadow = state;
}
-void LayerFE::onLayerDisplayed(ftl::SharedFuture<FenceResult> futureFenceResult,
- ui::LayerStack layerStack) {
- mCompositionResult.releaseFences.emplace_back(std::move(futureFenceResult), layerStack);
+void LayerFE::onPictureProfileCommitted() {
+ mCompositionResult.wasPictureProfileCommitted = true;
+ mCompositionResult.pictureProfileHandle = mSnapshot->pictureProfileHandle;
}
-CompositionResult&& LayerFE::stealCompositionResult() {
- return std::move(mCompositionResult);
+CompositionResult LayerFE::stealCompositionResult() {
+ CompositionResult result;
+ std::swap(mCompositionResult, result);
+ return result;
}
const char* LayerFE::getDebugName() const {
diff --git a/services/surfaceflinger/LayerFE.h b/services/surfaceflinger/LayerFE.h
index 658f949..9483aeb 100644
--- a/services/surfaceflinger/LayerFE.h
+++ b/services/surfaceflinger/LayerFE.h
@@ -18,19 +18,24 @@
#include <android/gui/CachingHint.h>
#include <gui/LayerMetadata.h>
+#include <ui/LayerStack.h>
+#include <ui/PictureProfileHandle.h>
+
#include "FrontEnd/LayerSnapshot.h"
#include "compositionengine/LayerFE.h"
#include "compositionengine/LayerFECompositionState.h"
#include "renderengine/LayerSettings.h"
-#include "ui/LayerStack.h"
#include <ftl/future.h>
namespace android {
struct CompositionResult {
- std::vector<std::pair<ftl::SharedFuture<FenceResult>, ui::LayerStack>> releaseFences;
sp<Fence> lastClientCompositionFence = nullptr;
+ bool wasPictureProfileCommitted = false;
+ // TODO(b/337330263): Why does LayerFE coming from SF have a null composition state?
+ // It would be better not to duplicate this information
+ PictureProfileHandle pictureProfileHandle = PictureProfileHandle::NONE;
};
class LayerFE : public virtual RefBase, public virtual compositionengine::LayerFE {
@@ -41,7 +46,6 @@
// compositionengine::LayerFE overrides
const compositionengine::LayerFECompositionState* getCompositionState() const override;
bool onPreComposition(bool updatingOutputGeometryThisFrame) override;
- void onLayerDisplayed(ftl::SharedFuture<FenceResult>, ui::LayerStack) override;
const char* getDebugName() const override;
int32_t getSequence() const override;
bool hasRoundedCorners() const override;
@@ -50,10 +54,11 @@
const gui::LayerMetadata* getRelativeMetadata() const override;
std::optional<compositionengine::LayerFE::LayerSettings> prepareClientComposition(
compositionengine::LayerFE::ClientCompositionTargetSettings&) const;
- CompositionResult&& stealCompositionResult();
+ CompositionResult stealCompositionResult();
ftl::Future<FenceResult> createReleaseFenceFuture() override;
void setReleaseFence(const FenceResult& releaseFence) override;
LayerFE::ReleaseFencePromiseStatus getReleaseFencePromiseStatus() override;
+ void onPictureProfileCommitted() override;
std::unique_ptr<surfaceflinger::frontend::LayerSnapshot> mSnapshot;
diff --git a/services/surfaceflinger/PowerAdvisor/Android.bp b/services/surfaceflinger/PowerAdvisor/Android.bp
new file mode 100644
index 0000000..4efbcb9
--- /dev/null
+++ b/services/surfaceflinger/PowerAdvisor/Android.bp
@@ -0,0 +1,47 @@
+/*
+ * Copyright 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+// ADPF uses FMQ which can't build to CPP backend, and is thus not
+// compatible with the rest of SF aidl for this reason
+
+aidl_interface {
+ name: "android.adpf.sessionmanager_aidl",
+ srcs: [
+ "aidl/android/adpf/*.aidl",
+ ],
+ local_include_dir: "aidl",
+ unstable: true,
+ backend: {
+ java: {
+ sdk_version: "module_current",
+ enabled: true,
+ },
+ cpp: {
+ enabled: false,
+ },
+ ndk: {
+ enabled: true,
+ },
+ },
+}
+
+cc_defaults {
+ name: "poweradvisor_deps",
+ shared_libs: [
+ "libpowermanager",
+ "android.adpf.sessionmanager_aidl-ndk",
+ ],
+}
diff --git a/services/surfaceflinger/DisplayHardware/PowerAdvisor.cpp b/services/surfaceflinger/PowerAdvisor/PowerAdvisor.cpp
similarity index 92%
rename from services/surfaceflinger/DisplayHardware/PowerAdvisor.cpp
rename to services/surfaceflinger/PowerAdvisor/PowerAdvisor.cpp
index c914ec3..c7d0b2c 100644
--- a/services/surfaceflinger/DisplayHardware/PowerAdvisor.cpp
+++ b/services/surfaceflinger/PowerAdvisor/PowerAdvisor.cpp
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-//#define LOG_NDEBUG 0
+// #define LOG_NDEBUG 0
#define ATRACE_TAG ATRACE_TAG_GRAPHICS
@@ -24,6 +24,7 @@
#include <unistd.h>
#include <cinttypes>
#include <cstdint>
+#include <functional>
#include <optional>
#include <android-base/properties.h>
@@ -33,45 +34,29 @@
#include <binder/IServiceManager.h>
-#include "../SurfaceFlingerProperties.h"
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Wconversion"
+#include <powermanager/PowerHalController.h>
+#include <powermanager/PowerHintSessionWrapper.h>
+#pragma clang diagnostic pop
+#include <common/FlagManager.h>
#include "PowerAdvisor.h"
-#include "SurfaceFlinger.h"
-namespace android {
-namespace Hwc2 {
+namespace hal = aidl::android::hardware::power;
-PowerAdvisor::~PowerAdvisor() = default;
+namespace android::adpf::impl {
-namespace impl {
-
-using aidl::android::hardware::power::Boost;
-using aidl::android::hardware::power::ChannelConfig;
-using aidl::android::hardware::power::Mode;
-using aidl::android::hardware::power::SessionHint;
-using aidl::android::hardware::power::SessionTag;
-using aidl::android::hardware::power::WorkDuration;
-using aidl::android::hardware::power::WorkDurationFixedV1;
-
-using aidl::android::hardware::common::fmq::MQDescriptor;
using aidl::android::hardware::common::fmq::SynchronizedReadWrite;
-using aidl::android::hardware::power::ChannelMessage;
using android::hardware::EventFlag;
-using ChannelMessageContents = ChannelMessage::ChannelMessageContents;
-using MsgQueue = android::AidlMessageQueue<ChannelMessage, SynchronizedReadWrite>;
+using ChannelMessageContents = hal::ChannelMessage::ChannelMessageContents;
+using MsgQueue = android::AidlMessageQueue<hal::ChannelMessage, SynchronizedReadWrite>;
using FlagQueue = android::AidlMessageQueue<int8_t, SynchronizedReadWrite>;
PowerAdvisor::~PowerAdvisor() = default;
namespace {
-std::chrono::milliseconds getUpdateTimeout() {
- // Default to a timeout of 80ms if nothing else is specified
- static std::chrono::milliseconds timeout =
- std::chrono::milliseconds(sysprop::display_update_imminent_timeout_ms(80));
- return timeout;
-}
-
void traceExpensiveRendering(bool enabled) {
if (enabled) {
SFTRACE_ASYNC_BEGIN("ExpensiveRendering", 0);
@@ -82,28 +67,30 @@
} // namespace
-PowerAdvisor::PowerAdvisor(SurfaceFlinger& flinger)
- : mPowerHal(std::make_unique<power::PowerHalController>()), mFlinger(flinger) {
- if (getUpdateTimeout() > 0ms) {
- mScreenUpdateTimer.emplace("UpdateImminentTimer", getUpdateTimeout(),
+PowerAdvisor::PowerAdvisor(std::function<void()>&& sfDisableExpensiveFn,
+ std::chrono::milliseconds timeout)
+ : mPowerHal(std::make_unique<power::PowerHalController>()) {
+ if (timeout > 0ms) {
+ mScreenUpdateTimer.emplace("UpdateImminentTimer", timeout,
/* resetCallback */ nullptr,
/* timeoutCallback */
- [this] {
+ [this, disableExpensiveFn = std::move(sfDisableExpensiveFn),
+ timeout] {
while (true) {
auto timeSinceLastUpdate = std::chrono::nanoseconds(
systemTime() - mLastScreenUpdatedTime.load());
- if (timeSinceLastUpdate >= getUpdateTimeout()) {
+ if (timeSinceLastUpdate >= timeout) {
break;
}
// We may try to disable expensive rendering and allow
// for sending DISPLAY_UPDATE_IMMINENT hints too early if
// we idled very shortly after updating the screen, so
// make sure we wait enough time.
- std::this_thread::sleep_for(getUpdateTimeout() -
+ std::this_thread::sleep_for(timeout -
timeSinceLastUpdate);
}
mSendUpdateImminent.store(true);
- mFlinger.disableExpensiveRendering();
+ disableExpensiveFn();
});
}
}
@@ -132,7 +119,7 @@
const bool expectsExpensiveRendering = !mExpensiveDisplays.empty();
if (mNotifiedExpensiveRendering != expectsExpensiveRendering) {
- auto ret = getPowerHal().setMode(Mode::EXPENSIVE_RENDERING, expectsExpensiveRendering);
+ auto ret = getPowerHal().setMode(hal::Mode::EXPENSIVE_RENDERING, expectsExpensiveRendering);
if (!ret.isOk()) {
if (ret.isUnsupported()) {
mHasExpensiveRendering = false;
@@ -151,7 +138,7 @@
if (!mBootFinished.load()) {
return;
}
- sendHintSessionHint(SessionHint::CPU_LOAD_UP);
+ sendHintSessionHint(hal::SessionHint::CPU_LOAD_UP);
}
void PowerAdvisor::notifyDisplayUpdateImminentAndCpuReset() {
@@ -163,12 +150,12 @@
if (mSendUpdateImminent.exchange(false)) {
ALOGV("AIDL notifyDisplayUpdateImminentAndCpuReset");
- sendHintSessionHint(SessionHint::CPU_LOAD_RESET);
+ sendHintSessionHint(hal::SessionHint::CPU_LOAD_RESET);
if (!mHasDisplayUpdateImminent) {
ALOGV("Skipped sending DISPLAY_UPDATE_IMMINENT because HAL doesn't support it");
} else {
- auto ret = getPowerHal().setBoost(Boost::DISPLAY_UPDATE_IMMINENT, 0);
+ auto ret = getPowerHal().setBoost(hal::Boost::DISPLAY_UPDATE_IMMINENT, 0);
if (ret.isUnsupported()) {
mHasDisplayUpdateImminent = false;
}
@@ -205,7 +192,7 @@
FlagManager::getInstance().adpf_use_fmq_channel();
}
-void PowerAdvisor::sendHintSessionHint(SessionHint hint) {
+void PowerAdvisor::sendHintSessionHint(hal::SessionHint hint) {
if (!mBootFinished || !usePowerHintSession()) {
ALOGV("Power hint session is not enabled, skip sending session hint");
return;
@@ -236,7 +223,7 @@
static_cast<int32_t>(getuid()),
mHintSessionThreadIds,
mTargetDuration.ns(),
- SessionTag::SURFACEFLINGER,
+ hal::SessionTag::SURFACEFLINGER,
&mSessionConfig);
if (ret.isOk()) {
mHintSession = ret.value();
@@ -326,7 +313,7 @@
return;
}
SFTRACE_CALL();
- std::optional<WorkDuration> actualDuration = estimateWorkDuration();
+ std::optional<hal::WorkDuration> actualDuration = estimateWorkDuration();
if (!actualDuration.has_value() || actualDuration->durationNanos < 0) {
ALOGV("Failed to send actual work duration, skipping");
return;
@@ -377,7 +364,7 @@
mHintSessionQueue.clear();
}
-template <ChannelMessage::ChannelMessageContents::Tag T, class In>
+template <hal::ChannelMessage::ChannelMessageContents::Tag T, class In>
bool PowerAdvisor::writeHintSessionMessage(In* contents, size_t count) {
if (!mMsgQueue) {
ALOGV("Skip using FMQ with message tag %hhd as it's not supported", T);
@@ -395,13 +382,13 @@
}
for (size_t i = 0; i < count; ++i) {
if constexpr (T == ChannelMessageContents::Tag::workDuration) {
- const WorkDuration& duration = contents[i];
- new (tx.getSlot(i)) ChannelMessage{
+ const hal::WorkDuration& duration = contents[i];
+ new (tx.getSlot(i)) hal::ChannelMessage{
.sessionID = static_cast<int32_t>(mSessionConfig.id),
.timeStampNanos =
(i == count - 1) ? ::android::uptimeNanos() : duration.timeStampNanos,
.data = ChannelMessageContents::make<ChannelMessageContents::Tag::workDuration,
- WorkDurationFixedV1>({
+ hal::WorkDurationFixedV1>({
.durationNanos = duration.durationNanos,
.workPeriodStartTimestampNanos = duration.workPeriodStartTimestampNanos,
.cpuDurationNanos = duration.cpuDurationNanos,
@@ -409,7 +396,7 @@
}),
};
} else {
- new (tx.getSlot(i)) ChannelMessage{
+ new (tx.getSlot(i)) hal::ChannelMessage{
.sessionID = static_cast<int32_t>(mSessionConfig.id),
.timeStampNanos = ::android::uptimeNanos(),
.data = ChannelMessageContents::make<T, In>(std::move(contents[i])),
@@ -572,7 +559,7 @@
return sortedDisplays;
}
-std::optional<WorkDuration> PowerAdvisor::estimateWorkDuration() {
+std::optional<hal::WorkDuration> PowerAdvisor::estimateWorkDuration() {
if (!mExpectedPresentTimes.isFull() || !mCommitStartTimes.isFull()) {
return std::nullopt;
}
@@ -657,7 +644,7 @@
Duration combinedDuration = combineTimingEstimates(totalDuration, flingerDuration);
Duration cpuDuration = combineTimingEstimates(totalDurationWithoutGpu, flingerDuration);
- WorkDuration duration{
+ hal::WorkDuration duration{
.timeStampNanos = TimePoint::now().ns(),
.durationNanos = combinedDuration.ns(),
.workPeriodStartTimestampNanos = mCommitStartTimes[0].ns(),
@@ -760,6 +747,4 @@
return *mPowerHal;
}
-} // namespace impl
-} // namespace Hwc2
-} // namespace android
+} // namespace android::adpf::impl
diff --git a/services/surfaceflinger/DisplayHardware/PowerAdvisor.h b/services/surfaceflinger/PowerAdvisor/PowerAdvisor.h
similarity index 97%
rename from services/surfaceflinger/DisplayHardware/PowerAdvisor.h
rename to services/surfaceflinger/PowerAdvisor/PowerAdvisor.h
index 1076b2b..458b46d 100644
--- a/services/surfaceflinger/DisplayHardware/PowerAdvisor.h
+++ b/services/surfaceflinger/PowerAdvisor/PowerAdvisor.h
@@ -17,7 +17,7 @@
#pragma once
#include <atomic>
-#include <chrono>
+#include <future>
#include <unordered_map>
#include <unordered_set>
@@ -30,10 +30,8 @@
#pragma clang diagnostic ignored "-Wconversion"
#include <aidl/android/hardware/power/IPower.h>
#include <fmq/AidlMessageQueue.h>
-#include <powermanager/PowerHalController.h>
#pragma clang diagnostic pop
-#include <compositionengine/impl/OutputCompositionState.h>
#include <scheduler/Time.h>
#include <ui/DisplayIdentification.h>
#include "../Scheduler/OneShotTimer.h"
@@ -42,13 +40,16 @@
namespace android {
-class SurfaceFlinger;
+namespace power {
+class PowerHalController;
+class PowerHintSessionWrapper;
+} // namespace power
-namespace Hwc2 {
+namespace adpf {
class PowerAdvisor {
public:
- virtual ~PowerAdvisor();
+ virtual ~PowerAdvisor() = default;
// Initializes resources that cannot be initialized on construction
virtual void init() = 0;
@@ -113,9 +114,9 @@
// PowerAdvisor is a wrapper around IPower HAL which takes into account the
// full state of the system when sending out power hints to things like the GPU.
-class PowerAdvisor final : public Hwc2::PowerAdvisor {
+class PowerAdvisor final : public adpf::PowerAdvisor {
public:
- PowerAdvisor(SurfaceFlinger& flinger);
+ PowerAdvisor(std::function<void()>&& function, std::chrono::milliseconds timeout);
~PowerAdvisor() override;
void init() override;
@@ -159,7 +160,6 @@
std::unordered_set<DisplayId> mExpensiveDisplays;
bool mNotifiedExpensiveRendering = false;
- SurfaceFlinger& mFlinger;
std::atomic_bool mSendUpdateImminent = true;
std::atomic<nsecs_t> mLastScreenUpdatedTime = 0;
std::optional<scheduler::OneShotTimer> mScreenUpdateTimer;
@@ -326,5 +326,5 @@
};
} // namespace impl
-} // namespace Hwc2
+} // namespace adpf
} // namespace android
diff --git a/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockPowerHintSessionWrapper.cpp b/services/surfaceflinger/PowerAdvisor/aidl/android/adpf/ISessionManager.aidl
similarity index 64%
copy from services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockPowerHintSessionWrapper.cpp
copy to services/surfaceflinger/PowerAdvisor/aidl/android/adpf/ISessionManager.aidl
index d383283..c1a6a9e 100644
--- a/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockPowerHintSessionWrapper.cpp
+++ b/services/surfaceflinger/PowerAdvisor/aidl/android/adpf/ISessionManager.aidl
@@ -14,12 +14,13 @@
* limitations under the License.
*/
-#include "mock/DisplayHardware/MockPowerHintSessionWrapper.h"
+package android.adpf;
-namespace android::Hwc2::mock {
-
-// Explicit default instantiation is recommended.
-MockPowerHintSessionWrapper::MockPowerHintSessionWrapper()
- : power::PowerHintSessionWrapper(nullptr) {}
-
-} // namespace android::Hwc2::mock
+/**
+ * Private service for SessionManager to use. Ideally this will
+ * eventually take the role of HintManagerService.
+ */
+interface ISessionManager {
+ oneway void associateSessionToLayers(in int sessionId, in int ownerUid, in IBinder[] layers);
+ oneway void trackedSessionsDied(in int[] sessionId);
+}
diff --git a/services/surfaceflinger/RegionSamplingThread.cpp b/services/surfaceflinger/RegionSamplingThread.cpp
index 011fd9e..21d3396 100644
--- a/services/surfaceflinger/RegionSamplingThread.cpp
+++ b/services/surfaceflinger/RegionSamplingThread.cpp
@@ -353,22 +353,13 @@
sampledBounds.getSize(), ui::Dataspace::V0_SRGB, displayWeak,
RenderArea::Options::CAPTURE_SECURE_LAYERS);
- FenceResult fenceResult;
- if (FlagManager::getInstance().single_hop_screenshot() &&
- mFlinger.mRenderEngine->isThreaded()) {
- std::vector<sp<LayerFE>> layerFEs;
- auto displayState = mFlinger.getSnapshotsFromMainThread(renderAreaBuilder,
- getLayerSnapshotsFn, layerFEs);
- fenceResult = mFlinger.captureScreenshot(renderAreaBuilder, buffer, kRegionSampling,
- kGrayscale, kIsProtected, kAttachGainmap, nullptr,
- displayState, layerFEs)
- .get();
- } else {
- fenceResult = mFlinger.captureScreenshotLegacy(renderAreaBuilder, getLayerSnapshotsFn,
- buffer, kRegionSampling, kGrayscale,
- kIsProtected, kAttachGainmap, nullptr)
- .get();
- }
+ std::vector<std::pair<Layer*, sp<LayerFE>>> layers;
+ auto displayState =
+ mFlinger.getSnapshotsFromMainThread(renderAreaBuilder, getLayerSnapshotsFn, layers);
+ FenceResult fenceResult =
+ mFlinger.captureScreenshot(renderAreaBuilder, buffer, kRegionSampling, kGrayscale,
+ kIsProtected, kAttachGainmap, nullptr, displayState, layers)
+ .get();
if (fenceResult.ok()) {
fenceResult.value()->waitForever(LOG_TAG);
}
diff --git a/services/surfaceflinger/Scheduler/EventThread.cpp b/services/surfaceflinger/Scheduler/EventThread.cpp
index e385f18..fff4284 100644
--- a/services/surfaceflinger/Scheduler/EventThread.cpp
+++ b/services/surfaceflinger/Scheduler/EventThread.cpp
@@ -44,10 +44,8 @@
#include <common/FlagManager.h>
#include <scheduler/VsyncConfig.h>
-#include "DisplayHardware/DisplayMode.h"
#include "FrameTimeline.h"
#include "VSyncDispatch.h"
-#include "VSyncTracker.h"
#include "EventThread.h"
@@ -420,6 +418,16 @@
mCondition.notify_all();
}
+void EventThread::omitVsyncDispatching(bool omitted) {
+ std::lock_guard<std::mutex> lock(mMutex);
+ if (!mVSyncState || mVSyncState->omitted == omitted) {
+ return;
+ }
+
+ mVSyncState->omitted = omitted;
+ mCondition.notify_all();
+}
+
void EventThread::onVsync(nsecs_t vsyncTime, nsecs_t wakeupTime, nsecs_t readyTime) {
std::lock_guard<std::mutex> lock(mMutex);
mLastVsyncCallbackTime = TimePoint::fromNs(vsyncTime);
@@ -472,6 +480,14 @@
mCondition.notify_all();
}
+// Merge lists of buffer stuffed Uids
+void EventThread::addBufferStuffedUids(BufferStuffingMap bufferStuffedUids) {
+ std::lock_guard<std::mutex> lock(mMutex);
+ for (auto& [uid, count] : bufferStuffedUids) {
+ mBufferStuffedUids.emplace_or_replace(uid, count);
+ }
+}
+
void EventThread::threadMain(std::unique_lock<std::mutex>& lock) {
DisplayEventConsumers consumers;
@@ -521,7 +537,17 @@
}
if (mVSyncState && vsyncRequested) {
- mState = mVSyncState->synthetic ? State::SyntheticVSync : State::VSync;
+ const bool vsyncOmitted =
+ FlagManager::getInstance().no_vsyncs_on_screen_off() && mVSyncState->omitted;
+ if (vsyncOmitted) {
+ mState = State::Idle;
+ SFTRACE_INT("VsyncPendingScreenOn", 1);
+ } else {
+ mState = mVSyncState->synthetic ? State::SyntheticVSync : State::VSync;
+ if (FlagManager::getInstance().no_vsyncs_on_screen_off()) {
+ SFTRACE_INT("VsyncPendingScreenOn", 0);
+ }
+ }
} else {
ALOGW_IF(!mVSyncState, "Ignoring VSYNC request while display is disconnected");
mState = State::Idle;
@@ -701,6 +727,10 @@
void EventThread::dispatchEvent(const DisplayEventReceiver::Event& event,
const DisplayEventConsumers& consumers) {
+ // List of Uids that have been sent vsync data with queued buffer count.
+ // Used to keep track of which Uids can be removed from the map of
+ // buffer stuffed clients.
+ ftl::SmallVector<uid_t, 10> uidsPostedQueuedBuffers;
for (const auto& consumer : consumers) {
DisplayEventReceiver::Event copy = event;
if (event.header.type == DisplayEventReceiver::DISPLAY_EVENT_VSYNC) {
@@ -710,6 +740,13 @@
event.vsync.vsyncData.preferredExpectedPresentationTime(),
event.vsync.vsyncData.preferredDeadlineTimestamp());
}
+ auto it = mBufferStuffedUids.find(consumer->mOwnerUid);
+ if (it != mBufferStuffedUids.end()) {
+ copy.vsync.vsyncData.numberQueuedBuffers = it->second;
+ uidsPostedQueuedBuffers.emplace_back(consumer->mOwnerUid);
+ } else {
+ copy.vsync.vsyncData.numberQueuedBuffers = 0;
+ }
switch (consumer->postEvent(copy)) {
case NO_ERROR:
break;
@@ -725,6 +762,12 @@
removeDisplayEventConnectionLocked(consumer);
}
}
+ // The clients that have already received the queued buffer count
+ // can be removed from the buffer stuffed Uid list to avoid
+ // being sent duplicate messages.
+ for (auto uid : uidsPostedQueuedBuffers) {
+ mBufferStuffedUids.erase(uid);
+ }
if (event.header.type == DisplayEventReceiver::DISPLAY_EVENT_VSYNC &&
FlagManager::getInstance().vrr_config()) {
mLastCommittedVsyncTime =
diff --git a/services/surfaceflinger/Scheduler/EventThread.h b/services/surfaceflinger/Scheduler/EventThread.h
index c3c7eb0..95632c7 100644
--- a/services/surfaceflinger/Scheduler/EventThread.h
+++ b/services/surfaceflinger/Scheduler/EventThread.h
@@ -32,7 +32,6 @@
#include <thread>
#include <vector>
-#include "DisplayHardware/DisplayMode.h"
#include "TracedOrdinal.h"
#include "VSyncDispatch.h"
#include "VsyncSchedule.h"
@@ -55,6 +54,7 @@
// ---------------------------------------------------------------------------
using FrameRateOverride = DisplayEventReceiver::Event::FrameRateOverride;
+using BufferStuffingMap = ftl::SmallMap<uid_t, uint32_t, 10>;
enum class VSyncRequest {
None = -2,
@@ -106,6 +106,8 @@
// Feed clients with fake VSYNC, e.g. while the display is off.
virtual void enableSyntheticVsync(bool) = 0;
+ virtual void omitVsyncDispatching(bool) = 0;
+
virtual void onHotplugReceived(PhysicalDisplayId displayId, bool connected) = 0;
virtual void onHotplugConnectionError(int32_t connectionError) = 0;
@@ -134,6 +136,10 @@
virtual void onHdcpLevelsChanged(PhysicalDisplayId displayId, int32_t connectedLevel,
int32_t maxLevel) = 0;
+
+ // An elevated number of queued buffers in the server is detected. This propagates a
+ // flag to Choreographer indicating that buffer stuffing recovery should begin.
+ virtual void addBufferStuffedUids(BufferStuffingMap bufferStuffedUids);
};
struct IEventThreadCallback {
@@ -165,6 +171,8 @@
void enableSyntheticVsync(bool) override;
+ void omitVsyncDispatching(bool) override;
+
void onHotplugReceived(PhysicalDisplayId displayId, bool connected) override;
void onHotplugConnectionError(int32_t connectionError) override;
@@ -184,6 +192,8 @@
void onHdcpLevelsChanged(PhysicalDisplayId displayId, int32_t connectedLevel,
int32_t maxLevel) override;
+ void addBufferStuffedUids(BufferStuffingMap bufferStuffedUids) override;
+
private:
friend EventThreadTest;
@@ -224,6 +234,10 @@
scheduler::VSyncCallbackRegistration mVsyncRegistration GUARDED_BY(mMutex);
frametimeline::TokenManager* const mTokenManager;
+ // All consumers that need to recover from buffer stuffing and the number
+ // of their queued buffers.
+ BufferStuffingMap mBufferStuffedUids GUARDED_BY(mMutex);
+
IEventThreadCallback& mCallback;
std::thread mThread;
@@ -240,6 +254,9 @@
// True if VSYNC should be faked, e.g. when display is off.
bool synthetic = false;
+
+ // True if VSYNC should not be delivered to apps. Used when the display is off.
+ bool omitted = false;
};
// TODO(b/74619554): Create per-display threads waiting on respective VSYNC signals,
diff --git a/services/surfaceflinger/Scheduler/LayerHistory.cpp b/services/surfaceflinger/Scheduler/LayerHistory.cpp
index 64b85c0..171342d 100644
--- a/services/surfaceflinger/Scheduler/LayerHistory.cpp
+++ b/services/surfaceflinger/Scheduler/LayerHistory.cpp
@@ -308,6 +308,15 @@
const auto setFrameRateVoteType =
info->isVisible() ? voteType : LayerVoteType::NoVote;
+ const bool hasSetFrameRateOpinion =
+ frameRate.isValuelessType() || frameRate.vote.rate.isValid();
+ const bool hasCategoryOpinion =
+ frameRate.category != FrameRateCategory::NoPreference &&
+ frameRate.category != FrameRateCategory::Default;
+ const bool hasFrameRateOpinionAboveGameDefault =
+ hasSetFrameRateOpinion || hasCategoryOpinion;
+ const bool hasFrameRateOpinionArr = frameRate.isValid() && !frameRate.isNoVote();
+
if (gameModeFrameRateOverride.isValid()) {
info->setLayerVote({gameFrameRateOverrideVoteType, gameModeFrameRateOverride});
SFTRACE_FORMAT_INSTANT("GameModeFrameRateOverride");
@@ -315,7 +324,8 @@
trace(*info, gameFrameRateOverrideVoteType,
gameModeFrameRateOverride.getIntValue());
}
- } else if (frameRate.isValid() && frameRate.isVoteValidForMrr(isVrrDevice)) {
+ } else if (hasFrameRateOpinionAboveGameDefault &&
+ frameRate.isVoteValidForMrr(isVrrDevice)) {
info->setLayerVote({setFrameRateVoteType,
isValuelessVote ? 0_Hz : frameRate.vote.rate,
frameRate.vote.seamlessness, frameRate.category});
@@ -331,8 +341,18 @@
trace(*info, gameFrameRateOverrideVoteType,
gameDefaultFrameRateOverride.getIntValue());
}
+ } else if (hasFrameRateOpinionArr && frameRate.isVoteValidForMrr(isVrrDevice)) {
+ // This allows NoPreference votes on ARR devices after considering the
+ // gameDefaultFrameRateOverride (above).
+ info->setLayerVote({setFrameRateVoteType,
+ isValuelessVote ? 0_Hz : frameRate.vote.rate,
+ frameRate.vote.seamlessness, frameRate.category});
+ if (CC_UNLIKELY(mTraceEnabled)) {
+ trace(*info, gameFrameRateOverrideVoteType,
+ frameRate.vote.rate.getIntValue());
+ }
} else {
- if (frameRate.isValid() && !frameRate.isVoteValidForMrr(isVrrDevice)) {
+ if (hasFrameRateOpinionArr && !frameRate.isVoteValidForMrr(isVrrDevice)) {
SFTRACE_FORMAT_INSTANT("Reset layer to ignore explicit vote on MRR %s: %s "
"%s %s",
info->getName().c_str(),
diff --git a/services/surfaceflinger/Scheduler/RefreshRateSelector.cpp b/services/surfaceflinger/Scheduler/RefreshRateSelector.cpp
index eca8df2..668fa54 100644
--- a/services/surfaceflinger/Scheduler/RefreshRateSelector.cpp
+++ b/services/surfaceflinger/Scheduler/RefreshRateSelector.cpp
@@ -489,6 +489,20 @@
return mGetRankedFrameRatesCache->result;
}
+using LayerRequirementPtrs = std::vector<const RefreshRateSelector::LayerRequirement*>;
+using PerUidLayerRequirements = std::unordered_map<uid_t, LayerRequirementPtrs>;
+
+PerUidLayerRequirements groupLayersByUid(
+ const std::vector<RefreshRateSelector::LayerRequirement>& layers) {
+ PerUidLayerRequirements layersByUid;
+ for (const auto& layer : layers) {
+ const auto it = layersByUid.emplace(layer.ownerUid, LayerRequirementPtrs()).first;
+ auto& layersWithSameUid = it->second;
+ layersWithSameUid.push_back(&layer);
+ }
+ return layersByUid;
+}
+
auto RefreshRateSelector::getRankedFrameRatesLocked(const std::vector<LayerRequirement>& layers,
GlobalSignals signals, Fps pacesetterFps) const
-> RankedFrameRates {
@@ -525,6 +539,43 @@
return {ranking, GlobalSignals{.powerOnImminent = true}};
}
+ // A method for UI Toolkit to send the touch signal via "HighHint" category vote,
+ // which will touch boost when there are no ExplicitDefault layer votes on the app.
+ // At most one app can have the "HighHint" touch boost vote at a time.
+ // This accounts for cases such as games that use `setFrameRate`
+ // with Default compatibility to limit the frame rate and disabling touch boost.
+ bool isAppTouchBoost = false;
+ const auto layersByUid = groupLayersByUid(layers);
+ for (const auto& [uid, layersWithSameUid] : layersByUid) {
+ bool hasHighHint = false;
+ bool hasExplicitDefault = false;
+ for (const auto& layer : layersWithSameUid) {
+ switch (layer->vote) {
+ case LayerVoteType::ExplicitDefault:
+ hasExplicitDefault = true;
+ break;
+ case LayerVoteType::ExplicitCategory:
+ if (layer->frameRateCategory == FrameRateCategory::HighHint) {
+ hasHighHint = true;
+ }
+ break;
+ default:
+ // No action
+ break;
+ }
+ if (hasHighHint && hasExplicitDefault) {
+ break;
+ }
+ }
+
+ if (hasHighHint && !hasExplicitDefault) {
+ // Focused app has touch signal (HighHint) and no frame rate ExplicitDefault votes
+ // (which prevents touch boost due to games use case).
+ isAppTouchBoost = true;
+ break;
+ }
+ }
+
int noVoteLayers = 0;
// Layers that prefer the same mode ("no-op").
int noPreferenceLayers = 0;
@@ -535,7 +586,6 @@
int explicitExact = 0;
int explicitGteLayers = 0;
int explicitCategoryVoteLayers = 0;
- int interactiveLayers = 0;
int seamedFocusedLayers = 0;
int categorySmoothSwitchOnlyLayers = 0;
@@ -563,11 +613,9 @@
explicitGteLayers++;
break;
case LayerVoteType::ExplicitCategory:
- if (layer.frameRateCategory == FrameRateCategory::HighHint) {
- // HighHint does not count as an explicit signal from an app. It may be
- // be a touch signal.
- interactiveLayers++;
- } else {
+ // HighHint does not count as an explicit signal from an app. It is a touch signal
+ // sent from UI Toolkit.
+ if (layer.frameRateCategory != FrameRateCategory::HighHint) {
explicitCategoryVoteLayers++;
}
if (layer.frameRateCategory == FrameRateCategory::NoPreference) {
@@ -722,17 +770,13 @@
const bool inPrimaryPhysicalRange =
policy->primaryRanges.physical.includes(modePtr->getPeakFps());
const bool inPrimaryRenderRange = policy->primaryRanges.render.includes(fps);
- if (!mIsVrrDevice.load() &&
- ((policy->primaryRangeIsSingleRate() && !inPrimaryPhysicalRange) ||
+ if (((policy->primaryRangeIsSingleRate() && !inPrimaryPhysicalRange) ||
!inPrimaryRenderRange) &&
!(layer.focused &&
(layer.vote == LayerVoteType::ExplicitDefault ||
layer.vote == LayerVoteType::ExplicitExact))) {
// Only focused layers with ExplicitDefault frame rate settings are allowed to score
// refresh rates outside the primary range.
- ALOGV("%s ignores %s (primaryRangeIsSingleRate). Current mode = %s",
- formatLayerInfo(layer, weight).c_str(), to_string(*modePtr).c_str(),
- to_string(activeMode).c_str());
continue;
}
@@ -856,8 +900,7 @@
to_string(descending.front().frameRateMode.fps).c_str());
return {descending, kNoSignals};
} else {
- ALOGV("%s (primaryRangeIsSingleRate)",
- to_string(ranking.front().frameRateMode.fps).c_str());
+ ALOGV("primaryRangeIsSingleRate");
SFTRACE_FORMAT_INSTANT("%s (primaryRangeIsSingleRate)",
to_string(ranking.front().frameRateMode.fps).c_str());
return {ranking, kNoSignals};
@@ -882,14 +925,11 @@
return explicitCategoryVoteLayers + noVoteLayers + explicitGteLayers != layers.size();
};
- // A method for UI Toolkit to send the touch signal via "HighHint" category vote,
- // which will touch boost when there are no ExplicitDefault layer votes. This is an
- // incomplete solution but accounts for cases such as games that use `setFrameRate` with default
+ // This accounts for cases such as games that use `setFrameRate` with Default
// compatibility to limit the frame rate, which should not have touch boost.
- const bool hasInteraction = signals.touch || interactiveLayers > 0;
-
- if (hasInteraction && explicitDefaultVoteLayers == 0 && isTouchBoostForExplicitExact() &&
- isTouchBoostForCategory()) {
+ const bool isLateGlobalTouchBoost = signals.touch && explicitDefaultVoteLayers == 0;
+ const bool isLateTouchBoost = isLateGlobalTouchBoost || isAppTouchBoost;
+ if (isLateTouchBoost && isTouchBoostForExplicitExact() && isTouchBoostForCategory()) {
const auto touchRefreshRates = rankFrameRates(anchorGroup, RefreshRateOrder::Descending);
using fps_approx_ops::operator<;
@@ -917,42 +957,6 @@
return {ranking, kNoSignals};
}
-using LayerRequirementPtrs = std::vector<const RefreshRateSelector::LayerRequirement*>;
-using PerUidLayerRequirements = std::unordered_map<uid_t, LayerRequirementPtrs>;
-
-PerUidLayerRequirements groupLayersByUid(
- const std::vector<RefreshRateSelector::LayerRequirement>& layers) {
- PerUidLayerRequirements layersByUid;
- for (const auto& layer : layers) {
- const auto it = layersByUid.emplace(layer.ownerUid, LayerRequirementPtrs()).first;
- auto& layersWithSameUid = it->second;
- layersWithSameUid.push_back(&layer);
- }
-
- // Remove uids that can't have a frame rate override
- for (auto it = layersByUid.begin(); it != layersByUid.end();) {
- const auto& layersWithSameUid = it->second;
- bool skipUid = false;
- for (const auto& layer : layersWithSameUid) {
- using LayerVoteType = RefreshRateSelector::LayerVoteType;
-
- if (layer->vote == LayerVoteType::Max || layer->vote == LayerVoteType::Heuristic) {
- ALOGV("%s: %s skips uid=%d due to the vote", __func__,
- formatLayerInfo(*layer, layer->weight).c_str(), layer->ownerUid);
- skipUid = true;
- break;
- }
- }
- if (skipUid) {
- it = layersByUid.erase(it);
- } else {
- ++it;
- }
- }
-
- return layersByUid;
-}
-
auto RefreshRateSelector::getFrameRateOverrides(const std::vector<LayerRequirement>& layers,
Fps displayRefreshRate,
GlobalSignals globalSignals) const
@@ -997,6 +1001,7 @@
bool hasExplicitExactOrMultiple = false;
bool hasExplicitDefault = false;
bool hasHighHint = false;
+ bool hasSkipOverrideLayer = false;
for (const auto& layer : layersWithSameUid) {
switch (layer->vote) {
case LayerVoteType::ExplicitExactOrMultiple:
@@ -1010,25 +1015,33 @@
hasHighHint = true;
}
break;
+ case LayerVoteType::Max:
+ case LayerVoteType::Heuristic:
+ hasSkipOverrideLayer = true;
+ break;
default:
// No action
break;
}
- if (hasExplicitExactOrMultiple && hasExplicitDefault && hasHighHint) {
+ if (hasExplicitExactOrMultiple && hasExplicitDefault && hasHighHint &&
+ hasSkipOverrideLayer) {
break;
}
}
+ if (hasSkipOverrideLayer) {
+ ALOGV("%s: Skipping due to vote(s): uid=%d", __func__, uid);
+ continue;
+ }
+
// Layers with ExplicitExactOrMultiple expect touch boost
if (globalSignals.touch && hasExplicitExactOrMultiple) {
- ALOGV("%s: Skipping for touch (input signal): uid=%d", __func__, uid);
continue;
}
// Mirrors getRankedFrameRates. If there is no ExplicitDefault, expect touch boost and
// skip frame rate override.
if (hasHighHint && !hasExplicitDefault) {
- ALOGV("%s: Skipping for touch (HighHint): uid=%d", __func__, uid);
continue;
}
@@ -1052,9 +1065,6 @@
constexpr bool isSeamlessSwitch = true;
const auto layerScore = calculateLayerScoreLocked(*layer, fps, isSeamlessSwitch);
score += layer->weight * layerScore;
- ALOGV("%s: %s gives %s fps score of %.4f", __func__,
- formatLayerInfo(*layer, layer->weight).c_str(), to_string(fps).c_str(),
- layerScore);
}
}
@@ -1309,8 +1319,6 @@
LOG_ALWAYS_FATAL_IF(!activeModeOpt);
mActiveModeOpt = FrameRateMode{activeModeOpt->get()->getPeakFps(),
ftl::as_non_null(activeModeOpt->get())};
- mIsVrrDevice = FlagManager::getInstance().vrr_config() &&
- activeModeOpt->get()->getVrrConfig().has_value();
const auto sortedModes = sortByRefreshRate(mDisplayModes);
mMinRefreshRateModeIt = sortedModes.front();
@@ -1549,6 +1557,19 @@
return distance1 < distance2 ? *lowerBound : *std::prev(lowerBound);
}
+std::vector<float> RefreshRateSelector::getSupportedFrameRates() const {
+ std::scoped_lock lock(mLock);
+ // TODO(b/356986687) Remove the limit once we have the anchor list implementation.
+ const size_t frameRatesSize = std::min<size_t>(11, mPrimaryFrameRates.size());
+ std::vector<float> supportedFrameRates;
+ supportedFrameRates.reserve(frameRatesSize);
+ std::transform(mPrimaryFrameRates.rbegin(),
+ mPrimaryFrameRates.rbegin() + static_cast<int>(frameRatesSize),
+ std::back_inserter(supportedFrameRates),
+ [](FrameRateMode mode) { return mode.fps.getValue(); });
+ return supportedFrameRates;
+}
+
auto RefreshRateSelector::getIdleTimerAction() const -> KernelIdleTimerAction {
std::lock_guard lock(mLock);
@@ -1652,9 +1673,9 @@
FpsRange RefreshRateSelector::getFrameRateCategoryRange(FrameRateCategory category) {
switch (category) {
case FrameRateCategory::High:
- return FpsRange{90_Hz, 120_Hz};
+ return FpsRange{kFrameRateCategoryRateHigh, 120_Hz};
case FrameRateCategory::Normal:
- return FpsRange{60_Hz, 120_Hz};
+ return FpsRange{kFrameRateCategoryRateNormal, 120_Hz};
case FrameRateCategory::Low:
return FpsRange{48_Hz, 120_Hz};
case FrameRateCategory::HighHint:
diff --git a/services/surfaceflinger/Scheduler/RefreshRateSelector.h b/services/surfaceflinger/Scheduler/RefreshRateSelector.h
index a398c01..508f9d7 100644
--- a/services/surfaceflinger/Scheduler/RefreshRateSelector.h
+++ b/services/surfaceflinger/Scheduler/RefreshRateSelector.h
@@ -52,6 +52,12 @@
// The lowest Render Frame Rate that will ever be selected
static constexpr Fps kMinSupportedFrameRate = 20_Hz;
+ // Start range for FrameRateCategory Normal and High.
+ static constexpr Fps kFrameRateCategoryRateHigh = 90_Hz;
+ static constexpr Fps kFrameRateCategoryRateNormal = 60_Hz;
+ static constexpr std::pair<Fps, Fps> kFrameRateCategoryRates = {kFrameRateCategoryRateNormal,
+ kFrameRateCategoryRateHigh};
+
class Policy {
static constexpr int kAllowGroupSwitchingDefault = false;
@@ -433,6 +439,10 @@
bool isVrrDevice() const;
+ std::pair<Fps, Fps> getFrameRateCategoryRates() const { return kFrameRateCategoryRates; }
+
+ std::vector<float> getSupportedFrameRates() const EXCLUDES(mLock);
+
private:
friend struct TestableRefreshRateSelector;
diff --git a/services/surfaceflinger/Scheduler/Scheduler.cpp b/services/surfaceflinger/Scheduler/Scheduler.cpp
index b83ff19..2875650 100644
--- a/services/surfaceflinger/Scheduler/Scheduler.cpp
+++ b/services/surfaceflinger/Scheduler/Scheduler.cpp
@@ -203,12 +203,16 @@
void Scheduler::onFrameSignal(ICompositor& compositor, VsyncId vsyncId,
TimePoint expectedVsyncTime) {
+ const auto debugPresentDelay = mDebugPresentDelay.load();
+ mDebugPresentDelay.store(std::nullopt);
+
const FrameTargeter::BeginFrameArgs beginFrameArgs =
{.frameBeginTime = SchedulerClock::now(),
.vsyncId = vsyncId,
.expectedVsyncTime = expectedVsyncTime,
.sfWorkDuration = mVsyncModulator->getVsyncConfig().sfWorkDuration,
- .hwcMinWorkDuration = mVsyncConfiguration->getCurrentConfigs().hwcMinWorkDuration};
+ .hwcMinWorkDuration = mVsyncConfiguration->getCurrentConfigs().hwcMinWorkDuration,
+ .debugPresentTimeDelay = debugPresentDelay};
ftl::NonNull<const Display*> pacesetterPtr = pacesetterPtrLocked();
pacesetterPtr->targeterPtr->beginFrame(beginFrameArgs, *pacesetterPtr->schedulePtr);
@@ -405,6 +409,14 @@
eventThreadFor(Cycle::Render).enableSyntheticVsync(enable);
}
+void Scheduler::omitVsyncDispatching(bool omitted) {
+ eventThreadFor(Cycle::Render).omitVsyncDispatching(omitted);
+ // Note: If we don't couple Cycle::LastComposite event thread, there is a black screen
+ // after boot. This is most likely sysui or system_server dependency on sf instance
+ // Choreographer
+ eventThreadFor(Cycle::LastComposite).omitVsyncDispatching(omitted);
+}
+
void Scheduler::onFrameRateOverridesChanged() {
const auto [pacesetterId, supportsFrameRateOverrideByContent] = [this] {
std::scoped_lock lock(mDisplayLock);
@@ -428,7 +440,8 @@
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wunused-value" // b/369277774
-bool Scheduler::onDisplayModeChanged(PhysicalDisplayId displayId, const FrameRateMode& mode) {
+bool Scheduler::onDisplayModeChanged(PhysicalDisplayId displayId, const FrameRateMode& mode,
+ bool clearContentRequirements) {
const bool isPacesetter =
FTL_FAKE_GUARD(kMainThreadContext,
(std::scoped_lock(mDisplayLock), displayId == mPacesetterDisplayId));
@@ -437,9 +450,11 @@
std::lock_guard<std::mutex> lock(mPolicyLock);
mPolicy.emittedModeOpt = mode;
- // Invalidate content based refresh rate selection so it could be calculated
- // again for the new refresh rate.
- mPolicy.contentRequirements.clear();
+ if (clearContentRequirements) {
+ // Invalidate content based refresh rate selection so it could be calculated
+ // again for the new refresh rate.
+ mPolicy.contentRequirements.clear();
+ }
}
if (hasEventThreads()) {
@@ -940,6 +955,11 @@
return mFrameRateOverrideMappings.updateFrameRateOverridesByContent(frameRateOverrides);
}
+void Scheduler::addBufferStuffedUids(BufferStuffingMap bufferStuffedUids) {
+ if (!mRenderEventThread) return;
+ mRenderEventThread->addBufferStuffedUids(std::move(bufferStuffedUids));
+}
+
void Scheduler::promotePacesetterDisplay(PhysicalDisplayId pacesetterId, PromotionParams params) {
std::shared_ptr<VsyncSchedule> pacesetterVsyncSchedule;
{
diff --git a/services/surfaceflinger/Scheduler/Scheduler.h b/services/surfaceflinger/Scheduler/Scheduler.h
index c88b563..61c68a9 100644
--- a/services/surfaceflinger/Scheduler/Scheduler.h
+++ b/services/surfaceflinger/Scheduler/Scheduler.h
@@ -42,7 +42,6 @@
#include <ui/DisplayId.h>
#include <ui/DisplayMap.h>
-#include "Display/DisplayModeRequest.h"
#include "EventThread.h"
#include "FrameRateOverrideMappings.h"
#include "ISchedulerCallback.h"
@@ -151,9 +150,11 @@
void dispatchHotplugError(int32_t errorCode);
// Returns true if the PhysicalDisplayId is the pacesetter.
- bool onDisplayModeChanged(PhysicalDisplayId, const FrameRateMode&) EXCLUDES(mPolicyLock);
+ bool onDisplayModeChanged(PhysicalDisplayId, const FrameRateMode&,
+ bool clearContentRequirements) EXCLUDES(mPolicyLock);
void enableSyntheticVsync(bool = true) REQUIRES(kMainThreadContext);
+ void omitVsyncDispatching(bool) REQUIRES(kMainThreadContext);
void onHdcpLevelsChanged(Cycle, PhysicalDisplayId, int32_t, int32_t);
@@ -332,6 +333,12 @@
mPacesetterFrameDurationFractionToSkip = frameDurationFraction;
}
+ // Propagates a flag to the EventThread indicating that buffer stuffing
+ // recovery should begin.
+ void addBufferStuffedUids(BufferStuffingMap bufferStuffedUids);
+
+ void setDebugPresentDelay(TimePoint delay) { mDebugPresentDelay = delay; }
+
private:
friend class TestableScheduler;
@@ -597,6 +604,8 @@
FrameRateOverrideMappings mFrameRateOverrideMappings;
SmallAreaDetectionAllowMappings mSmallAreaDetectionAllowMappings;
+
+ std::atomic<std::optional<TimePoint>> mDebugPresentDelay;
};
} // namespace scheduler
diff --git a/services/surfaceflinger/Scheduler/VSyncPredictor.cpp b/services/surfaceflinger/Scheduler/VSyncPredictor.cpp
index 6e36f02..ff360b7 100644
--- a/services/surfaceflinger/Scheduler/VSyncPredictor.cpp
+++ b/services/surfaceflinger/Scheduler/VSyncPredictor.cpp
@@ -458,7 +458,8 @@
Duration VSyncPredictor::ensureMinFrameDurationIsKept(TimePoint expectedPresentTime,
TimePoint lastConfirmedPresentTime) {
- SFTRACE_CALL();
+ SFTRACE_FORMAT("%s mNumVsyncsForFrame=%d mPastExpectedPresentTimes.size()=%zu", __func__,
+ mNumVsyncsForFrame, mPastExpectedPresentTimes.size());
if (mNumVsyncsForFrame <= 1) {
return 0ns;
@@ -470,12 +471,8 @@
auto prev = lastConfirmedPresentTime.ns();
for (auto& current : mPastExpectedPresentTimes) {
- if (CC_UNLIKELY(mTraceOn)) {
- SFTRACE_FORMAT_INSTANT("current %.2f past last signaled fence",
- static_cast<float>(current.ns() -
- lastConfirmedPresentTime.ns()) /
- 1e6f);
- }
+ SFTRACE_FORMAT_INSTANT("current %.2f past last signaled fence",
+ static_cast<float>(current.ns() - prev) / 1e6f);
const auto minPeriodViolation = current.ns() - prev + threshold < minFramePeriod.ns();
if (minPeriodViolation) {
@@ -522,11 +519,9 @@
const auto front = mPastExpectedPresentTimes.front().ns();
const bool frontIsBeforeConfirmed = front < lastConfirmedPresentTime.ns() + threshold;
if (frontIsBeforeConfirmed) {
- if (CC_UNLIKELY(mTraceOn)) {
- SFTRACE_FORMAT_INSTANT("Discarding old vsync - %.2f before last signaled fence",
- static_cast<float>(lastConfirmedPresentTime.ns() - front) /
- 1e6f);
- }
+ SFTRACE_FORMAT_INSTANT("Discarding old vsync - %.2f before last signaled fence",
+ static_cast<float>(lastConfirmedPresentTime.ns() - front) /
+ 1e6f);
mPastExpectedPresentTimes.pop_front();
} else {
break;
diff --git a/services/surfaceflinger/Scheduler/include/scheduler/FrameTargeter.h b/services/surfaceflinger/Scheduler/include/scheduler/FrameTargeter.h
index 2185bb0..813d4de 100644
--- a/services/surfaceflinger/Scheduler/include/scheduler/FrameTargeter.h
+++ b/services/surfaceflinger/Scheduler/include/scheduler/FrameTargeter.h
@@ -53,6 +53,8 @@
TimePoint expectedPresentTime() const { return mExpectedPresentTime; }
+ std::optional<TimePoint> debugPresentDelay() const { return mDebugPresentTimeDelay; }
+
std::optional<TimePoint> earliestPresentTime() const { return mEarliestPresentTime; }
// Equivalent to `expectedSignaledPresentFence` unless running N VSYNCs ahead.
@@ -84,6 +86,7 @@
TimePoint mFrameBeginTime;
TimePoint mExpectedPresentTime;
std::optional<TimePoint> mEarliestPresentTime;
+ std::optional<TimePoint> mDebugPresentTimeDelay;
TracedOrdinal<bool> mFramePending;
TracedOrdinal<bool> mFrameMissed;
@@ -135,6 +138,7 @@
TimePoint expectedVsyncTime;
Duration sfWorkDuration;
Duration hwcMinWorkDuration;
+ std::optional<TimePoint> debugPresentTimeDelay; // used to introduce jank for testing
};
void beginFrame(const BeginFrameArgs&, const IVsyncSource&);
diff --git a/services/surfaceflinger/Scheduler/src/FrameTargeter.cpp b/services/surfaceflinger/Scheduler/src/FrameTargeter.cpp
index 3ee1e54..5019949 100644
--- a/services/surfaceflinger/Scheduler/src/FrameTargeter.cpp
+++ b/services/surfaceflinger/Scheduler/src/FrameTargeter.cpp
@@ -31,6 +31,7 @@
std::pair<bool /* wouldBackpressure */, FrameTarget::PresentFence>
FrameTarget::expectedSignaledPresentFence(Period vsyncPeriod, Period minFramePeriod) const {
+ SFTRACE_CALL();
if (!FlagManager::getInstance().allow_n_vsyncs_in_targeter()) {
const size_t i = static_cast<size_t>(targetsVsyncsAhead<2>(minFramePeriod));
return {true, mPresentFencesLegacy[i]};
@@ -40,17 +41,28 @@
auto expectedPresentTime = mExpectedPresentTime;
for (size_t i = mPresentFences.size(); i != 0; --i) {
const auto& fence = mPresentFences[i - 1];
+ SFTRACE_FORMAT_INSTANT("fence at idx: %zu expectedPresentTime in %.2f", i - 1,
+ ticks<std::milli, float>(fence.expectedPresentTime -
+ TimePoint::now()));
if (fence.expectedPresentTime + minFramePeriod < expectedPresentTime - vsyncPeriod / 2) {
+ SFTRACE_FORMAT_INSTANT("would not backpressure");
wouldBackpressure = false;
}
if (fence.expectedPresentTime <= mFrameBeginTime) {
+ SFTRACE_FORMAT_INSTANT("fence at idx: %zu is %.2f before frame begin "
+ "(wouldBackpressure=%s)",
+ i - 1,
+ ticks<std::milli, float>(mFrameBeginTime -
+ fence.expectedPresentTime),
+ wouldBackpressure ? "true" : "false");
return {wouldBackpressure, fence};
}
expectedPresentTime = fence.expectedPresentTime;
}
+ SFTRACE_FORMAT_INSTANT("No fence found");
return {wouldBackpressure, PresentFence{}};
}
@@ -86,6 +98,7 @@
IsFencePendingFuncPtr isFencePendingFuncPtr) {
mVsyncId = args.vsyncId;
mFrameBeginTime = args.frameBeginTime;
+ mDebugPresentTimeDelay = args.debugPresentTimeDelay;
// The `expectedVsyncTime`, which was predicted when this frame was scheduled, is normally in
// the future relative to `frameBeginTime`, but may not be for delayed frames. Adjust
@@ -153,6 +166,12 @@
if (pastPresentTime < 0) return false;
mLastSignaledFrameTime = {.signalTime = TimePoint::fromNs(pastPresentTime),
.expectedPresentTime = fence.expectedPresentTime};
+ SFTRACE_FORMAT_INSTANT("LastSignaledFrameTime expectedPresentTime %.2f ago, signalTime "
+ "%.2f ago",
+ ticks<std::milli, float>(mLastSignaledFrameTime.expectedPresentTime -
+ TimePoint::now()),
+ ticks<std::milli, float>(mLastSignaledFrameTime.signalTime -
+ TimePoint::now()));
const nsecs_t frameMissedSlop = vsyncPeriod.ns() / 2;
return lastScheduledPresentTime.ns() < pastPresentTime - frameMissedSlop;
}();
diff --git a/services/surfaceflinger/SurfaceFlinger.cpp b/services/surfaceflinger/SurfaceFlinger.cpp
index d35a76a..97c8623 100644
--- a/services/surfaceflinger/SurfaceFlinger.cpp
+++ b/services/surfaceflinger/SurfaceFlinger.cpp
@@ -64,11 +64,13 @@
#include <ftl/concat.h>
#include <ftl/fake_guard.h>
#include <ftl/future.h>
+#include <ftl/small_map.h>
#include <ftl/unit.h>
#include <gui/AidlUtil.h>
#include <gui/BufferQueue.h>
#include <gui/DebugEGLImageTracker.h>
#include <gui/IProducerListener.h>
+#include <gui/JankInfo.h>
#include <gui/LayerMetadata.h>
#include <gui/LayerState.h>
#include <gui/Surface.h>
@@ -92,6 +94,7 @@
#include <ui/DisplayStatInfo.h>
#include <ui/DisplayState.h>
#include <ui/DynamicDisplayInfo.h>
+#include <ui/FrameRateCategoryRate.h>
#include <ui/GraphicBufferAllocator.h>
#include <ui/HdrRenderTypeUtils.h>
#include <ui/LayerStack.h>
@@ -123,6 +126,7 @@
#include <gui/SchedulingPolicy.h>
#include <gui/SyncScreenCaptureListener.h>
#include <ui/DisplayIdentification.h>
+#include "ActivePictureUpdater.h"
#include "BackgroundExecutor.h"
#include "Client.h"
#include "ClientCache.h"
@@ -132,7 +136,6 @@
#include "DisplayHardware/FramebufferSurface.h"
#include "DisplayHardware/HWComposer.h"
#include "DisplayHardware/Hal.h"
-#include "DisplayHardware/PowerAdvisor.h"
#include "DisplayHardware/VirtualDisplaySurface.h"
#include "DisplayRenderArea.h"
#include "Effects/Daltonizer.h"
@@ -152,6 +155,7 @@
#include "LayerVector.h"
#include "MutexUtils.h"
#include "NativeWindowSurface.h"
+#include "PowerAdvisor/PowerAdvisor.h"
#include "RegionSamplingThread.h"
#include "RenderAreaBuilder.h"
#include "Scheduler/EventThread.h"
@@ -369,6 +373,7 @@
const String16 sRotateSurfaceFlinger("android.permission.ROTATE_SURFACE_FLINGER");
const String16 sReadFramebuffer("android.permission.READ_FRAME_BUFFER");
const String16 sControlDisplayBrightness("android.permission.CONTROL_DISPLAY_BRIGHTNESS");
+const String16 sObservePictureProfiles("android.permission.OBSERVE_PICTURE_PROFILES");
const String16 sDump("android.permission.DUMP");
const String16 sCaptureBlackoutContent("android.permission.CAPTURE_BLACKOUT_CONTENT");
const String16 sInternalSystemWindow("android.permission.INTERNAL_SYSTEM_WINDOW");
@@ -425,7 +430,11 @@
mEmulatedDisplayDensity(getDensityFromProperty("qemu.sf.lcd_density", false)),
mInternalDisplayDensity(
getDensityFromProperty("ro.sf.lcd_density", !mEmulatedDisplayDensity)),
- mPowerAdvisor(std::make_unique<Hwc2::impl::PowerAdvisor>(*this)),
+ mPowerAdvisor(std::make_unique<
+ adpf::impl::PowerAdvisor>([this] { disableExpensiveRendering(); },
+ std::chrono::milliseconds(
+ sysprop::display_update_imminent_timeout_ms(
+ 80)))),
mWindowInfosListenerInvoker(sp<WindowInfosListenerInvoker>::make()),
mSkipPowerOnForQuiescent(base::GetBoolProperty("ro.boot.quiescent"s, false)) {
ALOGI("Using HWComposer service: %s", mHwcServiceName.c_str());
@@ -788,6 +797,12 @@
}));
}
+bool shouldUseGraphiteIfCompiledAndSupported() {
+ return FlagManager::getInstance().graphite_renderengine() ||
+ (FlagManager::getInstance().graphite_renderengine_preview_rollout() &&
+ base::GetBoolProperty(PROPERTY_DEBUG_RENDERENGINE_GRAPHITE_PREVIEW_OPTIN, false));
+}
+
void chooseRenderEngineType(renderengine::RenderEngineCreationArgs::Builder& builder) {
char prop[PROPERTY_VALUE_MAX];
property_get(PROPERTY_DEBUG_RENDERENGINE_BACKEND, prop, "");
@@ -816,14 +831,13 @@
// is used by layertracegenerator (which also needs SurfaceFlinger.cpp). :)
#if COM_ANDROID_GRAPHICS_SURFACEFLINGER_FLAGS_GRAPHITE_RENDERENGINE || \
COM_ANDROID_GRAPHICS_SURFACEFLINGER_FLAGS_FORCE_COMPILE_GRAPHITE_RENDERENGINE
- const bool useGraphite = FlagManager::getInstance().graphite_renderengine() &&
+ const bool useGraphite = shouldUseGraphiteIfCompiledAndSupported() &&
renderengine::RenderEngine::canSupport(kVulkan);
#else
const bool useGraphite = false;
- if (FlagManager::getInstance().graphite_renderengine()) {
- ALOGE("RenderEngine's Graphite Skia backend was requested with the "
- "debug.renderengine.graphite system property, but it is not compiled in this "
- "build! Falling back to Ganesh backend selection logic.");
+ if (shouldUseGraphiteIfCompiledAndSupported()) {
+ ALOGE("RenderEngine's Graphite Skia backend was requested, but it is not compiled in "
+ "this build! Falling back to Ganesh backend selection logic.");
}
#endif
const bool useVulkan = useGraphite ||
@@ -1009,7 +1023,8 @@
config.cacheUltraHDR =
base::GetBoolProperty("ro.surface_flinger.prime_shader_cache.ultrahdr"s, false);
config.cacheEdgeExtension =
- base::GetBoolProperty("debug.sf.edge_extension_shader"s, true);
+ base::GetBoolProperty("debug.sf.prime_shader_cache.edge_extension_shader"s,
+ true);
return getRenderEngine().primeCache(config);
});
@@ -1217,6 +1232,12 @@
info->activeDisplayModeId = ftl::to_underlying(mode.modePtr->getId());
info->renderFrameRate = mode.fps.getValue();
info->hasArrSupport = mode.modePtr->getVrrConfig() && FlagManager::getInstance().vrr_config();
+
+ const auto [normal, high] = display->refreshRateSelector().getFrameRateCategoryRates();
+ ui::FrameRateCategoryRate frameRateCategoryRate(normal.getValue(), high.getValue());
+ info->frameRateCategoryRate = frameRateCategoryRate;
+
+ info->supportedRefreshRates = display->refreshRateSelector().getSupportedFrameRates();
info->activeColorMode = display->getCompositionDisplay()->getState().colorMode;
info->hdrCapabilities = filterOut4k30(display->getHdrCapabilities());
@@ -1348,7 +1369,8 @@
mScheduler->updatePhaseConfiguration(displayId, mode.fps);
if (emitEvent) {
- mScheduler->onDisplayModeChanged(displayId, mode);
+ mScheduler->onDisplayModeChanged(displayId, mode,
+ /*clearContentRequirements*/ false);
}
break;
case DesiredModeAction::None:
@@ -1407,8 +1429,6 @@
return future.get();
}
-// TODO: b/241285876 - Restore thread safety analysis once mStateLock below is unconditional.
-[[clang::no_thread_safety_analysis]]
void SurfaceFlinger::finalizeDisplayModeChange(PhysicalDisplayId displayId) {
SFTRACE_NAME(ftl::Concat(__func__, ' ', displayId.value).c_str());
@@ -1424,8 +1444,6 @@
if (const auto oldResolution =
mDisplayModeController.getActiveMode(displayId).modePtr->getResolution();
oldResolution != activeMode.modePtr->getResolution()) {
- ConditionalLock lock(mStateLock, !FlagManager::getInstance().connected_display());
-
auto& state = mCurrentState.displays.editValueFor(getPhysicalDisplayTokenLocked(displayId));
// We need to generate new sequenceId in order to recreate the display (and this
// way the framebuffer).
@@ -1443,7 +1461,7 @@
mScheduler->updatePhaseConfiguration(displayId, activeMode.fps);
if (pendingModeOpt->emitEvent) {
- mScheduler->onDisplayModeChanged(displayId, activeMode);
+ mScheduler->onDisplayModeChanged(displayId, activeMode, /*clearContentRequirements*/ true);
}
}
@@ -1514,8 +1532,9 @@
constraints.seamlessRequired = false;
hal::VsyncPeriodChangeTimeline outTimeline;
- if (!mDisplayModeController.initiateModeChange(displayId, std::move(*desiredModeOpt),
- constraints, outTimeline)) {
+ if (mDisplayModeController.initiateModeChange(displayId, std::move(*desiredModeOpt),
+ constraints, outTimeline) !=
+ display::DisplayModeController::ModeChangeResult::Changed) {
continue;
}
@@ -1829,6 +1848,24 @@
}));
}
+status_t SurfaceFlinger::getMaxLayerPictureProfiles(const sp<IBinder>& displayToken,
+ int32_t* outMaxProfiles) {
+ const char* const whence = __func__;
+ auto future = mScheduler->schedule([=, this]() FTL_FAKE_GUARD(mStateLock) {
+ const ssize_t index = mCurrentState.displays.indexOfKey(displayToken);
+ if (index < 0) {
+ ALOGE("%s: Invalid display token %p", whence, displayToken.get());
+ return 0;
+ }
+ const DisplayDeviceState& state = mCurrentState.displays.valueAt(index);
+ return state.maxLayerPictureProfiles > 0 ? state.maxLayerPictureProfiles
+ : state.hasPictureProcessing ? 1
+ : 0;
+ });
+ *outMaxProfiles = future.get();
+ return NO_ERROR;
+}
+
status_t SurfaceFlinger::overrideHdrTypes(const sp<IBinder>& displayToken,
const std::vector<ui::Hdr>& hdrTypes) {
Mutex::Autolock lock(mStateLock);
@@ -2239,12 +2276,23 @@
return;
}
- if (FlagManager::getInstance().hotplug2()) {
- // TODO(b/311403559): use enum type instead of int
+ if (event < DisplayHotplugEvent::ERROR_LINK_UNSTABLE) {
+ // This needs to be kept in sync with DisplayHotplugEvent to prevent passing new errors.
const auto errorCode = static_cast<int32_t>(event);
- ALOGD("%s: Hotplug error %d for hwcDisplayId %" PRIu64, __func__, errorCode, hwcDisplayId);
- mScheduler->dispatchHotplugError(errorCode);
+ ALOGW("%s: Unknown hotplug error %d for hwcDisplayId %" PRIu64, __func__, errorCode,
+ hwcDisplayId);
+ return;
}
+
+ if (event == DisplayHotplugEvent::ERROR_LINK_UNSTABLE &&
+ !FlagManager::getInstance().display_config_error_hal()) {
+ return;
+ }
+
+ // TODO(b/311403559): use enum type instead of int
+ const auto errorCode = static_cast<int32_t>(event);
+ ALOGD("%s: Hotplug error %d for hwcDisplayId %" PRIu64, __func__, errorCode, hwcDisplayId);
+ mScheduler->dispatchHotplugError(errorCode);
}
void SurfaceFlinger::onComposerHalVsyncPeriodTimingChanged(
@@ -2526,17 +2574,13 @@
frontend::LayerSnapshot* snapshot = mLayerSnapshotBuilder.getSnapshot(it->second->sequence);
gui::GameMode gameMode = (snapshot) ? snapshot->gameMode : gui::GameMode::Unsupported;
mLayersWithQueuedFrames.emplace(it->second, gameMode);
- mLayersIdsWithQueuedFrames.emplace(it->second->sequence);
}
updateLayerHistory(latchTime);
mLayerSnapshotBuilder.forEachSnapshot([&](const frontend::LayerSnapshot& snapshot) {
- // update output dirty region if we have a queued buffer that is visible or a snapshot
- // recently became invisible
- // TODO(b/360050020) investigate if we need to update dirty region when layer color changes
- if ((snapshot.isVisible &&
- (mLayersIdsWithQueuedFrames.find(snapshot.path.id) !=
- mLayersIdsWithQueuedFrames.end())) ||
+ // update output's dirty region if a snapshot is visible and its
+ // content is dirty or if a snapshot recently became invisible
+ if ((snapshot.isVisible && snapshot.contentDirty) ||
(!snapshot.isVisible && snapshot.changes.test(Changes::Visibility))) {
Region visibleReg;
visibleReg.set(snapshot.transformedBoundsWithoutTransparentRegion);
@@ -2549,7 +2593,7 @@
}
{
- SFTRACE_NAME("LLM:commitChanges");
+ SFTRACE_NAME("LayerLifecycleManager:commitChanges");
mLayerLifecycleManager.commitChanges();
}
@@ -2590,7 +2634,7 @@
}
{
- ConditionalLock lock(mStateLock, FlagManager::getInstance().connected_display());
+ Mutex::Autolock lock(mStateLock);
for (const auto [displayId, _] : frameTargets) {
if (mDisplayModeController.isModeSetPending(displayId)) {
@@ -2693,13 +2737,6 @@
mScheduler->chooseRefreshRateForContent(&mLayerHierarchyBuilder.getHierarchy(),
updateAttachedChoreographer);
- if (FlagManager::getInstance().connected_display()) {
- initiateDisplayModeChanges();
- }
- }
-
- if (!FlagManager::getInstance().connected_display()) {
- ftl::FakeGuard guard(mStateLock);
initiateDisplayModeChanges();
}
@@ -2855,6 +2892,9 @@
if (compositionResult.lastClientCompositionFence) {
layer->setWasClientComposed(compositionResult.lastClientCompositionFence);
}
+ if (com_android_graphics_libgui_flags_apply_picture_profiles()) {
+ mActivePictureUpdater.onLayerComposed(*layer, *layerFE, compositionResult);
+ }
}
SFTRACE_NAME("postComposition");
@@ -2926,7 +2966,6 @@
mScheduler->modulateVsync({}, &VsyncModulator::onDisplayRefresh, hasGpuUseOrReuse);
mLayersWithQueuedFrames.clear();
- mLayersIdsWithQueuedFrames.clear();
doActiveLayersTracingIfNeeded(true, mVisibleRegionsDirty, pacesetterTarget.frameBeginTime(),
vsyncId);
@@ -3066,12 +3105,40 @@
const TimePoint presentTime = TimePoint::now();
+ // The Uids of layer owners that are in buffer stuffing mode, and their elevated
+ // buffer counts. Messages to start recovery are sent exclusively to these Uids.
+ BufferStuffingMap bufferStuffedUids;
+
// Set presentation information before calling Layer::releasePendingBuffer, such that jank
// information from previous' frame classification is already available when sending jank info
// to clients, so they get jank classification as early as possible.
mFrameTimeline->setSfPresent(presentTime.ns(), pacesetterPresentFenceTime,
pacesetterGpuCompositionDoneFenceTime);
+ // Find and register any layers that are in buffer stuffing mode
+ const auto& presentFrames = mFrameTimeline->getPresentFrames();
+
+ for (const auto& frame : presentFrames) {
+ const auto& layer = mLayerLifecycleManager.getLayerFromId(frame->getLayerId());
+ if (!layer) continue;
+ uint32_t numberQueuedBuffers = layer->pendingBuffers ? layer->pendingBuffers->load() : 0;
+ int32_t jankType = frame->getJankType().value_or(JankType::None);
+ if (jankType & JankType::BufferStuffing &&
+ layer->flags & layer_state_t::eRecoverableFromBufferStuffing) {
+ auto [it, wasEmplaced] =
+ bufferStuffedUids.try_emplace(layer->ownerUid.val(), numberQueuedBuffers);
+ // Update with maximum number of queued buffers, allows clients drawing
+ // multiple windows to account for the most severely stuffed window
+ if (!wasEmplaced && it->second < numberQueuedBuffers) {
+ it->second = numberQueuedBuffers;
+ }
+ }
+ }
+
+ if (!bufferStuffedUids.empty()) {
+ mScheduler->addBufferStuffedUids(std::move(bufferStuffedUids));
+ }
+
// We use the CompositionEngine::getLastFrameRefreshTimestamp() which might
// be sampled a little later than when we started doing work for this frame,
// but that should be okay since CompositorTiming has snapping logic.
@@ -3123,30 +3190,6 @@
layer->releasePendingBuffer(presentTime.ns());
}
- std::vector<std::pair<std::shared_ptr<compositionengine::Display>, sp<HdrLayerInfoReporter>>>
- hdrInfoListeners;
- bool haveNewListeners = false;
- {
- Mutex::Autolock lock(mStateLock);
- if (mFpsReporter) {
- mFpsReporter->dispatchLayerFps(mLayerHierarchyBuilder.getHierarchy());
- }
-
- if (mTunnelModeEnabledReporter) {
- mTunnelModeEnabledReporter->updateTunnelModeStatus();
- }
- hdrInfoListeners.reserve(mHdrLayerInfoListeners.size());
- for (const auto& [displayId, reporter] : mHdrLayerInfoListeners) {
- if (reporter && reporter->hasListeners()) {
- if (const auto display = getDisplayDeviceLocked(displayId)) {
- hdrInfoListeners.emplace_back(display->getCompositionDisplay(), reporter);
- }
- }
- }
- haveNewListeners = mAddingHDRLayerInfoListener; // grab this with state lock
- mAddingHDRLayerInfoListener = false;
- }
-
for (const auto& layerEvent : mLayerEvents) {
auto result =
stats::stats_write(stats::SURFACE_CONTROL_EVENT,
@@ -3157,10 +3200,40 @@
ALOGW("Failed to report layer event with error: %d", result);
}
}
-
mLayerEvents.clear();
- if (haveNewListeners || mHdrLayerInfoChanged) {
+ std::vector<std::pair<std::shared_ptr<compositionengine::Display>, sp<HdrLayerInfoReporter>>>
+ hdrInfoListeners;
+ bool haveNewHdrInfoListeners = false;
+ sp<gui::IActivePictureListener> activePictureListener;
+ bool haveNewActivePictureListener = false;
+ {
+ Mutex::Autolock lock(mStateLock);
+ if (mFpsReporter) {
+ mFpsReporter->dispatchLayerFps(mLayerHierarchyBuilder.getHierarchy());
+ }
+
+ if (mTunnelModeEnabledReporter) {
+ mTunnelModeEnabledReporter->updateTunnelModeStatus();
+ }
+
+ hdrInfoListeners.reserve(mHdrLayerInfoListeners.size());
+ for (const auto& [displayId, reporter] : mHdrLayerInfoListeners) {
+ if (reporter && reporter->hasListeners()) {
+ if (const auto display = getDisplayDeviceLocked(displayId)) {
+ hdrInfoListeners.emplace_back(display->getCompositionDisplay(), reporter);
+ }
+ }
+ }
+ haveNewHdrInfoListeners = mAddingHDRLayerInfoListener; // grab this with state lock
+ mAddingHDRLayerInfoListener = false;
+
+ activePictureListener = mActivePictureListener;
+ haveNewActivePictureListener = mHaveNewActivePictureListener;
+ mHaveNewActivePictureListener = false;
+ }
+
+ if (haveNewHdrInfoListeners || mHdrLayerInfoChanged) {
for (auto& [compositionDisplay, listener] : hdrInfoListeners) {
HdrLayerInfoReporter::HdrLayerInfo info;
int32_t maxArea = 0;
@@ -3178,7 +3251,15 @@
snapshot.desiredHdrSdrRatio < 1.f
? std::numeric_limits<float>::infinity()
: snapshot.desiredHdrSdrRatio;
- info.mergeDesiredRatio(desiredHdrSdrRatio);
+
+ float desiredRatio = desiredHdrSdrRatio;
+ if (FlagManager::getInstance().begone_bright_hlg() &&
+ desiredHdrSdrRatio ==
+ std::numeric_limits<float>::infinity()) {
+ desiredRatio = getIdealizedMaxHeadroom(snapshot.dataspace);
+ }
+
+ info.mergeDesiredRatio(desiredRatio);
info.numberOfHdrLayers++;
const auto displayFrame = outputLayer->getState().displayFrame;
const int32_t area =
@@ -3210,9 +3291,19 @@
listener->dispatchHdrLayerInfo(info);
}
}
-
mHdrLayerInfoChanged = false;
+ if (com_android_graphics_libgui_flags_apply_picture_profiles()) {
+ // Track, update and notify changes to active pictures - layers that are undergoing picture
+ // processing
+ if (mActivePictureUpdater.updateAndHasChanged() || haveNewActivePictureListener) {
+ if (activePictureListener) {
+ activePictureListener->onActivePicturesChanged(
+ mActivePictureUpdater.getActivePictures());
+ }
+ }
+ }
+
mTransactionCallbackInvoker.sendCallbacks(false /* onCommitOnly */);
mTransactionCallbackInvoker.clearCompletedTransactions();
@@ -3458,10 +3549,8 @@
processHotplugConnect(displayId, hwcDisplayId, std::move(*info),
displayString.c_str());
if (!activeModeIdOpt) {
- if (FlagManager::getInstance().hotplug2()) {
- mScheduler->dispatchHotplugError(
- static_cast<int32_t>(DisplayHotplugEvent::ERROR_UNKNOWN));
- }
+ mScheduler->dispatchHotplugError(
+ static_cast<int32_t>(DisplayHotplugEvent::ERROR_UNKNOWN));
getHwComposer().disconnectDisplay(displayId);
continue;
}
@@ -3552,7 +3641,9 @@
}
state.isProtected = true;
state.displayName = std::move(info.name);
-
+ state.maxLayerPictureProfiles = getHwComposer().getMaxLayerPictureProfiles(displayId);
+ state.hasPictureProcessing =
+ getHwComposer().hasDisplayCapability(displayId, DisplayCapability::PICTURE_PROCESSING);
mCurrentState.displays.add(token, state);
ALOGI("Connecting %s", displayString);
return activeModeId;
@@ -3660,6 +3751,26 @@
return display;
}
+void SurfaceFlinger::incRefreshableDisplays() {
+ if (FlagManager::getInstance().no_vsyncs_on_screen_off()) {
+ mRefreshableDisplays++;
+ if (mRefreshableDisplays == 1) {
+ ftl::FakeGuard guard(kMainThreadContext);
+ mScheduler->omitVsyncDispatching(false);
+ }
+ }
+}
+
+void SurfaceFlinger::decRefreshableDisplays() {
+ if (FlagManager::getInstance().no_vsyncs_on_screen_off()) {
+ mRefreshableDisplays--;
+ if (mRefreshableDisplays == 0) {
+ ftl::FakeGuard guard(kMainThreadContext);
+ mScheduler->omitVsyncDispatching(true);
+ }
+ }
+}
+
void SurfaceFlinger::processDisplayAdded(const wp<IBinder>& displayToken,
const DisplayDeviceState& state) {
ui::Size resolution(0, 0);
@@ -3693,6 +3804,8 @@
builder.setPixels(resolution);
builder.setIsSecure(state.isSecure);
builder.setIsProtected(state.isProtected);
+ builder.setHasPictureProcessing(state.hasPictureProcessing);
+ builder.setMaxLayerPictureProfiles(state.maxLayerPictureProfiles);
builder.setPowerAdvisor(mPowerAdvisor.get());
builder.setName(state.displayName);
auto compositionDisplay = getCompositionEngine().createDisplay(builder.build());
@@ -3751,6 +3864,10 @@
display->adjustRefreshRate(mScheduler->getPacesetterRefreshRate());
}
+ if (display->isRefreshable()) {
+ incRefreshableDisplays();
+ }
+
mDisplays.try_emplace(displayToken, std::move(display));
// For an external display, loadDisplayModes already attempted to select the same mode
@@ -3785,6 +3902,10 @@
} else {
mScheduler->unregisterDisplay(display->getPhysicalId(), mActiveDisplayId);
}
+
+ if (display->isRefreshable()) {
+ decRefreshableDisplays();
+ }
}
mDisplays.erase(displayToken);
@@ -3819,6 +3940,10 @@
if (display->isVirtual()) {
releaseVirtualDisplay(display->getVirtualId());
}
+
+ if (display->isRefreshable()) {
+ decRefreshableDisplays();
+ }
}
mDisplays.erase(displayToken);
@@ -3983,7 +4108,8 @@
inputWindowCommands =
std::move(mInputWindowCommands),
inputFlinger = mInputFlinger, this,
- visibleWindowsChanged, vsyncId, frameTime]() {
+ visibleWindowsChanged, vsyncId,
+ frameTime]() mutable {
SFTRACE_NAME("BackgroundExecutor::updateInputFlinger");
if (updateWindowInfo) {
mWindowInfosListenerInvoker
@@ -5317,7 +5443,15 @@
activeDisplay->isPoweredOn(),
"Trying to change power mode on inactive display without powering off active display");
+ const bool couldRefresh = display->isRefreshable();
display->setPowerMode(mode);
+ const bool canRefresh = display->isRefreshable();
+
+ if (couldRefresh && !canRefresh) {
+ decRefreshableDisplays();
+ } else if (!couldRefresh && canRefresh) {
+ incRefreshableDisplays();
+ }
const auto activeMode = display->refreshRateSelector().getActiveMode().modePtr;
if (currentMode == hal::PowerMode::OFF) {
@@ -6182,7 +6316,7 @@
}
// Numbers from 1000 to 1045 are currently used for backdoors. The code
// in onTransact verifies that the user is root, and has access to use SF.
- if (code >= 1000 && code <= 1045) {
+ if (code >= 1000 && code <= 1046) {
ALOGV("Accessing SurfaceFlinger through backdoor code: %u", code);
return OK;
}
@@ -6715,6 +6849,15 @@
}
return err;
}
+ // Introduce jank to HWC
+ case 1046: {
+ int32_t jankDelayMs = 0;
+ if (data.readInt32(&jankDelayMs) != NO_ERROR) {
+ return BAD_VALUE;
+ }
+ mScheduler->setDebugPresentDelay(TimePoint::fromNs(ms2ns(jankDelayMs)));
+ return NO_ERROR;
+ }
}
}
return err;
@@ -7158,9 +7301,10 @@
// typically a layer with DRM contents, or have the GRALLOC_USAGE_PROTECTED set on the buffer.
// A protected layer has no implication on whether it's secure, which is explicitly set by
// application to avoid being screenshot or drawn via unsecure display.
-bool SurfaceFlinger::layersHasProtectedLayer(const std::vector<sp<LayerFE>>& layers) const {
+bool SurfaceFlinger::layersHasProtectedLayer(
+ const std::vector<std::pair<Layer*, sp<LayerFE>>>& layers) const {
bool protectedLayerFound = false;
- for (auto& layerFE : layers) {
+ for (auto& [_, layerFE] : layers) {
protectedLayerFound |=
(layerFE->mSnapshot->isVisible && layerFE->mSnapshot->hasProtectedContent);
if (protectedLayerFound) {
@@ -7176,15 +7320,21 @@
// risk of deadlocks.
std::optional<SurfaceFlinger::OutputCompositionState> SurfaceFlinger::getSnapshotsFromMainThread(
RenderAreaBuilderVariant& renderAreaBuilder, GetLayerSnapshotsFunction getLayerSnapshotsFn,
- std::vector<sp<LayerFE>>& layerFEs) {
+ std::vector<std::pair<Layer*, sp<LayerFE>>>& layers) {
return mScheduler
- ->schedule([=, this, &renderAreaBuilder, &layerFEs]() REQUIRES(kMainThreadContext) {
+ ->schedule([=, this, &renderAreaBuilder, &layers]() REQUIRES(kMainThreadContext) {
SFTRACE_NAME("getSnapshotsFromMainThread");
- auto layers = getLayerSnapshotsFn();
- for (auto& [layer, layerFE] : layers) {
- attachReleaseFenceFutureToLayer(layer, layerFE.get(), ui::INVALID_LAYER_STACK);
+ layers = getLayerSnapshotsFn();
+ // Non-threaded RenderEngine eventually returns to the main thread a 2nd time
+ // to complete the screenshot. Release fences should only be added during the 2nd
+ // hop to main thread in order to avoid potential deadlocks from waiting for the
+ // the future fence to fire.
+ if (mRenderEngine->isThreaded()) {
+ for (auto& [layer, layerFE] : layers) {
+ attachReleaseFenceFutureToLayer(layer, layerFE.get(),
+ ui::INVALID_LAYER_STACK);
+ }
}
- layerFEs = extractLayerFEs(layers);
return getDisplayStateFromRenderAreaBuilder(renderAreaBuilder);
})
.get();
@@ -7205,79 +7355,41 @@
return;
}
- if (FlagManager::getInstance().single_hop_screenshot() && mRenderEngine->isThreaded()) {
- std::vector<sp<LayerFE>> layerFEs;
- auto displayState =
- getSnapshotsFromMainThread(renderAreaBuilder, getLayerSnapshotsFn, layerFEs);
+ std::vector<std::pair<Layer*, sp<LayerFE>>> layers;
+ auto displayState = getSnapshotsFromMainThread(renderAreaBuilder, getLayerSnapshotsFn, layers);
- const bool supportsProtected = getRenderEngine().supportsProtectedContent();
- bool hasProtectedLayer = false;
- if (allowProtected && supportsProtected) {
- hasProtectedLayer = layersHasProtectedLayer(layerFEs);
- }
- const bool isProtected = hasProtectedLayer && allowProtected && supportsProtected;
- const uint32_t usage = GRALLOC_USAGE_HW_COMPOSER | GRALLOC_USAGE_HW_RENDER |
- GRALLOC_USAGE_HW_TEXTURE |
- (isProtected ? GRALLOC_USAGE_PROTECTED
- : GRALLOC_USAGE_SW_READ_OFTEN | GRALLOC_USAGE_SW_WRITE_OFTEN);
- sp<GraphicBuffer> buffer =
- getFactory().createGraphicBuffer(bufferSize.getWidth(), bufferSize.getHeight(),
- static_cast<android_pixel_format>(reqPixelFormat),
- 1 /* layerCount */, usage, "screenshot");
-
- const status_t bufferStatus = buffer->initCheck();
- if (bufferStatus != OK) {
- // Animations may end up being really janky, but don't crash here.
- // Otherwise an irreponsible process may cause an SF crash by allocating
- // too much.
- ALOGE("%s: Buffer failed to allocate: %d", __func__, bufferStatus);
- invokeScreenCaptureError(bufferStatus, captureListener);
- return;
- }
- const std::shared_ptr<renderengine::ExternalTexture> texture = std::make_shared<
- renderengine::impl::ExternalTexture>(buffer, getRenderEngine(),
- renderengine::impl::ExternalTexture::Usage::
- WRITEABLE);
- auto futureFence = captureScreenshot(renderAreaBuilder, texture, false /* regionSampling */,
- grayscale, isProtected, attachGainmap, captureListener,
- displayState, layerFEs);
- futureFence.get();
-
- } else {
- const bool supportsProtected = getRenderEngine().supportsProtectedContent();
- bool hasProtectedLayer = false;
- if (allowProtected && supportsProtected) {
- auto layers = mScheduler->schedule([=]() { return getLayerSnapshotsFn(); }).get();
- hasProtectedLayer = layersHasProtectedLayer(extractLayerFEs(layers));
- }
- const bool isProtected = hasProtectedLayer && allowProtected && supportsProtected;
- const uint32_t usage = GRALLOC_USAGE_HW_COMPOSER | GRALLOC_USAGE_HW_RENDER |
- GRALLOC_USAGE_HW_TEXTURE |
- (isProtected ? GRALLOC_USAGE_PROTECTED
- : GRALLOC_USAGE_SW_READ_OFTEN | GRALLOC_USAGE_SW_WRITE_OFTEN);
- sp<GraphicBuffer> buffer =
- getFactory().createGraphicBuffer(bufferSize.getWidth(), bufferSize.getHeight(),
- static_cast<android_pixel_format>(reqPixelFormat),
- 1 /* layerCount */, usage, "screenshot");
-
- const status_t bufferStatus = buffer->initCheck();
- if (bufferStatus != OK) {
- // Animations may end up being really janky, but don't crash here.
- // Otherwise an irreponsible process may cause an SF crash by allocating
- // too much.
- ALOGE("%s: Buffer failed to allocate: %d", __func__, bufferStatus);
- invokeScreenCaptureError(bufferStatus, captureListener);
- return;
- }
- const std::shared_ptr<renderengine::ExternalTexture> texture = std::make_shared<
- renderengine::impl::ExternalTexture>(buffer, getRenderEngine(),
- renderengine::impl::ExternalTexture::Usage::
- WRITEABLE);
- auto futureFence = captureScreenshotLegacy(renderAreaBuilder, getLayerSnapshotsFn, texture,
- false /* regionSampling */, grayscale,
- isProtected, attachGainmap, captureListener);
- futureFence.get();
+ const bool supportsProtected = getRenderEngine().supportsProtectedContent();
+ bool hasProtectedLayer = false;
+ if (allowProtected && supportsProtected) {
+ hasProtectedLayer = layersHasProtectedLayer(layers);
}
+ const bool isProtected = hasProtectedLayer && allowProtected && supportsProtected;
+ const uint32_t usage = GRALLOC_USAGE_HW_COMPOSER | GRALLOC_USAGE_HW_RENDER |
+ GRALLOC_USAGE_HW_TEXTURE |
+ (isProtected ? GRALLOC_USAGE_PROTECTED
+ : GRALLOC_USAGE_SW_READ_OFTEN | GRALLOC_USAGE_SW_WRITE_OFTEN);
+ sp<GraphicBuffer> buffer =
+ getFactory().createGraphicBuffer(bufferSize.getWidth(), bufferSize.getHeight(),
+ static_cast<android_pixel_format>(reqPixelFormat),
+ 1 /* layerCount */, usage, "screenshot");
+
+ const status_t bufferStatus = buffer->initCheck();
+ if (bufferStatus != OK) {
+ // Animations may end up being really janky, but don't crash here.
+ // Otherwise an irreponsible process may cause an SF crash by allocating
+ // too much.
+ ALOGE("%s: Buffer failed to allocate: %d", __func__, bufferStatus);
+ invokeScreenCaptureError(bufferStatus, captureListener);
+ return;
+ }
+ const std::shared_ptr<renderengine::ExternalTexture> texture = std::make_shared<
+ renderengine::impl::ExternalTexture>(buffer, getRenderEngine(),
+ renderengine::impl::ExternalTexture::Usage::
+ WRITEABLE);
+ auto futureFence =
+ captureScreenshot(renderAreaBuilder, texture, false /* regionSampling */, grayscale,
+ isProtected, attachGainmap, captureListener, displayState, layers);
+ futureFence.get();
}
std::optional<SurfaceFlinger::OutputCompositionState>
@@ -7316,22 +7428,13 @@
return std::nullopt;
}
-std::vector<sp<LayerFE>> SurfaceFlinger::extractLayerFEs(
- const std::vector<std::pair<Layer*, sp<LayerFE>>>& layers) const {
- std::vector<sp<LayerFE>> layerFEs;
- layerFEs.reserve(layers.size());
- for (const auto& [_, layerFE] : layers) {
- layerFEs.push_back(layerFE);
- }
- return layerFEs;
-}
-
ftl::SharedFuture<FenceResult> SurfaceFlinger::captureScreenshot(
const RenderAreaBuilderVariant& renderAreaBuilder,
const std::shared_ptr<renderengine::ExternalTexture>& buffer, bool regionSampling,
bool grayscale, bool isProtected, bool attachGainmap,
const sp<IScreenCaptureListener>& captureListener,
- std::optional<OutputCompositionState>& displayState, std::vector<sp<LayerFE>>& layerFEs) {
+ std::optional<OutputCompositionState>& displayState,
+ std::vector<std::pair<Layer*, sp<LayerFE>>>& layers) {
SFTRACE_CALL();
ScreenCaptureResults captureResults;
@@ -7350,11 +7453,9 @@
float displayBrightnessNits = displayState.value().displayBrightnessNits;
float sdrWhitePointNits = displayState.value().sdrWhitePointNits;
- // Empty vector needed to pass into renderScreenImpl for legacy path
- std::vector<std::pair<Layer*, sp<android::LayerFE>>> layers;
ftl::SharedFuture<FenceResult> renderFuture =
renderScreenImpl(renderArea.get(), buffer, regionSampling, grayscale, isProtected,
- attachGainmap, captureResults, displayState, layers, layerFEs);
+ captureResults, displayState, layers);
if (captureResults.capturedHdrLayers && attachGainmap &&
FlagManager::getInstance().true_hdr_screenshots()) {
@@ -7389,8 +7490,7 @@
ScreenCaptureResults unusedResults;
ftl::SharedFuture<FenceResult> hdrRenderFuture =
renderScreenImpl(renderArea.get(), hdrTexture, regionSampling, grayscale,
- isProtected, attachGainmap, unusedResults, displayState,
- layers, layerFEs);
+ isProtected, unusedResults, displayState, layers);
renderFuture =
ftl::Future(std::move(renderFuture))
@@ -7436,75 +7536,14 @@
return renderFuture;
}
-ftl::SharedFuture<FenceResult> SurfaceFlinger::captureScreenshotLegacy(
- RenderAreaBuilderVariant renderAreaBuilder, GetLayerSnapshotsFunction getLayerSnapshotsFn,
- const std::shared_ptr<renderengine::ExternalTexture>& buffer, bool regionSampling,
- bool grayscale, bool isProtected, bool attachGainmap,
- const sp<IScreenCaptureListener>& captureListener) {
- SFTRACE_CALL();
-
- auto takeScreenshotFn = [=, this, renderAreaBuilder = std::move(renderAreaBuilder)]() REQUIRES(
- kMainThreadContext) mutable -> ftl::SharedFuture<FenceResult> {
- auto layers = getLayerSnapshotsFn();
- for (auto& [layer, layerFE] : layers) {
- attachReleaseFenceFutureToLayer(layer, layerFE.get(), ui::INVALID_LAYER_STACK);
- }
- auto displayState = getDisplayStateFromRenderAreaBuilder(renderAreaBuilder);
-
- ScreenCaptureResults captureResults;
- std::unique_ptr<const RenderArea> renderArea =
- std::visit([](auto&& arg) -> std::unique_ptr<RenderArea> { return arg.build(); },
- renderAreaBuilder);
-
- if (!renderArea) {
- ALOGW("Skipping screen capture because of invalid render area.");
- if (captureListener) {
- captureResults.fenceResult = base::unexpected(NO_MEMORY);
- captureListener->onScreenCaptureCompleted(captureResults);
- }
- return ftl::yield<FenceResult>(base::unexpected(NO_ERROR)).share();
- }
-
- auto layerFEs = extractLayerFEs(layers);
- ftl::SharedFuture<FenceResult> renderFuture =
- renderScreenImpl(renderArea.get(), buffer, regionSampling, grayscale, isProtected,
- attachGainmap, captureResults, displayState, layers, layerFEs);
-
- if (captureListener) {
- // Defer blocking on renderFuture back to the Binder thread.
- return ftl::Future(std::move(renderFuture))
- .then([captureListener, captureResults = std::move(captureResults)](
- FenceResult fenceResult) mutable -> FenceResult {
- captureResults.fenceResult = std::move(fenceResult);
- captureListener->onScreenCaptureCompleted(captureResults);
- return base::unexpected(NO_ERROR);
- })
- .share();
- }
- return renderFuture;
- };
-
- // TODO(b/294936197): Run takeScreenshotsFn() in a binder thread to reduce the number
- // of calls on the main thread.
- auto future =
- mScheduler->schedule(FTL_FAKE_GUARD(kMainThreadContext, std::move(takeScreenshotFn)));
-
- // Flatten nested futures.
- auto chain = ftl::Future(std::move(future)).then([](ftl::SharedFuture<FenceResult> future) {
- return future;
- });
-
- return chain.share();
-}
-
ftl::SharedFuture<FenceResult> SurfaceFlinger::renderScreenImpl(
const RenderArea* renderArea, const std::shared_ptr<renderengine::ExternalTexture>& buffer,
- bool regionSampling, bool grayscale, bool isProtected, bool attachGainmap,
- ScreenCaptureResults& captureResults, std::optional<OutputCompositionState>& displayState,
- std::vector<std::pair<Layer*, sp<LayerFE>>>& layers, std::vector<sp<LayerFE>>& layerFEs) {
+ bool regionSampling, bool grayscale, bool isProtected, ScreenCaptureResults& captureResults,
+ std::optional<OutputCompositionState>& displayState,
+ std::vector<std::pair<Layer*, sp<LayerFE>>>& layers) {
SFTRACE_CALL();
- for (auto& layerFE : layerFEs) {
+ for (auto& [_, layerFE] : layers) {
frontend::LayerSnapshot* snapshot = layerFE->mSnapshot.get();
captureResults.capturedSecureLayers |= (snapshot->isVisible && snapshot->isSecure);
captureResults.capturedHdrLayers |= isHdrLayer(*snapshot);
@@ -7563,29 +7602,32 @@
captureResults.buffer = capturedBuffer->getBuffer();
ui::LayerStack layerStack{ui::DEFAULT_LAYER_STACK};
- if (!layerFEs.empty()) {
- const sp<LayerFE>& layerFE = layerFEs.back();
+ if (!layers.empty()) {
+ const sp<LayerFE>& layerFE = layers.back().second;
layerStack = layerFE->getCompositionState()->outputFilter.layerStack;
}
- auto copyLayerFEs = [&layerFEs]() {
- std::vector<sp<compositionengine::LayerFE>> ceLayerFEs;
- ceLayerFEs.reserve(layerFEs.size());
- for (const auto& layerFE : layerFEs) {
- ceLayerFEs.push_back(layerFE);
- }
- return ceLayerFEs;
- };
-
auto present = [this, buffer = capturedBuffer, dataspace = captureResults.capturedDataspace,
sdrWhitePointNits, displayBrightnessNits, grayscale, isProtected,
- layerFEs = copyLayerFEs(), layerStack, regionSampling,
+ layers = std::move(layers), layerStack, regionSampling,
renderArea = std::move(renderArea), renderIntent,
enableLocalTonemapping]() -> FenceResult {
std::unique_ptr<compositionengine::CompositionEngine> compositionEngine =
mFactory.createCompositionEngine();
compositionEngine->setRenderEngine(mRenderEngine.get());
+ std::vector<sp<compositionengine::LayerFE>> layerFEs;
+ layerFEs.reserve(layers.size());
+ for (auto& [layer, layerFE] : layers) {
+ // Release fences were not yet added for non-threaded render engine. To avoid
+ // deadlocks between main thread and binder threads waiting for the future fence
+ // result, fences should be added to layers in the same hop onto the main thread.
+ if (!mRenderEngine->isThreaded()) {
+ attachReleaseFenceFutureToLayer(layer, layerFE.get(), ui::INVALID_LAYER_STACK);
+ }
+ layerFEs.push_back(layerFE);
+ }
+
compositionengine::Output::ColorProfile colorProfile{.dataspace = dataspace,
.renderIntent = renderIntent};
@@ -7643,13 +7685,9 @@
//
// TODO(b/196334700) Once we use RenderEngineThreaded everywhere we can always defer the call
// to CompositionEngine::present.
- ftl::SharedFuture<FenceResult> presentFuture;
- if (FlagManager::getInstance().single_hop_screenshot() && mRenderEngine->isThreaded()) {
- presentFuture = ftl::yield(present()).share();
- } else {
- presentFuture = mRenderEngine->isThreaded() ? ftl::defer(std::move(present)).share()
- : ftl::yield(present()).share();
- }
+ ftl::SharedFuture<FenceResult> presentFuture = mRenderEngine->isThreaded()
+ ? ftl::yield(present()).share()
+ : mScheduler->schedule(std::move(present)).share();
return presentFuture;
}
@@ -7713,7 +7751,8 @@
ALOGV("Setting desired display mode specs: %s", currentPolicy.toString().c_str());
if (const bool isPacesetter =
- mScheduler->onDisplayModeChanged(displayId, selector.getActiveMode())) {
+ mScheduler->onDisplayModeChanged(displayId, selector.getActiveMode(),
+ /*clearContentRequirements*/ true)) {
mDisplayModeController.updateKernelIdleTimer(displayId);
}
@@ -8102,6 +8141,14 @@
}));
}
+void SurfaceFlinger::setActivePictureListener(const sp<gui::IActivePictureListener>& listener) {
+ if (com_android_graphics_libgui_flags_apply_picture_profiles()) {
+ Mutex::Autolock lock(mStateLock);
+ mActivePictureListener = listener;
+ mHaveNewActivePictureListener = listener != nullptr;
+ }
+}
+
std::shared_ptr<renderengine::ExternalTexture> SurfaceFlinger::getExternalTextureFromBufferData(
BufferData& bufferData, const char* layerName, uint64_t transactionId) {
if (bufferData.buffer &&
@@ -8601,6 +8648,14 @@
outInfo->activeDisplayModeId = info.activeDisplayModeId;
outInfo->renderFrameRate = info.renderFrameRate;
outInfo->hasArrSupport = info.hasArrSupport;
+ gui::FrameRateCategoryRate& frameRateCategoryRate = outInfo->frameRateCategoryRate;
+ frameRateCategoryRate.normal = info.frameRateCategoryRate.getNormal();
+ frameRateCategoryRate.high = info.frameRateCategoryRate.getHigh();
+ outInfo->supportedRefreshRates.clear();
+ outInfo->supportedRefreshRates.reserve(info.supportedRefreshRates.size());
+ for (float supportedRefreshRate : info.supportedRefreshRates) {
+ outInfo->supportedRefreshRates.push_back(supportedRefreshRate);
+ }
outInfo->supportedColorModes.clear();
outInfo->supportedColorModes.reserve(info.supportedColorModes.size());
@@ -8756,6 +8811,16 @@
return binder::Status::ok();
}
+binder::Status SurfaceComposerAIDL::getMaxLayerPictureProfiles(const sp<IBinder>& display,
+ int32_t* outMaxProfiles) {
+ status_t status = checkAccessPermission();
+ if (status != OK) {
+ return binderStatusFromStatusT(status);
+ }
+ mFlinger->getMaxLayerPictureProfiles(display, outMaxProfiles);
+ return binder::Status::ok();
+}
+
binder::Status SurfaceComposerAIDL::captureDisplay(
const DisplayCaptureArgs& args, const sp<IScreenCaptureListener>& captureListener) {
mFlinger->captureDisplay(args, captureListener);
@@ -9034,6 +9099,15 @@
return binderStatusFromStatusT(status);
}
+binder::Status SurfaceComposerAIDL::setActivePictureListener(
+ const sp<gui::IActivePictureListener>& listener) {
+ status_t status = checkObservePictureProfilesPermission();
+ if (status == OK) {
+ mFlinger->setActivePictureListener(listener);
+ }
+ return binderStatusFromStatusT(status);
+}
+
binder::Status SurfaceComposerAIDL::notifyPowerBoost(int boostId) {
status_t status = checkAccessPermission();
if (status == OK) {
@@ -9104,26 +9178,46 @@
}
binder::Status SurfaceComposerAIDL::enableRefreshRateOverlay(bool active) {
+ status_t status = checkAccessPermission();
+ if (status != OK) {
+ return binderStatusFromStatusT(status);
+ }
mFlinger->sfdo_enableRefreshRateOverlay(active);
return binder::Status::ok();
}
binder::Status SurfaceComposerAIDL::setDebugFlash(int delay) {
+ status_t status = checkAccessPermission();
+ if (status != OK) {
+ return binderStatusFromStatusT(status);
+ }
mFlinger->sfdo_setDebugFlash(delay);
return binder::Status::ok();
}
binder::Status SurfaceComposerAIDL::scheduleComposite() {
+ status_t status = checkAccessPermission();
+ if (status != OK) {
+ return binderStatusFromStatusT(status);
+ }
mFlinger->sfdo_scheduleComposite();
return binder::Status::ok();
}
binder::Status SurfaceComposerAIDL::scheduleCommit() {
+ status_t status = checkAccessPermission();
+ if (status != OK) {
+ return binderStatusFromStatusT(status);
+ }
mFlinger->sfdo_scheduleCommit();
return binder::Status::ok();
}
binder::Status SurfaceComposerAIDL::forceClientComposition(bool enabled) {
+ status_t status = checkAccessPermission();
+ if (status != OK) {
+ return binderStatusFromStatusT(status);
+ }
mFlinger->sfdo_forceClientComposition(enabled);
return binder::Status::ok();
}
@@ -9289,6 +9383,17 @@
return OK;
}
+status_t SurfaceComposerAIDL::checkObservePictureProfilesPermission() {
+ IPCThreadState* ipc = IPCThreadState::self();
+ const int pid = ipc->getCallingPid();
+ const int uid = ipc->getCallingUid();
+ if (!PermissionCache::checkPermission(sObservePictureProfiles, pid, uid)) {
+ ALOGE("Permission Denial: can't manage picture profiles pid=%d, uid=%d", pid, uid);
+ return PERMISSION_DENIED;
+ }
+ return OK;
+}
+
void SurfaceFlinger::forceFutureUpdate(int delayInMs) {
static_cast<void>(mScheduler->scheduleDelayed([&]() { scheduleRepaint(); }, ms2ns(delayInMs)));
}
diff --git a/services/surfaceflinger/SurfaceFlinger.h b/services/surfaceflinger/SurfaceFlinger.h
index 31218ed..211f374 100644
--- a/services/surfaceflinger/SurfaceFlinger.h
+++ b/services/surfaceflinger/SurfaceFlinger.h
@@ -24,9 +24,11 @@
#include <android-base/stringprintf.h>
#include <android-base/strings.h>
#include <android-base/thread_annotations.h>
+#include <android/gui/ActivePicture.h>
#include <android/gui/BnSurfaceComposer.h>
#include <android/gui/DisplayStatInfo.h>
#include <android/gui/DisplayState.h>
+#include <android/gui/IActivePictureListener.h>
#include <android/gui/IJankListener.h>
#include <android/gui/ISurfaceComposerClient.h>
#include <common/trace.h>
@@ -57,6 +59,7 @@
#include <utils/threads.h>
#include <compositionengine/OutputColorSetting.h>
+#include <compositionengine/impl/OutputCompositionState.h>
#include <scheduler/Fps.h>
#include <scheduler/PresentLatencyTracker.h>
#include <scheduler/Time.h>
@@ -66,11 +69,12 @@
#include <ui/FenceResult.h>
#include <common/FlagManager.h>
+#include "ActivePictureUpdater.h"
+#include "BackgroundExecutor.h"
#include "Display/DisplayModeController.h"
#include "Display/PhysicalDisplay.h"
#include "DisplayDevice.h"
#include "DisplayHardware/HWC2.h"
-#include "DisplayHardware/PowerAdvisor.h"
#include "DisplayIdGenerator.h"
#include "Effects/Daltonizer.h"
#include "FrontEnd/DisplayInfo.h"
@@ -81,6 +85,7 @@
#include "FrontEnd/TransactionHandler.h"
#include "LayerVector.h"
#include "MutexUtils.h"
+#include "PowerAdvisor/PowerAdvisor.h"
#include "Scheduler/ISchedulerCallback.h"
#include "Scheduler/RefreshRateSelector.h"
#include "Scheduler/Scheduler.h"
@@ -92,6 +97,7 @@
#include "TransactionState.h"
#include "Utils/OnceFuture.h"
+#include <algorithm>
#include <atomic>
#include <cstdint>
#include <functional>
@@ -590,6 +596,7 @@
status_t getHdrOutputConversionSupport(bool* outSupport) const;
void setAutoLowLatencyMode(const sp<IBinder>& displayToken, bool on);
void setGameContentType(const sp<IBinder>& displayToken, bool on);
+ status_t getMaxLayerPictureProfiles(const sp<IBinder>& displayToken, int32_t* outMaxProfiles);
void setPowerMode(const sp<IBinder>& displayToken, int mode);
status_t overrideHdrTypes(const sp<IBinder>& displayToken,
const std::vector<ui::Hdr>& hdrTypes);
@@ -659,6 +666,8 @@
void updateHdcpLevels(hal::HWDisplayId hwcDisplayId, int32_t connectedLevel, int32_t maxLevel);
+ void setActivePictureListener(const sp<gui::IActivePictureListener>& listener);
+
// IBinder::DeathRecipient overrides:
void binderDied(const wp<IBinder>& who) override;
@@ -851,13 +860,14 @@
void attachReleaseFenceFutureToLayer(Layer* layer, LayerFE* layerFE, ui::LayerStack layerStack);
// Checks if a protected layer exists in a list of layers.
- bool layersHasProtectedLayer(const std::vector<sp<LayerFE>>& layers) const;
+ bool layersHasProtectedLayer(const std::vector<std::pair<Layer*, sp<LayerFE>>>& layers) const;
using OutputCompositionState = compositionengine::impl::OutputCompositionState;
std::optional<OutputCompositionState> getSnapshotsFromMainThread(
RenderAreaBuilderVariant& renderAreaBuilder,
- GetLayerSnapshotsFunction getLayerSnapshotsFn, std::vector<sp<LayerFE>>& layerFEs);
+ GetLayerSnapshotsFunction getLayerSnapshotsFn,
+ std::vector<std::pair<Layer*, sp<LayerFE>>>& layers);
void captureScreenCommon(RenderAreaBuilderVariant, GetLayerSnapshotsFunction,
ui::Size bufferSize, ui::PixelFormat, bool allowProtected,
@@ -866,32 +876,19 @@
std::optional<OutputCompositionState> getDisplayStateFromRenderAreaBuilder(
RenderAreaBuilderVariant& renderAreaBuilder) REQUIRES(kMainThreadContext);
- // Legacy layer raw pointer is not safe to access outside the main thread.
- // Creates a new vector consisting only of LayerFEs, which can be safely
- // accessed outside the main thread.
- std::vector<sp<LayerFE>> extractLayerFEs(
- const std::vector<std::pair<Layer*, sp<LayerFE>>>& layers) const;
-
ftl::SharedFuture<FenceResult> captureScreenshot(
const RenderAreaBuilderVariant& renderAreaBuilder,
const std::shared_ptr<renderengine::ExternalTexture>& buffer, bool regionSampling,
bool grayscale, bool isProtected, bool attachGainmap,
const sp<IScreenCaptureListener>& captureListener,
std::optional<OutputCompositionState>& displayState,
- std::vector<sp<LayerFE>>& layerFEs);
-
- ftl::SharedFuture<FenceResult> captureScreenshotLegacy(
- RenderAreaBuilderVariant, GetLayerSnapshotsFunction,
- const std::shared_ptr<renderengine::ExternalTexture>&, bool regionSampling,
- bool grayscale, bool isProtected, bool attachGainmap,
- const sp<IScreenCaptureListener>&);
+ std::vector<std::pair<Layer*, sp<LayerFE>>>& layers);
ftl::SharedFuture<FenceResult> renderScreenImpl(
const RenderArea*, const std::shared_ptr<renderengine::ExternalTexture>&,
- bool regionSampling, bool grayscale, bool isProtected, bool attachGainmap,
- ScreenCaptureResults&, std::optional<OutputCompositionState>& displayState,
- std::vector<std::pair<Layer*, sp<LayerFE>>>& layers,
- std::vector<sp<LayerFE>>& layerFEs);
+ bool regionSampling, bool grayscale, bool isProtected, ScreenCaptureResults&,
+ std::optional<OutputCompositionState>& displayState,
+ std::vector<std::pair<Layer*, sp<LayerFE>>>& layers);
void readPersistentProperties();
@@ -1253,7 +1250,6 @@
// latched.
std::unordered_set<std::pair<sp<Layer>, gui::GameMode>, LayerIntHash> mLayersWithQueuedFrames;
std::unordered_set<sp<Layer>, SpHash<Layer>> mLayersWithBuffersRemoved;
- std::unordered_set<uint32_t> mLayersIdsWithQueuedFrames;
// Sorted list of layers that were composed during previous frame. This is used to
// avoid an expensive traversal of the layer hierarchy when there are no
@@ -1373,7 +1369,7 @@
sp<os::IInputFlinger> mInputFlinger;
InputWindowCommands mInputWindowCommands;
- std::unique_ptr<Hwc2::PowerAdvisor> mPowerAdvisor;
+ std::unique_ptr<adpf::PowerAdvisor> mPowerAdvisor;
void enableRefreshRateOverlay(bool enable) REQUIRES(mStateLock, kMainThreadContext);
@@ -1382,11 +1378,17 @@
// Flag used to set override desired display mode from backdoor
bool mDebugDisplayModeSetByBackdoor = false;
+ // Tracks the number of maximum queued buffers by layer owner Uid.
+ using BufferStuffingMap = ftl::SmallMap<uid_t, uint32_t, 10>;
BufferCountTracker mBufferCountTracker;
std::unordered_map<DisplayId, sp<HdrLayerInfoReporter>> mHdrLayerInfoListeners
GUARDED_BY(mStateLock);
+ sp<gui::IActivePictureListener> mActivePictureListener GUARDED_BY(mStateLock);
+ bool mHaveNewActivePictureListener GUARDED_BY(mStateLock);
+ ActivePictureUpdater mActivePictureUpdater GUARDED_BY(kMainThreadContext);
+
std::atomic<ui::Transform::RotationFlags> mActiveDisplayTransformHint;
// Must only be accessed on the main thread.
@@ -1423,6 +1425,11 @@
// Whether a display should be turned on when initialized
bool mSkipPowerOnForQuiescent;
+ // used for omitting vsync callbacks to apps when the display is not updatable
+ int mRefreshableDisplays GUARDED_BY(mStateLock) = 0;
+ void incRefreshableDisplays() REQUIRES(mStateLock);
+ void decRefreshableDisplays() REQUIRES(mStateLock);
+
frontend::LayerLifecycleManager mLayerLifecycleManager GUARDED_BY(kMainThreadContext);
frontend::LayerHierarchyBuilder mLayerHierarchyBuilder GUARDED_BY(kMainThreadContext);
frontend::LayerSnapshotBuilder mLayerSnapshotBuilder GUARDED_BY(kMainThreadContext);
@@ -1527,6 +1534,8 @@
binder::Status getHdrOutputConversionSupport(bool* outSupport) override;
binder::Status setAutoLowLatencyMode(const sp<IBinder>& display, bool on) override;
binder::Status setGameContentType(const sp<IBinder>& display, bool on) override;
+ binder::Status getMaxLayerPictureProfiles(const sp<IBinder>& display,
+ int32_t* outMaxProfiles) override;
binder::Status captureDisplay(const DisplayCaptureArgs&,
const sp<IScreenCaptureListener>&) override;
binder::Status captureDisplayById(int64_t, const CaptureArgs&,
@@ -1615,12 +1624,15 @@
binder::Status flushJankData(int32_t layerId) override;
binder::Status removeJankListener(int32_t layerId, const sp<gui::IJankListener>& listener,
int64_t afterVsync) override;
+ binder::Status setActivePictureListener(const sp<gui::IActivePictureListener>& listener);
+ binder::Status clearActivePictureListener();
private:
static const constexpr bool kUsePermissionCache = true;
status_t checkAccessPermission(bool usePermissionCache = kUsePermissionCache);
status_t checkControlDisplayBrightnessPermission();
status_t checkReadFrameBufferPermission();
+ status_t checkObservePictureProfilesPermission();
static void getDynamicDisplayInfoInternal(ui::DynamicDisplayInfo& info,
gui::DynamicDisplayInfo*& outInfo);
diff --git a/services/surfaceflinger/TransactionCallbackInvoker.cpp b/services/surfaceflinger/TransactionCallbackInvoker.cpp
index de4825b..b22ec66 100644
--- a/services/surfaceflinger/TransactionCallbackInvoker.cpp
+++ b/services/surfaceflinger/TransactionCallbackInvoker.cpp
@@ -144,7 +144,7 @@
eventStats, handle->previousReleaseCallbackId);
if (handle->bufferReleaseChannel &&
handle->previousReleaseCallbackId != ReleaseCallbackId::INVALID_ID) {
- mBufferReleases.emplace_back(handle->bufferReleaseChannel,
+ mBufferReleases.emplace_back(handle->name, handle->bufferReleaseChannel,
handle->previousReleaseCallbackId,
handle->previousReleaseFence,
handle->currentMaxAcquiredBufferCount);
@@ -159,8 +159,13 @@
void TransactionCallbackInvoker::sendCallbacks(bool onCommitOnly) {
for (const auto& bufferRelease : mBufferReleases) {
- bufferRelease.channel->writeReleaseFence(bufferRelease.callbackId, bufferRelease.fence,
- bufferRelease.currentMaxAcquiredBufferCount);
+ status_t status = bufferRelease.channel
+ ->writeReleaseFence(bufferRelease.callbackId, bufferRelease.fence,
+ bufferRelease.currentMaxAcquiredBufferCount);
+ if (status != OK) {
+ ALOGE("[%s] writeReleaseFence failed. error %d (%s)", bufferRelease.layerName.c_str(),
+ -status, strerror(-status));
+ }
}
mBufferReleases.clear();
diff --git a/services/surfaceflinger/TransactionCallbackInvoker.h b/services/surfaceflinger/TransactionCallbackInvoker.h
index d81d8d0..178ddbb 100644
--- a/services/surfaceflinger/TransactionCallbackInvoker.h
+++ b/services/surfaceflinger/TransactionCallbackInvoker.h
@@ -83,6 +83,7 @@
mCompletedTransactions;
struct BufferRelease {
+ std::string layerName;
std::shared_ptr<gui::BufferReleaseChannel::ProducerEndpoint> channel;
ReleaseCallbackId callbackId;
sp<Fence> fence;
diff --git a/services/surfaceflinger/common/FlagManager.cpp b/services/surfaceflinger/common/FlagManager.cpp
index 8a81c56..f257c7c 100644
--- a/services/surfaceflinger/common/FlagManager.cpp
+++ b/services/surfaceflinger/common/FlagManager.cpp
@@ -89,9 +89,9 @@
mBootCompleted = true;
}
-void FlagManager::dumpFlag(std::string& result, bool readonly, const char* name,
+void FlagManager::dumpFlag(std::string& result, bool aconfig, const char* name,
std::function<bool()> getter) const {
- if (readonly || mBootCompleted) {
+ if (aconfig || mBootCompleted) {
base::StringAppendF(&result, "%s: %s\n", name, getter() ? "true" : "false");
} else {
base::StringAppendF(&result, "%s: in progress (still booting)\n", name);
@@ -99,66 +99,73 @@
}
void FlagManager::dump(std::string& result) const {
-#define DUMP_FLAG_INTERVAL(name, readonly) \
- dumpFlag(result, (readonly), #name, std::bind(&FlagManager::name, this))
-#define DUMP_SERVER_FLAG(name) DUMP_FLAG_INTERVAL(name, false)
-#define DUMP_READ_ONLY_FLAG(name) DUMP_FLAG_INTERVAL(name, true)
+#define DUMP_FLAG_INTERNAL(name, aconfig) \
+ dumpFlag(result, (aconfig), #name, std::bind(&FlagManager::name, this))
+#define DUMP_LEGACY_SERVER_FLAG(name) DUMP_FLAG_INTERNAL(name, false)
+#define DUMP_ACONFIG_FLAG(name) DUMP_FLAG_INTERNAL(name, true)
base::StringAppendF(&result, "FlagManager values: \n");
/// Legacy server flags ///
- DUMP_SERVER_FLAG(use_adpf_cpu_hint);
- DUMP_SERVER_FLAG(use_skia_tracing);
+ DUMP_LEGACY_SERVER_FLAG(use_adpf_cpu_hint);
+ DUMP_LEGACY_SERVER_FLAG(use_skia_tracing);
- /// Trunk stable server flags ///
- DUMP_SERVER_FLAG(refresh_rate_overlay_on_external_display);
- DUMP_SERVER_FLAG(adpf_gpu_sf);
- DUMP_SERVER_FLAG(adpf_use_fmq_channel);
+ /// Trunk stable server (R/W) flags ///
+ DUMP_ACONFIG_FLAG(refresh_rate_overlay_on_external_display);
+ DUMP_ACONFIG_FLAG(adpf_gpu_sf);
+ DUMP_ACONFIG_FLAG(adpf_native_session_manager);
+ DUMP_ACONFIG_FLAG(adpf_use_fmq_channel);
+ DUMP_ACONFIG_FLAG(graphite_renderengine_preview_rollout);
/// Trunk stable readonly flags ///
- DUMP_READ_ONLY_FLAG(adpf_fmq_sf);
- DUMP_READ_ONLY_FLAG(connected_display);
- DUMP_READ_ONLY_FLAG(enable_small_area_detection);
- DUMP_READ_ONLY_FLAG(frame_rate_category_mrr);
- DUMP_READ_ONLY_FLAG(misc1);
- DUMP_READ_ONLY_FLAG(vrr_config);
- DUMP_READ_ONLY_FLAG(hotplug2);
- DUMP_READ_ONLY_FLAG(hdcp_level_hal);
- DUMP_READ_ONLY_FLAG(multithreaded_present);
- DUMP_READ_ONLY_FLAG(add_sf_skipped_frames_to_trace);
- DUMP_READ_ONLY_FLAG(use_known_refresh_rate_for_fps_consistency);
- DUMP_READ_ONLY_FLAG(cache_when_source_crop_layer_only_moved);
- DUMP_READ_ONLY_FLAG(enable_fro_dependent_features);
- DUMP_READ_ONLY_FLAG(display_protected);
- DUMP_READ_ONLY_FLAG(fp16_client_target);
- DUMP_READ_ONLY_FLAG(game_default_frame_rate);
- DUMP_READ_ONLY_FLAG(enable_layer_command_batching);
- DUMP_READ_ONLY_FLAG(vulkan_renderengine);
- DUMP_READ_ONLY_FLAG(renderable_buffer_usage);
- DUMP_READ_ONLY_FLAG(vrr_bugfix_24q4);
- DUMP_READ_ONLY_FLAG(vrr_bugfix_dropped_frame);
- DUMP_READ_ONLY_FLAG(restore_blur_step);
- DUMP_READ_ONLY_FLAG(dont_skip_on_early_ro);
- DUMP_READ_ONLY_FLAG(protected_if_client);
- DUMP_READ_ONLY_FLAG(idle_screen_refresh_rate_timeout);
- DUMP_READ_ONLY_FLAG(graphite_renderengine);
- DUMP_READ_ONLY_FLAG(filter_frames_before_trace_starts);
- DUMP_READ_ONLY_FLAG(latch_unsignaled_with_auto_refresh_changed);
- DUMP_READ_ONLY_FLAG(deprecate_vsync_sf);
- DUMP_READ_ONLY_FLAG(allow_n_vsyncs_in_targeter);
- DUMP_READ_ONLY_FLAG(detached_mirror);
- DUMP_READ_ONLY_FLAG(commit_not_composited);
- DUMP_READ_ONLY_FLAG(correct_dpi_with_display_size);
- DUMP_READ_ONLY_FLAG(local_tonemap_screenshots);
- DUMP_READ_ONLY_FLAG(override_trusted_overlay);
- DUMP_READ_ONLY_FLAG(flush_buffer_slots_to_uncache);
- DUMP_READ_ONLY_FLAG(force_compile_graphite_renderengine);
- DUMP_READ_ONLY_FLAG(single_hop_screenshot);
- DUMP_READ_ONLY_FLAG(trace_frame_rate_override);
- DUMP_READ_ONLY_FLAG(true_hdr_screenshots);
+ DUMP_ACONFIG_FLAG(adpf_fmq_sf);
+ DUMP_ACONFIG_FLAG(connected_display);
+ DUMP_ACONFIG_FLAG(enable_small_area_detection);
+ DUMP_ACONFIG_FLAG(stable_edid_ids);
+ DUMP_ACONFIG_FLAG(frame_rate_category_mrr);
+ DUMP_ACONFIG_FLAG(misc1);
+ DUMP_ACONFIG_FLAG(vrr_config);
+ DUMP_ACONFIG_FLAG(hdcp_level_hal);
+ DUMP_ACONFIG_FLAG(multithreaded_present);
+ DUMP_ACONFIG_FLAG(add_sf_skipped_frames_to_trace);
+ DUMP_ACONFIG_FLAG(use_known_refresh_rate_for_fps_consistency);
+ DUMP_ACONFIG_FLAG(cache_when_source_crop_layer_only_moved);
+ DUMP_ACONFIG_FLAG(enable_fro_dependent_features);
+ DUMP_ACONFIG_FLAG(display_protected);
+ DUMP_ACONFIG_FLAG(fp16_client_target);
+ DUMP_ACONFIG_FLAG(game_default_frame_rate);
+ DUMP_ACONFIG_FLAG(enable_layer_command_batching);
+ DUMP_ACONFIG_FLAG(vulkan_renderengine);
+ DUMP_ACONFIG_FLAG(renderable_buffer_usage);
+ DUMP_ACONFIG_FLAG(vrr_bugfix_24q4);
+ DUMP_ACONFIG_FLAG(vrr_bugfix_dropped_frame);
+ DUMP_ACONFIG_FLAG(restore_blur_step);
+ DUMP_ACONFIG_FLAG(dont_skip_on_early_ro);
+ DUMP_ACONFIG_FLAG(no_vsyncs_on_screen_off);
+ DUMP_ACONFIG_FLAG(protected_if_client);
+ DUMP_ACONFIG_FLAG(idle_screen_refresh_rate_timeout);
+ DUMP_ACONFIG_FLAG(graphite_renderengine);
+ DUMP_ACONFIG_FLAG(filter_frames_before_trace_starts);
+ DUMP_ACONFIG_FLAG(latch_unsignaled_with_auto_refresh_changed);
+ DUMP_ACONFIG_FLAG(deprecate_vsync_sf);
+ DUMP_ACONFIG_FLAG(allow_n_vsyncs_in_targeter);
+ DUMP_ACONFIG_FLAG(detached_mirror);
+ DUMP_ACONFIG_FLAG(commit_not_composited);
+ DUMP_ACONFIG_FLAG(correct_dpi_with_display_size);
+ DUMP_ACONFIG_FLAG(local_tonemap_screenshots);
+ DUMP_ACONFIG_FLAG(override_trusted_overlay);
+ DUMP_ACONFIG_FLAG(flush_buffer_slots_to_uncache);
+ DUMP_ACONFIG_FLAG(force_compile_graphite_renderengine);
+ DUMP_ACONFIG_FLAG(trace_frame_rate_override);
+ DUMP_ACONFIG_FLAG(true_hdr_screenshots);
+ DUMP_ACONFIG_FLAG(display_config_error_hal);
+ DUMP_ACONFIG_FLAG(connected_display_hdr);
+ DUMP_ACONFIG_FLAG(deprecate_frame_tracker);
+ DUMP_ACONFIG_FLAG(skip_invisible_windows_in_input);
+ DUMP_ACONFIG_FLAG(begone_bright_hlg);
-#undef DUMP_READ_ONLY_FLAG
-#undef DUMP_SERVER_FLAG
+#undef DUMP_ACONFIG_FLAG
+#undef DUMP_LEGACY_SERVER_FLAG
#undef DUMP_FLAG_INTERVAL
}
@@ -183,35 +190,24 @@
return getServerConfigurableFlag(serverFlagName); \
}
-#define FLAG_MANAGER_FLAG_INTERNAL(name, syspropOverride, checkForBootCompleted, owner) \
- bool FlagManager::name() const { \
- if (checkForBootCompleted) { \
- LOG_ALWAYS_FATAL_IF(!mBootCompleted, \
- "Can't read %s before boot completed as it is server writable", \
- __func__); \
- } \
- static const std::optional<bool> debugOverride = getBoolProperty(syspropOverride); \
- static const bool value = getFlagValue([] { return owner ::name(); }, debugOverride); \
- if (mUnitTestMode) { \
- /* \
- * When testing, we don't want to rely on the cached `value` or the debugOverride. \
- */ \
- return owner ::name(); \
- } \
- return value; \
+#define FLAG_MANAGER_ACONFIG_INTERNAL(name, syspropOverride, owner) \
+ bool FlagManager::name() const { \
+ static const std::optional<bool> debugOverride = getBoolProperty(syspropOverride); \
+ static const bool value = getFlagValue([] { return owner ::name(); }, debugOverride); \
+ if (mUnitTestMode) { \
+ /* \
+ * When testing, we don't want to rely on the cached `value` or the debugOverride. \
+ */ \
+ return owner ::name(); \
+ } \
+ return value; \
}
-#define FLAG_MANAGER_SERVER_FLAG(name, syspropOverride) \
- FLAG_MANAGER_FLAG_INTERNAL(name, syspropOverride, true, flags)
+#define FLAG_MANAGER_ACONFIG_FLAG(name, syspropOverride) \
+ FLAG_MANAGER_ACONFIG_INTERNAL(name, syspropOverride, flags)
-#define FLAG_MANAGER_READ_ONLY_FLAG(name, syspropOverride) \
- FLAG_MANAGER_FLAG_INTERNAL(name, syspropOverride, false, flags)
-
-#define FLAG_MANAGER_SERVER_FLAG_IMPORTED(name, syspropOverride, owner) \
- FLAG_MANAGER_FLAG_INTERNAL(name, syspropOverride, true, owner)
-
-#define FLAG_MANAGER_READ_ONLY_FLAG_IMPORTED(name, syspropOverride, owner) \
- FLAG_MANAGER_FLAG_INTERNAL(name, syspropOverride, false, owner)
+#define FLAG_MANAGER_ACONFIG_FLAG_IMPORTED(name, syspropOverride, owner) \
+ FLAG_MANAGER_ACONFIG_INTERNAL(name, syspropOverride, owner)
/// Legacy server flags ///
FLAG_MANAGER_LEGACY_SERVER_FLAG(test_flag, "", "")
@@ -221,58 +217,65 @@
"SkiaTracingFeature__use_skia_tracing")
/// Trunk stable readonly flags ///
-FLAG_MANAGER_READ_ONLY_FLAG(adpf_fmq_sf, "")
-FLAG_MANAGER_READ_ONLY_FLAG(connected_display, "")
-FLAG_MANAGER_READ_ONLY_FLAG(enable_small_area_detection, "")
-FLAG_MANAGER_READ_ONLY_FLAG(frame_rate_category_mrr, "debug.sf.frame_rate_category_mrr")
-FLAG_MANAGER_READ_ONLY_FLAG(misc1, "")
-FLAG_MANAGER_READ_ONLY_FLAG(vrr_config, "debug.sf.enable_vrr_config")
-FLAG_MANAGER_READ_ONLY_FLAG(hotplug2, "")
-FLAG_MANAGER_READ_ONLY_FLAG(hdcp_level_hal, "")
-FLAG_MANAGER_READ_ONLY_FLAG(multithreaded_present, "debug.sf.multithreaded_present")
-FLAG_MANAGER_READ_ONLY_FLAG(add_sf_skipped_frames_to_trace, "")
-FLAG_MANAGER_READ_ONLY_FLAG(use_known_refresh_rate_for_fps_consistency, "")
-FLAG_MANAGER_READ_ONLY_FLAG(cache_when_source_crop_layer_only_moved,
- "debug.sf.cache_source_crop_only_moved")
-FLAG_MANAGER_READ_ONLY_FLAG(enable_fro_dependent_features, "")
-FLAG_MANAGER_READ_ONLY_FLAG(display_protected, "")
-FLAG_MANAGER_READ_ONLY_FLAG(fp16_client_target, "debug.sf.fp16_client_target")
-FLAG_MANAGER_READ_ONLY_FLAG(game_default_frame_rate, "")
-FLAG_MANAGER_READ_ONLY_FLAG(enable_layer_command_batching, "debug.sf.enable_layer_command_batching")
-FLAG_MANAGER_READ_ONLY_FLAG(vulkan_renderengine, "debug.renderengine.vulkan")
-FLAG_MANAGER_READ_ONLY_FLAG(renderable_buffer_usage, "")
-FLAG_MANAGER_READ_ONLY_FLAG(restore_blur_step, "debug.renderengine.restore_blur_step")
-FLAG_MANAGER_READ_ONLY_FLAG(dont_skip_on_early_ro, "")
-FLAG_MANAGER_READ_ONLY_FLAG(protected_if_client, "")
-FLAG_MANAGER_READ_ONLY_FLAG(vrr_bugfix_24q4, "");
-FLAG_MANAGER_READ_ONLY_FLAG(vrr_bugfix_dropped_frame, "")
-FLAG_MANAGER_READ_ONLY_FLAG(graphite_renderengine, "debug.renderengine.graphite")
-FLAG_MANAGER_READ_ONLY_FLAG(filter_frames_before_trace_starts, "")
-FLAG_MANAGER_READ_ONLY_FLAG(latch_unsignaled_with_auto_refresh_changed, "");
-FLAG_MANAGER_READ_ONLY_FLAG(deprecate_vsync_sf, "");
-FLAG_MANAGER_READ_ONLY_FLAG(allow_n_vsyncs_in_targeter, "");
-FLAG_MANAGER_READ_ONLY_FLAG(detached_mirror, "");
-FLAG_MANAGER_READ_ONLY_FLAG(commit_not_composited, "");
-FLAG_MANAGER_READ_ONLY_FLAG(correct_dpi_with_display_size, "");
-FLAG_MANAGER_READ_ONLY_FLAG(local_tonemap_screenshots, "debug.sf.local_tonemap_screenshots");
-FLAG_MANAGER_READ_ONLY_FLAG(override_trusted_overlay, "");
-FLAG_MANAGER_READ_ONLY_FLAG(flush_buffer_slots_to_uncache, "");
-FLAG_MANAGER_READ_ONLY_FLAG(force_compile_graphite_renderengine, "");
-FLAG_MANAGER_READ_ONLY_FLAG(single_hop_screenshot, "");
-FLAG_MANAGER_READ_ONLY_FLAG(true_hdr_screenshots, "debug.sf.true_hdr_screenshots");
+FLAG_MANAGER_ACONFIG_FLAG(adpf_fmq_sf, "")
+FLAG_MANAGER_ACONFIG_FLAG(connected_display, "")
+FLAG_MANAGER_ACONFIG_FLAG(enable_small_area_detection, "")
+FLAG_MANAGER_ACONFIG_FLAG(stable_edid_ids, "debug.sf.stable_edid_ids")
+FLAG_MANAGER_ACONFIG_FLAG(frame_rate_category_mrr, "debug.sf.frame_rate_category_mrr")
+FLAG_MANAGER_ACONFIG_FLAG(misc1, "")
+FLAG_MANAGER_ACONFIG_FLAG(vrr_config, "debug.sf.enable_vrr_config")
+FLAG_MANAGER_ACONFIG_FLAG(hdcp_level_hal, "")
+FLAG_MANAGER_ACONFIG_FLAG(multithreaded_present, "debug.sf.multithreaded_present")
+FLAG_MANAGER_ACONFIG_FLAG(add_sf_skipped_frames_to_trace, "")
+FLAG_MANAGER_ACONFIG_FLAG(use_known_refresh_rate_for_fps_consistency, "")
+FLAG_MANAGER_ACONFIG_FLAG(cache_when_source_crop_layer_only_moved,
+ "debug.sf.cache_source_crop_only_moved")
+FLAG_MANAGER_ACONFIG_FLAG(enable_fro_dependent_features, "")
+FLAG_MANAGER_ACONFIG_FLAG(display_protected, "")
+FLAG_MANAGER_ACONFIG_FLAG(fp16_client_target, "debug.sf.fp16_client_target")
+FLAG_MANAGER_ACONFIG_FLAG(game_default_frame_rate, "")
+FLAG_MANAGER_ACONFIG_FLAG(enable_layer_command_batching, "debug.sf.enable_layer_command_batching")
+FLAG_MANAGER_ACONFIG_FLAG(vulkan_renderengine, "debug.renderengine.vulkan")
+FLAG_MANAGER_ACONFIG_FLAG(renderable_buffer_usage, "")
+FLAG_MANAGER_ACONFIG_FLAG(restore_blur_step, "debug.renderengine.restore_blur_step")
+FLAG_MANAGER_ACONFIG_FLAG(dont_skip_on_early_ro, "")
+FLAG_MANAGER_ACONFIG_FLAG(no_vsyncs_on_screen_off, "debug.sf.no_vsyncs_on_screen_off")
+FLAG_MANAGER_ACONFIG_FLAG(protected_if_client, "")
+FLAG_MANAGER_ACONFIG_FLAG(vrr_bugfix_24q4, "");
+FLAG_MANAGER_ACONFIG_FLAG(vrr_bugfix_dropped_frame, "")
+FLAG_MANAGER_ACONFIG_FLAG(graphite_renderengine, "debug.renderengine.graphite")
+FLAG_MANAGER_ACONFIG_FLAG(filter_frames_before_trace_starts, "")
+FLAG_MANAGER_ACONFIG_FLAG(latch_unsignaled_with_auto_refresh_changed, "");
+FLAG_MANAGER_ACONFIG_FLAG(deprecate_vsync_sf, "");
+FLAG_MANAGER_ACONFIG_FLAG(allow_n_vsyncs_in_targeter, "");
+FLAG_MANAGER_ACONFIG_FLAG(detached_mirror, "");
+FLAG_MANAGER_ACONFIG_FLAG(commit_not_composited, "");
+FLAG_MANAGER_ACONFIG_FLAG(correct_dpi_with_display_size, "");
+FLAG_MANAGER_ACONFIG_FLAG(local_tonemap_screenshots, "debug.sf.local_tonemap_screenshots");
+FLAG_MANAGER_ACONFIG_FLAG(override_trusted_overlay, "");
+FLAG_MANAGER_ACONFIG_FLAG(flush_buffer_slots_to_uncache, "");
+FLAG_MANAGER_ACONFIG_FLAG(force_compile_graphite_renderengine, "");
+FLAG_MANAGER_ACONFIG_FLAG(true_hdr_screenshots, "debug.sf.true_hdr_screenshots");
+FLAG_MANAGER_ACONFIG_FLAG(display_config_error_hal, "");
+FLAG_MANAGER_ACONFIG_FLAG(connected_display_hdr, "");
+FLAG_MANAGER_ACONFIG_FLAG(deprecate_frame_tracker, "");
+FLAG_MANAGER_ACONFIG_FLAG(skip_invisible_windows_in_input, "");
+FLAG_MANAGER_ACONFIG_FLAG(begone_bright_hlg, "debug.sf.begone_bright_hlg");
-/// Trunk stable server flags ///
-FLAG_MANAGER_SERVER_FLAG(refresh_rate_overlay_on_external_display, "")
-FLAG_MANAGER_SERVER_FLAG(adpf_gpu_sf, "")
+/// Trunk stable server (R/W) flags ///
+FLAG_MANAGER_ACONFIG_FLAG(refresh_rate_overlay_on_external_display, "")
+FLAG_MANAGER_ACONFIG_FLAG(adpf_gpu_sf, "")
+FLAG_MANAGER_ACONFIG_FLAG(adpf_native_session_manager, "");
+FLAG_MANAGER_ACONFIG_FLAG(graphite_renderengine_preview_rollout, "");
-/// Trunk stable server flags from outside SurfaceFlinger ///
-FLAG_MANAGER_SERVER_FLAG_IMPORTED(adpf_use_fmq_channel, "", android::os)
+/// Trunk stable server (R/W) flags from outside SurfaceFlinger ///
+FLAG_MANAGER_ACONFIG_FLAG_IMPORTED(adpf_use_fmq_channel, "", android::os)
/// Trunk stable readonly flags from outside SurfaceFlinger ///
-FLAG_MANAGER_READ_ONLY_FLAG_IMPORTED(idle_screen_refresh_rate_timeout, "",
- com::android::server::display::feature::flags)
-FLAG_MANAGER_READ_ONLY_FLAG_IMPORTED(adpf_use_fmq_channel_fixed, "", android::os)
-FLAG_MANAGER_READ_ONLY_FLAG_IMPORTED(trace_frame_rate_override, "",
- com::android::graphics::libgui::flags);
+FLAG_MANAGER_ACONFIG_FLAG_IMPORTED(idle_screen_refresh_rate_timeout, "",
+ com::android::server::display::feature::flags)
+FLAG_MANAGER_ACONFIG_FLAG_IMPORTED(adpf_use_fmq_channel_fixed, "", android::os)
+FLAG_MANAGER_ACONFIG_FLAG_IMPORTED(trace_frame_rate_override, "",
+ com::android::graphics::libgui::flags);
} // namespace android
diff --git a/services/surfaceflinger/common/include/common/FlagManager.h b/services/surfaceflinger/common/include/common/FlagManager.h
index b097bf9..a461627 100644
--- a/services/surfaceflinger/common/include/common/FlagManager.h
+++ b/services/surfaceflinger/common/include/common/FlagManager.h
@@ -47,20 +47,22 @@
bool use_adpf_cpu_hint() const;
bool use_skia_tracing() const;
- /// Trunk stable server flags ///
+ /// Trunk stable server (R/W) flags ///
bool refresh_rate_overlay_on_external_display() const;
bool adpf_gpu_sf() const;
bool adpf_use_fmq_channel() const;
+ bool adpf_native_session_manager() const;
bool adpf_use_fmq_channel_fixed() const;
+ bool graphite_renderengine_preview_rollout() const;
/// Trunk stable readonly flags ///
bool adpf_fmq_sf() const;
bool connected_display() const;
bool frame_rate_category_mrr() const;
bool enable_small_area_detection() const;
+ bool stable_edid_ids() const;
bool misc1() const;
bool vrr_config() const;
- bool hotplug2() const;
bool hdcp_level_hal() const;
bool multithreaded_present() const;
bool add_sf_skipped_frames_to_trace() const;
@@ -77,6 +79,7 @@
bool renderable_buffer_usage() const;
bool restore_blur_step() const;
bool dont_skip_on_early_ro() const;
+ bool no_vsyncs_on_screen_off() const;
bool protected_if_client() const;
bool idle_screen_refresh_rate_timeout() const;
bool graphite_renderengine() const;
@@ -91,9 +94,13 @@
bool override_trusted_overlay() const;
bool flush_buffer_slots_to_uncache() const;
bool force_compile_graphite_renderengine() const;
- bool single_hop_screenshot() const;
bool trace_frame_rate_override() const;
bool true_hdr_screenshots() const;
+ bool display_config_error_hal() const;
+ bool connected_display_hdr() const;
+ bool deprecate_frame_tracker() const;
+ bool skip_invisible_windows_in_input() const;
+ bool begone_bright_hlg() const;
protected:
// overridden for unit tests
diff --git a/services/surfaceflinger/fuzzer/Android.bp b/services/surfaceflinger/fuzzer/Android.bp
index ae502cf..1de6b4a 100644
--- a/services/surfaceflinger/fuzzer/Android.bp
+++ b/services/surfaceflinger/fuzzer/Android.bp
@@ -28,16 +28,18 @@
cc_defaults {
name: "surfaceflinger_fuzz_defaults",
static_libs: [
- "libc++fs",
"libsurfaceflinger_common",
],
srcs: [
+ ":libsurfaceflinger_backend_mock_sources",
+ ":libsurfaceflinger_mock_sources",
":libsurfaceflinger_sources",
],
defaults: [
"libsurfaceflinger_defaults",
],
header_libs: [
+ "libsurfaceflinger_backend_mock_headers",
"libsurfaceflinger_headers",
],
cflags: [
diff --git a/services/surfaceflinger/surfaceflinger.logtags b/services/surfaceflinger/surfaceflinger.logtags
index e68d9f5..6dbe0dd 100644
--- a/services/surfaceflinger/surfaceflinger.logtags
+++ b/services/surfaceflinger/surfaceflinger.logtags
@@ -31,7 +31,7 @@
# 6: Percent
# Default value for data of type int/long is 2 (bytes).
#
-# See system/core/logcat/event.logtags for the master copy of the tags.
+# See system/logging/logcat/event.logtags for the master copy of the tags.
# 60100 - 60199 reserved for surfaceflinger
diff --git a/services/surfaceflinger/surfaceflinger_flags_new.aconfig b/services/surfaceflinger/surfaceflinger_flags_new.aconfig
index d724dfc..2c44e4c 100644
--- a/services/surfaceflinger/surfaceflinger_flags_new.aconfig
+++ b/services/surfaceflinger/surfaceflinger_flags_new.aconfig
@@ -19,14 +19,37 @@
} # adpf_gpu_sf
flag {
+ name: "adpf_native_session_manager"
+ namespace: "game"
+ description: "Controls ADPF SessionManager being enabled in SF"
+ bug: "367803904"
+} # adpf_sessionmanager
+
+flag {
name: "arr_setframerate_api"
namespace: "core_graphics"
- description: "New setFrameRate API for Android 16"
+ description: "New SDK Surface#setFrameRate API and Surface.FrameRateParams for Android 16"
bug: "356987016"
is_fixed_read_only: true
} # arr_setframerate_api
flag {
+ name: "arr_surfacecontrol_setframerate_api"
+ namespace: "core_graphics"
+ description: "New SDK SurfaceControl.Transaction#setFrameRate API for Android 16"
+ bug: "356987016"
+ is_fixed_read_only: true
+} # arr_surfacecontrol_setframerate_api
+
+flag {
+ name: "begone_bright_hlg"
+ namespace: "core_graphics"
+ description: "Caps HLG brightness relative to SDR"
+ bug: "362510107"
+ is_fixed_read_only: true
+} # begone_bright_hlg
+
+flag {
name: "ce_fence_promise"
namespace: "window_surfaces"
description: "Moves logic for buffer release fences into LayerFE"
@@ -49,6 +72,14 @@
} # commit_not_composited
flag {
+ name: "connected_display_hdr"
+ namespace: "core_graphics"
+ description: "enable connected display hdr capability"
+ bug: "374182788"
+ is_fixed_read_only: true
+} # connected_display_hdr
+
+flag {
name: "correct_dpi_with_display_size"
namespace: "core_graphics"
description: "indicate whether missing or likely incorrect dpi should be corrected using the display size."
@@ -60,6 +91,17 @@
} # correct_dpi_with_display_size
flag {
+ name: "deprecate_frame_tracker"
+ namespace: "core_graphics"
+ description: "Deprecate using FrameTracker to accumulate and provide FrameStats"
+ bug: "241394120"
+ is_fixed_read_only: true
+ metadata {
+ purpose: PURPOSE_BUGFIX
+ }
+} # deprecate_frame_tracker
+
+flag {
name: "deprecate_vsync_sf"
namespace: "core_graphics"
description: "Depracate eVsyncSourceSurfaceFlinger and use vsync_app everywhere"
@@ -82,6 +124,14 @@
} # detached_mirror
flag {
+ name: "display_config_error_hal"
+ namespace: "core_graphics"
+ description: "Report HAL display configuration errors like modeset failure or link training failure"
+ bug: "374184110"
+ is_fixed_read_only: true
+} # display_config_error_hal
+
+flag {
name: "filter_frames_before_trace_starts"
namespace: "core_graphics"
description: "Do not trace FrameTimeline events for frames started before the trace started"
@@ -123,6 +173,13 @@
} # frame_rate_category_mrr
flag {
+ name: "graphite_renderengine_preview_rollout"
+ namespace: "core_graphics"
+ description: "R/W flag to enable Skia's Graphite Vulkan backend in RenderEngine, IF it is already compiled with force_compile_graphite_renderengine, AND the debug.renderengine.graphite_preview_optin sysprop is set to true."
+ bug: "293371537"
+} # graphite_renderengine_preview_rollout
+
+flag {
name: "latch_unsignaled_with_auto_refresh_changed"
namespace: "core_graphics"
description: "Ignore eAutoRefreshChanged with latch unsignaled"
@@ -142,6 +199,14 @@
} # local_tonemap_screenshots
flag {
+ name: "no_vsyncs_on_screen_off"
+ namespace: "core_graphics"
+ description: "Stop vsync / Choreographer callbacks to apps when the screen is off"
+ bug: "331636736"
+ is_fixed_read_only: true
+} # no_vsyncs_on_screen_off
+
+flag {
name: "single_hop_screenshot"
namespace: "window_surfaces"
description: "Only access SF main thread once during a screenshot"
@@ -153,6 +218,25 @@
} # single_hop_screenshot
flag {
+ name: "skip_invisible_windows_in_input"
+ namespace: "window_surfaces"
+ description: "Only send visible windows to input list"
+ bug: "305254099"
+ is_fixed_read_only: true
+ metadata {
+ purpose: PURPOSE_BUGFIX
+ }
+ } # skip_invisible_windows_in_input
+
+flag {
+ name: "stable_edid_ids"
+ namespace: "core_graphics"
+ description: "Guard use of the new stable EDID-based display IDs system."
+ bug: "352320847"
+ is_fixed_read_only: true
+} # stable_edid_ids
+
+flag {
name: "true_hdr_screenshots"
namespace: "core_graphics"
description: "Enables screenshotting display content in HDR, sans tone mapping"
diff --git a/services/surfaceflinger/tests/Credentials_test.cpp b/services/surfaceflinger/tests/Credentials_test.cpp
index e6fed63..7b6e4bf 100644
--- a/services/surfaceflinger/tests/Credentials_test.cpp
+++ b/services/surfaceflinger/tests/Credentials_test.cpp
@@ -341,9 +341,9 @@
WindowInfosListenerUtils windowInfosListenerUtils;
std::string name = "Test Layer";
sp<IBinder> token = sp<BBinder>::make();
- WindowInfo windowInfo;
- windowInfo.name = name;
- windowInfo.token = token;
+ auto windowInfo = sp<gui::WindowInfoHandle>::make();
+ windowInfo->editInfo()->name = name;
+ windowInfo->editInfo()->token = token;
sp<SurfaceControl> surfaceControl =
mComposerClient->createSurface(String8(name.c_str()), 100, 100, PIXEL_FORMAT_RGBA_8888,
ISurfaceComposerClient::eFXSurfaceBufferState);
@@ -370,7 +370,8 @@
UIDFaker f(AID_SYSTEM);
auto windowIsPresentAndNotTrusted = [&](const std::vector<WindowInfo>& windowInfos) {
auto foundWindowInfo =
- WindowInfosListenerUtils::findMatchingWindowInfo(windowInfo, windowInfos);
+ WindowInfosListenerUtils::findMatchingWindowInfo(*windowInfo->getInfo(),
+ windowInfos);
if (!foundWindowInfo) {
return false;
}
@@ -386,7 +387,8 @@
Transaction().setTrustedOverlay(surfaceControl, true).apply(/*synchronous=*/true);
auto windowIsPresentAndTrusted = [&](const std::vector<WindowInfo>& windowInfos) {
auto foundWindowInfo =
- WindowInfosListenerUtils::findMatchingWindowInfo(windowInfo, windowInfos);
+ WindowInfosListenerUtils::findMatchingWindowInfo(*windowInfo->getInfo(),
+ windowInfos);
if (!foundWindowInfo) {
return false;
}
diff --git a/services/surfaceflinger/tests/WindowInfosListener_test.cpp b/services/surfaceflinger/tests/WindowInfosListener_test.cpp
index ad9a674..2dd0dd9 100644
--- a/services/surfaceflinger/tests/WindowInfosListener_test.cpp
+++ b/services/surfaceflinger/tests/WindowInfosListener_test.cpp
@@ -50,9 +50,9 @@
TEST_F(WindowInfosListenerTest, WindowInfoAddedAndRemoved) {
std::string name = "Test Layer";
sp<IBinder> token = sp<BBinder>::make();
- WindowInfo windowInfo;
- windowInfo.name = name;
- windowInfo.token = token;
+ auto windowInfo = sp<gui::WindowInfoHandle>::make();
+ windowInfo->editInfo()->name = name;
+ windowInfo->editInfo()->token = token;
sp<SurfaceControl> surfaceControl =
mClient->createSurface(String8(name.c_str()), 100, 100, PIXEL_FORMAT_RGBA_8888,
ISurfaceComposerClient::eFXSurfaceBufferState);
@@ -65,14 +65,14 @@
.apply();
auto windowPresent = [&](const std::vector<WindowInfo>& windowInfos) {
- return findMatchingWindowInfo(windowInfo, windowInfos);
+ return findMatchingWindowInfo(*windowInfo->getInfo(), windowInfos);
};
ASSERT_TRUE(waitForWindowInfosPredicate(windowPresent));
Transaction().reparent(surfaceControl, nullptr).apply();
auto windowNotPresent = [&](const std::vector<WindowInfo>& windowInfos) {
- return !findMatchingWindowInfo(windowInfo, windowInfos);
+ return !findMatchingWindowInfo(*windowInfo->getInfo(), windowInfos);
};
ASSERT_TRUE(waitForWindowInfosPredicate(windowNotPresent));
}
@@ -80,9 +80,9 @@
TEST_F(WindowInfosListenerTest, WindowInfoChanged) {
std::string name = "Test Layer";
sp<IBinder> token = sp<BBinder>::make();
- WindowInfo windowInfo;
- windowInfo.name = name;
- windowInfo.token = token;
+ auto windowInfo = sp<gui::WindowInfoHandle>::make();
+ windowInfo->editInfo()->name = name;
+ windowInfo->editInfo()->token = token;
sp<SurfaceControl> surfaceControl =
mClient->createSurface(String8(name.c_str()), 100, 100, PIXEL_FORMAT_RGBA_8888,
ISurfaceComposerClient::eFXSurfaceBufferState);
@@ -96,7 +96,7 @@
.apply();
auto windowIsPresentAndTouchableRegionEmpty = [&](const std::vector<WindowInfo>& windowInfos) {
- auto foundWindowInfo = findMatchingWindowInfo(windowInfo, windowInfos);
+ auto foundWindowInfo = findMatchingWindowInfo(*windowInfo->getInfo(), windowInfos);
if (!foundWindowInfo) {
return false;
}
@@ -104,19 +104,19 @@
};
ASSERT_TRUE(waitForWindowInfosPredicate(windowIsPresentAndTouchableRegionEmpty));
- windowInfo.addTouchableRegion({0, 0, 50, 50});
+ windowInfo->editInfo()->addTouchableRegion({0, 0, 50, 50});
Transaction().setInputWindowInfo(surfaceControl, windowInfo).apply();
auto windowIsPresentAndTouchableRegionMatches =
[&](const std::vector<WindowInfo>& windowInfos) {
- auto foundWindowInfo = findMatchingWindowInfo(windowInfo, windowInfos);
+ auto foundWindowInfo = findMatchingWindowInfo(*windowInfo->getInfo(), windowInfos);
if (!foundWindowInfo) {
return false;
}
auto touchableRegion =
foundWindowInfo->transform.transform(foundWindowInfo->touchableRegion);
- return touchableRegion.hasSameRects(windowInfo.touchableRegion);
+ return touchableRegion.hasSameRects(windowInfo->getInfo()->touchableRegion);
};
ASSERT_TRUE(waitForWindowInfosPredicate(windowIsPresentAndTouchableRegionMatches));
}
diff --git a/services/surfaceflinger/tests/common/LayerLifecycleManagerHelper.h b/services/surfaceflinger/tests/common/LayerLifecycleManagerHelper.h
index b472047..9794620 100644
--- a/services/surfaceflinger/tests/common/LayerLifecycleManagerHelper.h
+++ b/services/surfaceflinger/tests/common/LayerLifecycleManagerHelper.h
@@ -218,6 +218,17 @@
mLifecycleManager.applyTransactions(transactions);
}
+ void setAutoRefresh(uint32_t id, bool autoRefresh) {
+ std::vector<TransactionState> transactions;
+ transactions.emplace_back();
+ transactions.back().states.push_back({});
+
+ transactions.back().states.front().state.what = layer_state_t::eAutoRefreshChanged;
+ transactions.back().states.front().layerId = id;
+ transactions.back().states.front().state.autoRefresh = autoRefresh;
+ mLifecycleManager.applyTransactions(transactions);
+ }
+
void hideLayer(uint32_t id) {
setFlags(id, layer_state_t::eLayerHidden, layer_state_t::eLayerHidden);
}
diff --git a/services/surfaceflinger/tests/tracing/Android.bp b/services/surfaceflinger/tests/tracing/Android.bp
index bce1406..6eb7f4a 100644
--- a/services/surfaceflinger/tests/tracing/Android.bp
+++ b/services/surfaceflinger/tests/tracing/Android.bp
@@ -35,9 +35,6 @@
":libsurfaceflinger_mock_sources",
"TransactionTraceTestSuite.cpp",
],
- static_libs: [
- "libc++fs",
- ],
header_libs: [
"libsurfaceflinger_mocks_headers",
],
diff --git a/services/surfaceflinger/tests/unittests/ActivePictureUpdaterTest.cpp b/services/surfaceflinger/tests/unittests/ActivePictureUpdaterTest.cpp
new file mode 100644
index 0000000..b926d2f
--- /dev/null
+++ b/services/surfaceflinger/tests/unittests/ActivePictureUpdaterTest.cpp
@@ -0,0 +1,336 @@
+/*
+ * Copyright 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <gmock/gmock.h>
+#include <gtest/gtest.h>
+
+#include <android/gui/ActivePicture.h>
+#include <android/gui/IActivePictureListener.h>
+#include <compositionengine/mock/CompositionEngine.h>
+#include <mock/DisplayHardware/MockComposer.h>
+#include <mock/MockLayer.h>
+#include <renderengine/mock/RenderEngine.h>
+
+#include "ActivePictureUpdater.h"
+#include "LayerFE.h"
+#include "TestableSurfaceFlinger.h"
+
+namespace android {
+
+using android::compositionengine::LayerFECompositionState;
+using android::gui::ActivePicture;
+using android::gui::IActivePictureListener;
+using android::mock::MockLayer;
+using surfaceflinger::frontend::LayerSnapshot;
+using testing::_;
+using testing::NiceMock;
+using testing::Return;
+
+class TestableLayerFE : public LayerFE {
+public:
+ TestableLayerFE() : LayerFE("TestableLayerFE"), snapshot(*(new LayerSnapshot)) {
+ mSnapshot = std::unique_ptr<LayerSnapshot>(&snapshot);
+ }
+
+ LayerSnapshot& snapshot;
+};
+
+class ActivePictureUpdaterTest : public testing::Test {
+protected:
+ SurfaceFlinger* flinger() {
+ if (!mFlingerSetup) {
+ mFlinger.setupMockScheduler();
+ mFlinger.setupComposer(std::make_unique<Hwc2::mock::Composer>());
+ mFlinger.setupRenderEngine(std::make_unique<renderengine::mock::RenderEngine>());
+ mFlingerSetup = true;
+ }
+ return mFlinger.flinger();
+ }
+
+private:
+ TestableSurfaceFlinger mFlinger;
+ bool mFlingerSetup = false;
+};
+
+// Hack to workaround initializer lists not working for parcelables because parcelables inherit from
+// Parcelable, which has a virtual destructor.
+auto UnorderedElementsAre(std::initializer_list<std::tuple<int32_t, int32_t, int64_t>> tuples) {
+ std::vector<ActivePicture> activePictures;
+ for (auto tuple : tuples) {
+ ActivePicture ap;
+ ap.layerId = std::get<0>(tuple);
+ ap.ownerUid = std::get<1>(tuple);
+ ap.pictureProfileId = std::get<2>(tuple);
+ activePictures.push_back(ap);
+ }
+ return testing::UnorderedElementsAreArray(activePictures);
+}
+
+// Parcelables don't define this for matchers, which is unfortunate
+void PrintTo(const ActivePicture& activePicture, std::ostream* os) {
+ *os << activePicture.toString();
+}
+
+TEST_F(ActivePictureUpdaterTest, notCalledWithNoProfile) {
+ sp<NiceMock<MockLayer>> layer = sp<NiceMock<MockLayer>>::make(flinger(), 100);
+ TestableLayerFE layerFE;
+ EXPECT_CALL(*layer, getOwnerUid()).WillRepeatedly(Return(uid_t(10)));
+
+ ActivePictureUpdater updater;
+ {
+ layerFE.snapshot.pictureProfileHandle = PictureProfileHandle::NONE;
+ updater.onLayerComposed(*layer, layerFE, layerFE.stealCompositionResult());
+
+ ASSERT_FALSE(updater.updateAndHasChanged());
+ }
+}
+
+TEST_F(ActivePictureUpdaterTest, calledWhenLayerStartsUsingProfile) {
+ sp<NiceMock<MockLayer>> layer = sp<NiceMock<MockLayer>>::make(flinger(), 100);
+ TestableLayerFE layerFE;
+ EXPECT_CALL(*layer, getOwnerUid()).WillRepeatedly(Return(uid_t(10)));
+
+ ActivePictureUpdater updater;
+ {
+ layerFE.snapshot.pictureProfileHandle = PictureProfileHandle::NONE;
+ updater.onLayerComposed(*layer, layerFE, layerFE.stealCompositionResult());
+
+ ASSERT_FALSE(updater.updateAndHasChanged());
+ }
+ {
+ layerFE.snapshot.pictureProfileHandle = PictureProfileHandle(1);
+ layerFE.onPictureProfileCommitted();
+ updater.onLayerComposed(*layer, layerFE, layerFE.stealCompositionResult());
+
+ ASSERT_TRUE(updater.updateAndHasChanged());
+ EXPECT_THAT(updater.getActivePictures(), UnorderedElementsAre({{100, 10, 1}}));
+ }
+}
+
+TEST_F(ActivePictureUpdaterTest, notCalledWhenLayerContinuesUsingProfile) {
+ sp<NiceMock<MockLayer>> layer = sp<NiceMock<MockLayer>>::make(flinger(), 100);
+ TestableLayerFE layerFE;
+ EXPECT_CALL(*layer, getOwnerUid()).WillRepeatedly(Return(uid_t(10)));
+
+ ActivePictureUpdater updater;
+ {
+ layerFE.snapshot.pictureProfileHandle = PictureProfileHandle(1);
+ layerFE.onPictureProfileCommitted();
+ updater.onLayerComposed(*layer, layerFE, layerFE.stealCompositionResult());
+
+ ASSERT_TRUE(updater.updateAndHasChanged());
+ EXPECT_THAT(updater.getActivePictures(), UnorderedElementsAre({{100, 10, 1}}));
+ }
+ {
+ layerFE.snapshot.pictureProfileHandle = PictureProfileHandle(1);
+ layerFE.onPictureProfileCommitted();
+ updater.onLayerComposed(*layer, layerFE, layerFE.stealCompositionResult());
+
+ ASSERT_FALSE(updater.updateAndHasChanged());
+ }
+}
+
+TEST_F(ActivePictureUpdaterTest, calledWhenLayerStopsUsingProfile) {
+ sp<NiceMock<MockLayer>> layer = sp<NiceMock<MockLayer>>::make(flinger(), 100);
+ TestableLayerFE layerFE;
+ EXPECT_CALL(*layer, getOwnerUid()).WillRepeatedly(Return(uid_t(10)));
+
+ ActivePictureUpdater updater;
+ {
+ layerFE.snapshot.pictureProfileHandle = PictureProfileHandle(1);
+ layerFE.onPictureProfileCommitted();
+ updater.onLayerComposed(*layer, layerFE, layerFE.stealCompositionResult());
+
+ ASSERT_TRUE(updater.updateAndHasChanged());
+ EXPECT_THAT(updater.getActivePictures(), UnorderedElementsAre({{100, 10, 1}}));
+ }
+ {
+ layerFE.snapshot.pictureProfileHandle = PictureProfileHandle::NONE;
+ updater.onLayerComposed(*layer, layerFE, layerFE.stealCompositionResult());
+
+ ASSERT_TRUE(updater.updateAndHasChanged());
+ EXPECT_THAT(updater.getActivePictures(), UnorderedElementsAre({}));
+ }
+}
+
+TEST_F(ActivePictureUpdaterTest, calledWhenLayerChangesProfile) {
+ sp<NiceMock<MockLayer>> layer = sp<NiceMock<MockLayer>>::make(flinger(), 100);
+ TestableLayerFE layerFE;
+ EXPECT_CALL(*layer, getOwnerUid()).WillRepeatedly(Return(uid_t(10)));
+
+ ActivePictureUpdater updater;
+ {
+ layerFE.snapshot.pictureProfileHandle = PictureProfileHandle(1);
+ layerFE.onPictureProfileCommitted();
+ updater.onLayerComposed(*layer, layerFE, layerFE.stealCompositionResult());
+
+ ASSERT_TRUE(updater.updateAndHasChanged());
+ EXPECT_THAT(updater.getActivePictures(), UnorderedElementsAre({{100, 10, 1}}));
+ }
+ {
+ layerFE.snapshot.pictureProfileHandle = PictureProfileHandle(2);
+ layerFE.onPictureProfileCommitted();
+ updater.onLayerComposed(*layer, layerFE, layerFE.stealCompositionResult());
+
+ ASSERT_TRUE(updater.updateAndHasChanged());
+ EXPECT_THAT(updater.getActivePictures(), UnorderedElementsAre({{100, 10, 2}}));
+ }
+}
+
+TEST_F(ActivePictureUpdaterTest, notCalledWhenUncommittedLayerChangesProfile) {
+ sp<NiceMock<MockLayer>> layer1 = sp<NiceMock<MockLayer>>::make(flinger(), 100);
+ TestableLayerFE layerFE1;
+ EXPECT_CALL(*layer1, getOwnerUid()).WillRepeatedly(Return(uid_t(10)));
+
+ sp<NiceMock<MockLayer>> layer2 = sp<NiceMock<MockLayer>>::make(flinger(), 200);
+ TestableLayerFE layerFE2;
+ EXPECT_CALL(*layer2, getOwnerUid()).WillRepeatedly(Return(uid_t(20)));
+
+ ActivePictureUpdater updater;
+ {
+ layerFE1.snapshot.pictureProfileHandle = PictureProfileHandle(1);
+ layerFE1.onPictureProfileCommitted();
+ updater.onLayerComposed(*layer1, layerFE1, layerFE1.stealCompositionResult());
+
+ layerFE2.snapshot.pictureProfileHandle = PictureProfileHandle(1);
+ updater.onLayerComposed(*layer2, layerFE2, layerFE2.stealCompositionResult());
+
+ ASSERT_TRUE(updater.updateAndHasChanged());
+ EXPECT_THAT(updater.getActivePictures(), UnorderedElementsAre({{100, 10, 1}}));
+ }
+ {
+ layerFE1.snapshot.pictureProfileHandle = PictureProfileHandle(1);
+ layerFE1.onPictureProfileCommitted();
+ updater.onLayerComposed(*layer1, layerFE1, layerFE1.stealCompositionResult());
+
+ layerFE2.snapshot.pictureProfileHandle = PictureProfileHandle(2);
+ updater.onLayerComposed(*layer2, layerFE2, layerFE2.stealCompositionResult());
+
+ ASSERT_FALSE(updater.updateAndHasChanged());
+ }
+}
+
+TEST_F(ActivePictureUpdaterTest, calledWhenDifferentLayerUsesSameProfile) {
+ sp<NiceMock<MockLayer>> layer1 = sp<NiceMock<MockLayer>>::make(flinger(), 100);
+ TestableLayerFE layerFE1;
+ EXPECT_CALL(*layer1, getOwnerUid()).WillRepeatedly(Return(uid_t(10)));
+
+ sp<NiceMock<MockLayer>> layer2 = sp<NiceMock<MockLayer>>::make(flinger(), 200);
+ TestableLayerFE layerFE2;
+ EXPECT_CALL(*layer2, getOwnerUid()).WillRepeatedly(Return(uid_t(20)));
+
+ ActivePictureUpdater updater;
+ {
+ layerFE1.snapshot.pictureProfileHandle = PictureProfileHandle(1);
+ layerFE1.onPictureProfileCommitted();
+ updater.onLayerComposed(*layer1, layerFE1, layerFE1.stealCompositionResult());
+
+ layerFE2.snapshot.pictureProfileHandle = PictureProfileHandle(2);
+ layerFE2.onPictureProfileCommitted();
+ updater.onLayerComposed(*layer2, layerFE2, layerFE2.stealCompositionResult());
+
+ ASSERT_TRUE(updater.updateAndHasChanged());
+ EXPECT_THAT(updater.getActivePictures(),
+ UnorderedElementsAre({{100, 10, 1}, {200, 20, 2}}));
+ }
+ {
+ layerFE1.snapshot.pictureProfileHandle = PictureProfileHandle(2);
+ layerFE1.onPictureProfileCommitted();
+ updater.onLayerComposed(*layer1, layerFE1, layerFE1.stealCompositionResult());
+
+ layerFE2.snapshot.pictureProfileHandle = PictureProfileHandle(1);
+ layerFE2.onPictureProfileCommitted();
+ updater.onLayerComposed(*layer2, layerFE2, layerFE2.stealCompositionResult());
+
+ ASSERT_TRUE(updater.updateAndHasChanged());
+ EXPECT_THAT(updater.getActivePictures(),
+ UnorderedElementsAre({{100, 10, 2}, {200, 20, 1}}));
+ }
+}
+
+TEST_F(ActivePictureUpdaterTest, calledWhenSameUidUsesSameProfile) {
+ sp<NiceMock<MockLayer>> layer1 = sp<NiceMock<MockLayer>>::make(flinger(), 100);
+ TestableLayerFE layerFE1;
+ EXPECT_CALL(*layer1, getOwnerUid()).WillRepeatedly(Return(uid_t(10)));
+
+ sp<NiceMock<MockLayer>> layer2 = sp<NiceMock<MockLayer>>::make(flinger(), 200);
+ TestableLayerFE layerFE2;
+ EXPECT_CALL(*layer2, getOwnerUid()).WillRepeatedly(Return(uid_t(10)));
+
+ ActivePictureUpdater updater;
+ {
+ layerFE1.snapshot.pictureProfileHandle = PictureProfileHandle(1);
+ layerFE1.onPictureProfileCommitted();
+ updater.onLayerComposed(*layer1, layerFE1, layerFE1.stealCompositionResult());
+
+ layerFE2.snapshot.pictureProfileHandle = PictureProfileHandle(2);
+ layerFE2.onPictureProfileCommitted();
+ updater.onLayerComposed(*layer2, layerFE2, layerFE2.stealCompositionResult());
+
+ ASSERT_TRUE(updater.updateAndHasChanged());
+ EXPECT_THAT(updater.getActivePictures(),
+ UnorderedElementsAre({{100, 10, 1}, {200, 10, 2}}));
+ }
+ {
+ layerFE1.snapshot.pictureProfileHandle = PictureProfileHandle(2);
+ layerFE1.onPictureProfileCommitted();
+ updater.onLayerComposed(*layer1, layerFE1, layerFE1.stealCompositionResult());
+
+ layerFE2.snapshot.pictureProfileHandle = PictureProfileHandle(1);
+ layerFE2.onPictureProfileCommitted();
+ updater.onLayerComposed(*layer2, layerFE2, layerFE2.stealCompositionResult());
+
+ ASSERT_TRUE(updater.updateAndHasChanged());
+ EXPECT_THAT(updater.getActivePictures(),
+ UnorderedElementsAre({{100, 10, 2}, {200, 10, 1}}));
+ }
+}
+
+TEST_F(ActivePictureUpdaterTest, calledWhenNewLayerUsesSameProfile) {
+ sp<NiceMock<MockLayer>> layer1 = sp<NiceMock<MockLayer>>::make(flinger(), 100);
+ TestableLayerFE layerFE1;
+ EXPECT_CALL(*layer1, getOwnerUid()).WillRepeatedly(Return(uid_t(10)));
+
+ ActivePictureUpdater updater;
+ {
+ layerFE1.snapshot.pictureProfileHandle = PictureProfileHandle(1);
+ layerFE1.onPictureProfileCommitted();
+ updater.onLayerComposed(*layer1, layerFE1, layerFE1.stealCompositionResult());
+
+ ASSERT_TRUE(updater.updateAndHasChanged());
+ EXPECT_THAT(updater.getActivePictures(), UnorderedElementsAre({{100, 10, 1}}));
+ }
+
+ sp<NiceMock<MockLayer>> layer2 = sp<NiceMock<MockLayer>>::make(flinger(), 200);
+ TestableLayerFE layerFE2;
+ EXPECT_CALL(*layer2, getOwnerUid()).WillRepeatedly(Return(uid_t(10)));
+
+ {
+ layerFE1.snapshot.pictureProfileHandle = PictureProfileHandle(1);
+ layerFE1.onPictureProfileCommitted();
+ updater.onLayerComposed(*layer1, layerFE1, layerFE1.stealCompositionResult());
+
+ layerFE2.snapshot.pictureProfileHandle = PictureProfileHandle(1);
+ layerFE2.onPictureProfileCommitted();
+ updater.onLayerComposed(*layer2, layerFE2, layerFE2.stealCompositionResult());
+
+ ASSERT_TRUE(updater.updateAndHasChanged());
+ EXPECT_THAT(updater.getActivePictures(),
+ UnorderedElementsAre({{100, 10, 1}, {200, 10, 1}}));
+ }
+}
+
+} // namespace android
diff --git a/services/surfaceflinger/tests/unittests/Android.bp b/services/surfaceflinger/tests/unittests/Android.bp
index 40a6fb8..6af5143 100644
--- a/services/surfaceflinger/tests/unittests/Android.bp
+++ b/services/surfaceflinger/tests/unittests/Android.bp
@@ -22,15 +22,44 @@
default_team: "trendy_team_android_core_graphics_stack",
}
+// This is a step towards pulling out the "backend" sources to clean up the
+// dependency graph between CompositionEngine and SurfaceFlinger.
+// MockNativeWindow doesn't strictly belong here, but this works for now so
+// that CompositionEngine tests can use these mocks.
+filegroup {
+ name: "libsurfaceflinger_backend_mock_sources",
+ srcs: [
+ ":poweradvisor_mock_sources",
+ "mock/DisplayHardware/MockComposer.cpp",
+ "mock/DisplayHardware/MockHWC2.cpp",
+ "mock/DisplayHardware/MockHWComposer.cpp",
+ "mock/system/window/MockNativeWindow.cpp",
+ ],
+}
+
+cc_library_headers {
+ name: "libsurfaceflinger_backend_mock_headers",
+ export_include_dirs: ["."],
+ static_libs: [
+ "libgmock",
+ "libgtest",
+ ],
+ export_static_lib_headers: [
+ "libgmock",
+ "libgtest",
+ ],
+}
+
+filegroup {
+ name: "poweradvisor_mock_sources",
+ srcs: [
+ "mock/PowerAdvisor/*.cpp",
+ ],
+}
+
filegroup {
name: "libsurfaceflinger_mock_sources",
srcs: [
- "mock/DisplayHardware/MockPowerHalController.cpp",
- "mock/DisplayHardware/MockComposer.cpp",
- "mock/DisplayHardware/MockHWC2.cpp",
- "mock/DisplayHardware/MockIPower.cpp",
- "mock/DisplayHardware/MockPowerHintSessionWrapper.cpp",
- "mock/DisplayHardware/MockPowerAdvisor.cpp",
"mock/MockEventThread.cpp",
"mock/MockFrameTimeline.cpp",
"mock/MockFrameTracer.cpp",
@@ -39,7 +68,6 @@
"mock/MockVsyncController.cpp",
"mock/MockVSyncDispatch.cpp",
"mock/MockVSyncTracker.cpp",
- "mock/system/window/MockNativeWindow.cpp",
],
}
@@ -57,84 +85,12 @@
"surfaceflinger_defaults",
],
test_suites: ["device-tests"],
- static_libs: ["libc++fs"],
header_libs: ["surfaceflinger_tests_common_headers"],
srcs: [
+ ":libsurfaceflinger_backend_mock_sources",
":libsurfaceflinger_mock_sources",
":libsurfaceflinger_sources",
- "libsurfaceflinger_unittest_main.cpp",
- "ActiveDisplayRotationFlagsTest.cpp",
- "BackgroundExecutorTest.cpp",
- "CommitTest.cpp",
- "CompositionTest.cpp",
- "DaltonizerTest.cpp",
- "DisplayIdGeneratorTest.cpp",
- "DisplayTransactionTest.cpp",
- "DisplayDevice_GetBestColorModeTest.cpp",
- "DisplayDevice_SetDisplayBrightnessTest.cpp",
- "DisplayDevice_SetProjectionTest.cpp",
- "DisplayModeControllerTest.cpp",
- "EventThreadTest.cpp",
- "FlagManagerTest.cpp",
- "FpsReporterTest.cpp",
- "FpsTest.cpp",
- "FramebufferSurfaceTest.cpp",
- "FrameRateOverrideMappingsTest.cpp",
- "FrameTimelineTest.cpp",
- "HWComposerTest.cpp",
- "JankTrackerTest.cpp",
- "OneShotTimerTest.cpp",
- "LayerHistoryIntegrationTest.cpp",
- "LayerInfoTest.cpp",
- "LayerMetadataTest.cpp",
- "LayerHierarchyTest.cpp",
- "LayerLifecycleManagerTest.cpp",
- "LayerSnapshotTest.cpp",
- "LayerTestUtils.cpp",
- "MessageQueueTest.cpp",
- "PowerAdvisorTest.cpp",
- "SmallAreaDetectionAllowMappingsTest.cpp",
- "SurfaceFlinger_ColorMatrixTest.cpp",
- "SurfaceFlinger_CreateDisplayTest.cpp",
- "SurfaceFlinger_DestroyDisplayTest.cpp",
- "SurfaceFlinger_DisplayModeSwitching.cpp",
- "SurfaceFlinger_DisplayTransactionCommitTest.cpp",
- "SurfaceFlinger_ExcludeDolbyVisionTest.cpp",
- "SurfaceFlinger_FoldableTest.cpp",
- "SurfaceFlinger_GetDisplayNativePrimariesTest.cpp",
- "SurfaceFlinger_GetDisplayStatsTest.cpp",
- "SurfaceFlinger_HdrOutputControlTest.cpp",
- "SurfaceFlinger_HotplugTest.cpp",
- "SurfaceFlinger_InitializeDisplaysTest.cpp",
- "SurfaceFlinger_NotifyExpectedPresentTest.cpp",
- "SurfaceFlinger_NotifyPowerBoostTest.cpp",
- "SurfaceFlinger_PowerHintTest.cpp",
- "SurfaceFlinger_SetDisplayStateTest.cpp",
- "SurfaceFlinger_SetPowerModeInternalTest.cpp",
- "SurfaceFlinger_SetupNewDisplayDeviceInternalTest.cpp",
- "SchedulerTest.cpp",
- "RefreshRateSelectorTest.cpp",
- "RefreshRateStatsTest.cpp",
- "RegionSamplingTest.cpp",
- "TestableScheduler.cpp",
- "TimeStatsTest.cpp",
- "FrameTracerTest.cpp",
- "TransactionApplicationTest.cpp",
- "TransactionFrameTracerTest.cpp",
- "TransactionProtoParserTest.cpp",
- "TransactionSurfaceFrameTest.cpp",
- "TransactionTraceWriterTest.cpp",
- "TransactionTracingTest.cpp",
- "TunnelModeEnabledReporterTest.cpp",
- "VSyncCallbackRegistrationTest.cpp",
- "VSyncDispatchTimerQueueTest.cpp",
- "VSyncDispatchRealtimeTest.cpp",
- "VsyncModulatorTest.cpp",
- "VSyncPredictorTest.cpp",
- "VSyncReactorTest.cpp",
- "VsyncConfigurationTest.cpp",
- "VsyncScheduleTest.cpp",
- "WindowInfosListenerInvokerTest.cpp",
+ "*.cpp",
],
}
diff --git a/services/surfaceflinger/tests/unittests/BackgroundExecutorTest.cpp b/services/surfaceflinger/tests/unittests/BackgroundExecutorTest.cpp
index 5413bae..72d1351 100644
--- a/services/surfaceflinger/tests/unittests/BackgroundExecutorTest.cpp
+++ b/services/surfaceflinger/tests/unittests/BackgroundExecutorTest.cpp
@@ -1,3 +1,19 @@
+/*
+ * Copyright 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
#include <gtest/gtest.h>
#include <condition_variable>
diff --git a/services/surfaceflinger/tests/unittests/CommitAndCompositeTest.h b/services/surfaceflinger/tests/unittests/CommitAndCompositeTest.h
index d4c801f..b517ff0 100644
--- a/services/surfaceflinger/tests/unittests/CommitAndCompositeTest.h
+++ b/services/surfaceflinger/tests/unittests/CommitAndCompositeTest.h
@@ -22,8 +22,8 @@
#include "TestableSurfaceFlinger.h"
#include "mock/DisplayHardware/MockComposer.h"
-#include "mock/DisplayHardware/MockPowerAdvisor.h"
#include "mock/MockTimeStats.h"
+#include "mock/PowerAdvisor/MockPowerAdvisor.h"
#include "mock/system/window/MockNativeWindow.h"
namespace android {
@@ -33,11 +33,11 @@
void SetUp() override {
mFlinger.setupMockScheduler({.displayId = DEFAULT_DISPLAY_ID});
mComposer = new Hwc2::mock::Composer();
- mPowerAdvisor = new Hwc2::mock::PowerAdvisor();
+ mPowerAdvisor = new adpf::mock::PowerAdvisor();
mFlinger.setupRenderEngine(std::unique_ptr<renderengine::RenderEngine>(mRenderEngine));
mFlinger.setupTimeStats(std::shared_ptr<TimeStats>(mTimeStats));
mFlinger.setupComposer(std::unique_ptr<Hwc2::Composer>(mComposer));
- mFlinger.setupPowerAdvisor(std::unique_ptr<Hwc2::PowerAdvisor>(mPowerAdvisor));
+ mFlinger.setupPowerAdvisor(std::unique_ptr<adpf::PowerAdvisor>(mPowerAdvisor));
constexpr bool kIsPrimary = true;
FakeHwcDisplayInjector(DEFAULT_DISPLAY_ID, hal::DisplayType::PHYSICAL, kIsPrimary)
@@ -79,7 +79,7 @@
sp<compositionengine::mock::DisplaySurface>::make();
sp<mock::NativeWindow> mNativeWindow = sp<mock::NativeWindow>::make();
mock::TimeStats* mTimeStats = new mock::TimeStats();
- Hwc2::mock::PowerAdvisor* mPowerAdvisor = nullptr;
+ adpf::mock::PowerAdvisor* mPowerAdvisor = nullptr;
Hwc2::mock::Composer* mComposer = nullptr;
};
diff --git a/services/surfaceflinger/tests/unittests/CompositionTest.cpp b/services/surfaceflinger/tests/unittests/CompositionTest.cpp
index 4f72424..860ad2e 100644
--- a/services/surfaceflinger/tests/unittests/CompositionTest.cpp
+++ b/services/surfaceflinger/tests/unittests/CompositionTest.cpp
@@ -40,10 +40,10 @@
#include "Layer.h"
#include "TestableSurfaceFlinger.h"
#include "mock/DisplayHardware/MockComposer.h"
-#include "mock/DisplayHardware/MockPowerAdvisor.h"
#include "mock/MockEventThread.h"
#include "mock/MockTimeStats.h"
#include "mock/MockVsyncController.h"
+#include "mock/PowerAdvisor/MockPowerAdvisor.h"
#include "mock/system/window/MockNativeWindow.h"
namespace android {
@@ -110,9 +110,9 @@
mFlinger.setupTimeStats(std::shared_ptr<TimeStats>(mTimeStats));
mComposer = new Hwc2::mock::Composer();
- mPowerAdvisor = new Hwc2::mock::PowerAdvisor();
+ mPowerAdvisor = new adpf::mock::PowerAdvisor();
mFlinger.setupComposer(std::unique_ptr<Hwc2::Composer>(mComposer));
- mFlinger.setupPowerAdvisor(std::unique_ptr<Hwc2::PowerAdvisor>(mPowerAdvisor));
+ mFlinger.setupPowerAdvisor(std::unique_ptr<adpf::PowerAdvisor>(mPowerAdvisor));
mFlinger.mutableMaxRenderTargetSize() = 16384;
}
@@ -158,7 +158,7 @@
Hwc2::mock::Composer* mComposer = nullptr;
renderengine::mock::RenderEngine* mRenderEngine = new renderengine::mock::RenderEngine();
mock::TimeStats* mTimeStats = new mock::TimeStats();
- Hwc2::mock::PowerAdvisor* mPowerAdvisor = nullptr;
+ adpf::mock::PowerAdvisor* mPowerAdvisor = nullptr;
sp<Fence> mClientTargetAcquireFence = Fence::NO_FENCE;
diff --git a/services/surfaceflinger/tests/unittests/DisplayModeControllerTest.cpp b/services/surfaceflinger/tests/unittests/DisplayModeControllerTest.cpp
index d971150..29a1fab 100644
--- a/services/surfaceflinger/tests/unittests/DisplayModeControllerTest.cpp
+++ b/services/surfaceflinger/tests/unittests/DisplayModeControllerTest.cpp
@@ -20,6 +20,7 @@
#include "Display/DisplayModeController.h"
#include "Display/DisplaySnapshot.h"
#include "DisplayHardware/HWComposer.h"
+#include "DisplayHardware/Hal.h"
#include "DisplayIdentificationTestHelpers.h"
#include "FpsOps.h"
#include "mock/DisplayHardware/MockComposer.h"
@@ -103,7 +104,7 @@
EXPECT_CALL(*mComposerHal,
setActiveConfigWithConstraints(kHwcDisplayId, hwcModeId, constraints, _))
- .WillOnce(DoAll(SetArgPointee<3>(timeline), Return(hal::V2_4::Error::NONE)));
+ .WillOnce(DoAll(SetArgPointee<3>(timeline), Return(hal::Error::NONE)));
return constraints;
}
@@ -183,7 +184,8 @@
hal::VsyncPeriodChangeTimeline timeline;
const auto constraints = expectModeSet(modeRequest, timeline);
- EXPECT_TRUE(mDmc.initiateModeChange(mDisplayId, std::move(modeRequest), constraints, timeline));
+ EXPECT_EQ(DisplayModeController::ModeChangeResult::Changed,
+ mDmc.initiateModeChange(mDisplayId, std::move(modeRequest), constraints, timeline));
EXPECT_DISPLAY_MODE_REQUEST(kDesiredMode90, mDmc.getPendingMode(mDisplayId));
mDmc.clearDesiredMode(mDisplayId);
@@ -210,7 +212,8 @@
hal::VsyncPeriodChangeTimeline timeline;
auto constraints = expectModeSet(modeRequest, timeline);
- EXPECT_TRUE(mDmc.initiateModeChange(mDisplayId, std::move(modeRequest), constraints, timeline));
+ EXPECT_EQ(DisplayModeController::ModeChangeResult::Changed,
+ mDmc.initiateModeChange(mDisplayId, std::move(modeRequest), constraints, timeline));
EXPECT_DISPLAY_MODE_REQUEST(kDesiredMode90, mDmc.getPendingMode(mDisplayId));
// No action since a mode switch has already been initiated.
@@ -223,7 +226,8 @@
constexpr bool kSubsequent = true;
constraints = expectModeSet(modeRequest, timeline, kSubsequent);
- EXPECT_TRUE(mDmc.initiateModeChange(mDisplayId, std::move(modeRequest), constraints, timeline));
+ EXPECT_EQ(DisplayModeController::ModeChangeResult::Changed,
+ mDmc.initiateModeChange(mDisplayId, std::move(modeRequest), constraints, timeline));
EXPECT_DISPLAY_MODE_REQUEST(kDesiredMode120, mDmc.getPendingMode(mDisplayId));
mDmc.clearDesiredMode(mDisplayId);
diff --git a/services/surfaceflinger/tests/unittests/DisplayTransactionTestHelpers.h b/services/surfaceflinger/tests/unittests/DisplayTransactionTestHelpers.h
index db3c0a1..fa976c8 100644
--- a/services/surfaceflinger/tests/unittests/DisplayTransactionTestHelpers.h
+++ b/services/surfaceflinger/tests/unittests/DisplayTransactionTestHelpers.h
@@ -47,10 +47,10 @@
#include "TestableSurfaceFlinger.h"
#include "mock/DisplayHardware/MockComposer.h"
#include "mock/DisplayHardware/MockDisplayMode.h"
-#include "mock/DisplayHardware/MockPowerAdvisor.h"
#include "mock/MockEventThread.h"
#include "mock/MockNativeWindowSurface.h"
#include "mock/MockVsyncController.h"
+#include "mock/PowerAdvisor/MockPowerAdvisor.h"
#include "mock/system/window/MockNativeWindow.h"
namespace android {
@@ -118,7 +118,7 @@
sp<GraphicBuffer> mBuffer =
sp<GraphicBuffer>::make(1u, 1u, PIXEL_FORMAT_RGBA_8888,
GRALLOC_USAGE_SW_WRITE_OFTEN | GRALLOC_USAGE_SW_READ_OFTEN);
- Hwc2::mock::PowerAdvisor mPowerAdvisor;
+ adpf::mock::PowerAdvisor mPowerAdvisor;
FakeDisplayInjector mFakeDisplayInjector{mFlinger, mPowerAdvisor, mNativeWindow};
diff --git a/services/surfaceflinger/tests/unittests/EventThreadTest.cpp b/services/surfaceflinger/tests/unittests/EventThreadTest.cpp
index 625d2e6..268a6c4 100644
--- a/services/surfaceflinger/tests/unittests/EventThreadTest.cpp
+++ b/services/surfaceflinger/tests/unittests/EventThreadTest.cpp
@@ -23,6 +23,7 @@
#include <gmock/gmock.h>
#include <gtest/gtest.h>
+#include <gui/DisplayEventReceiver.h>
#include <log/log.h>
#include <scheduler/VsyncConfig.h>
#include <utils/Errors.h>
@@ -111,6 +112,8 @@
void expectOnExpectedPresentTimePosted(nsecs_t expectedPresentTime);
void expectUidFrameRateMappingEventReceivedByConnection(PhysicalDisplayId expectedDisplayId,
std::vector<FrameRateOverride>);
+ void expectQueuedBufferCountReceivedByConnection(
+ ConnectionEventRecorder& connectionEventRecorder, uint32_t expectedBufferCount);
void onVSyncEvent(nsecs_t timestamp, nsecs_t expectedPresentationTime,
nsecs_t deadlineTimestamp) {
@@ -144,6 +147,7 @@
sp<MockEventThreadConnection> mConnection;
sp<MockEventThreadConnection> mThrottledConnection;
std::unique_ptr<frametimeline::impl::TokenManager> mTokenManager;
+ std::vector<ConnectionEventRecorder*> mBufferStuffedConnectionRecorders;
std::chrono::nanoseconds mVsyncPeriod;
@@ -376,6 +380,14 @@
EXPECT_EQ(expectedDisplayId, event.header.displayId);
}
+void EventThreadTest::expectQueuedBufferCountReceivedByConnection(
+ ConnectionEventRecorder& connectionEventRecorder, uint32_t expectedBufferCount) {
+ auto args = connectionEventRecorder.waitForCall();
+ ASSERT_TRUE(args.has_value());
+ const auto& event = std::get<0>(args.value());
+ EXPECT_EQ(expectedBufferCount, event.vsync.vsyncData.numberQueuedBuffers);
+}
+
namespace {
using namespace testing;
@@ -868,6 +880,63 @@
EXPECT_EQ(HDCP_V2, event.hdcpLevelsChange.maxLevel);
}
+TEST_F(EventThreadTest, connectionReceivesBufferStuffing) {
+ setupEventThread();
+
+ // Create a connection that will experience buffer stuffing.
+ ConnectionEventRecorder stuffedConnectionEventRecorder{0};
+ sp<MockEventThreadConnection> stuffedConnection =
+ createConnection(stuffedConnectionEventRecorder,
+ gui::ISurfaceComposer::EventRegistration::modeChanged |
+ gui::ISurfaceComposer::EventRegistration::frameRateOverride,
+ 111);
+
+ // Add a connection and buffer count to the list of stuffed Uids that will receive
+ // data in the next vsync event.
+ BufferStuffingMap bufferStuffedUids;
+ bufferStuffedUids.try_emplace(stuffedConnection->mOwnerUid, 3);
+ mThread->addBufferStuffedUids(bufferStuffedUids);
+ mBufferStuffedConnectionRecorders.emplace_back(&stuffedConnectionEventRecorder);
+
+ // Signal that we want the next vsync event to be posted to two connections.
+ mThread->requestNextVsync(mConnection);
+ mThread->requestNextVsync(stuffedConnection);
+ onVSyncEvent(123, 456, 789);
+
+ // Vsync event data contains number of queued buffers.
+ expectQueuedBufferCountReceivedByConnection(mConnectionEventCallRecorder, 0);
+ expectQueuedBufferCountReceivedByConnection(stuffedConnectionEventRecorder, 3);
+}
+
+TEST_F(EventThreadTest, connectionsWithSameUidReceiveBufferStuffing) {
+ setupEventThread();
+
+ // Create a connection with the same Uid as another connection.
+ ConnectionEventRecorder secondConnectionEventRecorder{0};
+ sp<MockEventThreadConnection> secondConnection =
+ createConnection(secondConnectionEventRecorder,
+ gui::ISurfaceComposer::EventRegistration::modeChanged |
+ gui::ISurfaceComposer::EventRegistration::frameRateOverride,
+ mConnectionUid);
+
+ // Add connection Uid and buffer count to the list of stuffed Uids that will receive
+ // data in the next vsync event.
+ BufferStuffingMap bufferStuffedUids;
+ bufferStuffedUids.try_emplace(mConnectionUid, 3);
+ mThread->addBufferStuffedUids(bufferStuffedUids);
+ mBufferStuffedConnectionRecorders.emplace_back(&mConnectionEventCallRecorder);
+ mBufferStuffedConnectionRecorders.emplace_back(&secondConnectionEventRecorder);
+
+ // Signal that we want the next vsync event to be posted to two connections.
+ mThread->requestNextVsync(mConnection);
+ mThread->requestNextVsync(secondConnection);
+ onVSyncEvent(123, 456, 789);
+
+ // Vsync event data contains number of queued buffers.
+ expectQueuedBufferCountReceivedByConnection(mConnectionEventCallRecorder, 3);
+ expectQueuedBufferCountReceivedByConnection(secondConnectionEventRecorder, 3);
+}
+
} // namespace
} // namespace android
diff --git a/services/surfaceflinger/tests/unittests/FakeDisplayInjector.h b/services/surfaceflinger/tests/unittests/FakeDisplayInjector.h
index 6e4bf2b..744c536 100644
--- a/services/surfaceflinger/tests/unittests/FakeDisplayInjector.h
+++ b/services/surfaceflinger/tests/unittests/FakeDisplayInjector.h
@@ -19,14 +19,14 @@
#include <gmock/gmock.h>
#include "TestableSurfaceFlinger.h"
-#include "mock/DisplayHardware/MockPowerAdvisor.h"
+#include "mock/PowerAdvisor/MockPowerAdvisor.h"
#include "mock/system/window/MockNativeWindow.h"
namespace android {
using FakeDisplayDeviceInjector = TestableSurfaceFlinger::FakeDisplayDeviceInjector;
+using android::adpf::mock::PowerAdvisor;
using android::hardware::graphics::composer::hal::HWDisplayId;
-using android::Hwc2::mock::PowerAdvisor;
struct FakeDisplayInjectorArgs {
PhysicalDisplayId displayId = PhysicalDisplayId::fromPort(255u);
@@ -36,7 +36,7 @@
class FakeDisplayInjector {
public:
- FakeDisplayInjector(TestableSurfaceFlinger& flinger, Hwc2::mock::PowerAdvisor& powerAdvisor,
+ FakeDisplayInjector(TestableSurfaceFlinger& flinger, PowerAdvisor& powerAdvisor,
sp<mock::NativeWindow> nativeWindow)
: mFlinger(flinger), mPowerAdvisor(powerAdvisor), mNativeWindow(nativeWindow) {}
@@ -89,7 +89,7 @@
}
TestableSurfaceFlinger& mFlinger;
- Hwc2::mock::PowerAdvisor& mPowerAdvisor;
+ PowerAdvisor& mPowerAdvisor;
sp<mock::NativeWindow> mNativeWindow;
};
diff --git a/services/surfaceflinger/tests/unittests/FlagManagerTest.cpp b/services/surfaceflinger/tests/unittests/FlagManagerTest.cpp
index 51b5f40..a5b347a 100644
--- a/services/surfaceflinger/tests/unittests/FlagManagerTest.cpp
+++ b/services/surfaceflinger/tests/unittests/FlagManagerTest.cpp
@@ -85,12 +85,6 @@
EXPECT_EQ(false, mFlagManager.test_flag());
}
-TEST_F(FlagManagerTest, crashesIfQueriedBeforeBoot) {
- mFlagManager.markBootIncomplete();
- EXPECT_DEATH(FlagManager::getInstance()
- .refresh_rate_overlay_on_external_display(), "");
-}
-
TEST_F(FlagManagerTest, returnsOverrideTrue) {
mFlagManager.markBootCompleted();
diff --git a/services/surfaceflinger/tests/unittests/FrameTimelineTest.cpp b/services/surfaceflinger/tests/unittests/FrameTimelineTest.cpp
index 0dfbd61..08e4265 100644
--- a/services/surfaceflinger/tests/unittests/FrameTimelineTest.cpp
+++ b/services/surfaceflinger/tests/unittests/FrameTimelineTest.cpp
@@ -166,6 +166,7 @@
a.presentTime == b.presentTime;
}
+ NO_THREAD_SAFETY_ANALYSIS
const std::map<int64_t, TimelineItem>& getPredictions() const {
return mTokenManager->mPredictions;
}
diff --git a/services/surfaceflinger/tests/unittests/HWComposerTest.cpp b/services/surfaceflinger/tests/unittests/HWComposerTest.cpp
index e0753a3..30bce2e 100644
--- a/services/surfaceflinger/tests/unittests/HWComposerTest.cpp
+++ b/services/surfaceflinger/tests/unittests/HWComposerTest.cpp
@@ -69,7 +69,7 @@
using ::testing::StrictMock;
struct HWComposerTest : testing::Test {
- using HalError = hardware::graphics::composer::V2_1::Error;
+ using HalError = hal::Error;
Hwc2::mock::Composer* const mHal = new StrictMock<Hwc2::mock::Composer>();
impl::HWComposer mHwc{std::unique_ptr<Hwc2::Composer>(mHal)};
diff --git a/services/surfaceflinger/tests/unittests/LayerHistoryIntegrationTest.cpp b/services/surfaceflinger/tests/unittests/LayerHistoryIntegrationTest.cpp
index de37b63..767000e 100644
--- a/services/surfaceflinger/tests/unittests/LayerHistoryIntegrationTest.cpp
+++ b/services/surfaceflinger/tests/unittests/LayerHistoryIntegrationTest.cpp
@@ -715,6 +715,204 @@
EXPECT_EQ(0, frequentLayerCount(time));
}
+// Tests MRR NoPreference-only vote, no game default override. Expects vote reset.
+TEST_F(LayerHistoryIntegrationTest, oneLayerCategoryNoPreference_mrr) {
+ SET_FLAG_FOR_TEST(flags::frame_rate_category_mrr, false);
+ SET_FLAG_FOR_TEST(flags::game_default_frame_rate, true);
+ SET_FLAG_FOR_TEST(flags::vrr_config, true);
+
+ const LayerHistory::LayerVoteType defaultVote = LayerHistory::LayerVoteType::Min;
+
+ auto layer = createLegacyAndFrontedEndLayer(1);
+ setDefaultLayerVote(layer.get(), defaultVote);
+ showLayer(1);
+ setFrameRate(1, (0_Hz).getValue(), ANATIVEWINDOW_FRAME_RATE_COMPATIBILITY_DEFAULT,
+ ANATIVEWINDOW_CHANGE_FRAME_RATE_ONLY_IF_SEAMLESS);
+ setFrameRateCategory(1, ANATIVEWINDOW_FRAME_RATE_CATEGORY_NO_PREFERENCE);
+
+ EXPECT_EQ(1u, layerCount());
+ EXPECT_EQ(0u, activeLayerCount());
+
+ nsecs_t time = systemTime();
+ for (size_t i = 0; i < PRESENT_TIME_HISTORY_SIZE; i++) {
+ setBufferWithPresentTime(layer, time);
+ time += HI_FPS_PERIOD;
+ }
+
+ EXPECT_EQ(1u, summarizeLayerHistory(time).size());
+ EXPECT_EQ(1u, activeLayerCount());
+ EXPECT_EQ(1, frequentLayerCount(time));
+ EXPECT_EQ(defaultVote, summarizeLayerHistory(time)[0].vote);
+ EXPECT_EQ(0_Hz, summarizeLayerHistory(time)[0].desiredRefreshRate);
+ EXPECT_EQ(FrameRateCategory::Default, summarizeLayerHistory(time)[0].frameRateCategory);
+}
+
+// Tests VRR NoPreference-only vote, no game default override. Expects NoPreference, *not* vote
+// reset.
+TEST_F(LayerHistoryIntegrationTest, oneLayerCategoryNoPreference_vrr) {
+ SET_FLAG_FOR_TEST(flags::frame_rate_category_mrr, false);
+ SET_FLAG_FOR_TEST(flags::game_default_frame_rate, true);
+ SET_FLAG_FOR_TEST(flags::vrr_config, true);
+ mSelector->setActiveMode(kVrrModeId, HI_FPS);
+
+ const LayerHistory::LayerVoteType defaultVote = LayerHistory::LayerVoteType::Min;
+
+ auto layer = createLegacyAndFrontedEndLayer(1);
+ setDefaultLayerVote(layer.get(), defaultVote);
+ showLayer(1);
+ setFrameRate(1, (0_Hz).getValue(), ANATIVEWINDOW_FRAME_RATE_COMPATIBILITY_DEFAULT,
+ ANATIVEWINDOW_CHANGE_FRAME_RATE_ONLY_IF_SEAMLESS);
+ setFrameRateCategory(1, ANATIVEWINDOW_FRAME_RATE_CATEGORY_NO_PREFERENCE);
+
+ EXPECT_EQ(1u, layerCount());
+ EXPECT_EQ(0u, activeLayerCount());
+
+ nsecs_t time = systemTime();
+ for (size_t i = 0; i < PRESENT_TIME_HISTORY_SIZE; i++) {
+ setBufferWithPresentTime(layer, time);
+ time += HI_FPS_PERIOD;
+ }
+
+ EXPECT_EQ(1u, summarizeLayerHistory(time).size());
+ EXPECT_EQ(1u, activeLayerCount());
+ EXPECT_EQ(1, frequentLayerCount(time));
+ EXPECT_EQ(LayerHistory::LayerVoteType::ExplicitCategory, summarizeLayerHistory(time)[0].vote);
+ EXPECT_EQ(0_Hz, summarizeLayerHistory(time)[0].desiredRefreshRate);
+ EXPECT_EQ(FrameRateCategory::NoPreference, summarizeLayerHistory(time)[0].frameRateCategory);
+}
+
+TEST_F(LayerHistoryIntegrationTest, oneLayerCategoryNoPreferenceWithGameDefault_vrr) {
+ SET_FLAG_FOR_TEST(flags::frame_rate_category_mrr, false);
+ SET_FLAG_FOR_TEST(flags::game_default_frame_rate, true);
+ SET_FLAG_FOR_TEST(flags::vrr_config, true);
+ mSelector->setActiveMode(kVrrModeId, HI_FPS);
+
+ const Fps gameDefaultFrameRate = Fps::fromValue(30.0f);
+ const uid_t uid = 456;
+
+ history().updateGameDefaultFrameRateOverride(
+ FrameRateOverride({uid, gameDefaultFrameRate.getValue()}));
+
+ auto layer = createLegacyAndFrontedEndLayerWithUid(1, gui::Uid(uid));
+ showLayer(1);
+ setFrameRate(1, (0_Hz).getValue(), ANATIVEWINDOW_FRAME_RATE_COMPATIBILITY_DEFAULT,
+ ANATIVEWINDOW_CHANGE_FRAME_RATE_ONLY_IF_SEAMLESS);
+ setFrameRateCategory(1, ANATIVEWINDOW_FRAME_RATE_CATEGORY_NO_PREFERENCE);
+
+ EXPECT_EQ(1u, layerCount());
+ EXPECT_EQ(0u, activeLayerCount());
+
+ nsecs_t time = systemTime();
+ for (size_t i = 0; i < PRESENT_TIME_HISTORY_SIZE; i++) {
+ setBufferWithPresentTime(layer, time);
+ time += HI_FPS_PERIOD;
+ }
+
+ EXPECT_EQ(1u, summarizeLayerHistory(time).size());
+ EXPECT_EQ(1u, activeLayerCount());
+ EXPECT_EQ(1, frequentLayerCount(time));
+ EXPECT_EQ(LayerHistory::LayerVoteType::ExplicitDefault, summarizeLayerHistory(time)[0].vote);
+ EXPECT_EQ(gameDefaultFrameRate, summarizeLayerHistory(time)[0].desiredRefreshRate);
+ EXPECT_EQ(FrameRateCategory::Default, summarizeLayerHistory(time)[0].frameRateCategory);
+}
+
+TEST_F(LayerHistoryIntegrationTest, oneLayerCategoryNoPreferenceWithGameDefault_mrr) {
+ SET_FLAG_FOR_TEST(flags::frame_rate_category_mrr, false);
+ SET_FLAG_FOR_TEST(flags::game_default_frame_rate, true);
+ SET_FLAG_FOR_TEST(flags::vrr_config, true);
+
+ const Fps gameDefaultFrameRate = Fps::fromValue(30.0f);
+ const uid_t uid = 456;
+
+ history().updateGameDefaultFrameRateOverride(
+ FrameRateOverride({uid, gameDefaultFrameRate.getValue()}));
+
+ auto layer = createLegacyAndFrontedEndLayerWithUid(1, gui::Uid(uid));
+ showLayer(1);
+ setFrameRate(1, (0_Hz).getValue(), ANATIVEWINDOW_FRAME_RATE_COMPATIBILITY_DEFAULT,
+ ANATIVEWINDOW_CHANGE_FRAME_RATE_ONLY_IF_SEAMLESS);
+ setFrameRateCategory(1, ANATIVEWINDOW_FRAME_RATE_CATEGORY_NO_PREFERENCE);
+
+ EXPECT_EQ(1u, layerCount());
+ EXPECT_EQ(0u, activeLayerCount());
+
+ nsecs_t time = systemTime();
+ for (size_t i = 0; i < PRESENT_TIME_HISTORY_SIZE; i++) {
+ setBufferWithPresentTime(layer, time);
+ time += HI_FPS_PERIOD;
+ }
+
+ EXPECT_EQ(1u, summarizeLayerHistory(time).size());
+ EXPECT_EQ(1u, activeLayerCount());
+ EXPECT_EQ(1, frequentLayerCount(time));
+ EXPECT_EQ(LayerHistory::LayerVoteType::ExplicitDefault, summarizeLayerHistory(time)[0].vote);
+ EXPECT_EQ(gameDefaultFrameRate, summarizeLayerHistory(time)[0].desiredRefreshRate);
+ EXPECT_EQ(FrameRateCategory::Default, summarizeLayerHistory(time)[0].frameRateCategory);
+}
+
+TEST_F(LayerHistoryIntegrationTest, oneLayerNoVoteWithGameDefault_vrr) {
+ SET_FLAG_FOR_TEST(flags::frame_rate_category_mrr, false);
+ SET_FLAG_FOR_TEST(flags::game_default_frame_rate, true);
+ SET_FLAG_FOR_TEST(flags::vrr_config, true);
+ mSelector->setActiveMode(kVrrModeId, HI_FPS);
+
+ const Fps gameDefaultFrameRate = Fps::fromValue(30.0f);
+ const uid_t uid = 456;
+
+ history().updateGameDefaultFrameRateOverride(
+ FrameRateOverride({uid, gameDefaultFrameRate.getValue()}));
+
+ auto layer = createLegacyAndFrontedEndLayerWithUid(1, gui::Uid(uid));
+ showLayer(1);
+ setFrameRate(1, (0_Hz).getValue(), ANATIVEWINDOW_FRAME_RATE_NO_VOTE,
+ ANATIVEWINDOW_CHANGE_FRAME_RATE_ONLY_IF_SEAMLESS);
+
+ EXPECT_EQ(1u, layerCount());
+ EXPECT_EQ(0u, activeLayerCount());
+
+ nsecs_t time = systemTime();
+ for (size_t i = 0; i < PRESENT_TIME_HISTORY_SIZE; i++) {
+ setBufferWithPresentTime(layer, time);
+ time += HI_FPS_PERIOD;
+ }
+
+ // Expect NoVote to be skipped in summarize.
+ EXPECT_EQ(0u, summarizeLayerHistory(time).size());
+ EXPECT_EQ(1u, activeLayerCount());
+ EXPECT_EQ(1, frequentLayerCount(time));
+}
+
+TEST_F(LayerHistoryIntegrationTest, oneLayerNoVoteWithGameDefault_mrr) {
+ SET_FLAG_FOR_TEST(flags::frame_rate_category_mrr, false);
+ SET_FLAG_FOR_TEST(flags::game_default_frame_rate, true);
+ SET_FLAG_FOR_TEST(flags::vrr_config, true);
+
+ const Fps gameDefaultFrameRate = Fps::fromValue(30.0f);
+ const uid_t uid = 456;
+
+ history().updateGameDefaultFrameRateOverride(
+ FrameRateOverride({uid, gameDefaultFrameRate.getValue()}));
+
+ auto layer = createLegacyAndFrontedEndLayerWithUid(1, gui::Uid(uid));
+ showLayer(1);
+ setFrameRate(1, (0_Hz).getValue(), ANATIVEWINDOW_FRAME_RATE_NO_VOTE,
+ ANATIVEWINDOW_CHANGE_FRAME_RATE_ONLY_IF_SEAMLESS);
+
+ EXPECT_EQ(1u, layerCount());
+ EXPECT_EQ(0u, activeLayerCount());
+
+ nsecs_t time = systemTime();
+ for (size_t i = 0; i < PRESENT_TIME_HISTORY_SIZE; i++) {
+ setBufferWithPresentTime(layer, time);
+ time += HI_FPS_PERIOD;
+ }
+
+ // Expect NoVote to be skipped in summarize.
+ EXPECT_EQ(0u, summarizeLayerHistory(time).size());
+ EXPECT_EQ(1u, activeLayerCount());
+ EXPECT_EQ(1, frequentLayerCount(time));
+}
+
TEST_F(LayerHistoryIntegrationTest, oneLayerExplicitVoteWithCategory) {
SET_FLAG_FOR_TEST(flags::frame_rate_category_mrr, true);
diff --git a/services/surfaceflinger/tests/unittests/LayerSnapshotTest.cpp b/services/surfaceflinger/tests/unittests/LayerSnapshotTest.cpp
index a35ae15..8c53eef 100644
--- a/services/surfaceflinger/tests/unittests/LayerSnapshotTest.cpp
+++ b/services/surfaceflinger/tests/unittests/LayerSnapshotTest.cpp
@@ -28,7 +28,6 @@
#include "ui/GraphicTypes.h"
#include <com_android_graphics_libgui_flags.h>
-#include <com_android_graphics_surfaceflinger_flags.h>
#define UPDATE_AND_VERIFY(BUILDER, ...) \
({ \
@@ -1551,6 +1550,9 @@
}
TEST_F(LayerSnapshotTest, NonVisibleLayerWithInput) {
+ SET_FLAG_FOR_TEST(com::android::graphics::surfaceflinger::flags::
+ skip_invisible_windows_in_input,
+ false);
LayerHierarchyTestBase::createRootLayer(3);
setColor(3, {-1._hf, -1._hf, -1._hf});
UPDATE_AND_VERIFY(mSnapshotBuilder, STARTING_ZORDER);
@@ -1576,6 +1578,39 @@
EXPECT_TRUE(foundInputLayer);
}
+TEST_F(LayerSnapshotTest, NonVisibleLayerWithInputShouldNotBeIncluded) {
+ SET_FLAG_FOR_TEST(com::android::graphics::surfaceflinger::flags::
+ skip_invisible_windows_in_input,
+ true);
+ LayerHierarchyTestBase::createRootLayer(3);
+ setColor(3, {-1._hf, -1._hf, -1._hf});
+ UPDATE_AND_VERIFY(mSnapshotBuilder, STARTING_ZORDER);
+
+ std::vector<TransactionState> transactions;
+ transactions.emplace_back();
+ transactions.back().states.push_back({});
+ transactions.back().states.front().state.what = layer_state_t::eInputInfoChanged;
+ transactions.back().states.front().layerId = 3;
+ transactions.back().states.front().state.windowInfoHandle = sp<gui::WindowInfoHandle>::make();
+ auto inputInfo = transactions.back().states.front().state.windowInfoHandle->editInfo();
+ inputInfo->token = sp<BBinder>::make();
+ hideLayer(3);
+ mLifecycleManager.applyTransactions(transactions);
+
+ update(mSnapshotBuilder);
+
+ bool foundInputLayer = false;
+ mSnapshotBuilder.forEachInputSnapshot([&](const frontend::LayerSnapshot& snapshot) {
+ if (snapshot.uniqueSequence == 3) {
+ EXPECT_TRUE(
+ snapshot.inputInfo.inputConfig.test(gui::WindowInfo::InputConfig::NOT_VISIBLE));
+ EXPECT_FALSE(snapshot.isVisible);
+ foundInputLayer = true;
+ }
+ });
+ EXPECT_FALSE(foundInputLayer);
+}
+
TEST_F(LayerSnapshotTest, ForEachSnapshotsWithPredicate) {
std::vector<uint32_t> visitedUniqueSequences;
mSnapshotBuilder.forEachSnapshot(
@@ -1935,4 +1970,95 @@
EXPECT_FALSE(getSnapshot(2)->hasInputInfo());
}
+// content dirty test
+TEST_F(LayerSnapshotTest, contentDirtyWhenParentAlphaChanges) {
+ setAlpha(1, 0.5);
+ UPDATE_AND_VERIFY(mSnapshotBuilder, STARTING_ZORDER);
+ EXPECT_TRUE(getSnapshot(1)->contentDirty);
+ EXPECT_TRUE(getSnapshot(11)->contentDirty);
+ EXPECT_TRUE(getSnapshot(111)->contentDirty);
+
+ // subsequent updates clear the dirty bit
+ UPDATE_AND_VERIFY(mSnapshotBuilder, STARTING_ZORDER);
+ EXPECT_FALSE(getSnapshot(1)->contentDirty);
+ EXPECT_FALSE(getSnapshot(11)->contentDirty);
+ EXPECT_FALSE(getSnapshot(111)->contentDirty);
+}
+
+TEST_F(LayerSnapshotTest, contentDirtyWhenAutoRefresh) {
+ setAutoRefresh(1, true);
+ UPDATE_AND_VERIFY(mSnapshotBuilder, STARTING_ZORDER);
+ EXPECT_TRUE(getSnapshot(1)->contentDirty);
+
+ // subsequent updates don't clear the dirty bit
+ UPDATE_AND_VERIFY(mSnapshotBuilder, STARTING_ZORDER);
+ EXPECT_TRUE(getSnapshot(1)->contentDirty);
+
+ // second update after removing auto refresh will clear content dirty
+ setAutoRefresh(1, false);
+ UPDATE_AND_VERIFY(mSnapshotBuilder, STARTING_ZORDER);
+ UPDATE_AND_VERIFY(mSnapshotBuilder, STARTING_ZORDER);
+ EXPECT_FALSE(getSnapshot(1)->contentDirty);
+}
+
+TEST_F(LayerSnapshotTest, contentDirtyWhenColorChanges) {
+ setColor(1, {1, 2, 3});
+ UPDATE_AND_VERIFY(mSnapshotBuilder, STARTING_ZORDER);
+ EXPECT_TRUE(getSnapshot(1)->contentDirty);
+
+ // subsequent updates clear the dirty bit
+ UPDATE_AND_VERIFY(mSnapshotBuilder, STARTING_ZORDER);
+ EXPECT_FALSE(getSnapshot(1)->contentDirty);
+}
+
+TEST_F(LayerSnapshotTest, contentDirtyWhenParentGeometryChanges) {
+ setPosition(1, 2, 3);
+ UPDATE_AND_VERIFY(mSnapshotBuilder, STARTING_ZORDER);
+ EXPECT_TRUE(getSnapshot(1)->contentDirty);
+
+ // subsequent updates clear the dirty bit
+ UPDATE_AND_VERIFY(mSnapshotBuilder, STARTING_ZORDER);
+ EXPECT_FALSE(getSnapshot(1)->contentDirty);
+}
+TEST_F(LayerSnapshotTest, shouldUpdatePictureProfileHandle) {
+ if (!com_android_graphics_libgui_flags_apply_picture_profiles()) {
+ GTEST_SKIP() << "Flag disabled, skipping test";
+ }
+ std::vector<TransactionState> transactions;
+ transactions.emplace_back();
+ transactions.back().states.push_back({});
+ transactions.back().states.front().layerId = 1;
+ transactions.back().states.front().state.layerId = 1;
+ transactions.back().states.front().state.what = layer_state_t::ePictureProfileHandleChanged;
+ transactions.back().states.front().state.pictureProfileHandle = PictureProfileHandle(3);
+
+ mLifecycleManager.applyTransactions(transactions);
+ EXPECT_EQ(mLifecycleManager.getGlobalChanges(), RequestedLayerState::Changes::Content);
+
+ update(mSnapshotBuilder);
+
+ EXPECT_EQ(getSnapshot(1)->clientChanges, layer_state_t::ePictureProfileHandleChanged);
+ EXPECT_EQ(getSnapshot(1)->pictureProfileHandle, PictureProfileHandle(3));
+}
+
+TEST_F(LayerSnapshotTest, shouldUpdatePictureProfilePriorityFromAppContentPriority) {
+ if (!com_android_graphics_libgui_flags_apply_picture_profiles()) {
+ GTEST_SKIP() << "Flag disabled, skipping test";
+ }
+ std::vector<TransactionState> transactions;
+ transactions.emplace_back();
+ transactions.back().states.push_back({});
+ transactions.back().states.front().layerId = 1;
+ transactions.back().states.front().state.layerId = 1;
+ transactions.back().states.front().state.what = layer_state_t::eAppContentPriorityChanged;
+ transactions.back().states.front().state.appContentPriority = 3;
+
+ mLifecycleManager.applyTransactions(transactions);
+ EXPECT_EQ(mLifecycleManager.getGlobalChanges(), RequestedLayerState::Changes::Content);
+
+ update(mSnapshotBuilder);
+
+ EXPECT_EQ(getSnapshot(1)->pictureProfilePriority, 3);
+}
+
} // namespace android::surfaceflinger::frontend
diff --git a/services/surfaceflinger/tests/unittests/MessageQueueTest.cpp b/services/surfaceflinger/tests/unittests/MessageQueueTest.cpp
index 71f9f88..908637a 100644
--- a/services/surfaceflinger/tests/unittests/MessageQueueTest.cpp
+++ b/services/surfaceflinger/tests/unittests/MessageQueueTest.cpp
@@ -159,9 +159,9 @@
constexpr VsyncId vsyncId{42};
EXPECT_CALL(mTokenManager,
- generateTokenForPredictions(frametimeline::TimelineItem(kStartTime.ns(),
- kEndTime.ns(),
- kPresentTime.ns())))
+ generateTokenForPredictions(
+ frametimeline::TimelineItem(kStartTime.ns(), kEndTime.ns(),
+ kPresentTime.ns(), kPresentTime.ns())))
.WillOnce(Return(ftl::to_underlying(vsyncId)));
EXPECT_CALL(*mEventQueue.mHandler, dispatchFrame(vsyncId, kPresentTime)).Times(1);
EXPECT_NO_FATAL_FAILURE(
diff --git a/services/surfaceflinger/tests/unittests/PowerAdvisorTest.cpp b/services/surfaceflinger/tests/unittests/PowerAdvisorTest.cpp
index 8375bb9..5c25f34 100644
--- a/services/surfaceflinger/tests/unittests/PowerAdvisorTest.cpp
+++ b/services/surfaceflinger/tests/unittests/PowerAdvisorTest.cpp
@@ -17,7 +17,8 @@
#undef LOG_TAG
#define LOG_TAG "PowerAdvisorTest"
-#include <DisplayHardware/PowerAdvisor.h>
+#include "PowerAdvisor/PowerAdvisor.h"
+
#include <android_os.h>
#include <binder/Status.h>
#include <com_android_graphics_surfaceflinger_flags.h>
@@ -29,18 +30,17 @@
#include <ui/DisplayId.h>
#include <chrono>
#include <future>
-#include "TestableSurfaceFlinger.h"
-#include "mock/DisplayHardware/MockPowerHalController.h"
-#include "mock/DisplayHardware/MockPowerHintSessionWrapper.h"
+#include "mock/PowerAdvisor/MockPowerHalController.h"
+#include "mock/PowerAdvisor/MockPowerHintSessionWrapper.h"
using namespace android;
-using namespace android::Hwc2::mock;
+using namespace android::adpf::mock;
using namespace android::hardware::power;
using namespace std::chrono_literals;
using namespace testing;
using namespace android::power;
-namespace android::Hwc2::impl {
+namespace android::adpf::impl {
class PowerAdvisorTest : public testing::Test {
public:
@@ -73,7 +73,6 @@
void testGpuScenario(GpuTestConfig& config, WorkDuration& ret);
protected:
- TestableSurfaceFlinger mFlinger;
std::unique_ptr<PowerAdvisor> mPowerAdvisor;
MockPowerHalController* mMockPowerHalController;
std::shared_ptr<MockPowerHintSessionWrapper> mMockPowerHintSession;
@@ -98,7 +97,7 @@
}
void PowerAdvisorTest::SetUp() {
- mPowerAdvisor = std::make_unique<impl::PowerAdvisor>(*mFlinger.flinger());
+ mPowerAdvisor = std::make_unique<impl::PowerAdvisor>([]() {}, 80ms);
mPowerAdvisor->mPowerHal = std::make_unique<NiceMock<MockPowerHalController>>();
mMockPowerHalController =
reinterpret_cast<MockPowerHalController*>(mPowerAdvisor->mPowerHal.get());
@@ -844,4 +843,4 @@
}
} // namespace
-} // namespace android::Hwc2::impl
+} // namespace android::adpf::impl
diff --git a/services/surfaceflinger/tests/unittests/RefreshRateSelectorTest.cpp b/services/surfaceflinger/tests/unittests/RefreshRateSelectorTest.cpp
index 29e1c21..80b2b8d 100644
--- a/services/surfaceflinger/tests/unittests/RefreshRateSelectorTest.cpp
+++ b/services/surfaceflinger/tests/unittests/RefreshRateSelectorTest.cpp
@@ -308,42 +308,6 @@
<< " category=" << ftl::enum_string(testCase.frameRateCategory);
}
}
-
- template <class T>
- std::vector<LayerRequirement> createLayers(const std::initializer_list<T>& surfaceVotes) {
- std::vector<LayerRequirement> layers;
- for (auto surfaceVote : surfaceVotes) {
- ALOGI("**** %s: Adding layers for %s: (desiredFrameRate=%s, voteType=%s), "
- "(frameRateCategory=%s)",
- __func__, surfaceVote.name.c_str(),
- to_string(surfaceVote.desiredFrameRate).c_str(),
- ftl::enum_string(surfaceVote.voteType).c_str(),
- ftl::enum_string(surfaceVote.frameRateCategory).c_str());
-
- if (surfaceVote.desiredFrameRate.isValid()) {
- std::stringstream ss;
- ss << surfaceVote.name << " (" << surfaceVote.weight << "): ExplicitDefault ("
- << to_string(surfaceVote.desiredFrameRate) << ")";
- LayerRequirement layer = {.name = ss.str(),
- .vote = surfaceVote.voteType,
- .desiredRefreshRate = surfaceVote.desiredFrameRate,
- .weight = surfaceVote.weight};
- layers.push_back(layer);
- }
-
- if (surfaceVote.frameRateCategory != FrameRateCategory::Default) {
- std::stringstream ss;
- ss << surfaceVote.name << " (" << surfaceVote.weight << "): ExplicitCategory ("
- << ftl::enum_string(surfaceVote.frameRateCategory) << ")";
- LayerRequirement layer = {.name = ss.str(),
- .vote = LayerVoteType::ExplicitCategory,
- .frameRateCategory = surfaceVote.frameRateCategory,
- .weight = surfaceVote.weight};
- layers.push_back(layer);
- }
- }
- return layers;
- }
};
RefreshRateSelectorTest::RefreshRateSelectorTest() {
@@ -1812,98 +1776,6 @@
selector);
}
-TEST_P(RefreshRateSelectorTest, getBestFrameRateMode_multiSurface_arr) {
- if (GetParam() != Config::FrameRateOverride::Enabled) {
- return;
- }
-
- SET_FLAG_FOR_TEST(flags::vrr_config, true);
-
- auto selector = createSelector(kVrrMode_120, kModeId120);
-
- // Switch the policy to be more like an ARR device (primary range is a single rate).
- constexpr FpsRange k120_120Hz = {120_Hz, 120_Hz};
- constexpr FpsRange k0_120Hz = {0_Hz, 120_Hz};
- constexpr FpsRanges kPrimaryRanges = {/*physical*/ k120_120Hz,
- /*render*/ k120_120Hz};
- constexpr FpsRanges kAppRequestRanges = {/*physical*/ k120_120Hz,
- /*render*/ k0_120Hz};
- EXPECT_EQ(SetPolicyResult::Changed,
- selector.setDisplayManagerPolicy(
- {/*defaultMode*/ kModeId120, kPrimaryRanges, kAppRequestRanges}));
-
- // Surface can translate to multiple layers in SF scheduler due to category and frame rate
- // value.
- struct SurfaceVote {
- // Params
- std::string name = "";
- Fps desiredFrameRate = 0_Hz;
- LayerVoteType voteType = LayerVoteType::ExplicitDefault;
- FrameRateCategory frameRateCategory = FrameRateCategory::Default;
- float weight = 1.f;
- };
-
- auto layers = createLayers(
- std::initializer_list<SurfaceVote>{{.name = "60 fixed source",
- .desiredFrameRate = 60_Hz,
- .voteType = LayerVoteType::ExplicitExactOrMultiple,
- .weight = 0.27f},
- {.name = "1 fixed source + NoPreference",
- .desiredFrameRate = 1_Hz,
- .voteType = LayerVoteType::ExplicitExactOrMultiple,
- .frameRateCategory =
- FrameRateCategory::NoPreference}});
- auto actualRankedFrameRates = selector.getRankedFrameRates(layers);
- EXPECT_EQ(60_Hz, actualRankedFrameRates.ranking.front().frameRateMode.fps);
-
- layers = createLayers(
- std::initializer_list<SurfaceVote>{{.name = "60 fixed source",
- .desiredFrameRate = 60_Hz,
- .voteType = LayerVoteType::ExplicitExactOrMultiple,
- .weight = 0.27f},
- {.name = "1 fixed source + Normal",
- .desiredFrameRate = 1_Hz,
- .voteType = LayerVoteType::ExplicitExactOrMultiple,
- .frameRateCategory = FrameRateCategory::Normal}});
- actualRankedFrameRates = selector.getRankedFrameRates(layers);
- EXPECT_EQ(60_Hz, actualRankedFrameRates.ranking.front().frameRateMode.fps);
-
- layers = createLayers(std::initializer_list<SurfaceVote>{
- {.name = "30 fixed source + NoPreference",
- .desiredFrameRate = 30_Hz,
- .voteType = LayerVoteType::ExplicitExactOrMultiple,
- .frameRateCategory = FrameRateCategory::NoPreference}});
- actualRankedFrameRates = selector.getRankedFrameRates(layers);
- EXPECT_EQ(30_Hz, actualRankedFrameRates.ranking.front().frameRateMode.fps);
-
- layers = createLayers(std::initializer_list<SurfaceVote>{
- {.name = "1 fixed source + NoPreference",
- .desiredFrameRate = 1_Hz,
- .voteType = LayerVoteType::ExplicitExactOrMultiple,
- .frameRateCategory = FrameRateCategory::NoPreference}});
- actualRankedFrameRates = selector.getRankedFrameRates(layers);
- // Result affected by RefreshRateSelector.kMinSupportedFrameRate.
- EXPECT_EQ(20_Hz, actualRankedFrameRates.ranking.front().frameRateMode.fps);
-
- layers = createLayers(std::initializer_list<SurfaceVote>{
- {.name = "24 fixed source + NoPreference",
- .desiredFrameRate = 24_Hz,
- .voteType = LayerVoteType::ExplicitExactOrMultiple,
- .frameRateCategory = FrameRateCategory::NoPreference}});
- actualRankedFrameRates = selector.getRankedFrameRates(layers);
- EXPECT_EQ(24_Hz, actualRankedFrameRates.ranking.front().frameRateMode.fps);
-
- layers = createLayers(std::initializer_list<SurfaceVote>{
- {.name = "23.976 fixed source + NoPreference",
- .desiredFrameRate = 23.976_Hz,
- .voteType = LayerVoteType::ExplicitExactOrMultiple,
- .frameRateCategory = FrameRateCategory::NoPreference}});
- actualRankedFrameRates = selector.getRankedFrameRates(layers);
- // Chooses 120 unless certain threshold is set, see tests test23976Chooses120 and
- // test23976Chooses60IfThresholdIs120.
- EXPECT_EQ(120_Hz, actualRankedFrameRates.ranking.front().frameRateMode.fps);
-}
-
TEST_P(RefreshRateSelectorTest, getBestFrameRateMode_withFrameRateCategory_60_120) {
auto selector = createSelector(makeModes(kMode60, kMode120), kModeId60);
@@ -2240,6 +2112,46 @@
EXPECT_FALSE(actualRankedFrameRates.consideredSignals.touch);
}
+TEST_P(RefreshRateSelectorTest, getBestFrameRateMode_withFrameRateCategory_touchBoost_twoUids_arr) {
+ if (GetParam() != Config::FrameRateOverride::Enabled) {
+ return;
+ }
+
+ SET_FLAG_FOR_TEST(flags::vrr_config, true);
+ // Device with VRR config mode
+ auto selector = createSelector(kVrrMode_120, kModeId120);
+
+ std::vector<LayerRequirement> layers = {{.ownerUid = 1234, .weight = 1.f},
+ {.ownerUid = 5678, .weight = 1.f}};
+ auto& lr1 = layers[0];
+ auto& lr2 = layers[1];
+
+ lr1.vote = LayerVoteType::ExplicitCategory;
+ lr1.frameRateCategory = FrameRateCategory::Normal;
+ lr1.name = "ExplicitCategory Normal";
+ lr2.vote = LayerVoteType::ExplicitDefault;
+ lr2.desiredRefreshRate = 30_Hz;
+ lr2.name = "30Hz ExplicitDefault";
+ auto actualRankedFrameRates = selector.getRankedFrameRates(layers, {.touch = true});
+ // No global touch boost, for example a game that uses setFrameRate(30, default compatibility).
+ // However see 60 due to Normal vote.
+ EXPECT_FRAME_RATE_MODE(kVrrMode120TE240, 60_Hz,
+ actualRankedFrameRates.ranking.front().frameRateMode);
+ EXPECT_FALSE(actualRankedFrameRates.consideredSignals.touch);
+
+ lr1.vote = LayerVoteType::ExplicitCategory;
+ lr1.frameRateCategory = FrameRateCategory::HighHint;
+ lr1.name = "ExplicitCategory HighHint";
+ lr2.vote = LayerVoteType::ExplicitDefault;
+ lr2.desiredRefreshRate = 30_Hz;
+ lr2.name = "30Hz ExplicitDefault";
+ // Gets touch boost because the touched (HighHint) app is different from the 30 Default app.
+ actualRankedFrameRates = selector.getRankedFrameRates(layers, {.touch = true});
+ EXPECT_FRAME_RATE_MODE(kVrrMode120TE240, 120_Hz,
+ actualRankedFrameRates.ranking.front().frameRateMode);
+ EXPECT_TRUE(actualRankedFrameRates.consideredSignals.touch);
+}
+
TEST_P(RefreshRateSelectorTest,
getBestFrameRateMode_withFrameRateCategory_idleTimer_60_120_nonVrr) {
SET_FLAG_FOR_TEST(flags::vrr_config, false);
@@ -3825,6 +3737,51 @@
EXPECT_TRUE(frameRateOverrides.empty());
}
+TEST_P(RefreshRateSelectorTest, getFrameRateOverrides_twoUids_arr) {
+ if (GetParam() != Config::FrameRateOverride::Enabled) {
+ return;
+ }
+
+ SET_FLAG_FOR_TEST(flags::vrr_config, true);
+ // Device with VRR config mode
+ auto selector = createSelector(kVrrMode_120, kModeId120);
+
+ std::vector<LayerRequirement> layers = {{.ownerUid = 1234, .weight = 1.f},
+ {.ownerUid = 5678, .weight = 1.f}};
+ auto& lr1 = layers[0];
+ auto& lr2 = layers[1];
+
+ lr1.vote = LayerVoteType::ExplicitCategory;
+ lr1.frameRateCategory = FrameRateCategory::Normal;
+ lr1.name = "ExplicitCategory Normal";
+ lr2.vote = LayerVoteType::ExplicitDefault;
+ lr2.desiredRefreshRate = 30_Hz;
+ lr2.name = "30Hz ExplicitDefault";
+ // No global touch boost, for example a game that uses setFrameRate(30, default compatibility).
+ // The `displayFrameRate` is 60.
+ // However 30 Default app still gets frame rate override.
+ auto frameRateOverrides = selector.getFrameRateOverrides(layers, 60_Hz, {});
+ EXPECT_EQ(2u, frameRateOverrides.size());
+ ASSERT_EQ(1u, frameRateOverrides.count(1234));
+ EXPECT_EQ(60_Hz, frameRateOverrides.at(1234));
+ ASSERT_EQ(1u, frameRateOverrides.count(5678));
+ EXPECT_EQ(30_Hz, frameRateOverrides.at(5678));
+
+ lr1.vote = LayerVoteType::ExplicitCategory;
+ lr1.frameRateCategory = FrameRateCategory::HighHint;
+ lr1.name = "ExplicitCategory HighHint";
+ lr2.vote = LayerVoteType::ExplicitDefault;
+ lr2.desiredRefreshRate = 30_Hz;
+ lr2.name = "30Hz ExplicitDefault";
+ // Gets touch boost because the touched (HighHint) app is different from the 30 Default app.
+ // The `displayFrameRate` is 120 (late touch boost).
+ // However 30 Default app still gets frame rate override.
+ frameRateOverrides = selector.getFrameRateOverrides(layers, 120_Hz, {});
+ EXPECT_EQ(1u, frameRateOverrides.size());
+ ASSERT_EQ(1u, frameRateOverrides.count(5678));
+ EXPECT_EQ(30_Hz, frameRateOverrides.at(5678));
+}
+
TEST_P(RefreshRateSelectorTest, getFrameRateOverrides_withFrameRateCategory) {
if (GetParam() == Config::FrameRateOverride::Disabled) {
return;
diff --git a/services/surfaceflinger/tests/unittests/SchedulerTest.cpp b/services/surfaceflinger/tests/unittests/SchedulerTest.cpp
index ac09cbc..1fc874d 100644
--- a/services/surfaceflinger/tests/unittests/SchedulerTest.cpp
+++ b/services/surfaceflinger/tests/unittests/SchedulerTest.cpp
@@ -222,7 +222,7 @@
const auto selectorPtr =
std::make_shared<RefreshRateSelector>(kDisplay1Modes, kDisplay1Mode120->getId());
mScheduler->registerDisplay(kDisplayId1, selectorPtr);
- mScheduler->onDisplayModeChanged(kDisplayId1, kDisplay1Mode120_120);
+ mScheduler->onDisplayModeChanged(kDisplayId1, kDisplay1Mode120_120, true);
mScheduler->setContentRequirements({kLayer});
@@ -250,7 +250,7 @@
EXPECT_CALL(*mEventThread, onModeChanged(kDisplay1Mode120_120)).Times(1);
mScheduler->touchTimerCallback(TimerState::Reset);
- mScheduler->onDisplayModeChanged(kDisplayId1, kDisplay1Mode120_120);
+ mScheduler->onDisplayModeChanged(kDisplayId1, kDisplay1Mode120_120, true);
}
TEST_F(SchedulerTest, calculateMaxAcquiredBufferCount) {
diff --git a/services/surfaceflinger/tests/unittests/SurfaceFlinger_DisplayModeSwitching.cpp b/services/surfaceflinger/tests/unittests/SurfaceFlinger_DisplayModeSwitching.cpp
index 8699621..b0dd5c2 100644
--- a/services/surfaceflinger/tests/unittests/SurfaceFlinger_DisplayModeSwitching.cpp
+++ b/services/surfaceflinger/tests/unittests/SurfaceFlinger_DisplayModeSwitching.cpp
@@ -34,7 +34,7 @@
static_cast<hal::HWConfigId>( \
ftl::to_underlying(modeId)), \
_, _)) \
- .WillOnce(DoAll(SetArgPointee<3>(timeline), Return(Error::NONE)))
+ .WillOnce(DoAll(SetArgPointee<3>(timeline), Return(hal::Error::NONE)))
namespace android {
namespace {
diff --git a/services/surfaceflinger/tests/unittests/SurfaceFlinger_NotifyExpectedPresentTest.cpp b/services/surfaceflinger/tests/unittests/SurfaceFlinger_NotifyExpectedPresentTest.cpp
index 20a3315..6cc6322 100644
--- a/services/surfaceflinger/tests/unittests/SurfaceFlinger_NotifyExpectedPresentTest.cpp
+++ b/services/surfaceflinger/tests/unittests/SurfaceFlinger_NotifyExpectedPresentTest.cpp
@@ -18,12 +18,13 @@
#define LOG_TAG "LibSurfaceFlingerUnittests"
#include <gui/SurfaceComposerClient.h>
+#include "DisplayHardware/Hal.h"
#include "DisplayTransactionTestHelpers.h"
namespace android {
using FakeHwcDisplayInjector = TestableSurfaceFlinger::FakeHwcDisplayInjector;
-using android::hardware::graphics::composer::V2_1::Error;
+using android::hardware::graphics::composer::hal::Error;
class NotifyExpectedPresentTest : public DisplayTransactionTest {
public:
diff --git a/services/surfaceflinger/tests/unittests/SurfaceFlinger_PowerHintTest.cpp b/services/surfaceflinger/tests/unittests/SurfaceFlinger_PowerHintTest.cpp
index c3934e6..3ae5ed9 100644
--- a/services/surfaceflinger/tests/unittests/SurfaceFlinger_PowerHintTest.cpp
+++ b/services/surfaceflinger/tests/unittests/SurfaceFlinger_PowerHintTest.cpp
@@ -21,6 +21,8 @@
#include "CommitAndCompositeTest.h"
+#include "DisplayHardware/Hal.h"
+
using namespace std::chrono_literals;
using testing::_;
using testing::Return;
@@ -40,7 +42,7 @@
EXPECT_CALL(*mComposer, presentOrValidateDisplay(HWC_DISPLAY, _, _, _, _, _, _)).WillOnce([] {
constexpr Duration kMockHwcRunTime = 20ms;
std::this_thread::sleep_for(kMockHwcRunTime);
- return hardware::graphics::composer::V2_1::Error::NONE;
+ return hardware::graphics::composer::hal::Error::NONE;
});
EXPECT_CALL(*mPowerAdvisor, reportActualWorkDuration()).Times(1);
@@ -61,7 +63,7 @@
EXPECT_CALL(*mComposer, presentOrValidateDisplay(HWC_DISPLAY, _, _, _, _, _, _)).WillOnce([] {
constexpr Duration kMockHwcRunTime = 20ms;
std::this_thread::sleep_for(kMockHwcRunTime);
- return hardware::graphics::composer::V2_1::Error::NONE;
+ return hardware::graphics::composer::hal::Error::NONE;
});
EXPECT_CALL(*mPowerAdvisor, reportActualWorkDuration()).Times(0);
diff --git a/services/surfaceflinger/tests/unittests/TestableSurfaceFlinger.h b/services/surfaceflinger/tests/unittests/TestableSurfaceFlinger.h
index 4dec5f6..7f0b7a6 100644
--- a/services/surfaceflinger/tests/unittests/TestableSurfaceFlinger.h
+++ b/services/surfaceflinger/tests/unittests/TestableSurfaceFlinger.h
@@ -44,22 +44,22 @@
#include "NativeWindowSurface.h"
#include "RenderArea.h"
#include "Scheduler/RefreshRateSelector.h"
+#include "Scheduler/VSyncTracker.h"
+#include "Scheduler/VsyncController.h"
#include "SurfaceFlinger.h"
#include "TestableScheduler.h"
#include "android/gui/ISurfaceComposerClient.h"
+
#include "mock/DisplayHardware/MockComposer.h"
#include "mock/DisplayHardware/MockDisplayMode.h"
-#include "mock/DisplayHardware/MockPowerAdvisor.h"
#include "mock/MockEventThread.h"
#include "mock/MockFrameTimeline.h"
#include "mock/MockFrameTracer.h"
#include "mock/MockSchedulerCallback.h"
-#include "mock/system/window/MockNativeWindow.h"
-
-#include "Scheduler/VSyncTracker.h"
-#include "Scheduler/VsyncController.h"
#include "mock/MockVSyncTracker.h"
#include "mock/MockVsyncController.h"
+#include "mock/PowerAdvisor/MockPowerAdvisor.h"
+#include "mock/system/window/MockNativeWindow.h"
namespace android {
@@ -190,7 +190,7 @@
&mFlinger->mCompositionEngine->getHwComposer());
}
- void setupPowerAdvisor(std::unique_ptr<Hwc2::PowerAdvisor> powerAdvisor) {
+ void setupPowerAdvisor(std::unique_ptr<adpf::PowerAdvisor> powerAdvisor) {
mFlinger->mPowerAdvisor = std::move(powerAdvisor);
}
@@ -472,12 +472,10 @@
ScreenCaptureResults captureResults;
auto displayState = std::optional{display->getCompositionDisplay()->getState()};
auto layers = getLayerSnapshotsFn();
- auto layerFEs = mFlinger->extractLayerFEs(layers);
return mFlinger->renderScreenImpl(renderArea.get(), buffer, regionSampling,
false /* grayscale */, false /* isProtected */,
- false /* attachGainmap */, captureResults, displayState,
- layers, layerFEs);
+ captureResults, displayState, layers);
}
auto getLayerSnapshotsForScreenshotsFn(ui::LayerStack layerStack, uint32_t uid) {
@@ -1162,7 +1160,7 @@
scheduler::mock::NoOpSchedulerCallback mNoOpSchedulerCallback;
std::unique_ptr<frametimeline::impl::TokenManager> mTokenManager;
scheduler::TestableScheduler* mScheduler = nullptr;
- Hwc2::mock::PowerAdvisor mPowerAdvisor;
+ adpf::mock::PowerAdvisor mPowerAdvisor;
};
} // namespace android
diff --git a/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockComposer.h b/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockComposer.h
index 3e6a768..0d5266e 100644
--- a/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockComposer.h
+++ b/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockComposer.h
@@ -20,6 +20,7 @@
#include "DisplayHardware/ComposerHal.h"
#include "DisplayHardware/HWC2.h"
+#include "DisplayHardware/Hal.h"
namespace android {
@@ -34,9 +35,9 @@
using android::hardware::graphics::common::V1_2::Dataspace;
using android::hardware::graphics::common::V1_2::PixelFormat;
+using android::hardware::graphics::composer::hal::Error;
using android::hardware::graphics::composer::V2_1::Config;
using android::hardware::graphics::composer::V2_1::Display;
-using android::hardware::graphics::composer::V2_1::Error;
using android::hardware::graphics::composer::V2_1::IComposer;
using android::hardware::graphics::composer::V2_1::Layer;
using android::hardware::graphics::composer::V2_4::IComposerCallback;
@@ -142,8 +143,8 @@
V2_4::Error(Display, Config, std::vector<VsyncPeriodNanos>*));
MOCK_METHOD2(getDisplayVsyncPeriod, V2_4::Error(Display, VsyncPeriodNanos*));
MOCK_METHOD4(setActiveConfigWithConstraints,
- V2_4::Error(Display, Config, const IComposerClient::VsyncPeriodChangeConstraints&,
- VsyncPeriodChangeTimeline*));
+ Error(Display, Config, const IComposerClient::VsyncPeriodChangeConstraints&,
+ VsyncPeriodChangeTimeline*));
MOCK_METHOD2(setAutoLowLatencyMode, V2_4::Error(Display, bool));
MOCK_METHOD2(setBootDisplayConfig, Error(Display, Config));
MOCK_METHOD1(clearBootDisplayConfig, Error(Display));
@@ -186,6 +187,9 @@
std::vector<aidl::android::hardware::graphics::composer3::DisplayLuts::LayerLut>*));
MOCK_METHOD(Error, setLayerLuts,
(Display, Layer, aidl::android::hardware::graphics::composer3::Luts&));
+ MOCK_METHOD(Error, getMaxLayerPictureProfiles, (Display, int32_t*));
+ MOCK_METHOD(Error, setDisplayPictureProfileId, (Display, PictureProfileId id));
+ MOCK_METHOD(Error, setLayerPictureProfileId, (Display, Layer, PictureProfileId id));
};
} // namespace Hwc2::mock
diff --git a/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockHWC2.h b/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockHWC2.h
index 384b908..ec065a7 100644
--- a/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockHWC2.h
+++ b/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockHWC2.h
@@ -17,6 +17,7 @@
#pragma once
#include <gmock/gmock.h>
+#include <cstdint>
#include "DisplayHardware/HWC2.h"
@@ -52,7 +53,7 @@
(override));
MOCK_METHOD(hal::Error, getName, (std::string *), (const, override));
MOCK_METHOD(hal::Error, getRequests,
- (hal::DisplayRequest *, (std::unordered_map<Layer *, hal::LayerRequest> *)),
+ (hal::DisplayRequest*, (std::unordered_map<Layer*, hal::LayerRequest>*)),
(override));
MOCK_METHOD((ftl::Expected<ui::DisplayConnectionType, hal::Error>), getConnectionType, (),
(const, override));
@@ -85,8 +86,8 @@
MOCK_METHOD(ftl::Future<hal::Error>, setDisplayBrightness,
(float, float, const Hwc2::Composer::DisplayBrightnessOptions &), (override));
MOCK_METHOD(hal::Error, setActiveConfigWithConstraints,
- (hal::HWConfigId, const hal::VsyncPeriodChangeConstraints &,
- hal::VsyncPeriodChangeTimeline *),
+ (hal::HWConfigId, const hal::VsyncPeriodChangeConstraints&,
+ hal::VsyncPeriodChangeTimeline*),
(override));
MOCK_METHOD(hal::Error, setBootDisplayConfig, (hal::HWConfigId), (override));
MOCK_METHOD(hal::Error, clearBootDisplayConfig, (), (override));
@@ -111,7 +112,9 @@
(aidl::android::hardware::graphics::composer3::OverlayProperties *),
(const override));
MOCK_METHOD(hal::Error, getRequestedLuts,
- ((HWC2::Display::LayerLuts*), (HWC2::Display::LutFileDescriptorMapper&)),
+ (HWC2::Display::LayerLuts*, HWC2::Display::LutFileDescriptorMapper&), (override));
+ MOCK_METHOD(hal::Error, getMaxLayerPictureProfiles, (int32_t*), (override));
+ MOCK_METHOD(hal::Error, setPictureProfileHandle, (const android::PictureProfileHandle&),
(override));
};
@@ -126,6 +129,8 @@
(uint32_t, const android::sp<android::GraphicBuffer> &,
const android::sp<android::Fence> &),
(override));
+ MOCK_METHOD(hal::Error, setBufferSlotsToClear,
+ (const std::vector<uint32_t>& slotsToClear, uint32_t activeBufferSlot), (override));
MOCK_METHOD(hal::Error, setSurfaceDamage, (const android::Region &), (override));
MOCK_METHOD(hal::Error, setBlendMode, (hal::BlendMode), (override));
MOCK_METHOD(hal::Error, setColor, (aidl::android::hardware::graphics::composer3::Color),
@@ -149,6 +154,8 @@
MOCK_METHOD(hal::Error, setBlockingRegion, (const android::Region &), (override));
MOCK_METHOD(hal::Error, setLuts, (aidl::android::hardware::graphics::composer3::Luts&),
(override));
+ MOCK_METHOD(hal::Error, setPictureProfileHandle, (const android::PictureProfileHandle&),
+ (override));
};
} // namespace android::HWC2::mock
diff --git a/services/surfaceflinger/CompositionEngine/tests/MockHWComposer.cpp b/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockHWComposer.cpp
similarity index 81%
rename from services/surfaceflinger/CompositionEngine/tests/MockHWComposer.cpp
rename to services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockHWComposer.cpp
index ae52670..f310633 100644
--- a/services/surfaceflinger/CompositionEngine/tests/MockHWComposer.cpp
+++ b/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockHWComposer.cpp
@@ -16,17 +16,11 @@
#include "MockHWComposer.h"
-namespace android {
-
-// This will go away once HWComposer is moved into the "backend" library
-HWComposer::~HWComposer() = default;
-
-namespace mock {
+namespace android::mock {
// The Google Mock documentation recommends explicit non-header instantiations
// for better compile time performance.
HWComposer::HWComposer() = default;
HWComposer::~HWComposer() = default;
-} // namespace mock
-} // namespace android
+} // namespace android::mock
diff --git a/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockHWComposer.h b/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockHWComposer.h
new file mode 100644
index 0000000..88f83d2
--- /dev/null
+++ b/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockHWComposer.h
@@ -0,0 +1,156 @@
+/*
+ * Copyright 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include <gmock/gmock.h>
+
+#include "DisplayHardware/HWComposer.h"
+
+namespace android::mock {
+
+class HWComposer : public android::HWComposer {
+public:
+ using HWDisplayId = android::hardware::graphics::composer::hal::HWDisplayId;
+ using PowerMode = android::hardware::graphics::composer::hal::PowerMode;
+
+ HWComposer();
+ ~HWComposer() override;
+
+ MOCK_METHOD(void, setCallback, (HWC2::ComposerCallback&), (override));
+ MOCK_METHOD(bool, getDisplayIdentificationData,
+ (HWDisplayId, uint8_t*, DisplayIdentificationData*), (const, override));
+ MOCK_METHOD(bool, hasCapability, (aidl::android::hardware::graphics::composer3::Capability),
+ (const, override));
+ MOCK_METHOD(bool, hasDisplayCapability,
+ (HalDisplayId, aidl::android::hardware::graphics::composer3::DisplayCapability),
+ (const, override));
+
+ MOCK_METHOD(size_t, getMaxVirtualDisplayCount, (), (const, override));
+ MOCK_METHOD(size_t, getMaxVirtualDisplayDimension, (), (const, override));
+ MOCK_METHOD(bool, allocateVirtualDisplay, (HalVirtualDisplayId, ui::Size, ui::PixelFormat*),
+ (override));
+ MOCK_METHOD(void, allocatePhysicalDisplay,
+ (hal::HWDisplayId, PhysicalDisplayId, std::optional<ui::Size>), (override));
+
+ MOCK_METHOD(std::shared_ptr<HWC2::Layer>, createLayer, (HalDisplayId), (override));
+ MOCK_METHOD(status_t, getDeviceCompositionChanges,
+ (HalDisplayId, bool, std::optional<std::chrono::steady_clock::time_point>, nsecs_t,
+ Fps, std::optional<android::HWComposer::DeviceRequestedChanges>*));
+ MOCK_METHOD(status_t, setClientTarget,
+ (HalDisplayId, uint32_t, const sp<Fence>&, const sp<GraphicBuffer>&, ui::Dataspace,
+ float),
+ (override));
+ MOCK_METHOD(status_t, presentAndGetReleaseFences,
+ (HalDisplayId, std::optional<std::chrono::steady_clock::time_point>), (override));
+ MOCK_METHOD(status_t, executeCommands, (HalDisplayId));
+ MOCK_METHOD(status_t, setPowerMode, (PhysicalDisplayId, PowerMode), (override));
+ MOCK_METHOD(status_t, setColorTransform, (HalDisplayId, const mat4&), (override));
+ MOCK_METHOD(void, disconnectDisplay, (HalDisplayId), (override));
+ MOCK_METHOD(sp<Fence>, getPresentFence, (HalDisplayId), (const, override));
+ MOCK_METHOD(nsecs_t, getPresentTimestamp, (PhysicalDisplayId), (const, override));
+ MOCK_METHOD(sp<Fence>, getLayerReleaseFence, (HalDisplayId, HWC2::Layer*), (const, override));
+ MOCK_METHOD(status_t, setOutputBuffer,
+ (HalVirtualDisplayId, const sp<Fence>&, const sp<GraphicBuffer>&), (override));
+ MOCK_METHOD(void, clearReleaseFences, (HalDisplayId), (override));
+ MOCK_METHOD(status_t, getHdrCapabilities, (HalDisplayId, HdrCapabilities*), (override));
+ MOCK_METHOD(int32_t, getSupportedPerFrameMetadata, (HalDisplayId), (const, override));
+ MOCK_METHOD(std::vector<ui::RenderIntent>, getRenderIntents, (HalDisplayId, ui::ColorMode),
+ (const, override));
+ MOCK_METHOD(mat4, getDataspaceSaturationMatrix, (HalDisplayId, ui::Dataspace), (override));
+ MOCK_METHOD(status_t, getDisplayedContentSamplingAttributes,
+ (HalDisplayId, ui::PixelFormat*, ui::Dataspace*, uint8_t*), (override));
+ MOCK_METHOD(status_t, setDisplayContentSamplingEnabled, (HalDisplayId, bool, uint8_t, uint64_t),
+ (override));
+ MOCK_METHOD(status_t, getDisplayedContentSample,
+ (HalDisplayId, uint64_t, uint64_t, DisplayedFrameStats*), (override));
+ MOCK_METHOD(ftl::Future<status_t>, setDisplayBrightness,
+ (PhysicalDisplayId, float, float, const Hwc2::Composer::DisplayBrightnessOptions&),
+ (override));
+ MOCK_METHOD(std::optional<DisplayIdentificationInfo>, onHotplug,
+ (hal::HWDisplayId, hal::Connection), (override));
+ MOCK_METHOD(bool, updatesDeviceProductInfoOnHotplugReconnect, (), (const, override));
+ MOCK_METHOD(std::optional<PhysicalDisplayId>, onVsync, (hal::HWDisplayId, int64_t));
+ MOCK_METHOD(void, setVsyncEnabled, (PhysicalDisplayId, hal::Vsync), (override));
+ MOCK_METHOD(bool, isConnected, (PhysicalDisplayId), (const, override));
+ MOCK_METHOD(std::vector<HWComposer::HWCDisplayMode>, getModes, (PhysicalDisplayId, int32_t),
+ (const, override));
+ MOCK_METHOD((ftl::Expected<hal::HWConfigId, status_t>), getActiveMode, (PhysicalDisplayId),
+ (const, override));
+ MOCK_METHOD(std::vector<ui::ColorMode>, getColorModes, (PhysicalDisplayId), (const, override));
+ MOCK_METHOD(status_t, setActiveColorMode, (PhysicalDisplayId, ui::ColorMode, ui::RenderIntent),
+ (override));
+ MOCK_METHOD(ui::DisplayConnectionType, getDisplayConnectionType, (PhysicalDisplayId),
+ (const, override));
+ MOCK_METHOD(bool, isVsyncPeriodSwitchSupported, (PhysicalDisplayId), (const, override));
+ MOCK_METHOD((ftl::Expected<nsecs_t, status_t>), getDisplayVsyncPeriod, (PhysicalDisplayId),
+ (const, override));
+ MOCK_METHOD(status_t, setActiveModeWithConstraints,
+ (PhysicalDisplayId, hal::HWConfigId, const hal::VsyncPeriodChangeConstraints&,
+ hal::VsyncPeriodChangeTimeline*),
+ (override));
+ MOCK_METHOD(status_t, setBootDisplayMode, (PhysicalDisplayId, hal::HWConfigId), (override));
+ MOCK_METHOD(status_t, clearBootDisplayMode, (PhysicalDisplayId), (override));
+ MOCK_METHOD(std::optional<hal::HWConfigId>, getPreferredBootDisplayMode, (PhysicalDisplayId),
+ (override));
+
+ MOCK_METHOD(std::vector<aidl::android::hardware::graphics::common::HdrConversionCapability>,
+ getHdrConversionCapabilities, (), (const, override));
+ MOCK_METHOD(status_t, setHdrConversionStrategy,
+ (aidl::android::hardware::graphics::common::HdrConversionStrategy,
+ aidl::android::hardware::graphics::common::Hdr*),
+ (override));
+ MOCK_METHOD(status_t, setAutoLowLatencyMode, (PhysicalDisplayId, bool), (override));
+ MOCK_METHOD(status_t, getSupportedContentTypes,
+ (PhysicalDisplayId, std::vector<hal::ContentType>*), (const, override));
+ MOCK_METHOD(status_t, setContentType, (PhysicalDisplayId, hal::ContentType)), (override);
+ MOCK_METHOD((const std::unordered_map<std::string, bool>&), getSupportedLayerGenericMetadata,
+ (), (const, override));
+ MOCK_METHOD(void, dump, (std::string&), (const, override));
+ MOCK_METHOD(void, dumpOverlayProperties, (std::string&), (const, override));
+ MOCK_METHOD(android::Hwc2::Composer*, getComposer, (), (const, override));
+
+ MOCK_METHOD(hal::HWDisplayId, getPrimaryHwcDisplayId, (), (const, override));
+ MOCK_METHOD(PhysicalDisplayId, getPrimaryDisplayId, (), (const, override));
+ MOCK_METHOD(bool, isHeadless, (), (const, override));
+
+ MOCK_METHOD(std::optional<PhysicalDisplayId>, toPhysicalDisplayId, (hal::HWDisplayId),
+ (const, override));
+ MOCK_METHOD(std::optional<hal::HWDisplayId>, fromPhysicalDisplayId, (PhysicalDisplayId),
+ (const, override));
+ MOCK_METHOD(status_t, getDisplayDecorationSupport,
+ (PhysicalDisplayId,
+ std::optional<aidl::android::hardware::graphics::common::DisplayDecorationSupport>*
+ support),
+ (override));
+ MOCK_METHOD(status_t, setIdleTimerEnabled, (PhysicalDisplayId, std::chrono::milliseconds),
+ (override));
+ MOCK_METHOD(bool, hasDisplayIdleTimerCapability, (PhysicalDisplayId), (const, override));
+ MOCK_METHOD(Hwc2::AidlTransform, getPhysicalDisplayOrientation, (PhysicalDisplayId),
+ (const, override));
+ MOCK_METHOD(bool, getValidateSkipped, (HalDisplayId), (const, override));
+ MOCK_METHOD(const aidl::android::hardware::graphics::composer3::OverlayProperties&,
+ getOverlaySupport, (), (const, override));
+ MOCK_METHOD(status_t, setRefreshRateChangedCallbackDebugEnabled, (PhysicalDisplayId, bool));
+ MOCK_METHOD(status_t, notifyExpectedPresent, (PhysicalDisplayId, TimePoint, Fps));
+ MOCK_METHOD(HWC2::Display::LutFileDescriptorMapper&, getLutFileDescriptorMapper, (),
+ (override));
+ MOCK_METHOD(int32_t, getMaxLayerPictureProfiles, (PhysicalDisplayId));
+ MOCK_METHOD(status_t, setDisplayPictureProfileHandle,
+ (PhysicalDisplayId, const PictureProfileHandle&));
+};
+
+} // namespace android::mock
diff --git a/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockIPower.cpp b/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockIPower.cpp
deleted file mode 100644
index 2323ebb..0000000
--- a/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockIPower.cpp
+++ /dev/null
@@ -1,24 +0,0 @@
-/*
- * Copyright 2022 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#include "mock/DisplayHardware/MockIPower.h"
-
-namespace android::Hwc2::mock {
-
-// Explicit default instantiation is recommended.
-MockIPower::MockIPower() = default;
-
-} // namespace android::Hwc2::mock
diff --git a/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockIPower.h b/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockIPower.h
deleted file mode 100644
index ed1405b..0000000
--- a/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockIPower.h
+++ /dev/null
@@ -1,68 +0,0 @@
-/*
- * Copyright 2022 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#pragma once
-
-#include "binder/Status.h"
-
-// FMQ library in IPower does questionable conversions
-#pragma clang diagnostic push
-#pragma clang diagnostic ignored "-Wconversion"
-#include <aidl/android/hardware/power/IPower.h>
-#pragma clang diagnostic pop
-
-#include <gmock/gmock.h>
-
-using aidl::android::hardware::power::Boost;
-using aidl::android::hardware::power::ChannelConfig;
-using aidl::android::hardware::power::IPower;
-using aidl::android::hardware::power::IPowerHintSession;
-using aidl::android::hardware::power::SessionConfig;
-using aidl::android::hardware::power::SessionTag;
-
-using aidl::android::hardware::power::Mode;
-using android::binder::Status;
-
-namespace android::Hwc2::mock {
-
-class MockIPower : public IPower {
-public:
- MockIPower();
-
- MOCK_METHOD(ndk::ScopedAStatus, isBoostSupported, (Boost boost, bool* ret), (override));
- MOCK_METHOD(ndk::ScopedAStatus, setBoost, (Boost boost, int32_t durationMs), (override));
- MOCK_METHOD(ndk::ScopedAStatus, isModeSupported, (Mode mode, bool* ret), (override));
- MOCK_METHOD(ndk::ScopedAStatus, setMode, (Mode mode, bool enabled), (override));
- MOCK_METHOD(ndk::ScopedAStatus, createHintSession,
- (int32_t tgid, int32_t uid, const std::vector<int32_t>& threadIds,
- int64_t durationNanos, std::shared_ptr<IPowerHintSession>* session),
- (override));
- MOCK_METHOD(ndk::ScopedAStatus, getHintSessionPreferredRate, (int64_t * rate), (override));
- MOCK_METHOD(ndk::ScopedAStatus, createHintSessionWithConfig,
- (int32_t tgid, int32_t uid, const std::vector<int32_t>& threadIds,
- int64_t durationNanos, SessionTag tag, SessionConfig* config,
- std::shared_ptr<IPowerHintSession>* _aidl_return),
- (override));
- MOCK_METHOD(ndk::ScopedAStatus, getSessionChannel,
- (int32_t tgid, int32_t uid, ChannelConfig* _aidl_return), (override));
- MOCK_METHOD(ndk::ScopedAStatus, closeSessionChannel, (int32_t tgid, int32_t uid), (override));
- MOCK_METHOD(ndk::ScopedAStatus, getInterfaceVersion, (int32_t * version), (override));
- MOCK_METHOD(ndk::ScopedAStatus, getInterfaceHash, (std::string * hash), (override));
- MOCK_METHOD(ndk::SpAIBinder, asBinder, (), (override));
- MOCK_METHOD(bool, isRemote, (), (override));
-};
-
-} // namespace android::Hwc2::mock
diff --git a/services/surfaceflinger/tests/unittests/mock/MockEventThread.h b/services/surfaceflinger/tests/unittests/mock/MockEventThread.h
index 7398cbe..c976697 100644
--- a/services/surfaceflinger/tests/unittests/mock/MockEventThread.h
+++ b/services/surfaceflinger/tests/unittests/mock/MockEventThread.h
@@ -30,6 +30,7 @@
MOCK_METHOD(sp<EventThreadConnection>, createEventConnection, (EventRegistrationFlags),
(const, override));
MOCK_METHOD(void, enableSyntheticVsync, (bool), (override));
+ MOCK_METHOD(void, omitVsyncDispatching, (bool), (override));
MOCK_METHOD(void, onHotplugReceived, (PhysicalDisplayId, bool), (override));
MOCK_METHOD(void, onHotplugConnectionError, (int32_t), (override));
MOCK_METHOD(void, onModeChanged, (const scheduler::FrameRateMode&), (override));
@@ -52,6 +53,7 @@
MOCK_METHOD(void, onHdcpLevelsChanged,
(PhysicalDisplayId displayId, int32_t connectedLevel, int32_t maxLevel),
(override));
+ MOCK_METHOD(void, addBufferStuffedUids, (BufferStuffingMap), (override));
};
} // namespace android::mock
diff --git a/services/surfaceflinger/tests/unittests/mock/MockLayer.h b/services/surfaceflinger/tests/unittests/mock/MockLayer.h
index 45f86fa..59f0966 100644
--- a/services/surfaceflinger/tests/unittests/mock/MockLayer.h
+++ b/services/surfaceflinger/tests/unittests/mock/MockLayer.h
@@ -19,6 +19,8 @@
#include <gmock/gmock.h>
#include <optional>
+#include "Layer.h"
+
namespace android::mock {
class MockLayer : public Layer {
@@ -26,8 +28,11 @@
MockLayer(SurfaceFlinger* flinger, std::string name)
: Layer(LayerCreationArgs(flinger, nullptr, std::move(name), 0, {})) {}
- MockLayer(SurfaceFlinger* flinger, std::string name, std::optional<uint32_t> uid)
- : Layer(LayerCreationArgs(flinger, nullptr, std::move(name), 0, {}, uid)) {}
+ MockLayer(SurfaceFlinger* flinger, std::string name, std::optional<uint32_t> id)
+ : Layer(LayerCreationArgs(flinger, nullptr, std::move(name), 0, {}, id)) {}
+
+ MockLayer(SurfaceFlinger* flinger, std::optional<uint32_t> id)
+ : Layer(LayerCreationArgs(flinger, nullptr, "TestLayer", 0, {}, id)) {}
explicit MockLayer(SurfaceFlinger* flinger) : MockLayer(flinger, "TestLayer") {}
diff --git a/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockPowerAdvisor.cpp b/services/surfaceflinger/tests/unittests/mock/PowerAdvisor/MockPowerAdvisor.cpp
similarity index 91%
rename from services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockPowerAdvisor.cpp
rename to services/surfaceflinger/tests/unittests/mock/PowerAdvisor/MockPowerAdvisor.cpp
index 1ba38a8..f4c1e52 100644
--- a/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockPowerAdvisor.cpp
+++ b/services/surfaceflinger/tests/unittests/mock/PowerAdvisor/MockPowerAdvisor.cpp
@@ -16,10 +16,10 @@
#include "MockPowerAdvisor.h"
-namespace android::Hwc2::mock {
+namespace android::adpf::mock {
// Explicit default instantiation is recommended.
PowerAdvisor::PowerAdvisor() = default;
PowerAdvisor::~PowerAdvisor() = default;
-} // namespace android::Hwc2::mock
+} // namespace android::adpf::mock
diff --git a/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockPowerAdvisor.h b/services/surfaceflinger/tests/unittests/mock/PowerAdvisor/MockPowerAdvisor.h
similarity index 94%
rename from services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockPowerAdvisor.h
rename to services/surfaceflinger/tests/unittests/mock/PowerAdvisor/MockPowerAdvisor.h
index 4efdfe8..5c4512a 100644
--- a/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockPowerAdvisor.h
+++ b/services/surfaceflinger/tests/unittests/mock/PowerAdvisor/MockPowerAdvisor.h
@@ -18,11 +18,11 @@
#include <gmock/gmock.h>
-#include "DisplayHardware/PowerAdvisor.h"
+#include "PowerAdvisor/PowerAdvisor.h"
-namespace android::Hwc2::mock {
+namespace android::adpf::mock {
-class PowerAdvisor : public android::Hwc2::PowerAdvisor {
+class PowerAdvisor : public android::adpf::PowerAdvisor {
public:
PowerAdvisor();
~PowerAdvisor() override;
@@ -65,4 +65,4 @@
MOCK_METHOD(void, setTotalFrameTargetWorkDuration, (Duration targetDuration), (override));
};
-} // namespace android::Hwc2::mock
+} // namespace android::adpf::mock
diff --git a/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockPowerHalController.cpp b/services/surfaceflinger/tests/unittests/mock/PowerAdvisor/MockPowerHalController.cpp
similarity index 91%
rename from services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockPowerHalController.cpp
rename to services/surfaceflinger/tests/unittests/mock/PowerAdvisor/MockPowerHalController.cpp
index 3ec5c2d..3b8de55 100644
--- a/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockPowerHalController.cpp
+++ b/services/surfaceflinger/tests/unittests/mock/PowerAdvisor/MockPowerHalController.cpp
@@ -16,9 +16,9 @@
#include "MockPowerHalController.h"
-namespace android::Hwc2::mock {
+namespace android::adpf::mock {
MockPowerHalController::MockPowerHalController() = default;
MockPowerHalController::~MockPowerHalController() = default;
-} // namespace android::Hwc2::mock
+} // namespace android::adpf::mock
diff --git a/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockPowerHalController.h b/services/surfaceflinger/tests/unittests/mock/PowerAdvisor/MockPowerHalController.h
similarity index 92%
rename from services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockPowerHalController.h
rename to services/surfaceflinger/tests/unittests/mock/PowerAdvisor/MockPowerHalController.h
index af1862d..891f507 100644
--- a/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockPowerHalController.h
+++ b/services/surfaceflinger/tests/unittests/mock/PowerAdvisor/MockPowerHalController.h
@@ -19,10 +19,7 @@
#include <gmock/gmock.h>
#include <scheduler/Time.h>
-#pragma clang diagnostic push
-#pragma clang diagnostic ignored "-Wconversion"
#include <powermanager/PowerHalController.h>
-#pragma clang diagnostic pop
namespace android {
namespace hardware {
@@ -32,7 +29,7 @@
} // namespace hardware
} // namespace android
-namespace android::Hwc2::mock {
+namespace android::adpf::mock {
using android::power::HalResult;
@@ -57,6 +54,8 @@
MOCK_METHOD(HalResult<aidl::android::hardware::power::ChannelConfig>, getSessionChannel,
(int tgid, int uid), (override));
MOCK_METHOD(HalResult<void>, closeSessionChannel, (int tgid, int uid), (override));
+ MOCK_METHOD(HalResult<aidl::android::hardware::power::SupportInfo>, getSupportInfo, (),
+ (override));
};
-} // namespace android::Hwc2::mock
\ No newline at end of file
+} // namespace android::adpf::mock
\ No newline at end of file
diff --git a/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockPowerHintSessionWrapper.cpp b/services/surfaceflinger/tests/unittests/mock/PowerAdvisor/MockPowerHintSessionWrapper.cpp
similarity index 85%
rename from services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockPowerHintSessionWrapper.cpp
rename to services/surfaceflinger/tests/unittests/mock/PowerAdvisor/MockPowerHintSessionWrapper.cpp
index d383283..cb39783 100644
--- a/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockPowerHintSessionWrapper.cpp
+++ b/services/surfaceflinger/tests/unittests/mock/PowerAdvisor/MockPowerHintSessionWrapper.cpp
@@ -14,12 +14,12 @@
* limitations under the License.
*/
-#include "mock/DisplayHardware/MockPowerHintSessionWrapper.h"
+#include "MockPowerHintSessionWrapper.h"
-namespace android::Hwc2::mock {
+namespace android::adpf::mock {
// Explicit default instantiation is recommended.
MockPowerHintSessionWrapper::MockPowerHintSessionWrapper()
: power::PowerHintSessionWrapper(nullptr) {}
-} // namespace android::Hwc2::mock
+} // namespace android::adpf::mock
diff --git a/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockPowerHintSessionWrapper.h b/services/surfaceflinger/tests/unittests/mock/PowerAdvisor/MockPowerHintSessionWrapper.h
similarity index 88%
rename from services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockPowerHintSessionWrapper.h
rename to services/surfaceflinger/tests/unittests/mock/PowerAdvisor/MockPowerHintSessionWrapper.h
index bc6950c..4518de8 100644
--- a/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockPowerHintSessionWrapper.h
+++ b/services/surfaceflinger/tests/unittests/mock/PowerAdvisor/MockPowerHintSessionWrapper.h
@@ -16,13 +16,7 @@
#pragma once
-#include "binder/Status.h"
-
-#pragma clang diagnostic push
-#pragma clang diagnostic ignored "-Wconversion"
-#include <aidl/android/hardware/power/IPower.h>
#include <powermanager/PowerHintSessionWrapper.h>
-#pragma clang diagnostic pop
#include <gmock/gmock.h>
@@ -34,7 +28,7 @@
using namespace aidl::android::hardware::power;
-namespace android::Hwc2::mock {
+namespace android::adpf::mock {
class MockPowerHintSessionWrapper : public power::PowerHintSessionWrapper {
public:
@@ -52,4 +46,4 @@
MOCK_METHOD(power::HalResult<SessionConfig>, getSessionConfig, (), (override));
};
-} // namespace android::Hwc2::mock
+} // namespace android::adpf::mock
diff --git a/services/vibratorservice/VibratorManagerHalController.cpp b/services/vibratorservice/VibratorManagerHalController.cpp
index ba35d15..494f88f 100644
--- a/services/vibratorservice/VibratorManagerHalController.cpp
+++ b/services/vibratorservice/VibratorManagerHalController.cpp
@@ -150,6 +150,23 @@
return apply(cancelSyncedFn, "cancelSynced");
}
+HalResult<std::shared_ptr<Aidl::IVibrationSession>> ManagerHalController::startSession(
+ const std::vector<int32_t>& ids, const Aidl::VibrationSessionConfig& config,
+ const std::function<void()>& completionCallback) {
+ hal_fn<std::shared_ptr<Aidl::IVibrationSession>> startSessionFn =
+ [&](std::shared_ptr<ManagerHalWrapper> hal) {
+ return hal->startSession(ids, config, completionCallback);
+ };
+ return apply(startSessionFn, "startSession");
+}
+
+HalResult<void> ManagerHalController::clearSessions() {
+ hal_fn<void> clearSessionsFn = [](std::shared_ptr<ManagerHalWrapper> hal) {
+ return hal->clearSessions();
+ };
+ return apply(clearSessionsFn, "clearSessions");
+}
+
}; // namespace vibrator
}; // namespace android
diff --git a/services/vibratorservice/VibratorManagerHalWrapper.cpp b/services/vibratorservice/VibratorManagerHalWrapper.cpp
index 93ec781..3db8ff8 100644
--- a/services/vibratorservice/VibratorManagerHalWrapper.cpp
+++ b/services/vibratorservice/VibratorManagerHalWrapper.cpp
@@ -29,6 +29,30 @@
constexpr int32_t SINGLE_VIBRATOR_ID = 0;
const constexpr char* MISSING_VIBRATOR_MESSAGE_PREFIX = "No vibrator with id=";
+HalResult<void> ManagerHalWrapper::prepareSynced(const std::vector<int32_t>&) {
+ return HalResult<void>::unsupported();
+}
+
+HalResult<void> ManagerHalWrapper::triggerSynced(const std::function<void()>&) {
+ return HalResult<void>::unsupported();
+}
+
+HalResult<void> ManagerHalWrapper::cancelSynced() {
+ return HalResult<void>::unsupported();
+}
+
+HalResult<std::shared_ptr<Aidl::IVibrationSession>> ManagerHalWrapper::startSession(
+ const std::vector<int32_t>&, const Aidl::VibrationSessionConfig&,
+ const std::function<void()>&) {
+ return HalResult<std::shared_ptr<Aidl::IVibrationSession>>::unsupported();
+}
+
+HalResult<void> ManagerHalWrapper::clearSessions() {
+ return HalResult<void>::unsupported();
+}
+
+// -------------------------------------------------------------------------------------------------
+
HalResult<void> LegacyManagerHalWrapper::ping() {
auto pingFn = [](HalWrapper* hal) { return hal->ping(); };
return mController->doWithRetry<void>(pingFn, "ping");
@@ -59,18 +83,6 @@
(MISSING_VIBRATOR_MESSAGE_PREFIX + std::to_string(id)).c_str());
}
-HalResult<void> LegacyManagerHalWrapper::prepareSynced(const std::vector<int32_t>&) {
- return HalResult<void>::unsupported();
-}
-
-HalResult<void> LegacyManagerHalWrapper::triggerSynced(const std::function<void()>&) {
- return HalResult<void>::unsupported();
-}
-
-HalResult<void> LegacyManagerHalWrapper::cancelSynced() {
- return HalResult<void>::unsupported();
-}
-
// -------------------------------------------------------------------------------------------------
std::shared_ptr<HalWrapper> AidlManagerHalWrapper::connectToVibrator(
@@ -186,6 +198,17 @@
return HalResultFactory::fromStatus(getHal()->triggerSynced(cb));
}
+HalResult<std::shared_ptr<Aidl::IVibrationSession>> AidlManagerHalWrapper::startSession(
+ const std::vector<int32_t>& ids, const Aidl::VibrationSessionConfig& config,
+ const std::function<void()>& completionCallback) {
+ auto cb = ndk::SharedRefBase::make<HalCallbackWrapper>(completionCallback);
+ std::shared_ptr<Aidl::IVibrationSession> session;
+ auto status = getHal()->startSession(ids, config, cb, &session);
+ return HalResultFactory::fromStatus<std::shared_ptr<Aidl::IVibrationSession>>(std::move(status),
+ std::move(
+ session));
+}
+
HalResult<void> AidlManagerHalWrapper::cancelSynced() {
auto ret = HalResultFactory::fromStatus(getHal()->cancelSynced());
if (ret.isOk()) {
@@ -200,6 +223,10 @@
return ret;
}
+HalResult<void> AidlManagerHalWrapper::clearSessions() {
+ return HalResultFactory::fromStatus(getHal()->clearSessions());
+}
+
std::shared_ptr<Aidl::IVibratorManager> AidlManagerHalWrapper::getHal() {
std::lock_guard<std::mutex> lock(mHandleMutex);
return mHandle;
diff --git a/services/vibratorservice/include/vibratorservice/VibratorManagerHalController.h b/services/vibratorservice/include/vibratorservice/VibratorManagerHalController.h
index 70c846b..72d4752 100644
--- a/services/vibratorservice/include/vibratorservice/VibratorManagerHalController.h
+++ b/services/vibratorservice/include/vibratorservice/VibratorManagerHalController.h
@@ -62,6 +62,10 @@
HalResult<void> prepareSynced(const std::vector<int32_t>& ids) override final;
HalResult<void> triggerSynced(const std::function<void()>& completionCallback) override final;
HalResult<void> cancelSynced() override final;
+ HalResult<std::shared_ptr<IVibrationSession>> startSession(
+ const std::vector<int32_t>& ids, const VibrationSessionConfig& config,
+ const std::function<void()>& completionCallback) override final;
+ HalResult<void> clearSessions() override final;
private:
Connector mConnector;
diff --git a/services/vibratorservice/include/vibratorservice/VibratorManagerHalWrapper.h b/services/vibratorservice/include/vibratorservice/VibratorManagerHalWrapper.h
index 9e3f221..8d4ca0e 100644
--- a/services/vibratorservice/include/vibratorservice/VibratorManagerHalWrapper.h
+++ b/services/vibratorservice/include/vibratorservice/VibratorManagerHalWrapper.h
@@ -38,7 +38,8 @@
aidl::android::hardware::vibrator::IVibratorManager::CAP_MIXED_TRIGGER_PERFORM,
MIXED_TRIGGER_COMPOSE =
aidl::android::hardware::vibrator::IVibratorManager::CAP_MIXED_TRIGGER_COMPOSE,
- TRIGGER_CALLBACK = aidl::android::hardware::vibrator::IVibratorManager::CAP_TRIGGER_CALLBACK
+ TRIGGER_CALLBACK = aidl::android::hardware::vibrator::IVibratorManager::CAP_TRIGGER_CALLBACK,
+ START_SESSIONS = aidl::android::hardware::vibrator::IVibratorManager::CAP_START_SESSIONS
};
inline ManagerCapabilities operator|(ManagerCapabilities lhs, ManagerCapabilities rhs) {
@@ -64,6 +65,9 @@
// Wrapper for VibratorManager HAL handlers.
class ManagerHalWrapper {
public:
+ using IVibrationSession = aidl::android::hardware::vibrator::IVibrationSession;
+ using VibrationSessionConfig = aidl::android::hardware::vibrator::VibrationSessionConfig;
+
ManagerHalWrapper() = default;
virtual ~ManagerHalWrapper() = default;
@@ -78,9 +82,13 @@
virtual HalResult<std::vector<int32_t>> getVibratorIds() = 0;
virtual HalResult<std::shared_ptr<HalController>> getVibrator(int32_t id) = 0;
- virtual HalResult<void> prepareSynced(const std::vector<int32_t>& ids) = 0;
- virtual HalResult<void> triggerSynced(const std::function<void()>& completionCallback) = 0;
- virtual HalResult<void> cancelSynced() = 0;
+ virtual HalResult<void> prepareSynced(const std::vector<int32_t>& ids);
+ virtual HalResult<void> triggerSynced(const std::function<void()>& completionCallback);
+ virtual HalResult<void> cancelSynced();
+ virtual HalResult<std::shared_ptr<IVibrationSession>> startSession(
+ const std::vector<int32_t>& ids, const VibrationSessionConfig& config,
+ const std::function<void()>& completionCallback);
+ virtual HalResult<void> clearSessions();
};
// Wrapper for the VibratorManager over single Vibrator HAL.
@@ -98,10 +106,6 @@
HalResult<std::vector<int32_t>> getVibratorIds() override final;
HalResult<std::shared_ptr<HalController>> getVibrator(int32_t id) override final;
- HalResult<void> prepareSynced(const std::vector<int32_t>& ids) override final;
- HalResult<void> triggerSynced(const std::function<void()>& completionCallback) override final;
- HalResult<void> cancelSynced() override final;
-
private:
const std::shared_ptr<HalController> mController;
};
@@ -126,6 +130,10 @@
HalResult<void> prepareSynced(const std::vector<int32_t>& ids) override final;
HalResult<void> triggerSynced(const std::function<void()>& completionCallback) override final;
HalResult<void> cancelSynced() override final;
+ HalResult<std::shared_ptr<IVibrationSession>> startSession(
+ const std::vector<int32_t>& ids, const VibrationSessionConfig& config,
+ const std::function<void()>& completionCallback) override final;
+ HalResult<void> clearSessions() override final;
private:
std::mutex mHandleMutex;
diff --git a/services/vibratorservice/test/VibratorManagerHalControllerTest.cpp b/services/vibratorservice/test/VibratorManagerHalControllerTest.cpp
index c7214e0..b201670 100644
--- a/services/vibratorservice/test/VibratorManagerHalControllerTest.cpp
+++ b/services/vibratorservice/test/VibratorManagerHalControllerTest.cpp
@@ -27,6 +27,8 @@
#include "test_mocks.h"
#include "test_utils.h"
+using aidl::android::hardware::vibrator::IVibrationSession;
+using aidl::android::hardware::vibrator::VibrationSessionConfig;
using android::vibrator::HalController;
using namespace android;
@@ -34,6 +36,7 @@
static constexpr int MAX_ATTEMPTS = 2;
static const std::vector<int32_t> VIBRATOR_IDS = {1, 2};
+static const VibrationSessionConfig SESSION_CONFIG;
static constexpr int VIBRATOR_ID = 1;
// -------------------------------------------------------------------------------------------------
@@ -52,6 +55,11 @@
MOCK_METHOD(vibrator::HalResult<void>, triggerSynced,
(const std::function<void()>& completionCallback), (override));
MOCK_METHOD(vibrator::HalResult<void>, cancelSynced, (), (override));
+ MOCK_METHOD(vibrator::HalResult<std::shared_ptr<IVibrationSession>>, startSession,
+ (const std::vector<int32_t>& ids, const VibrationSessionConfig& s,
+ const std::function<void()>& completionCallback),
+ (override));
+ MOCK_METHOD(vibrator::HalResult<void>, clearSessions, (), (override));
};
// -------------------------------------------------------------------------------------------------
@@ -79,7 +87,8 @@
void setHalExpectations(int32_t cardinality, vibrator::HalResult<void> voidResult,
vibrator::HalResult<vibrator::ManagerCapabilities> capabilitiesResult,
vibrator::HalResult<std::vector<int32_t>> idsResult,
- vibrator::HalResult<std::shared_ptr<HalController>> vibratorResult) {
+ vibrator::HalResult<std::shared_ptr<HalController>> vibratorResult,
+ vibrator::HalResult<std::shared_ptr<IVibrationSession>> sessionResult) {
EXPECT_CALL(*mMockHal.get(), ping())
.Times(Exactly(cardinality))
.WillRepeatedly(Return(voidResult));
@@ -101,10 +110,16 @@
EXPECT_CALL(*mMockHal.get(), cancelSynced())
.Times(Exactly(cardinality))
.WillRepeatedly(Return(voidResult));
+ EXPECT_CALL(*mMockHal.get(), startSession(_, _, _))
+ .Times(Exactly(cardinality))
+ .WillRepeatedly(Return(sessionResult));
+ EXPECT_CALL(*mMockHal.get(), clearSessions())
+ .Times(Exactly(cardinality))
+ .WillRepeatedly(Return(voidResult));
if (cardinality > 1) {
// One reconnection for each retry.
- EXPECT_CALL(*mMockHal.get(), tryReconnect()).Times(Exactly(7 * (cardinality - 1)));
+ EXPECT_CALL(*mMockHal.get(), tryReconnect()).Times(Exactly(9 * (cardinality - 1)));
} else {
EXPECT_CALL(*mMockHal.get(), tryReconnect()).Times(Exactly(0));
}
@@ -127,7 +142,8 @@
vibrator::HalResult<vibrator::ManagerCapabilities>::ok(
vibrator::ManagerCapabilities::SYNC),
vibrator::HalResult<std::vector<int32_t>>::ok(VIBRATOR_IDS),
- vibrator::HalResult<std::shared_ptr<HalController>>::ok(nullptr));
+ vibrator::HalResult<std::shared_ptr<HalController>>::ok(nullptr),
+ vibrator::HalResult<std::shared_ptr<IVibrationSession>>::ok(nullptr));
ASSERT_TRUE(mController->ping().isOk());
@@ -146,6 +162,8 @@
ASSERT_TRUE(mController->prepareSynced(VIBRATOR_IDS).isOk());
ASSERT_TRUE(mController->triggerSynced([]() {}).isOk());
ASSERT_TRUE(mController->cancelSynced().isOk());
+ ASSERT_TRUE(mController->startSession(VIBRATOR_IDS, SESSION_CONFIG, []() {}).isOk());
+ ASSERT_TRUE(mController->clearSessions().isOk());
ASSERT_EQ(1, mConnectCounter);
}
@@ -154,7 +172,8 @@
setHalExpectations(/* cardinality= */ 1, vibrator::HalResult<void>::unsupported(),
vibrator::HalResult<vibrator::ManagerCapabilities>::unsupported(),
vibrator::HalResult<std::vector<int32_t>>::unsupported(),
- vibrator::HalResult<std::shared_ptr<HalController>>::unsupported());
+ vibrator::HalResult<std::shared_ptr<HalController>>::unsupported(),
+ vibrator::HalResult<std::shared_ptr<IVibrationSession>>::unsupported());
ASSERT_TRUE(mController->ping().isUnsupported());
ASSERT_TRUE(mController->getCapabilities().isUnsupported());
@@ -163,6 +182,8 @@
ASSERT_TRUE(mController->prepareSynced(VIBRATOR_IDS).isUnsupported());
ASSERT_TRUE(mController->triggerSynced([]() {}).isUnsupported());
ASSERT_TRUE(mController->cancelSynced().isUnsupported());
+ ASSERT_TRUE(mController->startSession(VIBRATOR_IDS, SESSION_CONFIG, []() {}).isUnsupported());
+ ASSERT_TRUE(mController->clearSessions().isUnsupported());
ASSERT_EQ(1, mConnectCounter);
}
@@ -171,7 +192,8 @@
setHalExpectations(/* cardinality= */ 1, vibrator::HalResult<void>::failed("msg"),
vibrator::HalResult<vibrator::ManagerCapabilities>::failed("msg"),
vibrator::HalResult<std::vector<int32_t>>::failed("msg"),
- vibrator::HalResult<std::shared_ptr<HalController>>::failed("msg"));
+ vibrator::HalResult<std::shared_ptr<HalController>>::failed("msg"),
+ vibrator::HalResult<std::shared_ptr<IVibrationSession>>::failed("msg"));
ASSERT_TRUE(mController->ping().isFailed());
ASSERT_TRUE(mController->getCapabilities().isFailed());
@@ -180,6 +202,8 @@
ASSERT_TRUE(mController->prepareSynced(VIBRATOR_IDS).isFailed());
ASSERT_TRUE(mController->triggerSynced([]() {}).isFailed());
ASSERT_TRUE(mController->cancelSynced().isFailed());
+ ASSERT_TRUE(mController->startSession(VIBRATOR_IDS, SESSION_CONFIG, []() {}).isFailed());
+ ASSERT_TRUE(mController->clearSessions().isFailed());
ASSERT_EQ(1, mConnectCounter);
}
@@ -188,7 +212,9 @@
setHalExpectations(MAX_ATTEMPTS, vibrator::HalResult<void>::transactionFailed("m"),
vibrator::HalResult<vibrator::ManagerCapabilities>::transactionFailed("m"),
vibrator::HalResult<std::vector<int32_t>>::transactionFailed("m"),
- vibrator::HalResult<std::shared_ptr<HalController>>::transactionFailed("m"));
+ vibrator::HalResult<std::shared_ptr<HalController>>::transactionFailed("m"),
+ vibrator::HalResult<std::shared_ptr<IVibrationSession>>::transactionFailed(
+ "m"));
ASSERT_TRUE(mController->ping().isFailed());
ASSERT_TRUE(mController->getCapabilities().isFailed());
@@ -197,6 +223,8 @@
ASSERT_TRUE(mController->prepareSynced(VIBRATOR_IDS).isFailed());
ASSERT_TRUE(mController->triggerSynced([]() {}).isFailed());
ASSERT_TRUE(mController->cancelSynced().isFailed());
+ ASSERT_TRUE(mController->startSession(VIBRATOR_IDS, SESSION_CONFIG, []() {}).isFailed());
+ ASSERT_TRUE(mController->clearSessions().isFailed());
ASSERT_EQ(1, mConnectCounter);
}
diff --git a/services/vibratorservice/test/VibratorManagerHalWrapperAidlTest.cpp b/services/vibratorservice/test/VibratorManagerHalWrapperAidlTest.cpp
index ca13c0b..a2f002d 100644
--- a/services/vibratorservice/test/VibratorManagerHalWrapperAidlTest.cpp
+++ b/services/vibratorservice/test/VibratorManagerHalWrapperAidlTest.cpp
@@ -69,12 +69,25 @@
MOCK_METHOD(bool, isRemote, (), (override));
};
+class MockIVibrationSession : public IVibrationSession {
+public:
+ MockIVibrationSession() = default;
+
+ MOCK_METHOD(ndk::ScopedAStatus, close, (), (override));
+ MOCK_METHOD(ndk::ScopedAStatus, abort, (), (override));
+ MOCK_METHOD(ndk::ScopedAStatus, getInterfaceVersion, (int32_t*), (override));
+ MOCK_METHOD(ndk::ScopedAStatus, getInterfaceHash, (std::string*), (override));
+ MOCK_METHOD(ndk::SpAIBinder, asBinder, (), (override));
+ MOCK_METHOD(bool, isRemote, (), (override));
+};
+
// -------------------------------------------------------------------------------------------------
class VibratorManagerHalWrapperAidlTest : public Test {
public:
void SetUp() override {
mMockVibrator = ndk::SharedRefBase::make<StrictMock<vibrator::MockIVibrator>>();
+ mMockSession = ndk::SharedRefBase::make<StrictMock<MockIVibrationSession>>();
mMockHal = ndk::SharedRefBase::make<StrictMock<MockIVibratorManager>>();
mMockScheduler = std::make_shared<StrictMock<vibrator::MockCallbackScheduler>>();
mWrapper = std::make_unique<vibrator::AidlManagerHalWrapper>(mMockScheduler, mMockHal);
@@ -86,11 +99,13 @@
std::unique_ptr<vibrator::ManagerHalWrapper> mWrapper = nullptr;
std::shared_ptr<StrictMock<MockIVibratorManager>> mMockHal = nullptr;
std::shared_ptr<StrictMock<vibrator::MockIVibrator>> mMockVibrator = nullptr;
+ std::shared_ptr<StrictMock<MockIVibrationSession>> mMockSession = nullptr;
};
// -------------------------------------------------------------------------------------------------
static const std::vector<int32_t> kVibratorIds = {1, 2};
+static const VibrationSessionConfig kSessionConfig;
static constexpr int kVibratorId = 1;
TEST_F(VibratorManagerHalWrapperAidlTest, TestGetCapabilitiesDoesNotCacheFailedResult) {
@@ -319,3 +334,35 @@
ASSERT_TRUE(mWrapper->getVibratorIds().isOk());
ASSERT_TRUE(mWrapper->cancelSynced().isOk());
}
+
+TEST_F(VibratorManagerHalWrapperAidlTest, TestStartSession) {
+ EXPECT_CALL(*mMockHal.get(), startSession(_, _, _, _))
+ .Times(Exactly(3))
+ .WillOnce(Return(ndk::ScopedAStatus::fromStatus(STATUS_UNKNOWN_TRANSACTION)))
+ .WillOnce(Return(ndk::ScopedAStatus::fromExceptionCode(EX_SECURITY)))
+ .WillOnce(
+ DoAll(DoAll(SetArgPointee<3>(mMockSession), Return(ndk::ScopedAStatus::ok()))));
+
+ std::unique_ptr<int32_t> callbackCounter = std::make_unique<int32_t>();
+ auto callback = vibrator::TestFactory::createCountingCallback(callbackCounter.get());
+
+ ASSERT_TRUE(mWrapper->startSession(kVibratorIds, kSessionConfig, callback).isUnsupported());
+ ASSERT_TRUE(mWrapper->startSession(kVibratorIds, kSessionConfig, callback).isFailed());
+
+ auto result = mWrapper->startSession(kVibratorIds, kSessionConfig, callback);
+ ASSERT_TRUE(result.isOk());
+ ASSERT_NE(nullptr, result.value().get());
+ ASSERT_EQ(0, *callbackCounter.get());
+}
+
+TEST_F(VibratorManagerHalWrapperAidlTest, TestClearSessions) {
+ EXPECT_CALL(*mMockHal.get(), clearSessions())
+ .Times(Exactly(3))
+ .WillOnce(Return(ndk::ScopedAStatus::fromStatus(STATUS_UNKNOWN_TRANSACTION)))
+ .WillOnce(Return(ndk::ScopedAStatus::fromExceptionCode(EX_SECURITY)))
+ .WillOnce(Return(ndk::ScopedAStatus::ok()));
+
+ ASSERT_TRUE(mWrapper->clearSessions().isUnsupported());
+ ASSERT_TRUE(mWrapper->clearSessions().isFailed());
+ ASSERT_TRUE(mWrapper->clearSessions().isOk());
+}
diff --git a/services/vibratorservice/test/VibratorManagerHalWrapperLegacyTest.cpp b/services/vibratorservice/test/VibratorManagerHalWrapperLegacyTest.cpp
index 7877236..52c865e 100644
--- a/services/vibratorservice/test/VibratorManagerHalWrapperLegacyTest.cpp
+++ b/services/vibratorservice/test/VibratorManagerHalWrapperLegacyTest.cpp
@@ -29,6 +29,8 @@
using aidl::android::hardware::vibrator::CompositePrimitive;
using aidl::android::hardware::vibrator::Effect;
using aidl::android::hardware::vibrator::EffectStrength;
+using aidl::android::hardware::vibrator::IVibrationSession;
+using aidl::android::hardware::vibrator::VibrationSessionConfig;
using std::chrono::milliseconds;
@@ -112,3 +114,12 @@
ASSERT_TRUE(mWrapper->triggerSynced([]() {}).isUnsupported());
ASSERT_TRUE(mWrapper->cancelSynced().isUnsupported());
}
+
+TEST_F(VibratorManagerHalWrapperLegacyTest, TestSessionOperationsUnsupported) {
+ std::vector<int32_t> vibratorIds;
+ vibratorIds.push_back(0);
+ VibrationSessionConfig config;
+
+ ASSERT_TRUE(mWrapper->startSession(vibratorIds, config, []() {}).isUnsupported());
+ ASSERT_TRUE(mWrapper->clearSessions().isUnsupported());
+}
diff --git a/vulkan/libvulkan/api.cpp b/vulkan/libvulkan/api.cpp
index c335e2a..8451ad1 100644
--- a/vulkan/libvulkan/api.cpp
+++ b/vulkan/libvulkan/api.cpp
@@ -45,6 +45,9 @@
#include "driver.h"
#include "layers_extensions.h"
+#include <com_android_graphics_libvulkan_flags.h>
+
+using namespace com::android::graphics::libvulkan;
namespace vulkan {
namespace api {
@@ -1473,7 +1476,7 @@
if (!EnsureInitialized())
return VK_ERROR_OUT_OF_HOST_MEMORY;
- *pApiVersion = VK_API_VERSION_1_3;
+ *pApiVersion = flags::vulkan_1_4_instance_api() ? VK_API_VERSION_1_4 : VK_API_VERSION_1_3;
return VK_SUCCESS;
}
diff --git a/vulkan/libvulkan/api_gen.cpp b/vulkan/libvulkan/api_gen.cpp
index 9ff0b46..131c97c 100644
--- a/vulkan/libvulkan/api_gen.cpp
+++ b/vulkan/libvulkan/api_gen.cpp
@@ -266,6 +266,7 @@
INIT_PROC(true, dev, CreateRenderPass);
INIT_PROC(true, dev, DestroyRenderPass);
INIT_PROC(true, dev, GetRenderAreaGranularity);
+ INIT_PROC(false, dev, GetRenderingAreaGranularity);
INIT_PROC(true, dev, CreateCommandPool);
INIT_PROC(true, dev, DestroyCommandPool);
INIT_PROC(true, dev, ResetCommandPool);
@@ -323,6 +324,7 @@
INIT_PROC_EXT(KHR_swapchain, true, dev, GetSwapchainImagesKHR);
INIT_PROC_EXT(KHR_swapchain, true, dev, AcquireNextImageKHR);
INIT_PROC_EXT(KHR_swapchain, true, dev, QueuePresentKHR);
+ INIT_PROC(false, dev, CmdPushDescriptorSet);
INIT_PROC(false, dev, TrimCommandPool);
INIT_PROC(false, dev, GetDeviceGroupPeerMemoryFeatures);
INIT_PROC(false, dev, BindBufferMemory2);
@@ -335,6 +337,7 @@
INIT_PROC(false, dev, CreateDescriptorUpdateTemplate);
INIT_PROC(false, dev, DestroyDescriptorUpdateTemplate);
INIT_PROC(false, dev, UpdateDescriptorSetWithTemplate);
+ INIT_PROC(false, dev, CmdPushDescriptorSetWithTemplate);
INIT_PROC(false, dev, GetBufferMemoryRequirements2);
INIT_PROC(false, dev, GetImageMemoryRequirements2);
INIT_PROC(false, dev, GetImageSparseMemoryRequirements2);
@@ -359,11 +362,13 @@
INIT_PROC(false, dev, GetBufferOpaqueCaptureAddress);
INIT_PROC(false, dev, GetBufferDeviceAddress);
INIT_PROC(false, dev, GetDeviceMemoryOpaqueCaptureAddress);
+ INIT_PROC(false, dev, CmdSetLineStipple);
INIT_PROC(false, dev, CmdSetCullMode);
INIT_PROC(false, dev, CmdSetFrontFace);
INIT_PROC(false, dev, CmdSetPrimitiveTopology);
INIT_PROC(false, dev, CmdSetViewportWithCount);
INIT_PROC(false, dev, CmdSetScissorWithCount);
+ INIT_PROC(false, dev, CmdBindIndexBuffer2);
INIT_PROC(false, dev, CmdBindVertexBuffers2);
INIT_PROC(false, dev, CmdSetDepthTestEnable);
INIT_PROC(false, dev, CmdSetDepthWriteEnable);
@@ -390,8 +395,22 @@
INIT_PROC(false, dev, CmdPipelineBarrier2);
INIT_PROC(false, dev, QueueSubmit2);
INIT_PROC(false, dev, CmdWriteTimestamp2);
+ INIT_PROC(false, dev, CopyMemoryToImage);
+ INIT_PROC(false, dev, CopyImageToMemory);
+ INIT_PROC(false, dev, CopyImageToImage);
+ INIT_PROC(false, dev, TransitionImageLayout);
INIT_PROC(false, dev, CmdBeginRendering);
INIT_PROC(false, dev, CmdEndRendering);
+ INIT_PROC(false, dev, GetImageSubresourceLayout2);
+ INIT_PROC(false, dev, GetDeviceImageSubresourceLayout);
+ INIT_PROC(false, dev, MapMemory2);
+ INIT_PROC(false, dev, UnmapMemory2);
+ INIT_PROC(false, dev, CmdBindDescriptorSets2);
+ INIT_PROC(false, dev, CmdPushConstants2);
+ INIT_PROC(false, dev, CmdPushDescriptorSet2);
+ INIT_PROC(false, dev, CmdPushDescriptorSetWithTemplate2);
+ INIT_PROC(false, dev, CmdSetRenderingAttachmentLocations);
+ INIT_PROC(false, dev, CmdSetRenderingInputAttachmentIndices);
// clang-format on
return success;
@@ -480,6 +499,7 @@
VKAPI_ATTR VkResult CreateRenderPass(VkDevice device, const VkRenderPassCreateInfo* pCreateInfo, const VkAllocationCallbacks* pAllocator, VkRenderPass* pRenderPass);
VKAPI_ATTR void DestroyRenderPass(VkDevice device, VkRenderPass renderPass, const VkAllocationCallbacks* pAllocator);
VKAPI_ATTR void GetRenderAreaGranularity(VkDevice device, VkRenderPass renderPass, VkExtent2D* pGranularity);
+VKAPI_ATTR void GetRenderingAreaGranularity(VkDevice device, const VkRenderingAreaInfo* pRenderingAreaInfo, VkExtent2D* pGranularity);
VKAPI_ATTR VkResult CreateCommandPool(VkDevice device, const VkCommandPoolCreateInfo* pCreateInfo, const VkAllocationCallbacks* pAllocator, VkCommandPool* pCommandPool);
VKAPI_ATTR void DestroyCommandPool(VkDevice device, VkCommandPool commandPool, const VkAllocationCallbacks* pAllocator);
VKAPI_ATTR VkResult ResetCommandPool(VkDevice device, VkCommandPool commandPool, VkCommandPoolResetFlags flags);
@@ -550,6 +570,7 @@
VKAPI_ATTR void GetPhysicalDeviceQueueFamilyProperties2(VkPhysicalDevice physicalDevice, uint32_t* pQueueFamilyPropertyCount, VkQueueFamilyProperties2* pQueueFamilyProperties);
VKAPI_ATTR void GetPhysicalDeviceMemoryProperties2(VkPhysicalDevice physicalDevice, VkPhysicalDeviceMemoryProperties2* pMemoryProperties);
VKAPI_ATTR void GetPhysicalDeviceSparseImageFormatProperties2(VkPhysicalDevice physicalDevice, const VkPhysicalDeviceSparseImageFormatInfo2* pFormatInfo, uint32_t* pPropertyCount, VkSparseImageFormatProperties2* pProperties);
+VKAPI_ATTR void CmdPushDescriptorSet(VkCommandBuffer commandBuffer, VkPipelineBindPoint pipelineBindPoint, VkPipelineLayout layout, uint32_t set, uint32_t descriptorWriteCount, const VkWriteDescriptorSet* pDescriptorWrites);
VKAPI_ATTR void TrimCommandPool(VkDevice device, VkCommandPool commandPool, VkCommandPoolTrimFlags flags);
VKAPI_ATTR void GetPhysicalDeviceExternalBufferProperties(VkPhysicalDevice physicalDevice, const VkPhysicalDeviceExternalBufferInfo* pExternalBufferInfo, VkExternalBufferProperties* pExternalBufferProperties);
VKAPI_ATTR void GetPhysicalDeviceExternalSemaphoreProperties(VkPhysicalDevice physicalDevice, const VkPhysicalDeviceExternalSemaphoreInfo* pExternalSemaphoreInfo, VkExternalSemaphoreProperties* pExternalSemaphoreProperties);
@@ -567,6 +588,7 @@
VKAPI_ATTR VkResult CreateDescriptorUpdateTemplate(VkDevice device, const VkDescriptorUpdateTemplateCreateInfo* pCreateInfo, const VkAllocationCallbacks* pAllocator, VkDescriptorUpdateTemplate* pDescriptorUpdateTemplate);
VKAPI_ATTR void DestroyDescriptorUpdateTemplate(VkDevice device, VkDescriptorUpdateTemplate descriptorUpdateTemplate, const VkAllocationCallbacks* pAllocator);
VKAPI_ATTR void UpdateDescriptorSetWithTemplate(VkDevice device, VkDescriptorSet descriptorSet, VkDescriptorUpdateTemplate descriptorUpdateTemplate, const void* pData);
+VKAPI_ATTR void CmdPushDescriptorSetWithTemplate(VkCommandBuffer commandBuffer, VkDescriptorUpdateTemplate descriptorUpdateTemplate, VkPipelineLayout layout, uint32_t set, const void* pData);
VKAPI_ATTR void GetBufferMemoryRequirements2(VkDevice device, const VkBufferMemoryRequirementsInfo2* pInfo, VkMemoryRequirements2* pMemoryRequirements);
VKAPI_ATTR void GetImageMemoryRequirements2(VkDevice device, const VkImageMemoryRequirementsInfo2* pInfo, VkMemoryRequirements2* pMemoryRequirements);
VKAPI_ATTR void GetImageSparseMemoryRequirements2(VkDevice device, const VkImageSparseMemoryRequirementsInfo2* pInfo, uint32_t* pSparseMemoryRequirementCount, VkSparseImageMemoryRequirements2* pSparseMemoryRequirements);
@@ -591,12 +613,14 @@
VKAPI_ATTR uint64_t GetBufferOpaqueCaptureAddress(VkDevice device, const VkBufferDeviceAddressInfo* pInfo);
VKAPI_ATTR VkDeviceAddress GetBufferDeviceAddress(VkDevice device, const VkBufferDeviceAddressInfo* pInfo);
VKAPI_ATTR uint64_t GetDeviceMemoryOpaqueCaptureAddress(VkDevice device, const VkDeviceMemoryOpaqueCaptureAddressInfo* pInfo);
+VKAPI_ATTR void CmdSetLineStipple(VkCommandBuffer commandBuffer, uint32_t lineStippleFactor, uint16_t lineStipplePattern);
VKAPI_ATTR VkResult GetPhysicalDeviceToolProperties(VkPhysicalDevice physicalDevice, uint32_t* pToolCount, VkPhysicalDeviceToolProperties* pToolProperties);
VKAPI_ATTR void CmdSetCullMode(VkCommandBuffer commandBuffer, VkCullModeFlags cullMode);
VKAPI_ATTR void CmdSetFrontFace(VkCommandBuffer commandBuffer, VkFrontFace frontFace);
VKAPI_ATTR void CmdSetPrimitiveTopology(VkCommandBuffer commandBuffer, VkPrimitiveTopology primitiveTopology);
VKAPI_ATTR void CmdSetViewportWithCount(VkCommandBuffer commandBuffer, uint32_t viewportCount, const VkViewport* pViewports);
VKAPI_ATTR void CmdSetScissorWithCount(VkCommandBuffer commandBuffer, uint32_t scissorCount, const VkRect2D* pScissors);
+VKAPI_ATTR void CmdBindIndexBuffer2(VkCommandBuffer commandBuffer, VkBuffer buffer, VkDeviceSize offset, VkDeviceSize size, VkIndexType indexType);
VKAPI_ATTR void CmdBindVertexBuffers2(VkCommandBuffer commandBuffer, uint32_t firstBinding, uint32_t bindingCount, const VkBuffer* pBuffers, const VkDeviceSize* pOffsets, const VkDeviceSize* pSizes, const VkDeviceSize* pStrides);
VKAPI_ATTR void CmdSetDepthTestEnable(VkCommandBuffer commandBuffer, VkBool32 depthTestEnable);
VKAPI_ATTR void CmdSetDepthWriteEnable(VkCommandBuffer commandBuffer, VkBool32 depthWriteEnable);
@@ -623,8 +647,22 @@
VKAPI_ATTR void CmdPipelineBarrier2(VkCommandBuffer commandBuffer, const VkDependencyInfo* pDependencyInfo);
VKAPI_ATTR VkResult QueueSubmit2(VkQueue queue, uint32_t submitCount, const VkSubmitInfo2* pSubmits, VkFence fence);
VKAPI_ATTR void CmdWriteTimestamp2(VkCommandBuffer commandBuffer, VkPipelineStageFlags2 stage, VkQueryPool queryPool, uint32_t query);
+VKAPI_ATTR VkResult CopyMemoryToImage(VkDevice device, const VkCopyMemoryToImageInfo* pCopyMemoryToImageInfo);
+VKAPI_ATTR VkResult CopyImageToMemory(VkDevice device, const VkCopyImageToMemoryInfo* pCopyImageToMemoryInfo);
+VKAPI_ATTR VkResult CopyImageToImage(VkDevice device, const VkCopyImageToImageInfo* pCopyImageToImageInfo);
+VKAPI_ATTR VkResult TransitionImageLayout(VkDevice device, uint32_t transitionCount, const VkHostImageLayoutTransitionInfo* pTransitions);
VKAPI_ATTR void CmdBeginRendering(VkCommandBuffer commandBuffer, const VkRenderingInfo* pRenderingInfo);
VKAPI_ATTR void CmdEndRendering(VkCommandBuffer commandBuffer);
+VKAPI_ATTR void GetImageSubresourceLayout2(VkDevice device, VkImage image, const VkImageSubresource2* pSubresource, VkSubresourceLayout2* pLayout);
+VKAPI_ATTR void GetDeviceImageSubresourceLayout(VkDevice device, const VkDeviceImageSubresourceInfo* pInfo, VkSubresourceLayout2* pLayout);
+VKAPI_ATTR VkResult MapMemory2(VkDevice device, const VkMemoryMapInfo* pMemoryMapInfo, void** ppData);
+VKAPI_ATTR VkResult UnmapMemory2(VkDevice device, const VkMemoryUnmapInfo* pMemoryUnmapInfo);
+VKAPI_ATTR void CmdBindDescriptorSets2(VkCommandBuffer commandBuffer, const VkBindDescriptorSetsInfo* pBindDescriptorSetsInfo);
+VKAPI_ATTR void CmdPushConstants2(VkCommandBuffer commandBuffer, const VkPushConstantsInfo* pPushConstantsInfo);
+VKAPI_ATTR void CmdPushDescriptorSet2(VkCommandBuffer commandBuffer, const VkPushDescriptorSetInfo* pPushDescriptorSetInfo);
+VKAPI_ATTR void CmdPushDescriptorSetWithTemplate2(VkCommandBuffer commandBuffer, const VkPushDescriptorSetWithTemplateInfo* pPushDescriptorSetWithTemplateInfo);
+VKAPI_ATTR void CmdSetRenderingAttachmentLocations(VkCommandBuffer commandBuffer, const VkRenderingAttachmentLocationInfo* pLocationInfo);
+VKAPI_ATTR void CmdSetRenderingInputAttachmentIndices(VkCommandBuffer commandBuffer, const VkRenderingInputAttachmentIndexInfo* pInputAttachmentIndexInfo);
VKAPI_ATTR VkResult EnumeratePhysicalDevices(VkInstance instance, uint32_t* pPhysicalDeviceCount, VkPhysicalDevice* pPhysicalDevices) {
return GetData(instance).dispatch.EnumeratePhysicalDevices(instance, pPhysicalDeviceCount, pPhysicalDevices);
@@ -764,7 +802,9 @@
{ "vkCmdBeginRenderPass2", reinterpret_cast<PFN_vkVoidFunction>(CmdBeginRenderPass2) },
{ "vkCmdBeginRendering", reinterpret_cast<PFN_vkVoidFunction>(CmdBeginRendering) },
{ "vkCmdBindDescriptorSets", reinterpret_cast<PFN_vkVoidFunction>(CmdBindDescriptorSets) },
+ { "vkCmdBindDescriptorSets2", reinterpret_cast<PFN_vkVoidFunction>(CmdBindDescriptorSets2) },
{ "vkCmdBindIndexBuffer", reinterpret_cast<PFN_vkVoidFunction>(CmdBindIndexBuffer) },
+ { "vkCmdBindIndexBuffer2", reinterpret_cast<PFN_vkVoidFunction>(CmdBindIndexBuffer2) },
{ "vkCmdBindPipeline", reinterpret_cast<PFN_vkVoidFunction>(CmdBindPipeline) },
{ "vkCmdBindVertexBuffers", reinterpret_cast<PFN_vkVoidFunction>(CmdBindVertexBuffers) },
{ "vkCmdBindVertexBuffers2", reinterpret_cast<PFN_vkVoidFunction>(CmdBindVertexBuffers2) },
@@ -802,6 +842,11 @@
{ "vkCmdPipelineBarrier", reinterpret_cast<PFN_vkVoidFunction>(CmdPipelineBarrier) },
{ "vkCmdPipelineBarrier2", reinterpret_cast<PFN_vkVoidFunction>(CmdPipelineBarrier2) },
{ "vkCmdPushConstants", reinterpret_cast<PFN_vkVoidFunction>(CmdPushConstants) },
+ { "vkCmdPushConstants2", reinterpret_cast<PFN_vkVoidFunction>(CmdPushConstants2) },
+ { "vkCmdPushDescriptorSet", reinterpret_cast<PFN_vkVoidFunction>(CmdPushDescriptorSet) },
+ { "vkCmdPushDescriptorSet2", reinterpret_cast<PFN_vkVoidFunction>(CmdPushDescriptorSet2) },
+ { "vkCmdPushDescriptorSetWithTemplate", reinterpret_cast<PFN_vkVoidFunction>(CmdPushDescriptorSetWithTemplate) },
+ { "vkCmdPushDescriptorSetWithTemplate2", reinterpret_cast<PFN_vkVoidFunction>(CmdPushDescriptorSetWithTemplate2) },
{ "vkCmdResetEvent", reinterpret_cast<PFN_vkVoidFunction>(CmdResetEvent) },
{ "vkCmdResetEvent2", reinterpret_cast<PFN_vkVoidFunction>(CmdResetEvent2) },
{ "vkCmdResetQueryPool", reinterpret_cast<PFN_vkVoidFunction>(CmdResetQueryPool) },
@@ -820,10 +865,13 @@
{ "vkCmdSetEvent", reinterpret_cast<PFN_vkVoidFunction>(CmdSetEvent) },
{ "vkCmdSetEvent2", reinterpret_cast<PFN_vkVoidFunction>(CmdSetEvent2) },
{ "vkCmdSetFrontFace", reinterpret_cast<PFN_vkVoidFunction>(CmdSetFrontFace) },
+ { "vkCmdSetLineStipple", reinterpret_cast<PFN_vkVoidFunction>(CmdSetLineStipple) },
{ "vkCmdSetLineWidth", reinterpret_cast<PFN_vkVoidFunction>(CmdSetLineWidth) },
{ "vkCmdSetPrimitiveRestartEnable", reinterpret_cast<PFN_vkVoidFunction>(CmdSetPrimitiveRestartEnable) },
{ "vkCmdSetPrimitiveTopology", reinterpret_cast<PFN_vkVoidFunction>(CmdSetPrimitiveTopology) },
{ "vkCmdSetRasterizerDiscardEnable", reinterpret_cast<PFN_vkVoidFunction>(CmdSetRasterizerDiscardEnable) },
+ { "vkCmdSetRenderingAttachmentLocations", reinterpret_cast<PFN_vkVoidFunction>(CmdSetRenderingAttachmentLocations) },
+ { "vkCmdSetRenderingInputAttachmentIndices", reinterpret_cast<PFN_vkVoidFunction>(CmdSetRenderingInputAttachmentIndices) },
{ "vkCmdSetScissor", reinterpret_cast<PFN_vkVoidFunction>(CmdSetScissor) },
{ "vkCmdSetScissorWithCount", reinterpret_cast<PFN_vkVoidFunction>(CmdSetScissorWithCount) },
{ "vkCmdSetStencilCompareMask", reinterpret_cast<PFN_vkVoidFunction>(CmdSetStencilCompareMask) },
@@ -838,6 +886,9 @@
{ "vkCmdWaitEvents2", reinterpret_cast<PFN_vkVoidFunction>(CmdWaitEvents2) },
{ "vkCmdWriteTimestamp", reinterpret_cast<PFN_vkVoidFunction>(CmdWriteTimestamp) },
{ "vkCmdWriteTimestamp2", reinterpret_cast<PFN_vkVoidFunction>(CmdWriteTimestamp2) },
+ { "vkCopyImageToImage", reinterpret_cast<PFN_vkVoidFunction>(CopyImageToImage) },
+ { "vkCopyImageToMemory", reinterpret_cast<PFN_vkVoidFunction>(CopyImageToMemory) },
+ { "vkCopyMemoryToImage", reinterpret_cast<PFN_vkVoidFunction>(CopyMemoryToImage) },
{ "vkCreateBuffer", reinterpret_cast<PFN_vkVoidFunction>(CreateBuffer) },
{ "vkCreateBufferView", reinterpret_cast<PFN_vkVoidFunction>(CreateBufferView) },
{ "vkCreateCommandPool", reinterpret_cast<PFN_vkVoidFunction>(CreateCommandPool) },
@@ -911,6 +962,7 @@
{ "vkGetDeviceGroupSurfacePresentModesKHR", reinterpret_cast<PFN_vkVoidFunction>(GetDeviceGroupSurfacePresentModesKHR) },
{ "vkGetDeviceImageMemoryRequirements", reinterpret_cast<PFN_vkVoidFunction>(GetDeviceImageMemoryRequirements) },
{ "vkGetDeviceImageSparseMemoryRequirements", reinterpret_cast<PFN_vkVoidFunction>(GetDeviceImageSparseMemoryRequirements) },
+ { "vkGetDeviceImageSubresourceLayout", reinterpret_cast<PFN_vkVoidFunction>(GetDeviceImageSubresourceLayout) },
{ "vkGetDeviceMemoryCommitment", reinterpret_cast<PFN_vkVoidFunction>(GetDeviceMemoryCommitment) },
{ "vkGetDeviceMemoryOpaqueCaptureAddress", reinterpret_cast<PFN_vkVoidFunction>(GetDeviceMemoryOpaqueCaptureAddress) },
{ "vkGetDeviceProcAddr", reinterpret_cast<PFN_vkVoidFunction>(GetDeviceProcAddr) },
@@ -923,16 +975,19 @@
{ "vkGetImageSparseMemoryRequirements", reinterpret_cast<PFN_vkVoidFunction>(GetImageSparseMemoryRequirements) },
{ "vkGetImageSparseMemoryRequirements2", reinterpret_cast<PFN_vkVoidFunction>(GetImageSparseMemoryRequirements2) },
{ "vkGetImageSubresourceLayout", reinterpret_cast<PFN_vkVoidFunction>(GetImageSubresourceLayout) },
+ { "vkGetImageSubresourceLayout2", reinterpret_cast<PFN_vkVoidFunction>(GetImageSubresourceLayout2) },
{ "vkGetInstanceProcAddr", reinterpret_cast<PFN_vkVoidFunction>(GetInstanceProcAddr) },
{ "vkGetMemoryAndroidHardwareBufferANDROID", reinterpret_cast<PFN_vkVoidFunction>(GetMemoryAndroidHardwareBufferANDROID) },
{ "vkGetPipelineCacheData", reinterpret_cast<PFN_vkVoidFunction>(GetPipelineCacheData) },
{ "vkGetPrivateData", reinterpret_cast<PFN_vkVoidFunction>(GetPrivateData) },
{ "vkGetQueryPoolResults", reinterpret_cast<PFN_vkVoidFunction>(GetQueryPoolResults) },
{ "vkGetRenderAreaGranularity", reinterpret_cast<PFN_vkVoidFunction>(GetRenderAreaGranularity) },
+ { "vkGetRenderingAreaGranularity", reinterpret_cast<PFN_vkVoidFunction>(GetRenderingAreaGranularity) },
{ "vkGetSemaphoreCounterValue", reinterpret_cast<PFN_vkVoidFunction>(GetSemaphoreCounterValue) },
{ "vkGetSwapchainImagesKHR", reinterpret_cast<PFN_vkVoidFunction>(GetSwapchainImagesKHR) },
{ "vkInvalidateMappedMemoryRanges", reinterpret_cast<PFN_vkVoidFunction>(InvalidateMappedMemoryRanges) },
{ "vkMapMemory", reinterpret_cast<PFN_vkVoidFunction>(MapMemory) },
+ { "vkMapMemory2", reinterpret_cast<PFN_vkVoidFunction>(MapMemory2) },
{ "vkMergePipelineCaches", reinterpret_cast<PFN_vkVoidFunction>(MergePipelineCaches) },
{ "vkQueueBindSparse", reinterpret_cast<PFN_vkVoidFunction>(QueueBindSparse) },
{ "vkQueuePresentKHR", reinterpret_cast<PFN_vkVoidFunction>(QueuePresentKHR) },
@@ -948,8 +1003,10 @@
{ "vkSetEvent", reinterpret_cast<PFN_vkVoidFunction>(SetEvent) },
{ "vkSetPrivateData", reinterpret_cast<PFN_vkVoidFunction>(SetPrivateData) },
{ "vkSignalSemaphore", reinterpret_cast<PFN_vkVoidFunction>(SignalSemaphore) },
+ { "vkTransitionImageLayout", reinterpret_cast<PFN_vkVoidFunction>(TransitionImageLayout) },
{ "vkTrimCommandPool", reinterpret_cast<PFN_vkVoidFunction>(TrimCommandPool) },
{ "vkUnmapMemory", reinterpret_cast<PFN_vkVoidFunction>(UnmapMemory) },
+ { "vkUnmapMemory2", reinterpret_cast<PFN_vkVoidFunction>(UnmapMemory2) },
{ "vkUpdateDescriptorSetWithTemplate", reinterpret_cast<PFN_vkVoidFunction>(UpdateDescriptorSetWithTemplate) },
{ "vkUpdateDescriptorSets", reinterpret_cast<PFN_vkVoidFunction>(UpdateDescriptorSets) },
{ "vkWaitForFences", reinterpret_cast<PFN_vkVoidFunction>(WaitForFences) },
@@ -1273,6 +1330,10 @@
GetData(device).dispatch.GetRenderAreaGranularity(device, renderPass, pGranularity);
}
+VKAPI_ATTR void GetRenderingAreaGranularity(VkDevice device, const VkRenderingAreaInfo* pRenderingAreaInfo, VkExtent2D* pGranularity) {
+ GetData(device).dispatch.GetRenderingAreaGranularity(device, pRenderingAreaInfo, pGranularity);
+}
+
VKAPI_ATTR VkResult CreateCommandPool(VkDevice device, const VkCommandPoolCreateInfo* pCreateInfo, const VkAllocationCallbacks* pAllocator, VkCommandPool* pCommandPool) {
return GetData(device).dispatch.CreateCommandPool(device, pCreateInfo, pAllocator, pCommandPool);
}
@@ -1553,6 +1614,10 @@
GetData(physicalDevice).dispatch.GetPhysicalDeviceSparseImageFormatProperties2(physicalDevice, pFormatInfo, pPropertyCount, pProperties);
}
+VKAPI_ATTR void CmdPushDescriptorSet(VkCommandBuffer commandBuffer, VkPipelineBindPoint pipelineBindPoint, VkPipelineLayout layout, uint32_t set, uint32_t descriptorWriteCount, const VkWriteDescriptorSet* pDescriptorWrites) {
+ GetData(commandBuffer).dispatch.CmdPushDescriptorSet(commandBuffer, pipelineBindPoint, layout, set, descriptorWriteCount, pDescriptorWrites);
+}
+
VKAPI_ATTR void TrimCommandPool(VkDevice device, VkCommandPool commandPool, VkCommandPoolTrimFlags flags) {
GetData(device).dispatch.TrimCommandPool(device, commandPool, flags);
}
@@ -1621,6 +1686,10 @@
GetData(device).dispatch.UpdateDescriptorSetWithTemplate(device, descriptorSet, descriptorUpdateTemplate, pData);
}
+VKAPI_ATTR void CmdPushDescriptorSetWithTemplate(VkCommandBuffer commandBuffer, VkDescriptorUpdateTemplate descriptorUpdateTemplate, VkPipelineLayout layout, uint32_t set, const void* pData) {
+ GetData(commandBuffer).dispatch.CmdPushDescriptorSetWithTemplate(commandBuffer, descriptorUpdateTemplate, layout, set, pData);
+}
+
VKAPI_ATTR void GetBufferMemoryRequirements2(VkDevice device, const VkBufferMemoryRequirementsInfo2* pInfo, VkMemoryRequirements2* pMemoryRequirements) {
GetData(device).dispatch.GetBufferMemoryRequirements2(device, pInfo, pMemoryRequirements);
}
@@ -1717,6 +1786,10 @@
return GetData(device).dispatch.GetDeviceMemoryOpaqueCaptureAddress(device, pInfo);
}
+VKAPI_ATTR void CmdSetLineStipple(VkCommandBuffer commandBuffer, uint32_t lineStippleFactor, uint16_t lineStipplePattern) {
+ GetData(commandBuffer).dispatch.CmdSetLineStipple(commandBuffer, lineStippleFactor, lineStipplePattern);
+}
+
VKAPI_ATTR VkResult GetPhysicalDeviceToolProperties(VkPhysicalDevice physicalDevice, uint32_t* pToolCount, VkPhysicalDeviceToolProperties* pToolProperties) {
return GetData(physicalDevice).dispatch.GetPhysicalDeviceToolProperties(physicalDevice, pToolCount, pToolProperties);
}
@@ -1741,6 +1814,10 @@
GetData(commandBuffer).dispatch.CmdSetScissorWithCount(commandBuffer, scissorCount, pScissors);
}
+VKAPI_ATTR void CmdBindIndexBuffer2(VkCommandBuffer commandBuffer, VkBuffer buffer, VkDeviceSize offset, VkDeviceSize size, VkIndexType indexType) {
+ GetData(commandBuffer).dispatch.CmdBindIndexBuffer2(commandBuffer, buffer, offset, size, indexType);
+}
+
VKAPI_ATTR void CmdBindVertexBuffers2(VkCommandBuffer commandBuffer, uint32_t firstBinding, uint32_t bindingCount, const VkBuffer* pBuffers, const VkDeviceSize* pOffsets, const VkDeviceSize* pSizes, const VkDeviceSize* pStrides) {
GetData(commandBuffer).dispatch.CmdBindVertexBuffers2(commandBuffer, firstBinding, bindingCount, pBuffers, pOffsets, pSizes, pStrides);
}
@@ -1845,6 +1922,22 @@
GetData(commandBuffer).dispatch.CmdWriteTimestamp2(commandBuffer, stage, queryPool, query);
}
+VKAPI_ATTR VkResult CopyMemoryToImage(VkDevice device, const VkCopyMemoryToImageInfo* pCopyMemoryToImageInfo) {
+ return GetData(device).dispatch.CopyMemoryToImage(device, pCopyMemoryToImageInfo);
+}
+
+VKAPI_ATTR VkResult CopyImageToMemory(VkDevice device, const VkCopyImageToMemoryInfo* pCopyImageToMemoryInfo) {
+ return GetData(device).dispatch.CopyImageToMemory(device, pCopyImageToMemoryInfo);
+}
+
+VKAPI_ATTR VkResult CopyImageToImage(VkDevice device, const VkCopyImageToImageInfo* pCopyImageToImageInfo) {
+ return GetData(device).dispatch.CopyImageToImage(device, pCopyImageToImageInfo);
+}
+
+VKAPI_ATTR VkResult TransitionImageLayout(VkDevice device, uint32_t transitionCount, const VkHostImageLayoutTransitionInfo* pTransitions) {
+ return GetData(device).dispatch.TransitionImageLayout(device, transitionCount, pTransitions);
+}
+
VKAPI_ATTR void CmdBeginRendering(VkCommandBuffer commandBuffer, const VkRenderingInfo* pRenderingInfo) {
GetData(commandBuffer).dispatch.CmdBeginRendering(commandBuffer, pRenderingInfo);
}
@@ -1853,6 +1946,46 @@
GetData(commandBuffer).dispatch.CmdEndRendering(commandBuffer);
}
+VKAPI_ATTR void GetImageSubresourceLayout2(VkDevice device, VkImage image, const VkImageSubresource2* pSubresource, VkSubresourceLayout2* pLayout) {
+ GetData(device).dispatch.GetImageSubresourceLayout2(device, image, pSubresource, pLayout);
+}
+
+VKAPI_ATTR void GetDeviceImageSubresourceLayout(VkDevice device, const VkDeviceImageSubresourceInfo* pInfo, VkSubresourceLayout2* pLayout) {
+ GetData(device).dispatch.GetDeviceImageSubresourceLayout(device, pInfo, pLayout);
+}
+
+VKAPI_ATTR VkResult MapMemory2(VkDevice device, const VkMemoryMapInfo* pMemoryMapInfo, void** ppData) {
+ return GetData(device).dispatch.MapMemory2(device, pMemoryMapInfo, ppData);
+}
+
+VKAPI_ATTR VkResult UnmapMemory2(VkDevice device, const VkMemoryUnmapInfo* pMemoryUnmapInfo) {
+ return GetData(device).dispatch.UnmapMemory2(device, pMemoryUnmapInfo);
+}
+
+VKAPI_ATTR void CmdBindDescriptorSets2(VkCommandBuffer commandBuffer, const VkBindDescriptorSetsInfo* pBindDescriptorSetsInfo) {
+ GetData(commandBuffer).dispatch.CmdBindDescriptorSets2(commandBuffer, pBindDescriptorSetsInfo);
+}
+
+VKAPI_ATTR void CmdPushConstants2(VkCommandBuffer commandBuffer, const VkPushConstantsInfo* pPushConstantsInfo) {
+ GetData(commandBuffer).dispatch.CmdPushConstants2(commandBuffer, pPushConstantsInfo);
+}
+
+VKAPI_ATTR void CmdPushDescriptorSet2(VkCommandBuffer commandBuffer, const VkPushDescriptorSetInfo* pPushDescriptorSetInfo) {
+ GetData(commandBuffer).dispatch.CmdPushDescriptorSet2(commandBuffer, pPushDescriptorSetInfo);
+}
+
+VKAPI_ATTR void CmdPushDescriptorSetWithTemplate2(VkCommandBuffer commandBuffer, const VkPushDescriptorSetWithTemplateInfo* pPushDescriptorSetWithTemplateInfo) {
+ GetData(commandBuffer).dispatch.CmdPushDescriptorSetWithTemplate2(commandBuffer, pPushDescriptorSetWithTemplateInfo);
+}
+
+VKAPI_ATTR void CmdSetRenderingAttachmentLocations(VkCommandBuffer commandBuffer, const VkRenderingAttachmentLocationInfo* pLocationInfo) {
+ GetData(commandBuffer).dispatch.CmdSetRenderingAttachmentLocations(commandBuffer, pLocationInfo);
+}
+
+VKAPI_ATTR void CmdSetRenderingInputAttachmentIndices(VkCommandBuffer commandBuffer, const VkRenderingInputAttachmentIndexInfo* pInputAttachmentIndexInfo) {
+ GetData(commandBuffer).dispatch.CmdSetRenderingInputAttachmentIndices(commandBuffer, pInputAttachmentIndexInfo);
+}
+
} // anonymous namespace
@@ -2299,6 +2432,11 @@
}
__attribute__((visibility("default")))
+VKAPI_ATTR void vkGetRenderingAreaGranularity(VkDevice device, const VkRenderingAreaInfo* pRenderingAreaInfo, VkExtent2D* pGranularity) {
+ vulkan::api::GetRenderingAreaGranularity(device, pRenderingAreaInfo, pGranularity);
+}
+
+__attribute__((visibility("default")))
VKAPI_ATTR VkResult vkCreateCommandPool(VkDevice device, const VkCommandPoolCreateInfo* pCreateInfo, const VkAllocationCallbacks* pAllocator, VkCommandPool* pCommandPool) {
return vulkan::api::CreateCommandPool(device, pCreateInfo, pAllocator, pCommandPool);
}
@@ -2649,6 +2787,11 @@
}
__attribute__((visibility("default")))
+VKAPI_ATTR void vkCmdPushDescriptorSet(VkCommandBuffer commandBuffer, VkPipelineBindPoint pipelineBindPoint, VkPipelineLayout layout, uint32_t set, uint32_t descriptorWriteCount, const VkWriteDescriptorSet* pDescriptorWrites) {
+ vulkan::api::CmdPushDescriptorSet(commandBuffer, pipelineBindPoint, layout, set, descriptorWriteCount, pDescriptorWrites);
+}
+
+__attribute__((visibility("default")))
VKAPI_ATTR void vkTrimCommandPool(VkDevice device, VkCommandPool commandPool, VkCommandPoolTrimFlags flags) {
vulkan::api::TrimCommandPool(device, commandPool, flags);
}
@@ -2734,6 +2877,11 @@
}
__attribute__((visibility("default")))
+VKAPI_ATTR void vkCmdPushDescriptorSetWithTemplate(VkCommandBuffer commandBuffer, VkDescriptorUpdateTemplate descriptorUpdateTemplate, VkPipelineLayout layout, uint32_t set, const void* pData) {
+ vulkan::api::CmdPushDescriptorSetWithTemplate(commandBuffer, descriptorUpdateTemplate, layout, set, pData);
+}
+
+__attribute__((visibility("default")))
VKAPI_ATTR void vkGetBufferMemoryRequirements2(VkDevice device, const VkBufferMemoryRequirementsInfo2* pInfo, VkMemoryRequirements2* pMemoryRequirements) {
vulkan::api::GetBufferMemoryRequirements2(device, pInfo, pMemoryRequirements);
}
@@ -2854,6 +3002,11 @@
}
__attribute__((visibility("default")))
+VKAPI_ATTR void vkCmdSetLineStipple(VkCommandBuffer commandBuffer, uint32_t lineStippleFactor, uint16_t lineStipplePattern) {
+ vulkan::api::CmdSetLineStipple(commandBuffer, lineStippleFactor, lineStipplePattern);
+}
+
+__attribute__((visibility("default")))
VKAPI_ATTR VkResult vkGetPhysicalDeviceToolProperties(VkPhysicalDevice physicalDevice, uint32_t* pToolCount, VkPhysicalDeviceToolProperties* pToolProperties) {
return vulkan::api::GetPhysicalDeviceToolProperties(physicalDevice, pToolCount, pToolProperties);
}
@@ -2884,6 +3037,11 @@
}
__attribute__((visibility("default")))
+VKAPI_ATTR void vkCmdBindIndexBuffer2(VkCommandBuffer commandBuffer, VkBuffer buffer, VkDeviceSize offset, VkDeviceSize size, VkIndexType indexType) {
+ vulkan::api::CmdBindIndexBuffer2(commandBuffer, buffer, offset, size, indexType);
+}
+
+__attribute__((visibility("default")))
VKAPI_ATTR void vkCmdBindVertexBuffers2(VkCommandBuffer commandBuffer, uint32_t firstBinding, uint32_t bindingCount, const VkBuffer* pBuffers, const VkDeviceSize* pOffsets, const VkDeviceSize* pSizes, const VkDeviceSize* pStrides) {
vulkan::api::CmdBindVertexBuffers2(commandBuffer, firstBinding, bindingCount, pBuffers, pOffsets, pSizes, pStrides);
}
@@ -3014,6 +3172,26 @@
}
__attribute__((visibility("default")))
+VKAPI_ATTR VkResult vkCopyMemoryToImage(VkDevice device, const VkCopyMemoryToImageInfo* pCopyMemoryToImageInfo) {
+ return vulkan::api::CopyMemoryToImage(device, pCopyMemoryToImageInfo);
+}
+
+__attribute__((visibility("default")))
+VKAPI_ATTR VkResult vkCopyImageToMemory(VkDevice device, const VkCopyImageToMemoryInfo* pCopyImageToMemoryInfo) {
+ return vulkan::api::CopyImageToMemory(device, pCopyImageToMemoryInfo);
+}
+
+__attribute__((visibility("default")))
+VKAPI_ATTR VkResult vkCopyImageToImage(VkDevice device, const VkCopyImageToImageInfo* pCopyImageToImageInfo) {
+ return vulkan::api::CopyImageToImage(device, pCopyImageToImageInfo);
+}
+
+__attribute__((visibility("default")))
+VKAPI_ATTR VkResult vkTransitionImageLayout(VkDevice device, uint32_t transitionCount, const VkHostImageLayoutTransitionInfo* pTransitions) {
+ return vulkan::api::TransitionImageLayout(device, transitionCount, pTransitions);
+}
+
+__attribute__((visibility("default")))
VKAPI_ATTR void vkCmdBeginRendering(VkCommandBuffer commandBuffer, const VkRenderingInfo* pRenderingInfo) {
vulkan::api::CmdBeginRendering(commandBuffer, pRenderingInfo);
}
@@ -3023,4 +3201,54 @@
vulkan::api::CmdEndRendering(commandBuffer);
}
+__attribute__((visibility("default")))
+VKAPI_ATTR void vkGetImageSubresourceLayout2(VkDevice device, VkImage image, const VkImageSubresource2* pSubresource, VkSubresourceLayout2* pLayout) {
+ vulkan::api::GetImageSubresourceLayout2(device, image, pSubresource, pLayout);
+}
+
+__attribute__((visibility("default")))
+VKAPI_ATTR void vkGetDeviceImageSubresourceLayout(VkDevice device, const VkDeviceImageSubresourceInfo* pInfo, VkSubresourceLayout2* pLayout) {
+ vulkan::api::GetDeviceImageSubresourceLayout(device, pInfo, pLayout);
+}
+
+__attribute__((visibility("default")))
+VKAPI_ATTR VkResult vkMapMemory2(VkDevice device, const VkMemoryMapInfo* pMemoryMapInfo, void** ppData) {
+ return vulkan::api::MapMemory2(device, pMemoryMapInfo, ppData);
+}
+
+__attribute__((visibility("default")))
+VKAPI_ATTR VkResult vkUnmapMemory2(VkDevice device, const VkMemoryUnmapInfo* pMemoryUnmapInfo) {
+ return vulkan::api::UnmapMemory2(device, pMemoryUnmapInfo);
+}
+
+__attribute__((visibility("default")))
+VKAPI_ATTR void vkCmdBindDescriptorSets2(VkCommandBuffer commandBuffer, const VkBindDescriptorSetsInfo* pBindDescriptorSetsInfo) {
+ vulkan::api::CmdBindDescriptorSets2(commandBuffer, pBindDescriptorSetsInfo);
+}
+
+__attribute__((visibility("default")))
+VKAPI_ATTR void vkCmdPushConstants2(VkCommandBuffer commandBuffer, const VkPushConstantsInfo* pPushConstantsInfo) {
+ vulkan::api::CmdPushConstants2(commandBuffer, pPushConstantsInfo);
+}
+
+__attribute__((visibility("default")))
+VKAPI_ATTR void vkCmdPushDescriptorSet2(VkCommandBuffer commandBuffer, const VkPushDescriptorSetInfo* pPushDescriptorSetInfo) {
+ vulkan::api::CmdPushDescriptorSet2(commandBuffer, pPushDescriptorSetInfo);
+}
+
+__attribute__((visibility("default")))
+VKAPI_ATTR void vkCmdPushDescriptorSetWithTemplate2(VkCommandBuffer commandBuffer, const VkPushDescriptorSetWithTemplateInfo* pPushDescriptorSetWithTemplateInfo) {
+ vulkan::api::CmdPushDescriptorSetWithTemplate2(commandBuffer, pPushDescriptorSetWithTemplateInfo);
+}
+
+__attribute__((visibility("default")))
+VKAPI_ATTR void vkCmdSetRenderingAttachmentLocations(VkCommandBuffer commandBuffer, const VkRenderingAttachmentLocationInfo* pLocationInfo) {
+ vulkan::api::CmdSetRenderingAttachmentLocations(commandBuffer, pLocationInfo);
+}
+
+__attribute__((visibility("default")))
+VKAPI_ATTR void vkCmdSetRenderingInputAttachmentIndices(VkCommandBuffer commandBuffer, const VkRenderingInputAttachmentIndexInfo* pInputAttachmentIndexInfo) {
+ vulkan::api::CmdSetRenderingInputAttachmentIndices(commandBuffer, pInputAttachmentIndexInfo);
+}
+
// clang-format on
diff --git a/vulkan/libvulkan/api_gen.h b/vulkan/libvulkan/api_gen.h
index b468a89..17dc62f 100644
--- a/vulkan/libvulkan/api_gen.h
+++ b/vulkan/libvulkan/api_gen.h
@@ -139,6 +139,7 @@
PFN_vkCreateRenderPass CreateRenderPass;
PFN_vkDestroyRenderPass DestroyRenderPass;
PFN_vkGetRenderAreaGranularity GetRenderAreaGranularity;
+ PFN_vkGetRenderingAreaGranularity GetRenderingAreaGranularity;
PFN_vkCreateCommandPool CreateCommandPool;
PFN_vkDestroyCommandPool DestroyCommandPool;
PFN_vkResetCommandPool ResetCommandPool;
@@ -196,6 +197,7 @@
PFN_vkGetSwapchainImagesKHR GetSwapchainImagesKHR;
PFN_vkAcquireNextImageKHR AcquireNextImageKHR;
PFN_vkQueuePresentKHR QueuePresentKHR;
+ PFN_vkCmdPushDescriptorSet CmdPushDescriptorSet;
PFN_vkTrimCommandPool TrimCommandPool;
PFN_vkGetDeviceGroupPeerMemoryFeatures GetDeviceGroupPeerMemoryFeatures;
PFN_vkBindBufferMemory2 BindBufferMemory2;
@@ -208,6 +210,7 @@
PFN_vkCreateDescriptorUpdateTemplate CreateDescriptorUpdateTemplate;
PFN_vkDestroyDescriptorUpdateTemplate DestroyDescriptorUpdateTemplate;
PFN_vkUpdateDescriptorSetWithTemplate UpdateDescriptorSetWithTemplate;
+ PFN_vkCmdPushDescriptorSetWithTemplate CmdPushDescriptorSetWithTemplate;
PFN_vkGetBufferMemoryRequirements2 GetBufferMemoryRequirements2;
PFN_vkGetImageMemoryRequirements2 GetImageMemoryRequirements2;
PFN_vkGetImageSparseMemoryRequirements2 GetImageSparseMemoryRequirements2;
@@ -232,11 +235,13 @@
PFN_vkGetBufferOpaqueCaptureAddress GetBufferOpaqueCaptureAddress;
PFN_vkGetBufferDeviceAddress GetBufferDeviceAddress;
PFN_vkGetDeviceMemoryOpaqueCaptureAddress GetDeviceMemoryOpaqueCaptureAddress;
+ PFN_vkCmdSetLineStipple CmdSetLineStipple;
PFN_vkCmdSetCullMode CmdSetCullMode;
PFN_vkCmdSetFrontFace CmdSetFrontFace;
PFN_vkCmdSetPrimitiveTopology CmdSetPrimitiveTopology;
PFN_vkCmdSetViewportWithCount CmdSetViewportWithCount;
PFN_vkCmdSetScissorWithCount CmdSetScissorWithCount;
+ PFN_vkCmdBindIndexBuffer2 CmdBindIndexBuffer2;
PFN_vkCmdBindVertexBuffers2 CmdBindVertexBuffers2;
PFN_vkCmdSetDepthTestEnable CmdSetDepthTestEnable;
PFN_vkCmdSetDepthWriteEnable CmdSetDepthWriteEnable;
@@ -263,8 +268,22 @@
PFN_vkCmdPipelineBarrier2 CmdPipelineBarrier2;
PFN_vkQueueSubmit2 QueueSubmit2;
PFN_vkCmdWriteTimestamp2 CmdWriteTimestamp2;
+ PFN_vkCopyMemoryToImage CopyMemoryToImage;
+ PFN_vkCopyImageToMemory CopyImageToMemory;
+ PFN_vkCopyImageToImage CopyImageToImage;
+ PFN_vkTransitionImageLayout TransitionImageLayout;
PFN_vkCmdBeginRendering CmdBeginRendering;
PFN_vkCmdEndRendering CmdEndRendering;
+ PFN_vkGetImageSubresourceLayout2 GetImageSubresourceLayout2;
+ PFN_vkGetDeviceImageSubresourceLayout GetDeviceImageSubresourceLayout;
+ PFN_vkMapMemory2 MapMemory2;
+ PFN_vkUnmapMemory2 UnmapMemory2;
+ PFN_vkCmdBindDescriptorSets2 CmdBindDescriptorSets2;
+ PFN_vkCmdPushConstants2 CmdPushConstants2;
+ PFN_vkCmdPushDescriptorSet2 CmdPushDescriptorSet2;
+ PFN_vkCmdPushDescriptorSetWithTemplate2 CmdPushDescriptorSetWithTemplate2;
+ PFN_vkCmdSetRenderingAttachmentLocations CmdSetRenderingAttachmentLocations;
+ PFN_vkCmdSetRenderingInputAttachmentIndices CmdSetRenderingInputAttachmentIndices;
// clang-format on
};
diff --git a/vulkan/libvulkan/driver.cpp b/vulkan/libvulkan/driver.cpp
index 01436db..7d0f545 100644
--- a/vulkan/libvulkan/driver.cpp
+++ b/vulkan/libvulkan/driver.cpp
@@ -398,7 +398,7 @@
const VkAllocationCallbacks& allocator)
: is_instance_(true),
allocator_(allocator),
- loader_api_version_(VK_API_VERSION_1_3),
+ loader_api_version_(flags::vulkan_1_4_instance_api() ? VK_API_VERSION_1_4 : VK_API_VERSION_1_3),
icd_api_version_(icd_api_version),
physical_dev_(VK_NULL_HANDLE),
instance_info_(create_info),
@@ -410,7 +410,7 @@
const VkAllocationCallbacks& allocator)
: is_instance_(false),
allocator_(allocator),
- loader_api_version_(VK_API_VERSION_1_3),
+ loader_api_version_(flags::vulkan_1_4_instance_api() ? VK_API_VERSION_1_4 : VK_API_VERSION_1_3),
icd_api_version_(icd_api_version),
physical_dev_(physical_dev),
dev_info_(create_info),
@@ -552,6 +552,10 @@
is_instance_ ? loader_api_version_
: std::min(icd_api_version_, loader_api_version_);
switch (api_version) {
+ case VK_API_VERSION_1_4:
+ hook_extensions_.set(ProcHook::EXTENSION_CORE_1_4);
+ hal_extensions_.set(ProcHook::EXTENSION_CORE_1_4);
+ [[clang::fallthrough]];
case VK_API_VERSION_1_3:
hook_extensions_.set(ProcHook::EXTENSION_CORE_1_3);
hal_extensions_.set(ProcHook::EXTENSION_CORE_1_3);
@@ -701,6 +705,7 @@
case ProcHook::EXTENSION_CORE_1_1:
case ProcHook::EXTENSION_CORE_1_2:
case ProcHook::EXTENSION_CORE_1_3:
+ case ProcHook::EXTENSION_CORE_1_4:
case ProcHook::EXTENSION_COUNT:
// Device and meta extensions. If we ever get here it's a bug in
// our code. But enumerating them lets us avoid having a default
@@ -766,6 +771,7 @@
case ProcHook::EXTENSION_CORE_1_1:
case ProcHook::EXTENSION_CORE_1_2:
case ProcHook::EXTENSION_CORE_1_3:
+ case ProcHook::EXTENSION_CORE_1_4:
case ProcHook::EXTENSION_COUNT:
// Instance and meta extensions. If we ever get here it's a bug
// in our code. But enumerating them lets us avoid having a
diff --git a/vulkan/libvulkan/driver_gen.h b/vulkan/libvulkan/driver_gen.h
index 649c0f1..204b16f 100644
--- a/vulkan/libvulkan/driver_gen.h
+++ b/vulkan/libvulkan/driver_gen.h
@@ -68,6 +68,7 @@
EXTENSION_CORE_1_1,
EXTENSION_CORE_1_2,
EXTENSION_CORE_1_3,
+ EXTENSION_CORE_1_4,
EXTENSION_COUNT,
EXTENSION_UNKNOWN,
};
diff --git a/vulkan/libvulkan/libvulkan.map.txt b/vulkan/libvulkan/libvulkan.map.txt
index b189c68..ffe46f7 100644
--- a/vulkan/libvulkan/libvulkan.map.txt
+++ b/vulkan/libvulkan/libvulkan.map.txt
@@ -6,32 +6,34 @@
vkAllocateDescriptorSets;
vkAllocateMemory;
vkBeginCommandBuffer;
- vkBindBufferMemory;
vkBindBufferMemory2; # introduced=28
- vkBindImageMemory;
+ vkBindBufferMemory;
vkBindImageMemory2; # introduced=28
+ vkBindImageMemory;
vkCmdBeginQuery;
- vkCmdBeginRendering; # introduced=33
- vkCmdBeginRenderPass;
vkCmdBeginRenderPass2; # introduced=31
+ vkCmdBeginRenderPass;
+ vkCmdBeginRendering; # introduced=33
+ vkCmdBindDescriptorSets2; #introduced=36
vkCmdBindDescriptorSets;
+ vkCmdBindIndexBuffer2; #introduced=36
vkCmdBindIndexBuffer;
vkCmdBindPipeline;
- vkCmdBindVertexBuffers;
vkCmdBindVertexBuffers2; #introduced=33
- vkCmdBlitImage;
+ vkCmdBindVertexBuffers;
vkCmdBlitImage2; #introduced=33
+ vkCmdBlitImage;
vkCmdClearAttachments;
vkCmdClearColorImage;
vkCmdClearDepthStencilImage;
- vkCmdCopyBuffer;
vkCmdCopyBuffer2; #introduced=33
- vkCmdCopyBufferToImage;
+ vkCmdCopyBuffer;
vkCmdCopyBufferToImage2; #introduced=33
- vkCmdCopyImage;
+ vkCmdCopyBufferToImage;
vkCmdCopyImage2; #introduced=33
- vkCmdCopyImageToBuffer;
+ vkCmdCopyImage;
vkCmdCopyImageToBuffer2; #introduced=33
+ vkCmdCopyImageToBuffer;
vkCmdCopyQueryPoolResults;
vkCmdDispatch;
vkCmdDispatchBase; # introduced=28
@@ -43,21 +45,26 @@
vkCmdDrawIndirect;
vkCmdDrawIndirectCount; # introduced=31
vkCmdEndQuery;
- vkCmdEndRendering; #introduced=33
- vkCmdEndRenderPass;
vkCmdEndRenderPass2; # introduced=31
+ vkCmdEndRenderPass;
+ vkCmdEndRendering; #introduced=33
vkCmdExecuteCommands;
vkCmdFillBuffer;
- vkCmdNextSubpass;
vkCmdNextSubpass2; # introduced=31
- vkCmdPipelineBarrier;
+ vkCmdNextSubpass;
vkCmdPipelineBarrier2; #introduced=33
+ vkCmdPipelineBarrier;
+ vkCmdPushConstants2; #introduced=36
vkCmdPushConstants;
- vkCmdResetEvent;
+ vkCmdPushDescriptorSet2; #introduced=36
+ vkCmdPushDescriptorSet; #introduced=36
+ vkCmdPushDescriptorSetWithTemplate2; #introduced=36
+ vkCmdPushDescriptorSetWithTemplate; #introduced=36
vkCmdResetEvent2; #introduced=33
+ vkCmdResetEvent;
vkCmdResetQueryPool;
- vkCmdResolveImage;
vkCmdResolveImage2; #introduced=33
+ vkCmdResolveImage;
vkCmdSetBlendConstants;
vkCmdSetCullMode; #introduced=33
vkCmdSetDepthBias;
@@ -68,13 +75,16 @@
vkCmdSetDepthTestEnable; #introduced=33
vkCmdSetDepthWriteEnable; #introduced=33
vkCmdSetDeviceMask; # introduced=28
- vkCmdSetEvent;
vkCmdSetEvent2; #introduced=33
+ vkCmdSetEvent;
vkCmdSetFrontFace; #introduced=33
+ vkCmdSetLineStipple; #introduced=36
vkCmdSetLineWidth;
vkCmdSetPrimitiveRestartEnable; #introduced=33
vkCmdSetPrimitiveTopology; #introduced=33
vkCmdSetRasterizerDiscardEnable; #introduced=33
+ vkCmdSetRenderingAttachmentLocations; #introduced=36
+ vkCmdSetRenderingInputAttachmentIndices; #introduced=36
vkCmdSetScissor;
vkCmdSetScissorWithCount; #introduced=33
vkCmdSetStencilCompareMask;
@@ -85,10 +95,12 @@
vkCmdSetViewport;
vkCmdSetViewportWithCount; #introduced=33
vkCmdUpdateBuffer;
- vkCmdWaitEvents;
vkCmdWaitEvents2; #introduced=33
- vkCmdWriteTimestamp;
+ vkCmdWaitEvents;
vkCmdWriteTimestamp2; #introduced=33
+ vkCmdWriteTimestamp;
+ vkCopyImageToMemory; #introduced=36
+ vkCopyMemoryToImage; #introduced=36
vkCreateAndroidSurfaceKHR;
vkCreateBuffer;
vkCreateBufferView;
@@ -109,8 +121,8 @@
vkCreatePipelineLayout;
vkCreatePrivateDataSlot; #introduced=33
vkCreateQueryPool;
- vkCreateRenderPass;
vkCreateRenderPass2; # introduced=31
+ vkCreateRenderPass;
vkCreateSampler;
vkCreateSamplerYcbcrConversion; # introduced=28
vkCreateSemaphore;
@@ -156,8 +168,8 @@
vkFreeMemory;
vkGetAndroidHardwareBufferPropertiesANDROID; # introduced=28
vkGetBufferDeviceAddress; # introduced=31
- vkGetBufferMemoryRequirements;
vkGetBufferMemoryRequirements2; # introduced=28
+ vkGetBufferMemoryRequirements;
vkGetBufferOpaqueCaptureAddress; # introduced=31
vkGetDescriptorSetLayoutSupport; # introduced=28
vkGetDeviceBufferMemoryRequirements; #introduced=33
@@ -166,39 +178,41 @@
vkGetDeviceGroupSurfacePresentModesKHR; # introduced=28
vkGetDeviceImageMemoryRequirements; #introduced=33
vkGetDeviceImageSparseMemoryRequirements; #introduced=33
+ vkGetDeviceImageSubresourceLayout; #introduced=36
vkGetDeviceMemoryCommitment;
vkGetDeviceMemoryOpaqueCaptureAddress; # introduced=31
vkGetDeviceProcAddr;
- vkGetDeviceQueue;
vkGetDeviceQueue2; # introduced=28
+ vkGetDeviceQueue;
vkGetEventStatus;
vkGetFenceStatus;
- vkGetImageMemoryRequirements;
vkGetImageMemoryRequirements2; # introduced=28
- vkGetImageSparseMemoryRequirements;
+ vkGetImageMemoryRequirements;
vkGetImageSparseMemoryRequirements2; # introduced=28
- vkGetImageSubresourceLayout;
+ vkGetImageSparseMemoryRequirements;
+ vkGetImageSubresourceLayout2; #introduced=36
vkGetImageSubresourceLayout2EXT; # introduced=UpsideDownCake
+ vkGetImageSubresourceLayout;
vkGetInstanceProcAddr;
vkGetMemoryAndroidHardwareBufferANDROID; # introduced=28
vkGetPhysicalDeviceExternalBufferProperties; # introduced=28
vkGetPhysicalDeviceExternalFenceProperties; # introduced=28
vkGetPhysicalDeviceExternalSemaphoreProperties; # introduced=28
- vkGetPhysicalDeviceFeatures;
vkGetPhysicalDeviceFeatures2; # introduced=28
- vkGetPhysicalDeviceFormatProperties;
+ vkGetPhysicalDeviceFeatures;
vkGetPhysicalDeviceFormatProperties2; # introduced=28
- vkGetPhysicalDeviceImageFormatProperties;
+ vkGetPhysicalDeviceFormatProperties;
vkGetPhysicalDeviceImageFormatProperties2; # introduced=28
- vkGetPhysicalDeviceMemoryProperties;
+ vkGetPhysicalDeviceImageFormatProperties;
vkGetPhysicalDeviceMemoryProperties2; # introduced=28
+ vkGetPhysicalDeviceMemoryProperties;
vkGetPhysicalDevicePresentRectanglesKHR; # introduced=28
- vkGetPhysicalDeviceProperties;
vkGetPhysicalDeviceProperties2; # introduced=28
- vkGetPhysicalDeviceQueueFamilyProperties;
+ vkGetPhysicalDeviceProperties;
vkGetPhysicalDeviceQueueFamilyProperties2; # introduced=28
- vkGetPhysicalDeviceSparseImageFormatProperties;
+ vkGetPhysicalDeviceQueueFamilyProperties;
vkGetPhysicalDeviceSparseImageFormatProperties2; # introduced=28
+ vkGetPhysicalDeviceSparseImageFormatProperties;
vkGetPhysicalDeviceSurfaceCapabilitiesKHR;
vkGetPhysicalDeviceSurfaceFormatsKHR;
vkGetPhysicalDeviceSurfacePresentModesKHR;
@@ -208,15 +222,17 @@
vkGetPrivateData; #introduced=33
vkGetQueryPoolResults;
vkGetRenderAreaGranularity;
+ vkGetRenderingAreaGranularity; #introduced=36
vkGetSemaphoreCounterValue; # introduced=31
vkGetSwapchainImagesKHR;
vkInvalidateMappedMemoryRanges;
+ vkMapMemory2; #introduced=36
vkMapMemory;
vkMergePipelineCaches;
vkQueueBindSparse;
vkQueuePresentKHR;
- vkQueueSubmit;
vkQueueSubmit2; #introduced=33
+ vkQueueSubmit;
vkQueueWaitIdle;
vkResetCommandBuffer;
vkResetCommandPool;
@@ -227,10 +243,12 @@
vkSetEvent;
vkSetPrivateData; # introduced=33
vkSignalSemaphore; # introduced=31
+ vkTransitionImageLayout; #introduced=36
vkTrimCommandPool; # introduced=28
+ vkUnmapMemory2; #introduced=36
vkUnmapMemory;
- vkUpdateDescriptorSets;
vkUpdateDescriptorSetWithTemplate; # introduced=28
+ vkUpdateDescriptorSets;
vkWaitForFences;
vkWaitSemaphores; # introduced=31
local:
diff --git a/vulkan/libvulkan/libvulkan_flags.aconfig b/vulkan/libvulkan/libvulkan_flags.aconfig
index 891bc02..dae5b52 100644
--- a/vulkan/libvulkan/libvulkan_flags.aconfig
+++ b/vulkan/libvulkan/libvulkan_flags.aconfig
@@ -8,3 +8,11 @@
bug: "341978292"
is_fixed_read_only: true
}
+
+flag {
+ name: "vulkan_1_4_instance_api"
+ namespace: "core_graphics"
+ description: "Enable support for the Vulkan 1.4 instance API"
+ bug: "370568136"
+ is_fixed_read_only: true
+}
diff --git a/vulkan/nulldrv/null_driver.cpp b/vulkan/nulldrv/null_driver.cpp
index 973e71c..48de3d6 100644
--- a/vulkan/nulldrv/null_driver.cpp
+++ b/vulkan/nulldrv/null_driver.cpp
@@ -1767,6 +1767,69 @@
return VK_SUCCESS;
}
+void GetRenderingAreaGranularity(VkDevice device, const VkRenderingAreaInfo* pRenderingAreaInfo, VkExtent2D* pGranularity) {
+}
+
+void CmdPushDescriptorSet(VkCommandBuffer commandBuffer, VkPipelineBindPoint pipelineBindPoint, VkPipelineLayout layout, uint32_t set, uint32_t descriptorWriteCount, const VkWriteDescriptorSet* pDescriptorWrites) {
+}
+
+void CmdPushDescriptorSetWithTemplate(VkCommandBuffer commandBuffer, VkDescriptorUpdateTemplate descriptorUpdateTemplate, VkPipelineLayout layout, uint32_t set, const void* pData) {
+}
+
+void CmdSetLineStipple(VkCommandBuffer commandBuffer, uint32_t lineStippleFactor, uint16_t lineStipplePattern) {
+}
+
+void CmdBindIndexBuffer2(VkCommandBuffer commandBuffer, VkBuffer buffer, VkDeviceSize offset, VkDeviceSize size, VkIndexType indexType) {
+}
+
+VkResult CopyMemoryToImage(VkDevice device, const VkCopyMemoryToImageInfo* pCopyMemoryToImageInfo) {
+ return VK_SUCCESS;
+}
+
+VkResult CopyImageToMemory(VkDevice device, const VkCopyImageToMemoryInfo* pCopyImageToMemoryInfo) {
+ return VK_SUCCESS;
+}
+
+VkResult CopyImageToImage(VkDevice device, const VkCopyImageToImageInfo* pCopyImageToImageInfo) {
+ return VK_SUCCESS;
+}
+
+VkResult TransitionImageLayout(VkDevice device, uint32_t transitionCount, const VkHostImageLayoutTransitionInfo* pTransitions) {
+ return VK_SUCCESS;
+}
+
+void GetImageSubresourceLayout2(VkDevice device, VkImage image, const VkImageSubresource2* pSubresource, VkSubresourceLayout2* pLayout) {
+}
+
+void GetDeviceImageSubresourceLayout(VkDevice device, const VkDeviceImageSubresourceInfo* pInfo, VkSubresourceLayout2* pLayout) {
+}
+
+VkResult MapMemory2(VkDevice device, const VkMemoryMapInfo* pMemoryMapInfo, void** ppData) {
+ return VK_SUCCESS;
+}
+
+VkResult UnmapMemory2(VkDevice device, const VkMemoryUnmapInfo* pMemoryUnmapInfo) {
+ return VK_SUCCESS;
+}
+
+void CmdBindDescriptorSets2(VkCommandBuffer commandBuffer, const VkBindDescriptorSetsInfo* pBindDescriptorSetsInfo) {
+}
+
+void CmdPushConstants2(VkCommandBuffer commandBuffer, const VkPushConstantsInfo* pPushConstantsInfo) {
+}
+
+void CmdPushDescriptorSet2(VkCommandBuffer commandBuffer, const VkPushDescriptorSetInfo* pPushDescriptorSetInfo) {
+}
+
+void CmdPushDescriptorSetWithTemplate2(VkCommandBuffer commandBuffer, const VkPushDescriptorSetWithTemplateInfo* pPushDescriptorSetWithTemplateInfo) {
+}
+
+void CmdSetRenderingAttachmentLocations(VkCommandBuffer commandBuffer, const VkRenderingAttachmentLocationInfo* pLocationInfo) {
+}
+
+void CmdSetRenderingInputAttachmentIndices(VkCommandBuffer commandBuffer, const VkRenderingInputAttachmentIndexInfo* pInputAttachmentIndexInfo) {
+}
+
#pragma clang diagnostic pop
// clang-format on
diff --git a/vulkan/nulldrv/null_driver_gen.cpp b/vulkan/nulldrv/null_driver_gen.cpp
index 40a45af..30967c2 100644
--- a/vulkan/nulldrv/null_driver_gen.cpp
+++ b/vulkan/nulldrv/null_driver_gen.cpp
@@ -75,7 +75,9 @@
{"vkCmdBeginRenderPass2", reinterpret_cast<PFN_vkVoidFunction>(static_cast<PFN_vkCmdBeginRenderPass2>(CmdBeginRenderPass2))},
{"vkCmdBeginRendering", reinterpret_cast<PFN_vkVoidFunction>(static_cast<PFN_vkCmdBeginRendering>(CmdBeginRendering))},
{"vkCmdBindDescriptorSets", reinterpret_cast<PFN_vkVoidFunction>(static_cast<PFN_vkCmdBindDescriptorSets>(CmdBindDescriptorSets))},
+ {"vkCmdBindDescriptorSets2", reinterpret_cast<PFN_vkVoidFunction>(static_cast<PFN_vkCmdBindDescriptorSets2>(CmdBindDescriptorSets2))},
{"vkCmdBindIndexBuffer", reinterpret_cast<PFN_vkVoidFunction>(static_cast<PFN_vkCmdBindIndexBuffer>(CmdBindIndexBuffer))},
+ {"vkCmdBindIndexBuffer2", reinterpret_cast<PFN_vkVoidFunction>(static_cast<PFN_vkCmdBindIndexBuffer2>(CmdBindIndexBuffer2))},
{"vkCmdBindPipeline", reinterpret_cast<PFN_vkVoidFunction>(static_cast<PFN_vkCmdBindPipeline>(CmdBindPipeline))},
{"vkCmdBindVertexBuffers", reinterpret_cast<PFN_vkVoidFunction>(static_cast<PFN_vkCmdBindVertexBuffers>(CmdBindVertexBuffers))},
{"vkCmdBindVertexBuffers2", reinterpret_cast<PFN_vkVoidFunction>(static_cast<PFN_vkCmdBindVertexBuffers2>(CmdBindVertexBuffers2))},
@@ -113,6 +115,11 @@
{"vkCmdPipelineBarrier", reinterpret_cast<PFN_vkVoidFunction>(static_cast<PFN_vkCmdPipelineBarrier>(CmdPipelineBarrier))},
{"vkCmdPipelineBarrier2", reinterpret_cast<PFN_vkVoidFunction>(static_cast<PFN_vkCmdPipelineBarrier2>(CmdPipelineBarrier2))},
{"vkCmdPushConstants", reinterpret_cast<PFN_vkVoidFunction>(static_cast<PFN_vkCmdPushConstants>(CmdPushConstants))},
+ {"vkCmdPushConstants2", reinterpret_cast<PFN_vkVoidFunction>(static_cast<PFN_vkCmdPushConstants2>(CmdPushConstants2))},
+ {"vkCmdPushDescriptorSet", reinterpret_cast<PFN_vkVoidFunction>(static_cast<PFN_vkCmdPushDescriptorSet>(CmdPushDescriptorSet))},
+ {"vkCmdPushDescriptorSet2", reinterpret_cast<PFN_vkVoidFunction>(static_cast<PFN_vkCmdPushDescriptorSet2>(CmdPushDescriptorSet2))},
+ {"vkCmdPushDescriptorSetWithTemplate", reinterpret_cast<PFN_vkVoidFunction>(static_cast<PFN_vkCmdPushDescriptorSetWithTemplate>(CmdPushDescriptorSetWithTemplate))},
+ {"vkCmdPushDescriptorSetWithTemplate2", reinterpret_cast<PFN_vkVoidFunction>(static_cast<PFN_vkCmdPushDescriptorSetWithTemplate2>(CmdPushDescriptorSetWithTemplate2))},
{"vkCmdResetEvent", reinterpret_cast<PFN_vkVoidFunction>(static_cast<PFN_vkCmdResetEvent>(CmdResetEvent))},
{"vkCmdResetEvent2", reinterpret_cast<PFN_vkVoidFunction>(static_cast<PFN_vkCmdResetEvent2>(CmdResetEvent2))},
{"vkCmdResetQueryPool", reinterpret_cast<PFN_vkVoidFunction>(static_cast<PFN_vkCmdResetQueryPool>(CmdResetQueryPool))},
@@ -131,10 +138,13 @@
{"vkCmdSetEvent", reinterpret_cast<PFN_vkVoidFunction>(static_cast<PFN_vkCmdSetEvent>(CmdSetEvent))},
{"vkCmdSetEvent2", reinterpret_cast<PFN_vkVoidFunction>(static_cast<PFN_vkCmdSetEvent2>(CmdSetEvent2))},
{"vkCmdSetFrontFace", reinterpret_cast<PFN_vkVoidFunction>(static_cast<PFN_vkCmdSetFrontFace>(CmdSetFrontFace))},
+ {"vkCmdSetLineStipple", reinterpret_cast<PFN_vkVoidFunction>(static_cast<PFN_vkCmdSetLineStipple>(CmdSetLineStipple))},
{"vkCmdSetLineWidth", reinterpret_cast<PFN_vkVoidFunction>(static_cast<PFN_vkCmdSetLineWidth>(CmdSetLineWidth))},
{"vkCmdSetPrimitiveRestartEnable", reinterpret_cast<PFN_vkVoidFunction>(static_cast<PFN_vkCmdSetPrimitiveRestartEnable>(CmdSetPrimitiveRestartEnable))},
{"vkCmdSetPrimitiveTopology", reinterpret_cast<PFN_vkVoidFunction>(static_cast<PFN_vkCmdSetPrimitiveTopology>(CmdSetPrimitiveTopology))},
{"vkCmdSetRasterizerDiscardEnable", reinterpret_cast<PFN_vkVoidFunction>(static_cast<PFN_vkCmdSetRasterizerDiscardEnable>(CmdSetRasterizerDiscardEnable))},
+ {"vkCmdSetRenderingAttachmentLocations", reinterpret_cast<PFN_vkVoidFunction>(static_cast<PFN_vkCmdSetRenderingAttachmentLocations>(CmdSetRenderingAttachmentLocations))},
+ {"vkCmdSetRenderingInputAttachmentIndices", reinterpret_cast<PFN_vkVoidFunction>(static_cast<PFN_vkCmdSetRenderingInputAttachmentIndices>(CmdSetRenderingInputAttachmentIndices))},
{"vkCmdSetScissor", reinterpret_cast<PFN_vkVoidFunction>(static_cast<PFN_vkCmdSetScissor>(CmdSetScissor))},
{"vkCmdSetScissorWithCount", reinterpret_cast<PFN_vkVoidFunction>(static_cast<PFN_vkCmdSetScissorWithCount>(CmdSetScissorWithCount))},
{"vkCmdSetStencilCompareMask", reinterpret_cast<PFN_vkVoidFunction>(static_cast<PFN_vkCmdSetStencilCompareMask>(CmdSetStencilCompareMask))},
@@ -149,6 +159,9 @@
{"vkCmdWaitEvents2", reinterpret_cast<PFN_vkVoidFunction>(static_cast<PFN_vkCmdWaitEvents2>(CmdWaitEvents2))},
{"vkCmdWriteTimestamp", reinterpret_cast<PFN_vkVoidFunction>(static_cast<PFN_vkCmdWriteTimestamp>(CmdWriteTimestamp))},
{"vkCmdWriteTimestamp2", reinterpret_cast<PFN_vkVoidFunction>(static_cast<PFN_vkCmdWriteTimestamp2>(CmdWriteTimestamp2))},
+ {"vkCopyImageToImage", reinterpret_cast<PFN_vkVoidFunction>(static_cast<PFN_vkCopyImageToImage>(CopyImageToImage))},
+ {"vkCopyImageToMemory", reinterpret_cast<PFN_vkVoidFunction>(static_cast<PFN_vkCopyImageToMemory>(CopyImageToMemory))},
+ {"vkCopyMemoryToImage", reinterpret_cast<PFN_vkVoidFunction>(static_cast<PFN_vkCopyMemoryToImage>(CopyMemoryToImage))},
{"vkCreateBuffer", reinterpret_cast<PFN_vkVoidFunction>(static_cast<PFN_vkCreateBuffer>(CreateBuffer))},
{"vkCreateBufferView", reinterpret_cast<PFN_vkVoidFunction>(static_cast<PFN_vkCreateBufferView>(CreateBufferView))},
{"vkCreateCommandPool", reinterpret_cast<PFN_vkVoidFunction>(static_cast<PFN_vkCreateCommandPool>(CreateCommandPool))},
@@ -222,6 +235,7 @@
{"vkGetDeviceGroupPeerMemoryFeatures", reinterpret_cast<PFN_vkVoidFunction>(static_cast<PFN_vkGetDeviceGroupPeerMemoryFeatures>(GetDeviceGroupPeerMemoryFeatures))},
{"vkGetDeviceImageMemoryRequirements", reinterpret_cast<PFN_vkVoidFunction>(static_cast<PFN_vkGetDeviceImageMemoryRequirements>(GetDeviceImageMemoryRequirements))},
{"vkGetDeviceImageSparseMemoryRequirements", reinterpret_cast<PFN_vkVoidFunction>(static_cast<PFN_vkGetDeviceImageSparseMemoryRequirements>(GetDeviceImageSparseMemoryRequirements))},
+ {"vkGetDeviceImageSubresourceLayout", reinterpret_cast<PFN_vkVoidFunction>(static_cast<PFN_vkGetDeviceImageSubresourceLayout>(GetDeviceImageSubresourceLayout))},
{"vkGetDeviceMemoryCommitment", reinterpret_cast<PFN_vkVoidFunction>(static_cast<PFN_vkGetDeviceMemoryCommitment>(GetDeviceMemoryCommitment))},
{"vkGetDeviceMemoryOpaqueCaptureAddress", reinterpret_cast<PFN_vkVoidFunction>(static_cast<PFN_vkGetDeviceMemoryOpaqueCaptureAddress>(GetDeviceMemoryOpaqueCaptureAddress))},
{"vkGetDeviceProcAddr", reinterpret_cast<PFN_vkVoidFunction>(static_cast<PFN_vkGetDeviceProcAddr>(GetDeviceProcAddr))},
@@ -234,6 +248,7 @@
{"vkGetImageSparseMemoryRequirements", reinterpret_cast<PFN_vkVoidFunction>(static_cast<PFN_vkGetImageSparseMemoryRequirements>(GetImageSparseMemoryRequirements))},
{"vkGetImageSparseMemoryRequirements2", reinterpret_cast<PFN_vkVoidFunction>(static_cast<PFN_vkGetImageSparseMemoryRequirements2>(GetImageSparseMemoryRequirements2))},
{"vkGetImageSubresourceLayout", reinterpret_cast<PFN_vkVoidFunction>(static_cast<PFN_vkGetImageSubresourceLayout>(GetImageSubresourceLayout))},
+ {"vkGetImageSubresourceLayout2", reinterpret_cast<PFN_vkVoidFunction>(static_cast<PFN_vkGetImageSubresourceLayout2>(GetImageSubresourceLayout2))},
{"vkGetInstanceProcAddr", reinterpret_cast<PFN_vkVoidFunction>(static_cast<PFN_vkGetInstanceProcAddr>(GetInstanceProcAddr))},
{"vkGetPhysicalDeviceExternalBufferProperties", reinterpret_cast<PFN_vkVoidFunction>(static_cast<PFN_vkGetPhysicalDeviceExternalBufferProperties>(GetPhysicalDeviceExternalBufferProperties))},
{"vkGetPhysicalDeviceExternalFenceProperties", reinterpret_cast<PFN_vkVoidFunction>(static_cast<PFN_vkGetPhysicalDeviceExternalFenceProperties>(GetPhysicalDeviceExternalFenceProperties))},
@@ -264,6 +279,7 @@
{"vkGetPrivateData", reinterpret_cast<PFN_vkVoidFunction>(static_cast<PFN_vkGetPrivateData>(GetPrivateData))},
{"vkGetQueryPoolResults", reinterpret_cast<PFN_vkVoidFunction>(static_cast<PFN_vkGetQueryPoolResults>(GetQueryPoolResults))},
{"vkGetRenderAreaGranularity", reinterpret_cast<PFN_vkVoidFunction>(static_cast<PFN_vkGetRenderAreaGranularity>(GetRenderAreaGranularity))},
+ {"vkGetRenderingAreaGranularity", reinterpret_cast<PFN_vkVoidFunction>(static_cast<PFN_vkGetRenderingAreaGranularity>(GetRenderingAreaGranularity))},
{"vkGetSemaphoreCounterValue", reinterpret_cast<PFN_vkVoidFunction>(static_cast<PFN_vkGetSemaphoreCounterValue>(GetSemaphoreCounterValue))},
{"vkGetSwapchainGrallocUsage2ANDROID", reinterpret_cast<PFN_vkVoidFunction>(static_cast<PFN_vkGetSwapchainGrallocUsage2ANDROID>(GetSwapchainGrallocUsage2ANDROID))},
{"vkGetSwapchainGrallocUsage3ANDROID", reinterpret_cast<PFN_vkVoidFunction>(static_cast<PFN_vkGetSwapchainGrallocUsage3ANDROID>(GetSwapchainGrallocUsage3ANDROID))},
@@ -271,6 +287,7 @@
{"vkGetSwapchainGrallocUsageANDROID", reinterpret_cast<PFN_vkVoidFunction>(static_cast<PFN_vkGetSwapchainGrallocUsageANDROID>(GetSwapchainGrallocUsageANDROID))},
{"vkInvalidateMappedMemoryRanges", reinterpret_cast<PFN_vkVoidFunction>(static_cast<PFN_vkInvalidateMappedMemoryRanges>(InvalidateMappedMemoryRanges))},
{"vkMapMemory", reinterpret_cast<PFN_vkVoidFunction>(static_cast<PFN_vkMapMemory>(MapMemory))},
+ {"vkMapMemory2", reinterpret_cast<PFN_vkVoidFunction>(static_cast<PFN_vkMapMemory2>(MapMemory2))},
{"vkMergePipelineCaches", reinterpret_cast<PFN_vkVoidFunction>(static_cast<PFN_vkMergePipelineCaches>(MergePipelineCaches))},
{"vkQueueBindSparse", reinterpret_cast<PFN_vkVoidFunction>(static_cast<PFN_vkQueueBindSparse>(QueueBindSparse))},
{"vkQueueSignalReleaseImageANDROID", reinterpret_cast<PFN_vkVoidFunction>(static_cast<PFN_vkQueueSignalReleaseImageANDROID>(QueueSignalReleaseImageANDROID))},
@@ -286,8 +303,10 @@
{"vkSetEvent", reinterpret_cast<PFN_vkVoidFunction>(static_cast<PFN_vkSetEvent>(SetEvent))},
{"vkSetPrivateData", reinterpret_cast<PFN_vkVoidFunction>(static_cast<PFN_vkSetPrivateData>(SetPrivateData))},
{"vkSignalSemaphore", reinterpret_cast<PFN_vkVoidFunction>(static_cast<PFN_vkSignalSemaphore>(SignalSemaphore))},
+ {"vkTransitionImageLayout", reinterpret_cast<PFN_vkVoidFunction>(static_cast<PFN_vkTransitionImageLayout>(TransitionImageLayout))},
{"vkTrimCommandPool", reinterpret_cast<PFN_vkVoidFunction>(static_cast<PFN_vkTrimCommandPool>(TrimCommandPool))},
{"vkUnmapMemory", reinterpret_cast<PFN_vkVoidFunction>(static_cast<PFN_vkUnmapMemory>(UnmapMemory))},
+ {"vkUnmapMemory2", reinterpret_cast<PFN_vkVoidFunction>(static_cast<PFN_vkUnmapMemory2>(UnmapMemory2))},
{"vkUpdateDescriptorSetWithTemplate", reinterpret_cast<PFN_vkVoidFunction>(static_cast<PFN_vkUpdateDescriptorSetWithTemplate>(UpdateDescriptorSetWithTemplate))},
{"vkUpdateDescriptorSets", reinterpret_cast<PFN_vkVoidFunction>(static_cast<PFN_vkUpdateDescriptorSets>(UpdateDescriptorSets))},
{"vkWaitForFences", reinterpret_cast<PFN_vkVoidFunction>(static_cast<PFN_vkWaitForFences>(WaitForFences))},
diff --git a/vulkan/nulldrv/null_driver_gen.h b/vulkan/nulldrv/null_driver_gen.h
index 0d1e223..f609e7e 100644
--- a/vulkan/nulldrv/null_driver_gen.h
+++ b/vulkan/nulldrv/null_driver_gen.h
@@ -118,6 +118,7 @@
VKAPI_ATTR VkResult CreateRenderPass(VkDevice device, const VkRenderPassCreateInfo* pCreateInfo, const VkAllocationCallbacks* pAllocator, VkRenderPass* pRenderPass);
VKAPI_ATTR void DestroyRenderPass(VkDevice device, VkRenderPass renderPass, const VkAllocationCallbacks* pAllocator);
VKAPI_ATTR void GetRenderAreaGranularity(VkDevice device, VkRenderPass renderPass, VkExtent2D* pGranularity);
+VKAPI_ATTR void GetRenderingAreaGranularity(VkDevice device, const VkRenderingAreaInfo* pRenderingAreaInfo, VkExtent2D* pGranularity);
VKAPI_ATTR VkResult CreateCommandPool(VkDevice device, const VkCommandPoolCreateInfo* pCreateInfo, const VkAllocationCallbacks* pAllocator, VkCommandPool* pCommandPool);
VKAPI_ATTR void DestroyCommandPool(VkDevice device, VkCommandPool commandPool, const VkAllocationCallbacks* pAllocator);
VKAPI_ATTR VkResult ResetCommandPool(VkDevice device, VkCommandPool commandPool, VkCommandPoolResetFlags flags);
@@ -187,6 +188,7 @@
VKAPI_ATTR void GetPhysicalDeviceMemoryProperties2KHR(VkPhysicalDevice physicalDevice, VkPhysicalDeviceMemoryProperties2* pMemoryProperties);
VKAPI_ATTR void GetPhysicalDeviceSparseImageFormatProperties2(VkPhysicalDevice physicalDevice, const VkPhysicalDeviceSparseImageFormatInfo2* pFormatInfo, uint32_t* pPropertyCount, VkSparseImageFormatProperties2* pProperties);
VKAPI_ATTR void GetPhysicalDeviceSparseImageFormatProperties2KHR(VkPhysicalDevice physicalDevice, const VkPhysicalDeviceSparseImageFormatInfo2* pFormatInfo, uint32_t* pPropertyCount, VkSparseImageFormatProperties2* pProperties);
+VKAPI_ATTR void CmdPushDescriptorSet(VkCommandBuffer commandBuffer, VkPipelineBindPoint pipelineBindPoint, VkPipelineLayout layout, uint32_t set, uint32_t descriptorWriteCount, const VkWriteDescriptorSet* pDescriptorWrites);
VKAPI_ATTR void TrimCommandPool(VkDevice device, VkCommandPool commandPool, VkCommandPoolTrimFlags flags);
VKAPI_ATTR void GetPhysicalDeviceExternalBufferProperties(VkPhysicalDevice physicalDevice, const VkPhysicalDeviceExternalBufferInfo* pExternalBufferInfo, VkExternalBufferProperties* pExternalBufferProperties);
VKAPI_ATTR void GetPhysicalDeviceExternalSemaphoreProperties(VkPhysicalDevice physicalDevice, const VkPhysicalDeviceExternalSemaphoreInfo* pExternalSemaphoreInfo, VkExternalSemaphoreProperties* pExternalSemaphoreProperties);
@@ -200,6 +202,7 @@
VKAPI_ATTR VkResult CreateDescriptorUpdateTemplate(VkDevice device, const VkDescriptorUpdateTemplateCreateInfo* pCreateInfo, const VkAllocationCallbacks* pAllocator, VkDescriptorUpdateTemplate* pDescriptorUpdateTemplate);
VKAPI_ATTR void DestroyDescriptorUpdateTemplate(VkDevice device, VkDescriptorUpdateTemplate descriptorUpdateTemplate, const VkAllocationCallbacks* pAllocator);
VKAPI_ATTR void UpdateDescriptorSetWithTemplate(VkDevice device, VkDescriptorSet descriptorSet, VkDescriptorUpdateTemplate descriptorUpdateTemplate, const void* pData);
+VKAPI_ATTR void CmdPushDescriptorSetWithTemplate(VkCommandBuffer commandBuffer, VkDescriptorUpdateTemplate descriptorUpdateTemplate, VkPipelineLayout layout, uint32_t set, const void* pData);
VKAPI_ATTR void GetBufferMemoryRequirements2(VkDevice device, const VkBufferMemoryRequirementsInfo2* pInfo, VkMemoryRequirements2* pMemoryRequirements);
VKAPI_ATTR void GetImageMemoryRequirements2(VkDevice device, const VkImageMemoryRequirementsInfo2* pInfo, VkMemoryRequirements2* pMemoryRequirements);
VKAPI_ATTR void GetImageSparseMemoryRequirements2(VkDevice device, const VkImageSparseMemoryRequirementsInfo2* pInfo, uint32_t* pSparseMemoryRequirementCount, VkSparseImageMemoryRequirements2* pSparseMemoryRequirements);
@@ -228,12 +231,14 @@
VKAPI_ATTR uint64_t GetBufferOpaqueCaptureAddress(VkDevice device, const VkBufferDeviceAddressInfo* pInfo);
VKAPI_ATTR VkDeviceAddress GetBufferDeviceAddress(VkDevice device, const VkBufferDeviceAddressInfo* pInfo);
VKAPI_ATTR uint64_t GetDeviceMemoryOpaqueCaptureAddress(VkDevice device, const VkDeviceMemoryOpaqueCaptureAddressInfo* pInfo);
+VKAPI_ATTR void CmdSetLineStipple(VkCommandBuffer commandBuffer, uint32_t lineStippleFactor, uint16_t lineStipplePattern);
VKAPI_ATTR VkResult GetPhysicalDeviceToolProperties(VkPhysicalDevice physicalDevice, uint32_t* pToolCount, VkPhysicalDeviceToolProperties* pToolProperties);
VKAPI_ATTR void CmdSetCullMode(VkCommandBuffer commandBuffer, VkCullModeFlags cullMode);
VKAPI_ATTR void CmdSetFrontFace(VkCommandBuffer commandBuffer, VkFrontFace frontFace);
VKAPI_ATTR void CmdSetPrimitiveTopology(VkCommandBuffer commandBuffer, VkPrimitiveTopology primitiveTopology);
VKAPI_ATTR void CmdSetViewportWithCount(VkCommandBuffer commandBuffer, uint32_t viewportCount, const VkViewport* pViewports);
VKAPI_ATTR void CmdSetScissorWithCount(VkCommandBuffer commandBuffer, uint32_t scissorCount, const VkRect2D* pScissors);
+VKAPI_ATTR void CmdBindIndexBuffer2(VkCommandBuffer commandBuffer, VkBuffer buffer, VkDeviceSize offset, VkDeviceSize size, VkIndexType indexType);
VKAPI_ATTR void CmdBindVertexBuffers2(VkCommandBuffer commandBuffer, uint32_t firstBinding, uint32_t bindingCount, const VkBuffer* pBuffers, const VkDeviceSize* pOffsets, const VkDeviceSize* pSizes, const VkDeviceSize* pStrides);
VKAPI_ATTR void CmdSetDepthTestEnable(VkCommandBuffer commandBuffer, VkBool32 depthTestEnable);
VKAPI_ATTR void CmdSetDepthWriteEnable(VkCommandBuffer commandBuffer, VkBool32 depthWriteEnable);
@@ -260,8 +265,22 @@
VKAPI_ATTR void CmdPipelineBarrier2(VkCommandBuffer commandBuffer, const VkDependencyInfo* pDependencyInfo);
VKAPI_ATTR VkResult QueueSubmit2(VkQueue queue, uint32_t submitCount, const VkSubmitInfo2* pSubmits, VkFence fence);
VKAPI_ATTR void CmdWriteTimestamp2(VkCommandBuffer commandBuffer, VkPipelineStageFlags2 stage, VkQueryPool queryPool, uint32_t query);
+VKAPI_ATTR VkResult CopyMemoryToImage(VkDevice device, const VkCopyMemoryToImageInfo* pCopyMemoryToImageInfo);
+VKAPI_ATTR VkResult CopyImageToMemory(VkDevice device, const VkCopyImageToMemoryInfo* pCopyImageToMemoryInfo);
+VKAPI_ATTR VkResult CopyImageToImage(VkDevice device, const VkCopyImageToImageInfo* pCopyImageToImageInfo);
+VKAPI_ATTR VkResult TransitionImageLayout(VkDevice device, uint32_t transitionCount, const VkHostImageLayoutTransitionInfo* pTransitions);
VKAPI_ATTR void CmdBeginRendering(VkCommandBuffer commandBuffer, const VkRenderingInfo* pRenderingInfo);
VKAPI_ATTR void CmdEndRendering(VkCommandBuffer commandBuffer);
+VKAPI_ATTR void GetImageSubresourceLayout2(VkDevice device, VkImage image, const VkImageSubresource2* pSubresource, VkSubresourceLayout2* pLayout);
+VKAPI_ATTR void GetDeviceImageSubresourceLayout(VkDevice device, const VkDeviceImageSubresourceInfo* pInfo, VkSubresourceLayout2* pLayout);
+VKAPI_ATTR VkResult MapMemory2(VkDevice device, const VkMemoryMapInfo* pMemoryMapInfo, void** ppData);
+VKAPI_ATTR VkResult UnmapMemory2(VkDevice device, const VkMemoryUnmapInfo* pMemoryUnmapInfo);
+VKAPI_ATTR void CmdBindDescriptorSets2(VkCommandBuffer commandBuffer, const VkBindDescriptorSetsInfo* pBindDescriptorSetsInfo);
+VKAPI_ATTR void CmdPushConstants2(VkCommandBuffer commandBuffer, const VkPushConstantsInfo* pPushConstantsInfo);
+VKAPI_ATTR void CmdPushDescriptorSet2(VkCommandBuffer commandBuffer, const VkPushDescriptorSetInfo* pPushDescriptorSetInfo);
+VKAPI_ATTR void CmdPushDescriptorSetWithTemplate2(VkCommandBuffer commandBuffer, const VkPushDescriptorSetWithTemplateInfo* pPushDescriptorSetWithTemplateInfo);
+VKAPI_ATTR void CmdSetRenderingAttachmentLocations(VkCommandBuffer commandBuffer, const VkRenderingAttachmentLocationInfo* pLocationInfo);
+VKAPI_ATTR void CmdSetRenderingInputAttachmentIndices(VkCommandBuffer commandBuffer, const VkRenderingInputAttachmentIndexInfo* pInputAttachmentIndexInfo);
// clang-format on
} // namespace null_driver
diff --git a/vulkan/scripts/generator_common.py b/vulkan/scripts/generator_common.py
index 6b4cbad..aa1e7f2 100644
--- a/vulkan/scripts/generator_common.py
+++ b/vulkan/scripts/generator_common.py
@@ -379,6 +379,9 @@
version_dict[cmd_name] = apiversion
for feature in root.iter('feature'):
+ # hack, 'feature' element has multiple meanings.. should be more precise with path match
+ if feature.get('api') is None:
+ continue
if 'vulkan' not in feature.get('api').split(','):
continue
diff --git a/vulkan/vkjson/vkjson.cc b/vulkan/vkjson/vkjson.cc
index bfb7bd6..9b508aa 100644
--- a/vulkan/vkjson/vkjson.cc
+++ b/vulkan/vkjson/vkjson.cc
@@ -1050,6 +1050,9 @@
bool ret = true;
switch (device->properties.apiVersion ^
VK_API_VERSION_PATCH(device->properties.apiVersion)) {
+ case VK_API_VERSION_1_4:
+ // TODO: real 1.4 support here
+ FALLTHROUGH_INTENDED;
case VK_API_VERSION_1_3:
ret &= visitor->Visit("core13", &device->core13);
FALLTHROUGH_INTENDED;
@@ -1110,6 +1113,8 @@
inline bool Iterate(Visitor* visitor, VkJsonInstance* instance) {
bool ret = true;
switch (instance->api_version ^ VK_API_VERSION_PATCH(instance->api_version)) {
+ case VK_API_VERSION_1_4:
+ FALLTHROUGH_INTENDED;
case VK_API_VERSION_1_3:
FALLTHROUGH_INTENDED;
case VK_API_VERSION_1_2: