Merge "Reference the runtime sensor range defined in the HAL from SensorService"
diff --git a/Android.bp b/Android.bp
index 615a7a8..3992f82 100644
--- a/Android.bp
+++ b/Android.bp
@@ -56,7 +56,8 @@
cc_library_headers {
name: "libandroid_sensor_headers",
- vendor: true,
+ vendor_available: true,
+ host_supported: true,
export_include_dirs: ["include_sensor"],
}
diff --git a/cmds/installd/InstalldNativeService.cpp b/cmds/installd/InstalldNativeService.cpp
index b1283eb..bb6639e 100644
--- a/cmds/installd/InstalldNativeService.cpp
+++ b/cmds/installd/InstalldNativeService.cpp
@@ -501,10 +501,6 @@
}
static bool prepare_app_profile_dir(const std::string& packageName, int32_t appId, int32_t userId) {
- if (!property_get_bool("dalvik.vm.usejitprofiles", false)) {
- return true;
- }
-
int32_t uid = multiuser_get_uid(userId, appId);
int shared_app_gid = multiuser_get_shared_gid(userId, appId);
if (shared_app_gid == -1) {
diff --git a/include/android/sensor.h b/include/android/sensor.h
index 105f952..085fc27 100644
--- a/include/android/sensor.h
+++ b/include/android/sensor.h
@@ -601,12 +601,15 @@
float accuracy;
} AHeadingEvent;
+// LINT.IfChange
/**
* Information that describes a sensor event, refer to
* <a href="/reference/android/hardware/SensorEvent">SensorEvent</a> for additional
* documentation.
+ *
+ * NOTE: changes to this struct has to be backward compatible and reflected in
+ * sensors_event_t
*/
-/* NOTE: changes to this struct has to be backward compatible */
typedef struct ASensorEvent {
int32_t version; /* sizeof(struct ASensorEvent) */
int32_t sensor; /** The sensor that generates this event */
@@ -651,6 +654,7 @@
uint32_t flags;
int32_t reserved1[3];
} ASensorEvent;
+// LINT.ThenChange (hardware/libhardware/include/hardware/sensors.h)
struct ASensorManager;
/**
diff --git a/include/ftl/optional.h b/include/ftl/optional.h
index 7b02bac..a818128 100644
--- a/include/ftl/optional.h
+++ b/include/ftl/optional.h
@@ -95,6 +95,14 @@
if (has_value()) return std::invoke(std::forward<F>(f), std::move(value()));
return R();
}
+
+ // Delete new for this class. Its base doesn't have a virtual destructor, and
+ // if it got deleted via base class pointer, it would cause undefined
+ // behavior. There's not a good reason to allocate this object on the heap
+ // anyway.
+ static void* operator new(size_t) = delete;
+ static void* operator new[](size_t) = delete;
+
};
template <typename T, typename U>
diff --git a/include/input/DisplayViewport.h b/include/input/DisplayViewport.h
index 98a18c9..7457496 100644
--- a/include/input/DisplayViewport.h
+++ b/include/input/DisplayViewport.h
@@ -21,6 +21,7 @@
#include <ftl/string.h>
#include <gui/constants.h>
#include <input/Input.h>
+#include <ui/Rotation.h>
#include <cinttypes>
#include <optional>
@@ -29,13 +30,6 @@
namespace android {
-enum {
- DISPLAY_ORIENTATION_0 = 0,
- DISPLAY_ORIENTATION_90 = 1,
- DISPLAY_ORIENTATION_180 = 2,
- DISPLAY_ORIENTATION_270 = 3
-};
-
/**
* Describes the different type of viewports supported by input flinger.
* Keep in sync with values in InputManagerService.java.
@@ -54,7 +48,7 @@
*/
struct DisplayViewport {
int32_t displayId; // -1 if invalid
- int32_t orientation;
+ ui::Rotation orientation;
int32_t logicalLeft;
int32_t logicalTop;
int32_t logicalRight;
@@ -74,7 +68,7 @@
DisplayViewport()
: displayId(ADISPLAY_ID_NONE),
- orientation(DISPLAY_ORIENTATION_0),
+ orientation(ui::ROTATION_0),
logicalLeft(0),
logicalTop(0),
logicalRight(0),
@@ -111,7 +105,7 @@
void setNonDisplayViewport(int32_t width, int32_t height) {
displayId = ADISPLAY_ID_NONE;
- orientation = DISPLAY_ORIENTATION_0;
+ orientation = ui::ROTATION_0;
logicalLeft = 0;
logicalTop = 0;
logicalRight = width;
diff --git a/include/input/Input.h b/include/input/Input.h
index d298d81..015efdd 100644
--- a/include/input/Input.h
+++ b/include/input/Input.h
@@ -577,7 +577,7 @@
inline const ui::Transform& getTransform() const { return mTransform; }
- int getSurfaceRotation() const;
+ std::optional<ui::Rotation> getSurfaceRotation() const;
inline float getXPrecision() const { return mXPrecision; }
diff --git a/include/input/KeyCharacterMap.h b/include/input/KeyCharacterMap.h
index dc928b8..867a089 100644
--- a/include/input/KeyCharacterMap.h
+++ b/include/input/KeyCharacterMap.h
@@ -125,14 +125,21 @@
bool getEvents(int32_t deviceId, const char16_t* chars, size_t numChars,
Vector<KeyEvent>& outEvents) const;
+ /* Maps an Android key code to another Android key code. This mapping is applied after scanCode
+ * and usageCodes are mapped to corresponding Android Keycode */
+ void addKeyRemapping(int32_t fromKeyCode, int32_t toKeyCode);
+
/* Maps a scan code and usage code to a key code, in case this key map overrides
* the mapping in some way. */
status_t mapKey(int32_t scanCode, int32_t usageCode, int32_t* outKeyCode) const;
- /* Tries to find a replacement key code for a given key code and meta state
- * in character map. */
- void tryRemapKey(int32_t scanCode, int32_t metaState,
- int32_t* outKeyCode, int32_t* outMetaState) const;
+ /* Returns keycode after applying Android key code remapping defined in mKeyRemapping */
+ int32_t applyKeyRemapping(int32_t fromKeyCode) const;
+
+ /* Returns the <keyCode, metaState> pair after applying key behavior defined in the kcm file,
+ * that tries to find a replacement key code based on current meta state */
+ std::pair<int32_t /*keyCode*/, int32_t /*metaState*/> applyKeyBehavior(int32_t keyCode,
+ int32_t metaState) const;
#ifdef __linux__
/* Reads a key map from a parcel. */
@@ -227,8 +234,9 @@
std::string mLoadFileName;
bool mLayoutOverlayApplied;
- KeyedVector<int32_t, int32_t> mKeysByScanCode;
- KeyedVector<int32_t, int32_t> mKeysByUsageCode;
+ std::map<int32_t /* fromAndroidKeyCode */, int32_t /* toAndroidKeyCode */> mKeyRemapping;
+ std::map<int32_t /* fromScanCode */, int32_t /* toAndroidKeyCode */> mKeysByScanCode;
+ std::map<int32_t /* fromHidUsageCode */, int32_t /* toAndroidKeyCode */> mKeysByUsageCode;
KeyCharacterMap(const std::string& filename);
diff --git a/include/input/TouchVideoFrame.h b/include/input/TouchVideoFrame.h
index a616a95..1e4f6e7 100644
--- a/include/input/TouchVideoFrame.h
+++ b/include/input/TouchVideoFrame.h
@@ -16,6 +16,8 @@
#pragma once
+#include <ui/Rotation.h>
+
#include <stdint.h>
#include <sys/time.h>
#include <vector>
@@ -58,7 +60,7 @@
* Rotate the video frame.
* The rotation value is an enum from ui/Rotation.h
*/
- void rotate(int32_t orientation);
+ void rotate(ui::Rotation orientation);
private:
uint32_t mHeight;
diff --git a/libs/binder/IPCThreadState.cpp b/libs/binder/IPCThreadState.cpp
index 7770374..c0f3e30 100644
--- a/libs/binder/IPCThreadState.cpp
+++ b/libs/binder/IPCThreadState.cpp
@@ -1017,6 +1017,10 @@
if (!reply && !acquireResult) goto finish;
break;
+ case BR_TRANSACTION_PENDING_FROZEN:
+ ALOGW("Sending oneway calls to frozen process.");
+ goto finish;
+
case BR_DEAD_REPLY:
err = DEAD_OBJECT;
goto finish;
diff --git a/libs/binder/IServiceManager.cpp b/libs/binder/IServiceManager.cpp
index a0c4334..2408307 100644
--- a/libs/binder/IServiceManager.cpp
+++ b/libs/binder/IServiceManager.cpp
@@ -444,7 +444,7 @@
bool declared;
if (Status status = mTheRealServiceManager->isDeclared(String8(name).c_str(), &declared);
!status.isOk()) {
- ALOGW("Failed to get isDeclard for %s: %s", String8(name).c_str(),
+ ALOGW("Failed to get isDeclared for %s: %s", String8(name).c_str(),
status.toString8().c_str());
return false;
}
diff --git a/libs/binder/ProcessState.cpp b/libs/binder/ProcessState.cpp
index 1f311ac..254dda8 100644
--- a/libs/binder/ProcessState.cpp
+++ b/libs/binder/ProcessState.cpp
@@ -439,6 +439,10 @@
return mCurrentThreads;
}
+bool ProcessState::isThreadPoolStarted() const {
+ return mThreadPoolStarted;
+}
+
#define DRIVER_FEATURES_PATH "/dev/binderfs/features/"
bool ProcessState::isDriverFeatureEnabled(const DriverFeature feature) {
static const char* const names[] = {
diff --git a/libs/binder/binder_module.h b/libs/binder/binder_module.h
index 793795e..eef07ae 100644
--- a/libs/binder/binder_module.h
+++ b/libs/binder/binder_module.h
@@ -100,4 +100,9 @@
#define BINDER_ENABLE_ONEWAY_SPAM_DETECTION _IOW('b', 16, __u32)
#endif // BINDER_ENABLE_ONEWAY_SPAM_DETECTION
+#ifndef BR_TRANSACTION_PENDING_FROZEN
+// Temporary definition of BR_TRANSACTION_PENDING_FROZEN until UAPI binder.h includes it.
+#define BR_TRANSACTION_PENDING_FROZEN _IO('r', 20)
+#endif // BR_TRANSACTION_PENDING_FROZEN
+
#endif // _BINDER_MODULE_H_
diff --git a/libs/binder/include/binder/IInterface.h b/libs/binder/include/binder/IInterface.h
index dc572ac..8cc8105 100644
--- a/libs/binder/include/binder/IInterface.h
+++ b/libs/binder/include/binder/IInterface.h
@@ -230,7 +230,6 @@
"android.graphicsenv.IGpuService",
"android.gui.IConsumerListener",
"android.gui.IGraphicBufferConsumer",
- "android.gui.ITransactionComposerListener",
"android.gui.SensorEventConnection",
"android.gui.SensorServer",
"android.hardware.ICamera",
diff --git a/libs/binder/include/binder/IPCThreadState.h b/libs/binder/include/binder/IPCThreadState.h
index 65b77c6..d261c21 100644
--- a/libs/binder/include/binder/IPCThreadState.h
+++ b/libs/binder/include/binder/IPCThreadState.h
@@ -141,11 +141,13 @@
void restoreCallingIdentity(int64_t token);
bool hasExplicitIdentity();
+ // For main functions - dangerous for libraries to use
status_t setupPolling(int* fd);
status_t handlePolledCommands();
void flushCommands();
bool flushIfNeeded();
+ // For main functions - dangerous for libraries to use
void joinThreadPool(bool isMain = true);
// Stop the local process.
diff --git a/libs/binder/include/binder/ProcessState.h b/libs/binder/include/binder/ProcessState.h
index 9679a5f..bad8cb1 100644
--- a/libs/binder/include/binder/ProcessState.h
+++ b/libs/binder/include/binder/ProcessState.h
@@ -50,6 +50,7 @@
sp<IBinder> getContextObject(const sp<IBinder>& caller);
+ // For main functions - dangerous for libraries to use
void startThreadPool();
bool becomeContextManager();
@@ -57,8 +58,10 @@
sp<IBinder> getStrongProxyForHandle(int32_t handle);
void expungeHandle(int32_t handle, IBinder* binder);
+ // TODO: deprecate.
void spawnPooledThread(bool isMain);
+ // For main functions - dangerous for libraries to use
status_t setThreadPoolMaxThreadCount(size_t maxThreads);
status_t enableOnewaySpamDetection(bool enable);
void giveThreadPoolName();
@@ -94,6 +97,11 @@
*/
size_t getThreadPoolMaxTotalThreadCount() const;
+ /**
+ * Check to see if the thread pool has started.
+ */
+ bool isThreadPoolStarted() const;
+
enum class DriverFeature {
ONEWAY_SPAM_DETECTION,
EXTENDED_ERROR,
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 81975e7..9949de2 100644
--- a/libs/binder/ndk/include_cpp/android/binder_interface_utils.h
+++ b/libs/binder/ndk/include_cpp/android/binder_interface_utils.h
@@ -196,6 +196,10 @@
bool isRemote() override final { return false; }
+ static std::string makeServiceName(std::string_view instance) {
+ return INTERFACE::descriptor + ("/" + std::string(instance));
+ }
+
protected:
/**
* This function should only be called by asBinder. Otherwise, there is a possibility of
diff --git a/libs/binder/ndk/include_cpp/android/binder_to_string.h b/libs/binder/ndk/include_cpp/android/binder_to_string.h
index 6a25db2..2a00736 100644
--- a/libs/binder/ndk/include_cpp/android/binder_to_string.h
+++ b/libs/binder/ndk/include_cpp/android/binder_to_string.h
@@ -160,7 +160,7 @@
template <typename _T>
std::string ToString(const _T& t) {
if constexpr (details::ToEmptyString<_T>::value) {
- return "";
+ return "<unimplemented>";
} else if constexpr (std::is_same_v<bool, _T>) {
return t ? "true" : "false";
} else if constexpr (std::is_same_v<char16_t, _T>) {
@@ -176,9 +176,11 @@
return t;
#ifdef HAS_NDK_INTERFACE
} else if constexpr (std::is_same_v<::ndk::SpAIBinder, _T>) {
- return (t.get() == nullptr) ? "(null)" : "";
+ std::stringstream ss;
+ ss << "binder:" << std::hex << t.get();
+ return ss.str();
} else if constexpr (std::is_same_v<::ndk::ScopedFileDescriptor, _T>) {
- return (t.get() == -1) ? "(null)" : "";
+ return "fd:" + std::to_string(t.get());
#endif
#ifdef HAS_STRING16
} else if constexpr (std::is_same_v<String16, _T>) {
diff --git a/libs/binder/ndk/include_ndk/android/binder_ibinder.h b/libs/binder/ndk/include_ndk/android/binder_ibinder.h
index 4163897..1af2119 100644
--- a/libs/binder/ndk/include_ndk/android/binder_ibinder.h
+++ b/libs/binder/ndk/include_ndk/android/binder_ibinder.h
@@ -613,7 +613,8 @@
*
* 1. If the binder died, shortly after the call to onBinderDied.
* 2. If the binder is explicitly unlinked with AIBinder_unlinkToDeath or
- * AIBinder_DeathRecipient_delete.
+ * AIBinder_DeathRecipient_delete, after any pending onBinderDied calls
+ * finish.
* 3. During or shortly after the AIBinder_linkToDeath call if it returns an error.
*
* It is guaranteed that the callback is called exactly once for each call to linkToDeath unless the
diff --git a/libs/binder/ndk/include_platform/android/binder_process.h b/libs/binder/ndk/include_platform/android/binder_process.h
index f408fad..ffcad55 100644
--- a/libs/binder/ndk/include_platform/android/binder_process.h
+++ b/libs/binder/ndk/include_platform/android/binder_process.h
@@ -28,17 +28,33 @@
*
* When using this, it is expected that ABinderProcess_setupPolling and
* ABinderProcess_handlePolledCommands are not used.
+ *
+ * Do not use this from a library. Apps setup their own threadpools, and otherwise, the main
+ * function should be responsible for configuring the threadpool for the entire application.
*/
void ABinderProcess_startThreadPool();
/**
* This sets the maximum number of threads that can be started in the threadpool. By default, after
* startThreadPool is called, this is 15. If it is called additional times, it will only prevent
* the kernel from starting new threads and will not delete already existing threads.
+ *
+ * Do not use this from a library. Apps setup their own threadpools, and otherwise, the main
+ * function should be responsible for configuring the threadpool for the entire application.
*/
bool ABinderProcess_setThreadPoolMaxThreadCount(uint32_t numThreads);
/**
+ * Check if the threadpool has already been started.
+ * This tells whether someone in the process has called ABinderProcess_startThreadPool. Usually,
+ * you should use this in a library to abort if the threadpool is not started.
+ * Programs should configure binder threadpools once at the beginning.
+ */
+bool ABinderProcess_isThreadPoolStarted();
+/**
* This adds the current thread to the threadpool. This may cause the threadpool to exceed the
* maximum size.
+ *
+ * Do not use this from a library. Apps setup their own threadpools, and otherwise, the main
+ * function should be responsible for configuring the threadpool for the entire application.
*/
void ABinderProcess_joinThreadPool();
diff --git a/libs/binder/ndk/include_platform/android/binder_stability.h b/libs/binder/ndk/include_platform/android/binder_stability.h
index 683a433..c1f62e5 100644
--- a/libs/binder/ndk/include_platform/android/binder_stability.h
+++ b/libs/binder/ndk/include_platform/android/binder_stability.h
@@ -50,6 +50,15 @@
* requirements associated with that higher stability level. For instance, a
* VINTF stability binder is required to be in the VINTF manifest. This API
* can be called to use that same interface within the vendor partition.
+ *
+ * WARNING: you must hold on to a binder instance after this is set, while you
+ * are using it. If you get a binder (e.g. `...->asBinder().get()`), you must
+ * save this binder and then
+ * use it. For instance:
+ *
+ * auto binder = ...->asBinder();
+ * AIBinder_forceDowngradeToVendorStability(binder.get());
+ * doSomething(binder);
*/
void AIBinder_forceDowngradeToVendorStability(AIBinder* binder);
@@ -79,6 +88,15 @@
* requirements associated with that higher stability level. For instance, a
* VINTF stability binder is required to be in the VINTF manifest. This API
* can be called to use that same interface within the system partition.
+ *
+ * WARNING: you must hold on to a binder instance after this is set, while you
+ * are using it. If you get a binder (e.g. `...->asBinder().get()`), you must
+ * save this binder and then
+ * use it. For instance:
+ *
+ * auto binder = ...->asBinder();
+ * AIBinder_forceDowngradeToSystemStability(binder.get());
+ * doSomething(binder);
*/
void AIBinder_forceDowngradeToSystemStability(AIBinder* binder);
diff --git a/libs/binder/ndk/libbinder_ndk.map.txt b/libs/binder/ndk/libbinder_ndk.map.txt
index 5c7005c..54e4628 100644
--- a/libs/binder/ndk/libbinder_ndk.map.txt
+++ b/libs/binder/ndk/libbinder_ndk.map.txt
@@ -154,6 +154,7 @@
LIBBINDER_NDK34 { # introduced=UpsideDownCake
global:
+ ABinderProcess_isThreadPoolStarted; # systemapi llndk
AServiceManager_getUpdatableApexName; # systemapi
AServiceManager_registerForServiceNotifications; # systemapi llndk
AServiceManager_NotificationRegistration_delete; # systemapi llndk
diff --git a/libs/binder/ndk/process.cpp b/libs/binder/ndk/process.cpp
index ac582a4..bc6610e 100644
--- a/libs/binder/ndk/process.cpp
+++ b/libs/binder/ndk/process.cpp
@@ -31,6 +31,9 @@
bool ABinderProcess_setThreadPoolMaxThreadCount(uint32_t numThreads) {
return ProcessState::self()->setThreadPoolMaxThreadCount(numThreads) == 0;
}
+bool ABinderProcess_isThreadPoolStarted() {
+ return ProcessState::self()->isThreadPoolStarted();
+}
void ABinderProcess_joinThreadPool() {
IPCThreadState::self()->joinThreadPool();
}
diff --git a/libs/binder/rust/tests/parcel_fuzzer/Android.bp b/libs/binder/rust/tests/parcel_fuzzer/Android.bp
index 28e0200..df8a2af 100644
--- a/libs/binder/rust/tests/parcel_fuzzer/Android.bp
+++ b/libs/binder/rust/tests/parcel_fuzzer/Android.bp
@@ -21,5 +21,7 @@
"waghpawan@google.com",
"smoreland@google.com",
],
+ // hotlist "AIDL fuzzers bugs" on buganizer
+ hotlists: ["4637097"],
},
}
diff --git a/libs/binder/rust/tests/parcel_fuzzer/random_parcel/fuzz_service_test/Android.bp b/libs/binder/rust/tests/parcel_fuzzer/random_parcel/fuzz_service_test/Android.bp
index 43e407c..5cb406a 100644
--- a/libs/binder/rust/tests/parcel_fuzzer/random_parcel/fuzz_service_test/Android.bp
+++ b/libs/binder/rust/tests/parcel_fuzzer/random_parcel/fuzz_service_test/Android.bp
@@ -29,5 +29,7 @@
"waghpawan@google.com",
"smoreland@google.com",
],
+ // hotlist "AIDL fuzzers bugs" on buganizer
+ hotlists: ["4637097"],
},
}
diff --git a/libs/binder/rust/tests/parcel_fuzzer/read_utils.rs b/libs/binder/rust/tests/parcel_fuzzer/read_utils.rs
index d2bfde1..a2d48b6 100644
--- a/libs/binder/rust/tests/parcel_fuzzer/read_utils.rs
+++ b/libs/binder/rust/tests/parcel_fuzzer/read_utils.rs
@@ -16,9 +16,9 @@
use binder::binder_impl::BorrowedParcel;
use binder::{ParcelFileDescriptor, Parcelable, SpIBinder};
-use binderReadParcelIface::aidl::EmptyParcelable::EmptyParcelable;
-use binderReadParcelIface::aidl::GenericDataParcelable::GenericDataParcelable;
-use binderReadParcelIface::aidl::SingleDataParcelable::SingleDataParcelable;
+use binderReadParcelIface::aidl::parcelables::EmptyParcelable::EmptyParcelable;
+use binderReadParcelIface::aidl::parcelables::GenericDataParcelable::GenericDataParcelable;
+use binderReadParcelIface::aidl::parcelables::SingleDataParcelable::SingleDataParcelable;
macro_rules! read_parcel_interface {
($data_type:ty) => {
diff --git a/libs/binder/tests/binderLibTest.cpp b/libs/binder/tests/binderLibTest.cpp
index 25b524f..f7498c4 100644
--- a/libs/binder/tests/binderLibTest.cpp
+++ b/libs/binder/tests/binderLibTest.cpp
@@ -120,6 +120,7 @@
BINDER_LIB_TEST_CAN_GET_SID,
BINDER_LIB_TEST_GET_MAX_THREAD_COUNT,
BINDER_LIB_TEST_SET_MAX_THREAD_COUNT,
+ BINDER_LIB_TEST_IS_THREADPOOL_STARTED,
BINDER_LIB_TEST_LOCK_UNLOCK,
BINDER_LIB_TEST_PROCESS_LOCK,
BINDER_LIB_TEST_UNLOCK_AFTER_MS,
@@ -1383,6 +1384,14 @@
EXPECT_EQ(replyi, kKernelThreads + 1);
}
+TEST_F(BinderLibTest, ThreadPoolStarted) {
+ Parcel data, reply;
+ sp<IBinder> server = addServer();
+ ASSERT_TRUE(server != nullptr);
+ EXPECT_THAT(server->transact(BINDER_LIB_TEST_IS_THREADPOOL_STARTED, data, &reply), NO_ERROR);
+ EXPECT_TRUE(reply.readBool());
+}
+
size_t epochMillis() {
using std::chrono::duration_cast;
using std::chrono::milliseconds;
@@ -1849,6 +1858,10 @@
reply->writeInt32(ProcessState::self()->getThreadPoolMaxTotalThreadCount());
return NO_ERROR;
}
+ case BINDER_LIB_TEST_IS_THREADPOOL_STARTED: {
+ reply->writeBool(ProcessState::self()->isThreadPoolStarted());
+ return NO_ERROR;
+ }
case BINDER_LIB_TEST_PROCESS_LOCK: {
m_blockMutex.lock();
return NO_ERROR;
diff --git a/libs/binder/tests/parcel_fuzzer/Android.bp b/libs/binder/tests/parcel_fuzzer/Android.bp
index 61a2412..35866ad 100644
--- a/libs/binder/tests/parcel_fuzzer/Android.bp
+++ b/libs/binder/tests/parcel_fuzzer/Android.bp
@@ -12,13 +12,14 @@
host_supported: true,
unstable: true,
srcs: [
- "EmptyParcelable.aidl",
- "SingleDataParcelable.aidl",
- "GenericDataParcelable.aidl",
+ "parcelables/EmptyParcelable.aidl",
+ "parcelables/SingleDataParcelable.aidl",
+ "parcelables/GenericDataParcelable.aidl",
],
backend: {
java: {
- enabled: false,
+ enabled: true,
+ platform_apis: true,
},
rust: {
enabled: true,
diff --git a/libs/binder/tests/parcel_fuzzer/EmptyParcelable.aidl b/libs/binder/tests/parcel_fuzzer/EmptyParcelable.aidl
deleted file mode 100644
index 96d6223..0000000
--- a/libs/binder/tests/parcel_fuzzer/EmptyParcelable.aidl
+++ /dev/null
@@ -1,18 +0,0 @@
-/*
- * Copyright (C) 2022 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-parcelable EmptyParcelable{
-}
\ No newline at end of file
diff --git a/libs/binder/tests/parcel_fuzzer/binder.cpp b/libs/binder/tests/parcel_fuzzer/binder.cpp
index 9dac2c9..768fbe1 100644
--- a/libs/binder/tests/parcel_fuzzer/binder.cpp
+++ b/libs/binder/tests/parcel_fuzzer/binder.cpp
@@ -16,9 +16,9 @@
#define FUZZ_LOG_TAG "binder"
#include "binder.h"
-#include "EmptyParcelable.h"
-#include "GenericDataParcelable.h"
-#include "SingleDataParcelable.h"
+#include "parcelables/EmptyParcelable.h"
+#include "parcelables/GenericDataParcelable.h"
+#include "parcelables/SingleDataParcelable.h"
#include "util.h"
#include <android-base/hex.h>
@@ -359,19 +359,19 @@
},
[] (const ::android::Parcel& p, FuzzedDataProvider& /*provider*/) {
FUZZ_LOG() << "about to call readFromParcel() with status for EmptyParcelable";
- EmptyParcelable emptyParcelable{};
+ parcelables::EmptyParcelable emptyParcelable{};
status_t status = emptyParcelable.readFromParcel(&p);
FUZZ_LOG() << " status: " << status;
},
[] (const ::android::Parcel& p , FuzzedDataProvider& /*provider*/) {
FUZZ_LOG() << "about to call readFromParcel() with status for SingleDataParcelable";
- SingleDataParcelable singleDataParcelable;
+ parcelables::SingleDataParcelable singleDataParcelable;
status_t status = singleDataParcelable.readFromParcel(&p);
FUZZ_LOG() <<" status: " << status;
},
[] (const ::android::Parcel& p, FuzzedDataProvider& /*provider*/) {
FUZZ_LOG() << "about to call readFromParcel() with status for GenericDataParcelable";
- GenericDataParcelable genericDataParcelable;
+ parcelables::GenericDataParcelable genericDataParcelable;
status_t status = genericDataParcelable.readFromParcel(&p);
FUZZ_LOG() <<" status: " << status;
},
diff --git a/libs/binder/tests/parcel_fuzzer/binder_ndk.cpp b/libs/binder/tests/parcel_fuzzer/binder_ndk.cpp
index af773a0..53e7de4 100644
--- a/libs/binder/tests/parcel_fuzzer/binder_ndk.cpp
+++ b/libs/binder/tests/parcel_fuzzer/binder_ndk.cpp
@@ -16,9 +16,9 @@
#define FUZZ_LOG_TAG "binder_ndk"
#include "binder_ndk.h"
-#include "aidl/EmptyParcelable.h"
-#include "aidl/GenericDataParcelable.h"
-#include "aidl/SingleDataParcelable.h"
+#include "aidl/parcelables/EmptyParcelable.h"
+#include "aidl/parcelables/GenericDataParcelable.h"
+#include "aidl/parcelables/SingleDataParcelable.h"
#include <android/binder_parcel_utils.h>
#include <android/binder_parcelable_utils.h>
@@ -183,19 +183,19 @@
[](const NdkParcelAdapter& p, FuzzedDataProvider& /*provider*/) {
FUZZ_LOG() << "about to read parcel using readFromParcel for EmptyParcelable";
- aidl::EmptyParcelable emptyParcelable;
+ aidl::parcelables::EmptyParcelable emptyParcelable;
binder_status_t status = emptyParcelable.readFromParcel(p.aParcel());
FUZZ_LOG() << "status: " << status;
},
[](const NdkParcelAdapter& p, FuzzedDataProvider& /*provider*/) {
FUZZ_LOG() << "about to read parcel using readFromParcel for SingleDataParcelable";
- aidl::SingleDataParcelable singleDataParcelable;
+ aidl::parcelables::SingleDataParcelable singleDataParcelable;
binder_status_t status = singleDataParcelable.readFromParcel(p.aParcel());
FUZZ_LOG() << "status: " << status;
},
[](const NdkParcelAdapter& p, FuzzedDataProvider& /*provider*/) {
FUZZ_LOG() << "about to read parcel using readFromParcel for GenericDataParcelable";
- aidl::GenericDataParcelable genericDataParcelable;
+ aidl::parcelables::GenericDataParcelable genericDataParcelable;
binder_status_t status = genericDataParcelable.readFromParcel(p.aParcel());
FUZZ_LOG() << "status: " << status;
},
diff --git a/libs/binder/tests/parcel_fuzzer/libbinder_driver.cpp b/libs/binder/tests/parcel_fuzzer/libbinder_driver.cpp
index 86461c8..8bef33f 100644
--- a/libs/binder/tests/parcel_fuzzer/libbinder_driver.cpp
+++ b/libs/binder/tests/parcel_fuzzer/libbinder_driver.cpp
@@ -37,7 +37,9 @@
}
while (provider.remaining_bytes() > 0) {
- uint32_t code = provider.ConsumeIntegral<uint32_t>();
+ // Most of the AIDL services will have small set of transaction codes.
+ uint32_t code = provider.ConsumeBool() ? provider.ConsumeIntegral<uint32_t>()
+ : provider.ConsumeIntegralInRange<uint32_t>(0, 100);
uint32_t flags = provider.ConsumeIntegral<uint32_t>();
Parcel data;
// for increased fuzz coverage
diff --git a/libs/binder/tests/parcel_fuzzer/SingleDataParcelable.aidl b/libs/binder/tests/parcel_fuzzer/parcelables/EmptyParcelable.aidl
similarity index 92%
copy from libs/binder/tests/parcel_fuzzer/SingleDataParcelable.aidl
copy to libs/binder/tests/parcel_fuzzer/parcelables/EmptyParcelable.aidl
index d62891b..1216250 100644
--- a/libs/binder/tests/parcel_fuzzer/SingleDataParcelable.aidl
+++ b/libs/binder/tests/parcel_fuzzer/parcelables/EmptyParcelable.aidl
@@ -13,7 +13,6 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-
-parcelable SingleDataParcelable{
- int data;
+package parcelables;
+parcelable EmptyParcelable {
}
\ No newline at end of file
diff --git a/libs/binder/tests/parcel_fuzzer/GenericDataParcelable.aidl b/libs/binder/tests/parcel_fuzzer/parcelables/GenericDataParcelable.aidl
similarity index 97%
rename from libs/binder/tests/parcel_fuzzer/GenericDataParcelable.aidl
rename to libs/binder/tests/parcel_fuzzer/parcelables/GenericDataParcelable.aidl
index fc2542b..f1079e9 100644
--- a/libs/binder/tests/parcel_fuzzer/GenericDataParcelable.aidl
+++ b/libs/binder/tests/parcel_fuzzer/parcelables/GenericDataParcelable.aidl
@@ -13,6 +13,7 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
+package parcelables;
parcelable GenericDataParcelable {
int data;
diff --git a/libs/binder/tests/parcel_fuzzer/SingleDataParcelable.aidl b/libs/binder/tests/parcel_fuzzer/parcelables/SingleDataParcelable.aidl
similarity index 96%
rename from libs/binder/tests/parcel_fuzzer/SingleDataParcelable.aidl
rename to libs/binder/tests/parcel_fuzzer/parcelables/SingleDataParcelable.aidl
index d62891b..0187168 100644
--- a/libs/binder/tests/parcel_fuzzer/SingleDataParcelable.aidl
+++ b/libs/binder/tests/parcel_fuzzer/parcelables/SingleDataParcelable.aidl
@@ -13,6 +13,7 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
+package parcelables;
parcelable SingleDataParcelable{
int data;
diff --git a/libs/binder/tests/parcel_fuzzer/random_parcel.cpp b/libs/binder/tests/parcel_fuzzer/random_parcel.cpp
index edc695f..f0beed2 100644
--- a/libs/binder/tests/parcel_fuzzer/random_parcel.cpp
+++ b/libs/binder/tests/parcel_fuzzer/random_parcel.cpp
@@ -73,6 +73,11 @@
1));
CHECK(OK == p->writeFileDescriptor(fd.get(), false /*takeOwnership*/));
} else {
+ // b/260119717 - Adding more FDs can eventually lead to FD limit exhaustion
+ if (options->extraFds.size() > 1000) {
+ return;
+ }
+
std::vector<base::unique_fd> fds = getRandomFds(&provider);
CHECK(OK ==
p->writeFileDescriptor(fds.begin()->release(),
diff --git a/libs/binder/trusty/include/log/log.h b/libs/binder/trusty/include/log/log.h
index d88d18a..de84617 100644
--- a/libs/binder/trusty/include/log/log.h
+++ b/libs/binder/trusty/include/log/log.h
@@ -121,6 +121,8 @@
TLOGE("android_errorWriteLog: tag:%x subTag:%s\n", tag, subTag); \
} while (0)
-extern "C" inline void __assert(const char* file, int line, const char* str) {
- LOG_ALWAYS_FATAL("%s:%d: assertion \"%s\" failed", file, line, str);
-}
+// Override the definition of __assert from binder_status.h
+#ifndef __BIONIC__
+#undef __assert
+#define __assert(file, line, str) LOG_ALWAYS_FATAL("%s:%d: %s", file, line, str)
+#endif // __BIONIC__
diff --git a/libs/gui/ISurfaceComposer.cpp b/libs/gui/ISurfaceComposer.cpp
index a77ca04..a0e75ff 100644
--- a/libs/gui/ISurfaceComposer.cpp
+++ b/libs/gui/ISurfaceComposer.cpp
@@ -42,6 +42,7 @@
namespace android {
+using gui::CallbackId;
using gui::DisplayCaptureArgs;
using gui::IDisplayEventConnection;
using gui::IRegionSamplingListener;
diff --git a/libs/gui/ITransactionCompletedListener.cpp b/libs/gui/ITransactionCompletedListener.cpp
index 2b25b61..23d7d50 100644
--- a/libs/gui/ITransactionCompletedListener.cpp
+++ b/libs/gui/ITransactionCompletedListener.cpp
@@ -21,22 +21,11 @@
#include <optional>
#include <gui/ISurfaceComposer.h>
-#include <gui/ITransactionCompletedListener.h>
#include <gui/LayerState.h>
+#include <gui/ListenerStats.h>
#include <private/gui/ParcelUtils.h>
-namespace android {
-
-namespace { // Anonymous
-
-enum class Tag : uint32_t {
- ON_TRANSACTION_COMPLETED = IBinder::FIRST_CALL_TRANSACTION,
- ON_RELEASE_BUFFER,
- ON_TRANSACTION_QUEUE_STALLED,
- LAST = ON_TRANSACTION_QUEUE_STALLED,
-};
-
-} // Anonymous namespace
+namespace android::gui {
status_t FrameEventHistoryStats::writeToParcel(Parcel* output) const {
status_t err = output->writeUint64(frameNumber);
@@ -274,60 +263,6 @@
return listenerStats;
}
-class BpTransactionCompletedListener : public SafeBpInterface<ITransactionCompletedListener> {
-public:
- explicit BpTransactionCompletedListener(const sp<IBinder>& impl)
- : SafeBpInterface<ITransactionCompletedListener>(impl, "BpTransactionCompletedListener") {
- }
-
- ~BpTransactionCompletedListener() override;
-
- void onTransactionCompleted(ListenerStats stats) override {
- callRemoteAsync<decltype(&ITransactionCompletedListener::
- onTransactionCompleted)>(Tag::ON_TRANSACTION_COMPLETED,
- stats);
- }
-
- void onReleaseBuffer(ReleaseCallbackId callbackId, sp<Fence> releaseFence,
- uint32_t currentMaxAcquiredBufferCount) override {
- callRemoteAsync<decltype(&ITransactionCompletedListener::
- onReleaseBuffer)>(Tag::ON_RELEASE_BUFFER, callbackId,
- releaseFence,
- currentMaxAcquiredBufferCount);
- }
-
- void onTransactionQueueStalled(const String8& reason) override {
- callRemoteAsync<
- decltype(&ITransactionCompletedListener::
- onTransactionQueueStalled)>(Tag::ON_TRANSACTION_QUEUE_STALLED,
- reason);
- }
-};
-
-// Out-of-line virtual method definitions to trigger vtable emission in this translation unit (see
-// clang warning -Wweak-vtables)
-BpTransactionCompletedListener::~BpTransactionCompletedListener() = default;
-
-IMPLEMENT_META_INTERFACE(TransactionCompletedListener, "android.gui.ITransactionComposerListener");
-
-status_t BnTransactionCompletedListener::onTransact(uint32_t code, const Parcel& data,
- Parcel* reply, uint32_t flags) {
- if (code < IBinder::FIRST_CALL_TRANSACTION || code > static_cast<uint32_t>(Tag::LAST)) {
- return BBinder::onTransact(code, data, reply, flags);
- }
- auto tag = static_cast<Tag>(code);
- switch (tag) {
- case Tag::ON_TRANSACTION_COMPLETED:
- return callLocalAsync(data, reply,
- &ITransactionCompletedListener::onTransactionCompleted);
- case Tag::ON_RELEASE_BUFFER:
- return callLocalAsync(data, reply, &ITransactionCompletedListener::onReleaseBuffer);
- case Tag::ON_TRANSACTION_QUEUE_STALLED:
- return callLocalAsync(data, reply,
- &ITransactionCompletedListener::onTransactionQueueStalled);
- }
-}
-
ListenerCallbacks ListenerCallbacks::filter(CallbackId::Type type) const {
std::vector<CallbackId> filteredCallbackIds;
for (const auto& callbackId : callbackIds) {
@@ -366,4 +301,4 @@
const ReleaseCallbackId ReleaseCallbackId::INVALID_ID = ReleaseCallbackId(0, 0);
-}; // namespace android
+}; // namespace android::gui
diff --git a/libs/gui/LayerState.cpp b/libs/gui/LayerState.cpp
index 95962af..0d1a69b 100644
--- a/libs/gui/LayerState.cpp
+++ b/libs/gui/LayerState.cpp
@@ -51,6 +51,7 @@
namespace android {
+using gui::CallbackId;
using gui::FocusRequest;
using gui::WindowInfoHandle;
@@ -388,6 +389,27 @@
}
}
+void DisplayState::sanitize(int32_t permissions) {
+ if (what & DisplayState::eLayerStackChanged) {
+ if (!(permissions & layer_state_t::Permission::ACCESS_SURFACE_FLINGER)) {
+ what &= ~DisplayState::eLayerStackChanged;
+ ALOGE("Stripped attempt to set eLayerStackChanged in sanitize");
+ }
+ }
+ if (what & DisplayState::eDisplayProjectionChanged) {
+ if (!(permissions & layer_state_t::Permission::ACCESS_SURFACE_FLINGER)) {
+ what &= ~DisplayState::eDisplayProjectionChanged;
+ ALOGE("Stripped attempt to set eDisplayProjectionChanged in sanitize");
+ }
+ }
+ if (what & DisplayState::eSurfaceChanged) {
+ if (!(permissions & layer_state_t::Permission::ACCESS_SURFACE_FLINGER)) {
+ what &= ~DisplayState::eSurfaceChanged;
+ ALOGE("Stripped attempt to set eSurfaceChanged in sanitize");
+ }
+ }
+}
+
void layer_state_t::sanitize(int32_t permissions) {
// TODO: b/109894387
//
diff --git a/libs/gui/SurfaceComposerClient.cpp b/libs/gui/SurfaceComposerClient.cpp
index 1e43700..a2ed8aa 100644
--- a/libs/gui/SurfaceComposerClient.cpp
+++ b/libs/gui/SurfaceComposerClient.cpp
@@ -314,7 +314,8 @@
}
}
-void TransactionCompletedListener::onTransactionCompleted(ListenerStats listenerStats) {
+binder::Status TransactionCompletedListener::onTransactionCompleted(
+ const ListenerStats& listenerStats) {
std::unordered_map<CallbackId, CallbackTranslation, CallbackIdHash> callbacksMap;
std::multimap<int32_t, sp<JankDataListener>> jankListenersMap;
{
@@ -454,9 +455,10 @@
}
}
}
+ return binder::Status::ok();
}
-void TransactionCompletedListener::onTransactionQueueStalled(const String8& reason) {
+binder::Status TransactionCompletedListener::onTransactionQueueStalled(const std::string& reason) {
std::unordered_map<void*, std::function<void(const std::string&)>> callbackCopy;
{
std::scoped_lock<std::mutex> lock(mMutex);
@@ -465,6 +467,7 @@
for (auto const& it : callbackCopy) {
it.second(reason.c_str());
}
+ return binder::Status::ok();
}
void TransactionCompletedListener::addQueueStallListener(
@@ -478,9 +481,12 @@
mQueueStallListeners.erase(id);
}
-void TransactionCompletedListener::onReleaseBuffer(ReleaseCallbackId callbackId,
- sp<Fence> releaseFence,
- uint32_t currentMaxAcquiredBufferCount) {
+binder::Status TransactionCompletedListener::onReleaseBuffer(
+ const ReleaseCallbackId& callbackId,
+ const std::optional<os::ParcelFileDescriptor>& releaseFenceFd,
+ int32_t currentMaxAcquiredBufferCount) {
+ sp<Fence> releaseFence(releaseFenceFd ? new Fence(::dup(releaseFenceFd->get()))
+ : Fence::NO_FENCE);
ReleaseBufferCallback callback;
{
std::scoped_lock<std::mutex> lock(mMutex);
@@ -489,13 +495,14 @@
if (!callback) {
ALOGE("Could not call release buffer callback, buffer not found %s",
callbackId.to_string().c_str());
- return;
+ return binder::Status::fromExceptionCode(binder::Status::EX_ILLEGAL_ARGUMENT);
}
std::optional<uint32_t> optionalMaxAcquiredBufferCount =
- currentMaxAcquiredBufferCount == UINT_MAX
+ static_cast<uint32_t>(currentMaxAcquiredBufferCount) == UINT_MAX
? std::nullopt
: std::make_optional<uint32_t>(currentMaxAcquiredBufferCount);
callback(callbackId, releaseFence, optionalMaxAcquiredBufferCount);
+ return binder::Status::ok();
}
ReleaseBufferCallback TransactionCompletedListener::popReleaseBufferCallbackLocked(
@@ -825,7 +832,11 @@
->mReleaseCallbackThread
.addReleaseCallback(state.bufferData->generateReleaseCallbackId(), fence);
} else {
- listener->onReleaseBuffer(state.bufferData->generateReleaseCallbackId(), fence, UINT_MAX);
+ std::optional<os::ParcelFileDescriptor> fenceFd;
+ if (fence != Fence::NO_FENCE) {
+ fenceFd = os::ParcelFileDescriptor(base::unique_fd(::dup(fence->get())));
+ }
+ listener->onReleaseBuffer(state.bufferData->generateReleaseCallbackId(), fenceFd, UINT_MAX);
}
}
@@ -971,14 +982,16 @@
class SyncCallback {
public:
- static void function(void* callbackContext, nsecs_t /* latchTime */,
- const sp<Fence>& /* presentFence */,
- const std::vector<SurfaceControlStats>& /* stats */) {
- if (!callbackContext) {
- ALOGE("failed to get callback context for SyncCallback");
- }
- SyncCallback* helper = static_cast<SyncCallback*>(callbackContext);
- LOG_ALWAYS_FATAL_IF(sem_post(&helper->mSemaphore), "sem_post failed");
+ static auto getCallback(std::shared_ptr<SyncCallback>& callbackContext) {
+ return [callbackContext](void* /* unused context */, nsecs_t /* latchTime */,
+ const sp<Fence>& /* presentFence */,
+ const std::vector<SurfaceControlStats>& /* stats */) {
+ if (!callbackContext) {
+ ALOGE("failed to get callback context for SyncCallback");
+ return;
+ }
+ LOG_ALWAYS_FATAL_IF(sem_post(&callbackContext->mSemaphore), "sem_post failed");
+ };
}
~SyncCallback() {
if (mInitialized) {
@@ -1013,10 +1026,11 @@
return mStatus;
}
- SyncCallback syncCallback;
+ std::shared_ptr<SyncCallback> syncCallback = std::make_shared<SyncCallback>();
if (synchronous) {
- syncCallback.init();
- addTransactionCommittedCallback(syncCallback.function, syncCallback.getContext());
+ syncCallback->init();
+ addTransactionCommittedCallback(SyncCallback::getCallback(syncCallback),
+ /*callbackContext=*/nullptr);
}
bool hasListenerCallbacks = !mListenerCallbacks.empty();
@@ -1092,7 +1106,7 @@
clear();
if (synchronous) {
- syncCallback.wait();
+ syncCallback->wait();
}
mStatus = NO_ERROR;
@@ -2144,6 +2158,12 @@
mStatus = NO_INIT;
}
+status_t SurfaceComposerClient::bootFinished() {
+ sp<gui::ISurfaceComposer> sf(ComposerServiceAIDL::getComposerService());
+ binder::Status status = sf->bootFinished();
+ return statusTFromBinderStatus(status);
+}
+
sp<SurfaceControl> SurfaceComposerClient::createSurface(const String8& name, uint32_t w, uint32_t h,
PixelFormat format, int32_t flags,
const sp<IBinder>& parentHandle,
@@ -2260,12 +2280,12 @@
return statusTFromBinderStatus(status);
}
-status_t SurfaceComposerClient::getStaticDisplayInfo(const sp<IBinder>& display,
+status_t SurfaceComposerClient::getStaticDisplayInfo(int64_t displayId,
ui::StaticDisplayInfo* outInfo) {
using Tag = android::gui::DeviceProductInfo::ManufactureOrModelDate::Tag;
gui::StaticDisplayInfo ginfo;
binder::Status status =
- ComposerServiceAIDL::getComposerService()->getStaticDisplayInfo(display, &ginfo);
+ ComposerServiceAIDL::getComposerService()->getStaticDisplayInfo(displayId, &ginfo);
if (status.isOk()) {
// convert gui::StaticDisplayInfo to ui::StaticDisplayInfo
outInfo->connectionType = static_cast<ui::DisplayConnectionType>(ginfo.connectionType);
@@ -2309,52 +2329,74 @@
return statusTFromBinderStatus(status);
}
-status_t SurfaceComposerClient::getDynamicDisplayInfo(const sp<IBinder>& display,
- ui::DynamicDisplayInfo* outInfo) {
+void SurfaceComposerClient::getDynamicDisplayInfoInternal(gui::DynamicDisplayInfo& ginfo,
+ ui::DynamicDisplayInfo*& outInfo) {
+ // convert gui::DynamicDisplayInfo to ui::DynamicDisplayInfo
+ outInfo->supportedDisplayModes.clear();
+ outInfo->supportedDisplayModes.reserve(ginfo.supportedDisplayModes.size());
+ for (const auto& mode : ginfo.supportedDisplayModes) {
+ ui::DisplayMode outMode;
+ outMode.id = mode.id;
+ outMode.resolution.width = mode.resolution.width;
+ outMode.resolution.height = mode.resolution.height;
+ outMode.xDpi = mode.xDpi;
+ outMode.yDpi = mode.yDpi;
+ outMode.refreshRate = mode.refreshRate;
+ outMode.appVsyncOffset = mode.appVsyncOffset;
+ outMode.sfVsyncOffset = mode.sfVsyncOffset;
+ outMode.presentationDeadline = mode.presentationDeadline;
+ outMode.group = mode.group;
+ std::transform(mode.supportedHdrTypes.begin(), mode.supportedHdrTypes.end(),
+ std::back_inserter(outMode.supportedHdrTypes),
+ [](const int32_t& value) { return static_cast<ui::Hdr>(value); });
+ outInfo->supportedDisplayModes.push_back(outMode);
+ }
+
+ outInfo->activeDisplayModeId = ginfo.activeDisplayModeId;
+ outInfo->renderFrameRate = ginfo.renderFrameRate;
+
+ outInfo->supportedColorModes.clear();
+ outInfo->supportedColorModes.reserve(ginfo.supportedColorModes.size());
+ for (const auto& cmode : ginfo.supportedColorModes) {
+ outInfo->supportedColorModes.push_back(static_cast<ui::ColorMode>(cmode));
+ }
+
+ outInfo->activeColorMode = static_cast<ui::ColorMode>(ginfo.activeColorMode);
+
+ std::vector<ui::Hdr> types;
+ types.reserve(ginfo.hdrCapabilities.supportedHdrTypes.size());
+ for (const auto& hdr : ginfo.hdrCapabilities.supportedHdrTypes) {
+ types.push_back(static_cast<ui::Hdr>(hdr));
+ }
+ outInfo->hdrCapabilities = HdrCapabilities(types, ginfo.hdrCapabilities.maxLuminance,
+ ginfo.hdrCapabilities.maxAverageLuminance,
+ ginfo.hdrCapabilities.minLuminance);
+
+ outInfo->autoLowLatencyModeSupported = ginfo.autoLowLatencyModeSupported;
+ outInfo->gameContentTypeSupported = ginfo.gameContentTypeSupported;
+ outInfo->preferredBootDisplayMode = ginfo.preferredBootDisplayMode;
+}
+
+status_t SurfaceComposerClient::getDynamicDisplayInfoFromId(int64_t displayId,
+ ui::DynamicDisplayInfo* outInfo) {
gui::DynamicDisplayInfo ginfo;
binder::Status status =
- ComposerServiceAIDL::getComposerService()->getDynamicDisplayInfo(display, &ginfo);
+ ComposerServiceAIDL::getComposerService()->getDynamicDisplayInfoFromId(displayId,
+ &ginfo);
if (status.isOk()) {
- // convert gui::DynamicDisplayInfo to ui::DynamicDisplayInfo
- outInfo->supportedDisplayModes.clear();
- outInfo->supportedDisplayModes.reserve(ginfo.supportedDisplayModes.size());
- for (const auto& mode : ginfo.supportedDisplayModes) {
- ui::DisplayMode outMode;
- outMode.id = mode.id;
- outMode.resolution.width = mode.resolution.width;
- outMode.resolution.height = mode.resolution.height;
- outMode.xDpi = mode.xDpi;
- outMode.yDpi = mode.yDpi;
- outMode.refreshRate = mode.refreshRate;
- outMode.appVsyncOffset = mode.appVsyncOffset;
- outMode.sfVsyncOffset = mode.sfVsyncOffset;
- outMode.presentationDeadline = mode.presentationDeadline;
- outMode.group = mode.group;
- outInfo->supportedDisplayModes.push_back(outMode);
- }
+ getDynamicDisplayInfoInternal(ginfo, outInfo);
+ }
+ return statusTFromBinderStatus(status);
+}
- outInfo->activeDisplayModeId = ginfo.activeDisplayModeId;
-
- outInfo->supportedColorModes.clear();
- outInfo->supportedColorModes.reserve(ginfo.supportedColorModes.size());
- for (const auto& cmode : ginfo.supportedColorModes) {
- outInfo->supportedColorModes.push_back(static_cast<ui::ColorMode>(cmode));
- }
-
- outInfo->activeColorMode = static_cast<ui::ColorMode>(ginfo.activeColorMode);
-
- std::vector<ui::Hdr> types;
- types.reserve(ginfo.hdrCapabilities.supportedHdrTypes.size());
- for (const auto& hdr : ginfo.hdrCapabilities.supportedHdrTypes) {
- types.push_back(static_cast<ui::Hdr>(hdr));
- }
- outInfo->hdrCapabilities = HdrCapabilities(types, ginfo.hdrCapabilities.maxLuminance,
- ginfo.hdrCapabilities.maxAverageLuminance,
- ginfo.hdrCapabilities.minLuminance);
-
- outInfo->autoLowLatencyModeSupported = ginfo.autoLowLatencyModeSupported;
- outInfo->gameContentTypeSupported = ginfo.gameContentTypeSupported;
- outInfo->preferredBootDisplayMode = ginfo.preferredBootDisplayMode;
+status_t SurfaceComposerClient::getDynamicDisplayInfoFromToken(const sp<IBinder>& display,
+ ui::DynamicDisplayInfo* outInfo) {
+ gui::DynamicDisplayInfo ginfo;
+ binder::Status status =
+ ComposerServiceAIDL::getComposerService()->getDynamicDisplayInfoFromToken(display,
+ &ginfo);
+ if (status.isOk()) {
+ getDynamicDisplayInfoInternal(ginfo, outInfo);
}
return statusTFromBinderStatus(status);
}
@@ -2362,7 +2404,8 @@
status_t SurfaceComposerClient::getActiveDisplayMode(const sp<IBinder>& display,
ui::DisplayMode* mode) {
ui::DynamicDisplayInfo info;
- status_t result = getDynamicDisplayInfo(display, &info);
+
+ status_t result = getDynamicDisplayInfoFromToken(display, &info);
if (result != NO_ERROR) {
return result;
}
@@ -2538,7 +2581,7 @@
gui::PullAtomData pad;
binder::Status status = ComposerServiceAIDL::getComposerService()->onPullAtom(atomId, &pad);
if (status.isOk()) {
- outData->assign((const char*)pad.data.data(), pad.data.size());
+ outData->assign(pad.data.begin(), pad.data.end());
*success = pad.success;
}
return statusTFromBinderStatus(status);
@@ -2823,7 +2866,11 @@
while (!callbackInfos.empty()) {
auto [callbackId, releaseFence] = callbackInfos.front();
- listener->onReleaseBuffer(callbackId, std::move(releaseFence), UINT_MAX);
+ std::optional<os::ParcelFileDescriptor> fenceFd;
+ if (releaseFence != Fence::NO_FENCE) {
+ fenceFd = os::ParcelFileDescriptor(base::unique_fd(::dup(releaseFence->get())));
+ }
+ listener->onReleaseBuffer(callbackId, fenceFd, UINT_MAX);
callbackInfos.pop();
}
diff --git a/libs/gui/aidl/android/gui/DisplayMode.aidl b/libs/gui/aidl/android/gui/DisplayMode.aidl
index 3cd77f8..ce30426 100644
--- a/libs/gui/aidl/android/gui/DisplayMode.aidl
+++ b/libs/gui/aidl/android/gui/DisplayMode.aidl
@@ -27,6 +27,7 @@
Size resolution;
float xDpi = 0.0f;
float yDpi = 0.0f;
+ int[] supportedHdrTypes;
float refreshRate = 0.0f;
long appVsyncOffset = 0;
diff --git a/libs/gui/aidl/android/gui/DynamicDisplayInfo.aidl b/libs/gui/aidl/android/gui/DynamicDisplayInfo.aidl
index 57e6081..3114929 100644
--- a/libs/gui/aidl/android/gui/DynamicDisplayInfo.aidl
+++ b/libs/gui/aidl/android/gui/DynamicDisplayInfo.aidl
@@ -27,6 +27,7 @@
List<DisplayMode> supportedDisplayModes;
int activeDisplayModeId;
+ float renderFrameRate;
int[] supportedColorModes;
int activeColorMode;
diff --git a/libs/gui/aidl/android/gui/ISurfaceComposer.aidl b/libs/gui/aidl/android/gui/ISurfaceComposer.aidl
index 40410fb..a0b613c 100644
--- a/libs/gui/aidl/android/gui/ISurfaceComposer.aidl
+++ b/libs/gui/aidl/android/gui/ISurfaceComposer.aidl
@@ -62,8 +62,6 @@
* Signal that we're done booting.
* Requires ACCESS_SURFACE_FLINGER permission
*/
- // Note this must be the 1st method, so IBinder::FIRST_CALL_TRANSACTION
- // is assigned, as it is called from Java by ActivityManagerService.
void bootFinished();
/**
@@ -126,12 +124,14 @@
/**
* Gets immutable information about given physical display.
*/
- StaticDisplayInfo getStaticDisplayInfo(IBinder display);
+ StaticDisplayInfo getStaticDisplayInfo(long displayId);
/**
* Gets dynamic information about given physical display.
*/
- DynamicDisplayInfo getDynamicDisplayInfo(IBinder display);
+ DynamicDisplayInfo getDynamicDisplayInfoFromId(long displayId);
+
+ DynamicDisplayInfo getDynamicDisplayInfoFromToken(IBinder display);
DisplayPrimaries getDisplayNativePrimaries(IBinder display);
diff --git a/libs/gui/aidl/android/gui/ITransactionCompletedListener.aidl b/libs/gui/aidl/android/gui/ITransactionCompletedListener.aidl
new file mode 100644
index 0000000..dde4d38
--- /dev/null
+++ b/libs/gui/aidl/android/gui/ITransactionCompletedListener.aidl
@@ -0,0 +1,31 @@
+/*
+ * 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.
+ */
+
+package android.gui;
+
+import android.gui.ListenerStats;
+import android.gui.ReleaseCallbackId;
+
+/** @hide */
+oneway interface ITransactionCompletedListener {
+ void onTransactionCompleted(in ListenerStats stats);
+
+ void onReleaseBuffer(in ReleaseCallbackId callbackId,
+ in @nullable ParcelFileDescriptor releaseFenceFd,
+ int currentMaxAcquiredBufferCount);
+
+ void onTransactionQueueStalled(@utf8InCpp String name);
+}
diff --git a/libs/binder/tests/parcel_fuzzer/SingleDataParcelable.aidl b/libs/gui/aidl/android/gui/ListenerStats.aidl
similarity index 80%
copy from libs/binder/tests/parcel_fuzzer/SingleDataParcelable.aidl
copy to libs/gui/aidl/android/gui/ListenerStats.aidl
index d62891b..63248b2 100644
--- a/libs/binder/tests/parcel_fuzzer/SingleDataParcelable.aidl
+++ b/libs/gui/aidl/android/gui/ListenerStats.aidl
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2022 The Android Open Source Project
+ * 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.
@@ -14,6 +14,6 @@
* limitations under the License.
*/
-parcelable SingleDataParcelable{
- int data;
-}
\ No newline at end of file
+package android.gui;
+
+parcelable ListenerStats cpp_header "gui/ListenerStats.h";
diff --git a/libs/gui/aidl/android/gui/PullAtomData.aidl b/libs/gui/aidl/android/gui/PullAtomData.aidl
index 14d33c6..c307cef 100644
--- a/libs/gui/aidl/android/gui/PullAtomData.aidl
+++ b/libs/gui/aidl/android/gui/PullAtomData.aidl
@@ -18,6 +18,6 @@
/** @hide */
parcelable PullAtomData {
- @utf8InCpp String data;
+ byte[] data;
boolean success;
}
diff --git a/libs/binder/tests/parcel_fuzzer/SingleDataParcelable.aidl b/libs/gui/aidl/android/gui/ReleaseCallbackId.aidl
similarity index 79%
copy from libs/binder/tests/parcel_fuzzer/SingleDataParcelable.aidl
copy to libs/gui/aidl/android/gui/ReleaseCallbackId.aidl
index d62891b..c86de34 100644
--- a/libs/binder/tests/parcel_fuzzer/SingleDataParcelable.aidl
+++ b/libs/gui/aidl/android/gui/ReleaseCallbackId.aidl
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2022 The Android Open Source Project
+ * 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.
@@ -14,6 +14,6 @@
* limitations under the License.
*/
-parcelable SingleDataParcelable{
- int data;
-}
\ No newline at end of file
+package android.gui;
+
+parcelable ReleaseCallbackId cpp_header "gui/ReleaseCallbackId.h";
diff --git a/libs/gui/fuzzer/libgui_fuzzer_utils.h b/libs/gui/fuzzer/libgui_fuzzer_utils.h
index 9d1ee8f..8810e4e 100644
--- a/libs/gui/fuzzer/libgui_fuzzer_utils.h
+++ b/libs/gui/fuzzer/libgui_fuzzer_utils.h
@@ -79,9 +79,11 @@
(override));
MOCK_METHOD(binder::Status, getDisplayState, (const sp<IBinder>&, gui::DisplayState*),
(override));
- MOCK_METHOD(binder::Status, getStaticDisplayInfo, (const sp<IBinder>&, gui::StaticDisplayInfo*),
+ MOCK_METHOD(binder::Status, getStaticDisplayInfo, (int64_t, gui::StaticDisplayInfo*),
(override));
- MOCK_METHOD(binder::Status, getDynamicDisplayInfo,
+ MOCK_METHOD(binder::Status, getDynamicDisplayInfoFromId, (int64_t, gui::DynamicDisplayInfo*),
+ (override));
+ MOCK_METHOD(binder::Status, getDynamicDisplayInfoFromToken,
(const sp<IBinder>&, gui::DynamicDisplayInfo*), (override));
MOCK_METHOD(binder::Status, getDisplayNativePrimaries,
(const sp<IBinder>&, gui::DisplayPrimaries*), (override));
diff --git a/libs/gui/include/gui/ISurfaceComposer.h b/libs/gui/include/gui/ISurfaceComposer.h
index d517e99..d70a7f0 100644
--- a/libs/gui/include/gui/ISurfaceComposer.h
+++ b/libs/gui/include/gui/ISurfaceComposer.h
@@ -23,11 +23,11 @@
#include <android/gui/IHdrLayerInfoListener.h>
#include <android/gui/IRegionSamplingListener.h>
#include <android/gui/IScreenCaptureListener.h>
+#include <android/gui/ITransactionCompletedListener.h>
#include <android/gui/ITunnelModeEnabledListener.h>
#include <android/gui/IWindowInfosListener.h>
#include <binder/IBinder.h>
#include <binder/IInterface.h>
-#include <gui/ITransactionCompletedListener.h>
#include <gui/SpHash.h>
#include <math/vec4.h>
#include <stdint.h>
@@ -66,6 +66,7 @@
using gui::IDisplayEventConnection;
using gui::IRegionSamplingListener;
using gui::IScreenCaptureListener;
+using gui::ListenerCallbacks;
using gui::SpHash;
namespace gui {
diff --git a/libs/gui/include/gui/LayerState.h b/libs/gui/include/gui/LayerState.h
index 6ec6bd7..c5fdf82 100644
--- a/libs/gui/include/gui/LayerState.h
+++ b/libs/gui/include/gui/LayerState.h
@@ -21,10 +21,10 @@
#include <stdint.h>
#include <sys/types.h>
+#include <android/gui/ITransactionCompletedListener.h>
#include <android/gui/IWindowInfosReportedListener.h>
#include <android/native_window.h>
#include <gui/IGraphicBufferProducer.h>
-#include <gui/ITransactionCompletedListener.h>
#include <math/mat4.h>
#include <android/gui/DropInputMode.h>
@@ -35,6 +35,7 @@
#include <gui/ISurfaceComposer.h>
#include <gui/LayerCaptureArgs.h>
#include <gui/LayerMetadata.h>
+#include <gui/ReleaseCallbackId.h>
#include <gui/SpHash.h>
#include <gui/SurfaceControl.h>
#include <gui/WindowInfo.h>
@@ -56,6 +57,9 @@
using gui::ISurfaceComposerClient;
using gui::LayerMetadata;
+using gui::ITransactionCompletedListener;
+using gui::ReleaseCallbackId;
+
struct client_cache_t {
wp<IBinder> token = nullptr;
uint64_t id;
@@ -359,6 +363,7 @@
DisplayState();
void merge(const DisplayState& other);
+ void sanitize(int32_t permissions);
uint32_t what = 0;
uint32_t flags = 0;
diff --git a/libs/gui/include/gui/ITransactionCompletedListener.h b/libs/gui/include/gui/ListenerStats.h
similarity index 81%
rename from libs/gui/include/gui/ITransactionCompletedListener.h
rename to libs/gui/include/gui/ListenerStats.h
index 453e8f3..3a12802 100644
--- a/libs/gui/include/gui/ITransactionCompletedListener.h
+++ b/libs/gui/include/gui/ListenerStats.h
@@ -24,6 +24,8 @@
#include <binder/SafeInterface.h>
#include <gui/FrameTimestamps.h>
+#include <gui/ReleaseCallbackId.h>
+
#include <ui/Fence.h>
#include <utils/Timers.h>
@@ -32,10 +34,7 @@
#include <unordered_set>
#include <variant>
-namespace android {
-
-class ITransactionCompletedListener;
-class ListenerCallbacks;
+namespace android::gui {
class CallbackId : public Parcelable {
public:
@@ -54,30 +53,6 @@
std::size_t operator()(const CallbackId& key) const { return std::hash<int64_t>()(key.id); }
};
-class ReleaseCallbackId : public Parcelable {
-public:
- static const ReleaseCallbackId INVALID_ID;
-
- uint64_t bufferId;
- uint64_t framenumber;
- ReleaseCallbackId() {}
- ReleaseCallbackId(uint64_t bufferId, uint64_t framenumber)
- : bufferId(bufferId), framenumber(framenumber) {}
- status_t writeToParcel(Parcel* output) const override;
- status_t readFromParcel(const Parcel* input) override;
-
- bool operator==(const ReleaseCallbackId& rhs) const {
- return bufferId == rhs.bufferId && framenumber == rhs.framenumber;
- }
- bool operator!=(const ReleaseCallbackId& rhs) const { return !operator==(rhs); }
- std::string to_string() const {
- if (*this == INVALID_ID) return "INVALID_ID";
-
- return "bufferId:" + std::to_string(bufferId) +
- " framenumber:" + std::to_string(framenumber);
- }
-};
-
struct ReleaseBufferCallbackIdHash {
std::size_t operator()(const ReleaseCallbackId& key) const {
return std::hash<uint64_t>()(key.bufferId);
@@ -186,27 +161,6 @@
std::vector<TransactionStats> transactionStats;
};
-class ITransactionCompletedListener : public IInterface {
-public:
- DECLARE_META_INTERFACE(TransactionCompletedListener)
-
- virtual void onTransactionCompleted(ListenerStats stats) = 0;
-
- virtual void onReleaseBuffer(ReleaseCallbackId callbackId, sp<Fence> releaseFence,
- uint32_t currentMaxAcquiredBufferCount) = 0;
-
- virtual void onTransactionQueueStalled(const String8& name) = 0;
-};
-
-class BnTransactionCompletedListener : public SafeBnInterface<ITransactionCompletedListener> {
-public:
- BnTransactionCompletedListener()
- : SafeBnInterface<ITransactionCompletedListener>("BnTransactionCompletedListener") {}
-
- status_t onTransact(uint32_t code, const Parcel& data, Parcel* reply,
- uint32_t flags = 0) override;
-};
-
class ListenerCallbacks {
public:
ListenerCallbacks(const sp<IBinder>& listener,
@@ -268,4 +222,4 @@
}
};
-} // namespace android
+} // namespace android::gui
diff --git a/libs/gui/include/gui/ReleaseCallbackId.h b/libs/gui/include/gui/ReleaseCallbackId.h
new file mode 100644
index 0000000..142ee5a
--- /dev/null
+++ b/libs/gui/include/gui/ReleaseCallbackId.h
@@ -0,0 +1,50 @@
+/*
+ * 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/Parcel.h>
+#include <binder/Parcelable.h>
+
+#include <cstdint>
+
+namespace android::gui {
+
+class ReleaseCallbackId : public Parcelable {
+public:
+ static const ReleaseCallbackId INVALID_ID;
+
+ uint64_t bufferId;
+ uint64_t framenumber;
+ ReleaseCallbackId() {}
+ ReleaseCallbackId(uint64_t bufferId, uint64_t framenumber)
+ : bufferId(bufferId), framenumber(framenumber) {}
+ status_t writeToParcel(Parcel* output) const override;
+ status_t readFromParcel(const Parcel* input) override;
+
+ bool operator==(const ReleaseCallbackId& rhs) const {
+ return bufferId == rhs.bufferId && framenumber == rhs.framenumber;
+ }
+ bool operator!=(const ReleaseCallbackId& rhs) const { return !operator==(rhs); }
+ std::string to_string() const {
+ if (*this == INVALID_ID) return "INVALID_ID";
+
+ return "bufferId:" + std::to_string(bufferId) +
+ " framenumber:" + std::to_string(framenumber);
+ }
+};
+
+} // namespace android::gui
diff --git a/libs/gui/include/gui/SurfaceComposerClient.h b/libs/gui/include/gui/SurfaceComposerClient.h
index 2038f14..cc459c5 100644
--- a/libs/gui/include/gui/SurfaceComposerClient.h
+++ b/libs/gui/include/gui/SurfaceComposerClient.h
@@ -42,10 +42,13 @@
#include <android/gui/ISurfaceComposerClient.h>
+#include <android/gui/BnTransactionCompletedListener.h>
+
#include <gui/CpuConsumer.h>
#include <gui/ISurfaceComposer.h>
-#include <gui/ITransactionCompletedListener.h>
#include <gui/LayerState.h>
+#include <gui/ListenerStats.h>
+#include <gui/ReleaseCallbackId.h>
#include <gui/SurfaceControl.h>
#include <gui/WindowInfosListenerReporter.h>
#include <math/vec3.h>
@@ -59,11 +62,21 @@
class ITunnelModeEnabledListener;
class Region;
+using gui::BnTransactionCompletedListener;
+using gui::CallbackId;
+using gui::CallbackIdHash;
using gui::DisplayCaptureArgs;
+using gui::FrameEventHistoryStats;
using gui::IRegionSamplingListener;
using gui::ISurfaceComposerClient;
+using gui::ITransactionCompletedListener;
+using gui::JankData;
using gui::LayerCaptureArgs;
using gui::LayerMetadata;
+using gui::ListenerStats;
+using gui::ReleaseBufferCallbackIdHash;
+using gui::ReleaseCallbackId;
+using gui::SurfaceStats;
struct SurfaceControlStats {
SurfaceControlStats(const sp<SurfaceControl>& sc, nsecs_t latchTime,
@@ -145,14 +158,17 @@
status_t linkToComposerDeath(const sp<IBinder::DeathRecipient>& recipient,
void* cookie = nullptr, uint32_t flags = 0);
+ // Notify the SurfaceComposerClient that the boot procedure has completed
+ static status_t bootFinished();
+
// Get transactional state of given display.
static status_t getDisplayState(const sp<IBinder>& display, ui::DisplayState*);
// Get immutable information about given physical display.
- static status_t getStaticDisplayInfo(const sp<IBinder>& display, ui::StaticDisplayInfo*);
+ static status_t getStaticDisplayInfo(int64_t, ui::StaticDisplayInfo*);
- // Get dynamic information about given physical display.
- static status_t getDynamicDisplayInfo(const sp<IBinder>& display, ui::DynamicDisplayInfo*);
+ // Get dynamic information about given physical display from display id
+ static status_t getDynamicDisplayInfoFromId(int64_t, ui::DynamicDisplayInfo*);
// Shorthand for the active display mode from getDynamicDisplayInfo().
// TODO(b/180391891): Update clients to use getDynamicDisplayInfo and remove this function.
@@ -714,6 +730,12 @@
ReleaseCallbackThread mReleaseCallbackThread;
private:
+ // Get dynamic information about given physical display from token
+ static status_t getDynamicDisplayInfoFromToken(const sp<IBinder>& display,
+ ui::DynamicDisplayInfo*);
+
+ static void getDynamicDisplayInfoInternal(gui::DynamicDisplayInfo& ginfo,
+ ui::DynamicDisplayInfo*& outInfo);
virtual void onFirstRef();
mutable Mutex mLock;
@@ -819,17 +841,17 @@
void setReleaseBufferCallback(const ReleaseCallbackId&, ReleaseBufferCallback);
// BnTransactionCompletedListener overrides
- void onTransactionCompleted(ListenerStats stats) override;
- void onReleaseBuffer(ReleaseCallbackId, sp<Fence> releaseFence,
- uint32_t currentMaxAcquiredBufferCount) override;
+ binder::Status onTransactionCompleted(const ListenerStats& stats) override;
+ binder::Status onReleaseBuffer(const ReleaseCallbackId& callbackId,
+ const std::optional<os::ParcelFileDescriptor>& releaseFenceFd,
+ int32_t currentMaxAcquiredBufferCount) override;
+ binder::Status onTransactionQueueStalled(const std::string& reason) override;
void removeReleaseBufferCallback(const ReleaseCallbackId& callbackId);
// For Testing Only
static void setInstance(const sp<TransactionCompletedListener>&);
- void onTransactionQueueStalled(const String8& reason) override;
-
private:
ReleaseBufferCallback popReleaseBufferCallbackLocked(const ReleaseCallbackId&);
static sp<TransactionCompletedListener> sInstance;
diff --git a/libs/gui/tests/Surface_test.cpp b/libs/gui/tests/Surface_test.cpp
index 6d3b425..55242df 100644
--- a/libs/gui/tests/Surface_test.cpp
+++ b/libs/gui/tests/Surface_test.cpp
@@ -782,13 +782,18 @@
return binder::Status::ok();
}
- binder::Status getStaticDisplayInfo(const sp<IBinder>& /*display*/,
+ binder::Status getStaticDisplayInfo(int64_t /*displayId*/,
gui::StaticDisplayInfo* /*outInfo*/) override {
return binder::Status::ok();
}
- binder::Status getDynamicDisplayInfo(const sp<IBinder>& /*display*/,
- gui::DynamicDisplayInfo* /*outInfo*/) override {
+ binder::Status getDynamicDisplayInfoFromId(int64_t /*displayId*/,
+ gui::DynamicDisplayInfo* /*outInfo*/) override {
+ return binder::Status::ok();
+ }
+
+ binder::Status getDynamicDisplayInfoFromToken(const sp<IBinder>& /*display*/,
+ gui::DynamicDisplayInfo* /*outInfo*/) override {
return binder::Status::ok();
}
diff --git a/libs/input/Input.cpp b/libs/input/Input.cpp
index 3685f54..9e8ebf3 100644
--- a/libs/input/Input.cpp
+++ b/libs/input/Input.cpp
@@ -21,6 +21,7 @@
#include <cutils/compiler.h>
#include <inttypes.h>
#include <string.h>
+#include <optional>
#include <android-base/file.h>
#include <android-base/logging.h>
@@ -552,21 +553,21 @@
&pointerCoords[getPointerCount()]);
}
-int MotionEvent::getSurfaceRotation() const {
+std::optional<ui::Rotation> MotionEvent::getSurfaceRotation() const {
// The surface rotation is the rotation from the window's coordinate space to that of the
// display. Since the event's transform takes display space coordinates to window space, the
// returned surface rotation is the inverse of the rotation for the surface.
switch (mTransform.getOrientation()) {
case ui::Transform::ROT_0:
- return DISPLAY_ORIENTATION_0;
+ return ui::ROTATION_0;
case ui::Transform::ROT_90:
- return DISPLAY_ORIENTATION_270;
+ return ui::ROTATION_270;
case ui::Transform::ROT_180:
- return DISPLAY_ORIENTATION_180;
+ return ui::ROTATION_180;
case ui::Transform::ROT_270:
- return DISPLAY_ORIENTATION_90;
+ return ui::ROTATION_90;
default:
- return -1;
+ return std::nullopt;
}
}
diff --git a/libs/input/KeyCharacterMap.cpp b/libs/input/KeyCharacterMap.cpp
index 422e6e0..fa5c41f 100644
--- a/libs/input/KeyCharacterMap.cpp
+++ b/libs/input/KeyCharacterMap.cpp
@@ -43,7 +43,6 @@
// Enables debug output for mapping.
#define DEBUG_MAPPING 0
-
namespace android {
static const char* WHITESPACE = " \t\r";
@@ -93,6 +92,7 @@
: mType(other.mType),
mLoadFileName(other.mLoadFileName),
mLayoutOverlayApplied(other.mLayoutOverlayApplied),
+ mKeyRemapping(other.mKeyRemapping),
mKeysByScanCode(other.mKeysByScanCode),
mKeysByUsageCode(other.mKeysByUsageCode) {
for (size_t i = 0; i < other.mKeys.size(); i++) {
@@ -114,7 +114,7 @@
if (mLayoutOverlayApplied != other.mLayoutOverlayApplied) {
return false;
}
- if (mKeys.size() != other.mKeys.size() ||
+ if (mKeys.size() != other.mKeys.size() || mKeyRemapping.size() != other.mKeyRemapping.size() ||
mKeysByScanCode.size() != other.mKeysByScanCode.size() ||
mKeysByUsageCode.size() != other.mKeysByUsageCode.size()) {
return false;
@@ -131,22 +131,9 @@
}
}
- for (size_t i = 0; i < mKeysByScanCode.size(); i++) {
- if (mKeysByScanCode.keyAt(i) != other.mKeysByScanCode.keyAt(i)) {
- return false;
- }
- if (mKeysByScanCode.valueAt(i) != other.mKeysByScanCode.valueAt(i)) {
- return false;
- }
- }
-
- for (size_t i = 0; i < mKeysByUsageCode.size(); i++) {
- if (mKeysByUsageCode.keyAt(i) != other.mKeysByUsageCode.keyAt(i)) {
- return false;
- }
- if (mKeysByUsageCode.valueAt(i) != other.mKeysByUsageCode.valueAt(i)) {
- return false;
- }
+ if (mKeyRemapping != other.mKeyRemapping || mKeysByScanCode != other.mKeysByScanCode ||
+ mKeysByUsageCode != other.mKeysByUsageCode) {
+ return false;
}
return true;
@@ -258,14 +245,12 @@
}
}
- for (size_t i = 0; i < overlay.mKeysByScanCode.size(); i++) {
- mKeysByScanCode.replaceValueFor(overlay.mKeysByScanCode.keyAt(i),
- overlay.mKeysByScanCode.valueAt(i));
+ for (auto const& it : overlay.mKeysByScanCode) {
+ mKeysByScanCode.insert_or_assign(it.first, it.second);
}
- for (size_t i = 0; i < overlay.mKeysByUsageCode.size(); i++) {
- mKeysByUsageCode.replaceValueFor(overlay.mKeysByUsageCode.keyAt(i),
- overlay.mKeysByUsageCode.valueAt(i));
+ for (auto const& it : overlay.mKeysByUsageCode) {
+ mKeysByUsageCode.insert_or_assign(it.first, it.second);
}
mLayoutOverlayApplied = true;
}
@@ -400,11 +385,26 @@
return true;
}
+void KeyCharacterMap::addKeyRemapping(int32_t fromKeyCode, int32_t toKeyCode) {
+ if (fromKeyCode == toKeyCode) {
+ mKeyRemapping.erase(fromKeyCode);
+#if DEBUG_MAPPING
+ ALOGD("addKeyRemapping: Cleared remapping forKeyCode=%d ~ Result Successful.", fromKeyCode);
+#endif
+ return;
+ }
+ mKeyRemapping.insert_or_assign(fromKeyCode, toKeyCode);
+#if DEBUG_MAPPING
+ ALOGD("addKeyRemapping: fromKeyCode=%d, toKeyCode=%d ~ Result Successful.", fromKeyCode,
+ toKeyCode);
+#endif
+}
+
status_t KeyCharacterMap::mapKey(int32_t scanCode, int32_t usageCode, int32_t* outKeyCode) const {
if (usageCode) {
- ssize_t index = mKeysByUsageCode.indexOfKey(usageCode);
- if (index >= 0) {
- *outKeyCode = mKeysByUsageCode.valueAt(index);
+ const auto it = mKeysByUsageCode.find(usageCode);
+ if (it != mKeysByUsageCode.end()) {
+ *outKeyCode = it->second;
#if DEBUG_MAPPING
ALOGD("mapKey: scanCode=%d, usageCode=0x%08x ~ Result keyCode=%d.",
scanCode, usageCode, *outKeyCode);
@@ -413,9 +413,9 @@
}
}
if (scanCode) {
- ssize_t index = mKeysByScanCode.indexOfKey(scanCode);
- if (index >= 0) {
- *outKeyCode = mKeysByScanCode.valueAt(index);
+ const auto it = mKeysByScanCode.find(scanCode);
+ if (it != mKeysByScanCode.end()) {
+ *outKeyCode = it->second;
#if DEBUG_MAPPING
ALOGD("mapKey: scanCode=%d, usageCode=0x%08x ~ Result keyCode=%d.",
scanCode, usageCode, *outKeyCode);
@@ -431,45 +431,59 @@
return NAME_NOT_FOUND;
}
-void KeyCharacterMap::tryRemapKey(int32_t keyCode, int32_t metaState,
- int32_t *outKeyCode, int32_t *outMetaState) const {
- *outKeyCode = keyCode;
- *outMetaState = metaState;
+int32_t KeyCharacterMap::applyKeyRemapping(int32_t fromKeyCode) const {
+ int32_t toKeyCode = fromKeyCode;
- const Behavior* behavior = getKeyBehavior(keyCode, metaState);
+ const auto it = mKeyRemapping.find(fromKeyCode);
+ if (it != mKeyRemapping.end()) {
+ toKeyCode = it->second;
+ }
+#if DEBUG_MAPPING
+ ALOGD("applyKeyRemapping: keyCode=%d ~ replacement keyCode=%d.", fromKeyCode, toKeyCode);
+#endif
+ return toKeyCode;
+}
+
+std::pair<int32_t, int32_t> KeyCharacterMap::applyKeyBehavior(int32_t fromKeyCode,
+ int32_t fromMetaState) const {
+ int32_t toKeyCode = fromKeyCode;
+ int32_t toMetaState = fromMetaState;
+
+ const Behavior* behavior = getKeyBehavior(fromKeyCode, fromMetaState);
if (behavior != nullptr) {
if (behavior->replacementKeyCode) {
- *outKeyCode = behavior->replacementKeyCode;
- int32_t newMetaState = metaState & ~behavior->metaState;
+ toKeyCode = behavior->replacementKeyCode;
+ toMetaState = fromMetaState & ~behavior->metaState;
// Reset dependent meta states.
if (behavior->metaState & AMETA_ALT_ON) {
- newMetaState &= ~(AMETA_ALT_LEFT_ON | AMETA_ALT_RIGHT_ON);
+ toMetaState &= ~(AMETA_ALT_LEFT_ON | AMETA_ALT_RIGHT_ON);
}
if (behavior->metaState & (AMETA_ALT_LEFT_ON | AMETA_ALT_RIGHT_ON)) {
- newMetaState &= ~AMETA_ALT_ON;
+ toMetaState &= ~AMETA_ALT_ON;
}
if (behavior->metaState & AMETA_CTRL_ON) {
- newMetaState &= ~(AMETA_CTRL_LEFT_ON | AMETA_CTRL_RIGHT_ON);
+ toMetaState &= ~(AMETA_CTRL_LEFT_ON | AMETA_CTRL_RIGHT_ON);
}
if (behavior->metaState & (AMETA_CTRL_LEFT_ON | AMETA_CTRL_RIGHT_ON)) {
- newMetaState &= ~AMETA_CTRL_ON;
+ toMetaState &= ~AMETA_CTRL_ON;
}
if (behavior->metaState & AMETA_SHIFT_ON) {
- newMetaState &= ~(AMETA_SHIFT_LEFT_ON | AMETA_SHIFT_RIGHT_ON);
+ toMetaState &= ~(AMETA_SHIFT_LEFT_ON | AMETA_SHIFT_RIGHT_ON);
}
if (behavior->metaState & (AMETA_SHIFT_LEFT_ON | AMETA_SHIFT_RIGHT_ON)) {
- newMetaState &= ~AMETA_SHIFT_ON;
+ toMetaState &= ~AMETA_SHIFT_ON;
}
// ... and put universal bits back if needed
- *outMetaState = normalizeMetaState(newMetaState);
+ toMetaState = normalizeMetaState(toMetaState);
}
}
#if DEBUG_MAPPING
- ALOGD("tryRemapKey: keyCode=%d, metaState=0x%08x ~ "
- "replacement keyCode=%d, replacement metaState=0x%08x.",
- keyCode, metaState, *outKeyCode, *outMetaState);
+ ALOGD("applyKeyBehavior: keyCode=%d, metaState=0x%08x ~ "
+ "replacement keyCode=%d, replacement metaState=0x%08x.",
+ fromKeyCode, fromMetaState, toKeyCode, toMetaState);
#endif
+ return std::make_pair(toKeyCode, toMetaState);
}
bool KeyCharacterMap::getKey(int32_t keyCode, const Key** outKey) const {
@@ -720,6 +734,18 @@
return nullptr;
}
}
+ size_t numKeyRemapping = parcel->readInt32();
+ if (parcel->errorCheck()) {
+ return nullptr;
+ }
+ for (size_t i = 0; i < numKeyRemapping; i++) {
+ int32_t key = parcel->readInt32();
+ int32_t value = parcel->readInt32();
+ map->mKeyRemapping.insert_or_assign(key, value);
+ if (parcel->errorCheck()) {
+ return nullptr;
+ }
+ }
size_t numKeysByScanCode = parcel->readInt32();
if (parcel->errorCheck()) {
return nullptr;
@@ -727,7 +753,7 @@
for (size_t i = 0; i < numKeysByScanCode; i++) {
int32_t key = parcel->readInt32();
int32_t value = parcel->readInt32();
- map->mKeysByScanCode.add(key, value);
+ map->mKeysByScanCode.insert_or_assign(key, value);
if (parcel->errorCheck()) {
return nullptr;
}
@@ -739,7 +765,7 @@
for (size_t i = 0; i < numKeysByUsageCode; i++) {
int32_t key = parcel->readInt32();
int32_t value = parcel->readInt32();
- map->mKeysByUsageCode.add(key, value);
+ map->mKeysByUsageCode.insert_or_assign(key, value);
if (parcel->errorCheck()) {
return nullptr;
}
@@ -773,17 +799,23 @@
}
parcel->writeInt32(0);
}
+ size_t numKeyRemapping = mKeyRemapping.size();
+ parcel->writeInt32(numKeyRemapping);
+ for (auto const& [fromAndroidKeyCode, toAndroidKeyCode] : mKeyRemapping) {
+ parcel->writeInt32(fromAndroidKeyCode);
+ parcel->writeInt32(toAndroidKeyCode);
+ }
size_t numKeysByScanCode = mKeysByScanCode.size();
parcel->writeInt32(numKeysByScanCode);
- for (size_t i = 0; i < numKeysByScanCode; i++) {
- parcel->writeInt32(mKeysByScanCode.keyAt(i));
- parcel->writeInt32(mKeysByScanCode.valueAt(i));
+ for (auto const& [fromScanCode, toAndroidKeyCode] : mKeysByScanCode) {
+ parcel->writeInt32(fromScanCode);
+ parcel->writeInt32(toAndroidKeyCode);
}
size_t numKeysByUsageCode = mKeysByUsageCode.size();
parcel->writeInt32(numKeysByUsageCode);
- for (size_t i = 0; i < numKeysByUsageCode; i++) {
- parcel->writeInt32(mKeysByUsageCode.keyAt(i));
- parcel->writeInt32(mKeysByUsageCode.valueAt(i));
+ for (auto const& [fromUsageCode, toAndroidKeyCode] : mKeysByUsageCode) {
+ parcel->writeInt32(fromUsageCode);
+ parcel->writeInt32(toAndroidKeyCode);
}
}
#endif // __linux__
@@ -950,9 +982,9 @@
mapUsage ? "usage" : "scan code", codeToken.string());
return BAD_VALUE;
}
- KeyedVector<int32_t, int32_t>& map =
- mapUsage ? mMap->mKeysByUsageCode : mMap->mKeysByScanCode;
- if (map.indexOfKey(code) >= 0) {
+ std::map<int32_t, int32_t>& map = mapUsage ? mMap->mKeysByUsageCode : mMap->mKeysByScanCode;
+ const auto it = map.find(code);
+ if (it != map.end()) {
ALOGE("%s: Duplicate entry for key %s '%s'.", mTokenizer->getLocation().string(),
mapUsage ? "usage" : "scan code", codeToken.string());
return BAD_VALUE;
@@ -971,7 +1003,7 @@
ALOGD("Parsed map key %s: code=%d, keyCode=%d.",
mapUsage ? "usage" : "scan code", code, keyCode);
#endif
- map.add(code, keyCode);
+ map.insert_or_assign(code, keyCode);
return NO_ERROR;
}
diff --git a/libs/input/TouchVideoFrame.cpp b/libs/input/TouchVideoFrame.cpp
index c62e098..c9393f4 100644
--- a/libs/input/TouchVideoFrame.cpp
+++ b/libs/input/TouchVideoFrame.cpp
@@ -40,17 +40,20 @@
const struct timeval& TouchVideoFrame::getTimestamp() const { return mTimestamp; }
-void TouchVideoFrame::rotate(int32_t orientation) {
+void TouchVideoFrame::rotate(ui::Rotation orientation) {
switch (orientation) {
- case DISPLAY_ORIENTATION_90:
+ case ui::ROTATION_90:
rotateQuarterTurn(false /*clockwise*/);
break;
- case DISPLAY_ORIENTATION_180:
+ case ui::ROTATION_180:
rotate180();
break;
- case DISPLAY_ORIENTATION_270:
+ case ui::ROTATION_270:
rotateQuarterTurn(true /*clockwise*/);
break;
+ case ui::ROTATION_0:
+ // No need to rotate if there's no rotation.
+ break;
}
}
diff --git a/libs/input/tests/TouchVideoFrame_test.cpp b/libs/input/tests/TouchVideoFrame_test.cpp
index 654b236..081a995 100644
--- a/libs/input/tests/TouchVideoFrame_test.cpp
+++ b/libs/input/tests/TouchVideoFrame_test.cpp
@@ -73,38 +73,38 @@
TEST(TouchVideoFrame, Rotate90_0x0) {
TouchVideoFrame frame(0, 0, {}, TIMESTAMP);
TouchVideoFrame frameRotated(0, 0, {}, TIMESTAMP);
- frame.rotate(DISPLAY_ORIENTATION_90);
+ frame.rotate(ui::ROTATION_90);
ASSERT_EQ(frame, frameRotated);
}
TEST(TouchVideoFrame, Rotate90_1x1) {
TouchVideoFrame frame(1, 1, {1}, TIMESTAMP);
TouchVideoFrame frameRotated(1, 1, {1}, TIMESTAMP);
- frame.rotate(DISPLAY_ORIENTATION_90);
+ frame.rotate(ui::ROTATION_90);
ASSERT_EQ(frame, frameRotated);
}
TEST(TouchVideoFrame, Rotate90_2x2) {
TouchVideoFrame frame(2, 2, {1, 2, 3, 4}, TIMESTAMP);
TouchVideoFrame frameRotated(2, 2, {2, 4, 1, 3}, TIMESTAMP);
- frame.rotate(DISPLAY_ORIENTATION_90);
+ frame.rotate(ui::ROTATION_90);
ASSERT_EQ(frame, frameRotated);
}
TEST(TouchVideoFrame, Rotate90_3x2) {
TouchVideoFrame frame(3, 2, {1, 2, 3, 4, 5, 6}, TIMESTAMP);
TouchVideoFrame frameRotated(2, 3, {2, 4, 6, 1, 3, 5}, TIMESTAMP);
- frame.rotate(DISPLAY_ORIENTATION_90);
+ frame.rotate(ui::ROTATION_90);
ASSERT_EQ(frame, frameRotated);
}
TEST(TouchVideoFrame, Rotate90_3x2_4times) {
TouchVideoFrame frame(3, 2, {1, 2, 3, 4, 5, 6}, TIMESTAMP);
TouchVideoFrame frameOriginal(3, 2, {1, 2, 3, 4, 5, 6}, TIMESTAMP);
- frame.rotate(DISPLAY_ORIENTATION_90);
- frame.rotate(DISPLAY_ORIENTATION_90);
- frame.rotate(DISPLAY_ORIENTATION_90);
- frame.rotate(DISPLAY_ORIENTATION_90);
+ frame.rotate(ui::ROTATION_90);
+ frame.rotate(ui::ROTATION_90);
+ frame.rotate(ui::ROTATION_90);
+ frame.rotate(ui::ROTATION_90);
ASSERT_EQ(frame, frameOriginal);
}
@@ -113,43 +113,43 @@
TEST(TouchVideoFrame, Rotate180_0x0) {
TouchVideoFrame frame(0, 0, {}, TIMESTAMP);
TouchVideoFrame frameRotated(0, 0, {}, TIMESTAMP);
- frame.rotate(DISPLAY_ORIENTATION_180);
+ frame.rotate(ui::ROTATION_180);
ASSERT_EQ(frame, frameRotated);
}
TEST(TouchVideoFrame, Rotate180_1x1) {
TouchVideoFrame frame(1, 1, {1}, TIMESTAMP);
TouchVideoFrame frameRotated(1, 1, {1}, TIMESTAMP);
- frame.rotate(DISPLAY_ORIENTATION_180);
+ frame.rotate(ui::ROTATION_180);
ASSERT_EQ(frame, frameRotated);
}
TEST(TouchVideoFrame, Rotate180_2x2) {
TouchVideoFrame frame(2, 2, {1, 2, 3, 4}, TIMESTAMP);
TouchVideoFrame frameRotated(2, 2, {4, 3, 2, 1}, TIMESTAMP);
- frame.rotate(DISPLAY_ORIENTATION_180);
+ frame.rotate(ui::ROTATION_180);
ASSERT_EQ(frame, frameRotated);
}
TEST(TouchVideoFrame, Rotate180_3x2) {
TouchVideoFrame frame(3, 2, {1, 2, 3, 4, 5, 6}, TIMESTAMP);
TouchVideoFrame frameRotated(3, 2, {6, 5, 4, 3, 2, 1}, TIMESTAMP);
- frame.rotate(DISPLAY_ORIENTATION_180);
+ frame.rotate(ui::ROTATION_180);
ASSERT_EQ(frame, frameRotated);
}
TEST(TouchVideoFrame, Rotate180_3x2_2times) {
TouchVideoFrame frame(3, 2, {1, 2, 3, 4, 5, 6}, TIMESTAMP);
TouchVideoFrame frameOriginal(3, 2, {1, 2, 3, 4, 5, 6}, TIMESTAMP);
- frame.rotate(DISPLAY_ORIENTATION_180);
- frame.rotate(DISPLAY_ORIENTATION_180);
+ frame.rotate(ui::ROTATION_180);
+ frame.rotate(ui::ROTATION_180);
ASSERT_EQ(frame, frameOriginal);
}
TEST(TouchVideoFrame, Rotate180_3x3) {
TouchVideoFrame frame(3, 3, {1, 2, 3, 4, 5, 6, 7, 8, 9}, TIMESTAMP);
TouchVideoFrame frameRotated(3, 3, {9, 8, 7, 6, 5, 4, 3, 2, 1}, TIMESTAMP);
- frame.rotate(DISPLAY_ORIENTATION_180);
+ frame.rotate(ui::ROTATION_180);
ASSERT_EQ(frame, frameRotated);
}
@@ -158,38 +158,38 @@
TEST(TouchVideoFrame, Rotate270_0x0) {
TouchVideoFrame frame(0, 0, {}, TIMESTAMP);
TouchVideoFrame frameRotated(0, 0, {}, TIMESTAMP);
- frame.rotate(DISPLAY_ORIENTATION_270);
+ frame.rotate(ui::ROTATION_270);
ASSERT_EQ(frame, frameRotated);
}
TEST(TouchVideoFrame, Rotate270_1x1) {
TouchVideoFrame frame(1, 1, {1}, TIMESTAMP);
TouchVideoFrame frameRotated(1, 1, {1}, TIMESTAMP);
- frame.rotate(DISPLAY_ORIENTATION_270);
+ frame.rotate(ui::ROTATION_270);
ASSERT_EQ(frame, frameRotated);
}
TEST(TouchVideoFrame, Rotate270_2x2) {
TouchVideoFrame frame(2, 2, {1, 2, 3, 4}, TIMESTAMP);
TouchVideoFrame frameRotated(2, 2, {3, 1, 4, 2}, TIMESTAMP);
- frame.rotate(DISPLAY_ORIENTATION_270);
+ frame.rotate(ui::ROTATION_270);
ASSERT_EQ(frame, frameRotated);
}
TEST(TouchVideoFrame, Rotate270_3x2) {
TouchVideoFrame frame(3, 2, {1, 2, 3, 4, 5, 6}, TIMESTAMP);
TouchVideoFrame frameRotated(2, 3, {5, 3, 1, 6, 4, 2}, TIMESTAMP);
- frame.rotate(DISPLAY_ORIENTATION_270);
+ frame.rotate(ui::ROTATION_270);
ASSERT_EQ(frame, frameRotated);
}
TEST(TouchVideoFrame, Rotate270_3x2_4times) {
TouchVideoFrame frame(3, 2, {1, 2, 3, 4, 5, 6}, TIMESTAMP);
TouchVideoFrame frameOriginal(3, 2, {1, 2, 3, 4, 5, 6}, TIMESTAMP);
- frame.rotate(DISPLAY_ORIENTATION_270);
- frame.rotate(DISPLAY_ORIENTATION_270);
- frame.rotate(DISPLAY_ORIENTATION_270);
- frame.rotate(DISPLAY_ORIENTATION_270);
+ frame.rotate(ui::ROTATION_270);
+ frame.rotate(ui::ROTATION_270);
+ frame.rotate(ui::ROTATION_270);
+ frame.rotate(ui::ROTATION_270);
ASSERT_EQ(frame, frameOriginal);
}
diff --git a/libs/jpegrecoverymap/include/jpegrecoverymap/jpegrerrorcode.h b/libs/jpegrecoverymap/include/jpegrecoverymap/jpegrerrorcode.h
index 9f53a57..6995762 100644
--- a/libs/jpegrecoverymap/include/jpegrecoverymap/jpegrerrorcode.h
+++ b/libs/jpegrecoverymap/include/jpegrecoverymap/jpegrerrorcode.h
@@ -44,6 +44,7 @@
ERROR_JPEGR_DECODE_ERROR = JPEGR_RUNTIME_ERROR_BASE - 2,
ERROR_JPEGR_CALCULATION_ERROR = JPEGR_RUNTIME_ERROR_BASE - 3,
ERROR_JPEGR_METADATA_ERROR = JPEGR_RUNTIME_ERROR_BASE - 4,
+ ERROR_JPEGR_TONEMAP_ERROR = JPEGR_RUNTIME_ERROR_BASE - 5,
};
} // namespace android::recoverymap
diff --git a/libs/jpegrecoverymap/include/jpegrecoverymap/recoverymap.h b/libs/jpegrecoverymap/include/jpegrecoverymap/recoverymap.h
index 5597303..fca78a0 100644
--- a/libs/jpegrecoverymap/include/jpegrecoverymap/recoverymap.h
+++ b/libs/jpegrecoverymap/include/jpegrecoverymap/recoverymap.h
@@ -30,8 +30,9 @@
// Transfer functions as defined for XMP metadata
typedef enum {
- JPEGR_TF_HLG = 0,
- JPEGR_TF_PQ = 1,
+ JPEGR_TF_LINEAR = 0,
+ JPEGR_TF_HLG = 1,
+ JPEGR_TF_PQ = 2,
} jpegr_transfer_function;
struct jpegr_info_struct {
@@ -129,6 +130,28 @@
class RecoveryMap {
public:
/*
+ * Encode API-0
+ * Compress JPEGR image from 10-bit HDR YUV.
+ *
+ * Tonemap the HDR input to a SDR image, generate recovery map from the HDR and SDR images,
+ * compress SDR YUV to 8-bit JPEG and append the recovery map to the end of the compressed
+ * JPEG.
+ * @param uncompressed_p010_image uncompressed HDR image in P010 color format
+ * @param hdr_tf transfer function of the HDR image
+ * @param dest destination of the compressed JPEGR image
+ * @param quality target quality of the JPEG encoding, must be in range of 0-100 where 100 is
+ * the highest quality
+ * @param exif pointer to the exif metadata.
+ * @return NO_ERROR if encoding succeeds, error code if error occurs.
+ */
+ status_t encodeJPEGR(jr_uncompressed_ptr uncompressed_p010_image,
+ jpegr_transfer_function hdr_tf,
+ jr_compressed_ptr dest,
+ int quality,
+ jr_exif_ptr exif);
+
+ /*
+ * Encode API-1
* Compress JPEGR image from 10-bit HDR YUV and 8-bit SDR YUV.
*
* Generate recovery map from the HDR and SDR inputs, compress SDR YUV to 8-bit JPEG and append
@@ -151,6 +174,7 @@
jr_exif_ptr exif);
/*
+ * Encode API-2
* Compress JPEGR image from 10-bit HDR YUV, 8-bit SDR YUV and compressed 8-bit JPEG.
*
* This method requires HAL Hardware JPEG encoder.
@@ -159,6 +183,8 @@
* compressed JPEG. HDR and SDR inputs must be the same resolution and color space.
* @param uncompressed_p010_image uncompressed HDR image in P010 color format
* @param uncompressed_yuv_420_image uncompressed SDR image in YUV_420 color format
+ * Note: the SDR image must be the decoded version of the JPEG
+ * input
* @param compressed_jpeg_image compressed 8-bit JPEG image
* @param hdr_tf transfer function of the HDR image
* @param dest destination of the compressed JPEGR image
@@ -171,6 +197,7 @@
jr_compressed_ptr dest);
/*
+ * Encode API-3
* Compress JPEGR image from 10-bit HDR YUV and 8-bit SDR YUV.
*
* This method requires HAL Hardware JPEG encoder.
@@ -190,6 +217,7 @@
jr_compressed_ptr dest);
/*
+ * Decode API
* Decompress JPEGR image.
*
* The output JPEGR image is in RGBA_1010102 data format if decoding to HDR.
@@ -317,45 +345,14 @@
jr_compressed_ptr dest);
/*
- * This method generates XMP metadata.
+ * This method will tone map a HDR image to an SDR image.
*
- * below is an example of the XMP metadata that this function generates where
- * secondary_image_length = 1000
- * range_scaling_factor = 1.25
- *
- * <x:xmpmeta
- * xmlns:x="adobe:ns:meta/"
- * x:xmptk="Adobe XMP Core 5.1.2">
- * <rdf:RDF
- * xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#">
- * <rdf:Description
- * xmlns:GContainer="http://ns.google.com/photos/1.0/container/">
- * <GContainer:Version>1</GContainer:Version>
- * <GContainer:RangeScalingFactor>1.25</GContainer:RangeScalingFactor>
- * <GContainer:Directory>
- * <rdf:Seq>
- * <rdf:li>
- * <GContainer:Item
- * Item:Semantic="Primary"
- * Item:Mime="image/jpeg"/>
- * </rdf:li>
- * <rdf:li>
- * <GContainer:Item
- * Item:Semantic="RecoveryMap"
- * Item:Mime="image/jpeg"
- * Item:Length="1000"/>
- * </rdf:li>
- * </rdf:Seq>
- * </GContainer:Directory>
- * </rdf:Description>
- * </rdf:RDF>
- * </x:xmpmeta>
- *
- * @param secondary_image_length length of secondary image
- * @param metadata JPEG/R metadata to encode as XMP
- * @return XMP metadata in type of string
+ * @param uncompressed_p010_image (input) uncompressed P010 image
+ * @param dest (output) tone mapping result as a YUV_420 image
+ * @return NO_ERROR if calculation succeeds, error code if error occurs.
*/
- std::string generateXmp(int secondary_image_length, jpegr_metadata& metadata);
+ status_t toneMap(jr_uncompressed_ptr uncompressed_p010_image,
+ jr_uncompressed_ptr dest);
};
} // namespace android::recoverymap
diff --git a/libs/jpegrecoverymap/include/jpegrecoverymap/recoverymaputils.h b/libs/jpegrecoverymap/include/jpegrecoverymap/recoverymaputils.h
index e35f2d7..e61d0c4 100644
--- a/libs/jpegrecoverymap/include/jpegrecoverymap/recoverymaputils.h
+++ b/libs/jpegrecoverymap/include/jpegrecoverymap/recoverymaputils.h
@@ -17,10 +17,11 @@
#ifndef ANDROID_JPEGRECOVERYMAP_RECOVERYMAPUTILS_H
#define ANDROID_JPEGRECOVERYMAP_RECOVERYMAPUTILS_H
+#include <sstream>
#include <stdint.h>
+#include <string>
#include <cstdio>
-
namespace android::recoverymap {
struct jpegr_metadata;
@@ -35,6 +36,53 @@
*/
bool getMetadataFromXMP(uint8_t* xmp_data, size_t xmp_size, jpegr_metadata* metadata);
+/*
+ * This method generates XMP metadata.
+ *
+ * below is an example of the XMP metadata that this function generates where
+ * secondary_image_length = 1000
+ * range_scaling_factor = 1.25
+ *
+ * <x:xmpmeta
+ * xmlns:x="adobe:ns:meta/"
+ * x:xmptk="Adobe XMP Core 5.1.2">
+ * <rdf:RDF
+ * xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#">
+ * <rdf:Description
+ * xmlns:GContainer="http://ns.google.com/photos/1.0/container/"
+ * xmlns:RecoveryMap="http://ns.google.com/photos/1.0/recoverymap/">
+ * <GContainer:Version>1</GContainer:Version>
+ * <GContainer:Directory>
+ * <rdf:Seq>
+ * <rdf:li>
+ * <GContainer:Item
+ * Item:Semantic="Primary"
+ * Item:Mime="image/jpeg"
+ * RecoveryMap:Version=”1”
+ * RecoveryMap:RangeScalingFactor=”1.25”
+ * RecoveryMap:TransferFunction=”2”/>
+ * <RecoveryMap:HDR10Metadata
+ * // some attributes
+ * // some elements
+ * </RecoveryMap:HDR10Metadata>
+ * </rdf:li>
+ * <rdf:li>
+ * <GContainer:Item
+ * Item:Semantic="RecoveryMap"
+ * Item:Mime="image/jpeg"
+ * Item:Length="1000"/>
+ * </rdf:li>
+ * </rdf:Seq>
+ * </GContainer:Directory>
+ * </rdf:Description>
+ * </rdf:RDF>
+ * </x:xmpmeta>
+ *
+ * @param secondary_image_length length of secondary image
+ * @param metadata JPEG/R metadata to encode as XMP
+ * @return XMP metadata in type of string
+ */
+std::string generateXmp(int secondary_image_length, jpegr_metadata& metadata);
}
#endif //ANDROID_JPEGRECOVERYMAP_RECOVERYMAPUTILS_H
\ No newline at end of file
diff --git a/libs/jpegrecoverymap/recoverymap.cpp b/libs/jpegrecoverymap/recoverymap.cpp
index f7f3622..3d713ce 100644
--- a/libs/jpegrecoverymap/recoverymap.cpp
+++ b/libs/jpegrecoverymap/recoverymap.cpp
@@ -21,7 +21,6 @@
#include <jpegrecoverymap/recoverymaputils.h>
#include <image_io/jpeg/jpeg_marker.h>
-#include <image_io/xml/xml_writer.h>
#include <image_io/jpeg/jpeg_info.h>
#include <image_io/jpeg/jpeg_scanner.h>
#include <image_io/jpeg/jpeg_info_builder.h>
@@ -65,19 +64,6 @@
};
/*
- * Helper function used for generating XMP metadata.
- *
- * @param prefix The prefix part of the name.
- * @param suffix The suffix part of the name.
- * @return A name of the form "prefix:suffix".
- */
-string Name(const string &prefix, const string &suffix) {
- std::stringstream ss;
- ss << prefix << ":" << suffix;
- return ss.str();
-}
-
-/*
* Helper function used for writing data to destination.
*
* @param destination destination of the data to be written.
@@ -96,6 +82,59 @@
return NO_ERROR;
}
+/* Encode API-0 */
+status_t RecoveryMap::encodeJPEGR(jr_uncompressed_ptr uncompressed_p010_image,
+ jpegr_transfer_function hdr_tf,
+ jr_compressed_ptr dest,
+ int quality,
+ jr_exif_ptr /* exif */) {
+ if (uncompressed_p010_image == nullptr || dest == nullptr) {
+ return ERROR_JPEGR_INVALID_NULL_PTR;
+ }
+
+ if (quality < 0 || quality > 100) {
+ return ERROR_JPEGR_INVALID_INPUT_TYPE;
+ }
+
+ jpegr_metadata metadata;
+ metadata.version = kJpegrVersion;
+ metadata.transferFunction = hdr_tf;
+ if (hdr_tf == JPEGR_TF_PQ) {
+ metadata.hdr10Metadata.st2086Metadata = kSt2086Metadata;
+ }
+
+ jpegr_uncompressed_struct uncompressed_yuv_420_image;
+ JPEGR_CHECK(toneMap(uncompressed_p010_image, &uncompressed_yuv_420_image));
+
+ jpegr_uncompressed_struct map;
+ JPEGR_CHECK(generateRecoveryMap(
+ &uncompressed_yuv_420_image, uncompressed_p010_image, &metadata, &map));
+ std::unique_ptr<uint8_t[]> map_data;
+ map_data.reset(reinterpret_cast<uint8_t*>(map.data));
+
+ jpegr_compressed_struct compressed_map;
+ compressed_map.maxLength = map.width * map.height;
+ unique_ptr<uint8_t[]> compressed_map_data = make_unique<uint8_t[]>(compressed_map.maxLength);
+ compressed_map.data = compressed_map_data.get();
+ JPEGR_CHECK(compressRecoveryMap(&map, &compressed_map));
+
+ JpegEncoder jpeg_encoder;
+ // TODO: determine ICC data based on color gamut information
+ if (!jpeg_encoder.compressImage(uncompressed_yuv_420_image.data,
+ uncompressed_yuv_420_image.width,
+ uncompressed_yuv_420_image.height, quality, nullptr, 0)) {
+ return ERROR_JPEGR_ENCODE_ERROR;
+ }
+ jpegr_compressed_struct jpeg;
+ jpeg.data = jpeg_encoder.getCompressedImagePtr();
+ jpeg.length = jpeg_encoder.getCompressedImageSize();
+
+ JPEGR_CHECK(appendRecoveryMap(&jpeg, &compressed_map, &metadata, dest));
+
+ return NO_ERROR;
+}
+
+/* Encode API-1 */
status_t RecoveryMap::encodeJPEGR(jr_uncompressed_ptr uncompressed_p010_image,
jr_uncompressed_ptr uncompressed_yuv_420_image,
jpegr_transfer_function hdr_tf,
@@ -152,6 +191,7 @@
return NO_ERROR;
}
+/* Encode API-2 */
status_t RecoveryMap::encodeJPEGR(jr_uncompressed_ptr uncompressed_p010_image,
jr_uncompressed_ptr uncompressed_yuv_420_image,
jr_compressed_ptr compressed_jpeg_image,
@@ -193,6 +233,7 @@
return NO_ERROR;
}
+/* Encode API-3 */
status_t RecoveryMap::encodeJPEGR(jr_uncompressed_ptr uncompressed_p010_image,
jr_compressed_ptr compressed_jpeg_image,
jpegr_transfer_function hdr_tf,
@@ -262,7 +303,7 @@
return NO_ERROR;
}
-
+/* Decode API */
status_t RecoveryMap::decodeJPEGR(jr_compressed_ptr compressed_jpegr_image,
jr_uncompressed_ptr dest,
jr_exif_ptr exif,
@@ -392,6 +433,9 @@
ColorTransformFn hdrInvOetf = nullptr;
float hdr_white_nits = 0.0f;
switch (metadata->transferFunction) {
+ case JPEGR_TF_LINEAR:
+ hdrInvOetf = identityConversion;
+ break;
case JPEGR_TF_HLG:
hdrInvOetf = hlgInvOetf;
hdr_white_nits = kHlgMaxNits;
@@ -489,6 +533,9 @@
ColorTransformFn hdrOetf = nullptr;
switch (metadata->transferFunction) {
+ case JPEGR_TF_LINEAR:
+ hdrOetf = identityConversion;
+ break;
case JPEGR_TF_HLG:
hdrOetf = hlgOetf;
break;
@@ -625,55 +672,20 @@
return NO_ERROR;
}
-string RecoveryMap::generateXmp(int secondary_image_length, jpegr_metadata& metadata) {
- const string kContainerPrefix = "GContainer";
- const string kContainerUri = "http://ns.google.com/photos/1.0/container/";
- const string kItemPrefix = "Item";
- const string kRecoveryMap = "RecoveryMap";
- const string kDirectory = "Directory";
- const string kImageJpeg = "image/jpeg";
- const string kItem = "Item";
- const string kLength = "Length";
- const string kMime = "Mime";
- const string kPrimary = "Primary";
- const string kSemantic = "Semantic";
- const string kVersion = "Version";
+status_t RecoveryMap::toneMap(jr_uncompressed_ptr uncompressed_p010_image,
+ jr_uncompressed_ptr dest) {
+ if (uncompressed_p010_image == nullptr || dest == nullptr) {
+ return ERROR_JPEGR_INVALID_NULL_PTR;
+ }
- const string kConDir = Name(kContainerPrefix, kDirectory);
- const string kContainerItem = Name(kContainerPrefix, kItem);
- const string kItemLength = Name(kItemPrefix, kLength);
- const string kItemMime = Name(kItemPrefix, kMime);
- const string kItemSemantic = Name(kItemPrefix, kSemantic);
+ dest->width = uncompressed_p010_image->width;
+ dest->height = uncompressed_p010_image->height;
+ unique_ptr<uint8_t[]> dest_data = make_unique<uint8_t[]>(dest->width * dest->height * 3 / 2);
+ dest->data = dest_data.get();
- const vector<string> kConDirSeq({kConDir, string("rdf:Seq")});
- const vector<string> kLiItem({string("rdf:li"), kContainerItem});
+ // TODO: Tone map algorighm here.
- std::stringstream ss;
- photos_editing_formats::image_io::XmlWriter writer(ss);
- writer.StartWritingElement("x:xmpmeta");
- writer.WriteXmlns("x", "adobe:ns:meta/");
- writer.WriteAttributeNameAndValue("x:xmptk", "Adobe XMP Core 5.1.2");
- writer.StartWritingElement("rdf:RDF");
- writer.WriteXmlns("rdf", "http://www.w3.org/1999/02/22-rdf-syntax-ns#");
- writer.StartWritingElement("rdf:Description");
- writer.WriteXmlns(kContainerPrefix, kContainerUri);
- writer.WriteElementAndContent(Name(kContainerPrefix, kVersion), metadata.version);
- writer.WriteElementAndContent(Name(kContainerPrefix, "rangeScalingFactor"),
- metadata.rangeScalingFactor);
- // TODO: determine structure for hdr10 metadata
- // TODO: write rest of metadata
- writer.StartWritingElements(kConDirSeq);
- size_t item_depth = writer.StartWritingElements(kLiItem);
- writer.WriteAttributeNameAndValue(kItemSemantic, kPrimary);
- writer.WriteAttributeNameAndValue(kItemMime, kImageJpeg);
- writer.FinishWritingElementsToDepth(item_depth);
- writer.StartWritingElements(kLiItem);
- writer.WriteAttributeNameAndValue(kItemSemantic, kRecoveryMap);
- writer.WriteAttributeNameAndValue(kItemMime, kImageJpeg);
- writer.WriteAttributeNameAndValue(kItemLength, secondary_image_length);
- writer.FinishWriting();
-
- return ss.str();
+ return NO_ERROR;
}
} // namespace android::recoverymap
diff --git a/libs/jpegrecoverymap/recoverymaputils.cpp b/libs/jpegrecoverymap/recoverymaputils.cpp
index fe46cba..63b25f7 100644
--- a/libs/jpegrecoverymap/recoverymaputils.cpp
+++ b/libs/jpegrecoverymap/recoverymaputils.cpp
@@ -17,26 +17,36 @@
#include <jpegrecoverymap/recoverymaputils.h>
#include <jpegrecoverymap/recoverymap.h>
#include <image_io/xml/xml_reader.h>
+#include <image_io/xml/xml_writer.h>
#include <image_io/base/message_handler.h>
#include <image_io/xml/xml_element_rules.h>
#include <image_io/xml/xml_handler.h>
#include <image_io/xml/xml_rule.h>
-#include <string>
-#include <sstream>
-
using namespace photos_editing_formats::image_io;
using namespace std;
namespace android::recoverymap {
+/*
+ * Helper function used for generating XMP metadata.
+ *
+ * @param prefix The prefix part of the name.
+ * @param suffix The suffix part of the name.
+ * @return A name of the form "prefix:suffix".
+ */
+string Name(const string &prefix, const string &suffix) {
+ std::stringstream ss;
+ ss << prefix << ":" << suffix;
+ return ss.str();
+}
// Extremely simple XML Handler - just searches for interesting elements
class XMPXmlHandler : public XmlHandler {
public:
XMPXmlHandler() : XmlHandler() {
- rangeScalingFactorState = NotStrarted;
+ gContainerItemState = NotStrarted;
}
enum ParseState {
@@ -48,11 +58,11 @@
virtual DataMatchResult StartElement(const XmlTokenContext& context) {
string val;
if (context.BuildTokenValue(&val)) {
- if (!val.compare(rangeScalingFactorName)) {
- rangeScalingFactorState = Started;
+ if (!val.compare(gContainerItemName)) {
+ gContainerItemState = Started;
} else {
- if (rangeScalingFactorState != Done) {
- rangeScalingFactorState = NotStrarted;
+ if (gContainerItemState != Done) {
+ gContainerItemState = NotStrarted;
}
}
}
@@ -60,24 +70,45 @@
}
virtual DataMatchResult FinishElement(const XmlTokenContext& context) {
- if (rangeScalingFactorState == Started) {
- rangeScalingFactorState = Done;
+ if (gContainerItemState == Started) {
+ gContainerItemState = Done;
+ lastAttributeName = "";
}
return context.GetResult();
}
- virtual DataMatchResult ElementContent(const XmlTokenContext& context) {
+ virtual DataMatchResult AttributeName(const XmlTokenContext& context) {
string val;
- if (rangeScalingFactorState == Started) {
+ if (gContainerItemState == Started) {
if (context.BuildTokenValue(&val)) {
- rangeScalingFactorStr.assign(val);
+ if (!val.compare(rangeScalingFactorAttrName)) {
+ lastAttributeName = rangeScalingFactorAttrName;
+ } else if (!val.compare(transferFunctionAttrName)) {
+ lastAttributeName = transferFunctionAttrName;
+ } else {
+ lastAttributeName = "";
+ }
+ }
+ }
+ return context.GetResult();
+ }
+
+ virtual DataMatchResult AttributeValue(const XmlTokenContext& context) {
+ string val;
+ if (gContainerItemState == Started) {
+ if (context.BuildTokenValue(&val, true)) {
+ if (!lastAttributeName.compare(rangeScalingFactorAttrName)) {
+ rangeScalingFactorStr = val;
+ } else if (!lastAttributeName.compare(transferFunctionAttrName)) {
+ transferFunctionStr = val;
+ }
}
}
return context.GetResult();
}
bool getRangeScalingFactor(float* scaling_factor) {
- if (rangeScalingFactorState == Done) {
+ if (gContainerItemState == Done) {
stringstream ss(rangeScalingFactorStr);
float val;
if (ss >> val) {
@@ -92,19 +123,67 @@
}
bool getTransferFunction(jpegr_transfer_function* transfer_function) {
- *transfer_function = JPEGR_TF_HLG;
+ if (gContainerItemState == Done) {
+ stringstream ss(transferFunctionStr);
+ int val;
+ if (ss >> val) {
+ *transfer_function = static_cast<jpegr_transfer_function>(val);
+ return true;
+ } else {
+ return false;
+ }
+ } else {
+ return false;
+ }
return true;
}
private:
- static const string rangeScalingFactorName;
+ static const string gContainerItemName;
+ static const string rangeScalingFactorAttrName;
+ static const string transferFunctionAttrName;
string rangeScalingFactorStr;
- ParseState rangeScalingFactorState;
+ string transferFunctionStr;
+ string lastAttributeName;
+ ParseState gContainerItemState;
};
-const string XMPXmlHandler::rangeScalingFactorName = "GContainer:rangeScalingFactor";
+const string XMPXmlHandler::gContainerItemName = "GContainer:Item";
+const string XMPXmlHandler::rangeScalingFactorAttrName = "RecoveryMap:RangeScalingFactor";
+const string XMPXmlHandler::transferFunctionAttrName = "RecoveryMap:TransferFunction";
+const string kContainerPrefix = "GContainer";
+const string kContainerUri = "http://ns.google.com/photos/1.0/container/";
+const string kRecoveryMapUri = "http://ns.google.com/photos/1.0/recoverymap/";
+const string kItemPrefix = "Item";
+const string kRecoveryMap = "RecoveryMap";
+const string kDirectory = "Directory";
+const string kImageJpeg = "image/jpeg";
+const string kItem = "Item";
+const string kLength = "Length";
+const string kMime = "Mime";
+const string kPrimary = "Primary";
+const string kSemantic = "Semantic";
+const string kVersion = "Version";
+const string kHdr10Metadata = "HDR10Metadata";
+const string kSt2086Metadata = "ST2086Metadata";
+const string kSt2086Coordinate = "ST2086Coordinate";
+const string kSt2086CoordinateX = "ST2086CoordinateX";
+const string kSt2086CoordinateY = "ST2086CoordinateY";
+const string kSt2086Primary = "ST2086Primary";
+const int kSt2086PrimaryRed = 0;
+const int kSt2086PrimaryGreen = 1;
+const int kSt2086PrimaryBlue = 2;
+const int kSt2086PrimaryWhite = 3;
+const int kGContainerVersion = 1;
+
+const string kConDir = Name(kContainerPrefix, kDirectory);
+const string kContainerItem = Name(kContainerPrefix, kItem);
+const string kItemLength = Name(kItemPrefix, kLength);
+const string kItemMime = Name(kItemPrefix, kMime);
+const string kItemSemantic = Name(kItemPrefix, kSemantic);
+
bool getMetadataFromXMP(uint8_t* xmp_data, size_t xmp_size, jpegr_metadata* metadata) {
string nameSpace = "http://ns.adobe.com/xap/1.0/\0";
@@ -150,4 +229,96 @@
return true;
}
+string generateXmp(int secondary_image_length, jpegr_metadata& metadata) {
+ const vector<string> kConDirSeq({kConDir, string("rdf:Seq")});
+ const vector<string> kLiItem({string("rdf:li"), kContainerItem});
+
+ std::stringstream ss;
+ photos_editing_formats::image_io::XmlWriter writer(ss);
+ writer.StartWritingElement("x:xmpmeta");
+ writer.WriteXmlns("x", "adobe:ns:meta/");
+ writer.WriteAttributeNameAndValue("x:xmptk", "Adobe XMP Core 5.1.2");
+ writer.StartWritingElement("rdf:RDF");
+ writer.WriteXmlns("rdf", "http://www.w3.org/1999/02/22-rdf-syntax-ns#");
+ writer.StartWritingElement("rdf:Description");
+ writer.WriteXmlns(kContainerPrefix, kContainerUri);
+ writer.WriteXmlns(kRecoveryMap, kRecoveryMapUri);
+ writer.WriteElementAndContent(Name(kContainerPrefix, kVersion), kGContainerVersion);
+ writer.StartWritingElements(kConDirSeq);
+ size_t item_depth = writer.StartWritingElements(kLiItem);
+ writer.WriteAttributeNameAndValue(kItemSemantic, kPrimary);
+ writer.WriteAttributeNameAndValue(kItemMime, kImageJpeg);
+ writer.WriteAttributeNameAndValue(Name(kRecoveryMap, kVersion), metadata.version);
+ writer.WriteAttributeNameAndValue(
+ Name(kRecoveryMap, "RangeScalingFactor"), metadata.rangeScalingFactor);
+ writer.WriteAttributeNameAndValue(
+ Name(kRecoveryMap, "TransferFunction"), metadata.transferFunction);
+ if (metadata.transferFunction == JPEGR_TF_PQ) {
+ writer.StartWritingElement(Name(kRecoveryMap, kHdr10Metadata));
+ writer.WriteAttributeNameAndValue(
+ Name(kRecoveryMap, "HDR10MaxFALL"), metadata.hdr10Metadata.maxFALL);
+ writer.WriteAttributeNameAndValue(
+ Name(kRecoveryMap, "HDR10MaxCLL"), metadata.hdr10Metadata.maxCLL);
+ writer.StartWritingElement(Name(kRecoveryMap, kSt2086Metadata));
+ writer.WriteAttributeNameAndValue(
+ Name(kRecoveryMap, "ST2086MaxLuminance"),
+ metadata.hdr10Metadata.st2086Metadata.maxLuminance);
+ writer.WriteAttributeNameAndValue(
+ Name(kRecoveryMap, "ST2086MinLuminance"),
+ metadata.hdr10Metadata.st2086Metadata.minLuminance);
+
+ // red
+ writer.StartWritingElement(Name(kRecoveryMap, kSt2086Coordinate));
+ writer.WriteAttributeNameAndValue(Name(kRecoveryMap, kSt2086Primary), kSt2086PrimaryRed);
+ writer.WriteAttributeNameAndValue(
+ Name(kRecoveryMap, kSt2086CoordinateX),
+ metadata.hdr10Metadata.st2086Metadata.redPrimary.x);
+ writer.WriteAttributeNameAndValue(
+ Name(kRecoveryMap, kSt2086CoordinateY),
+ metadata.hdr10Metadata.st2086Metadata.redPrimary.y);
+ writer.FinishWritingElement();
+
+ // green
+ writer.StartWritingElement(Name(kRecoveryMap, kSt2086Coordinate));
+ writer.WriteAttributeNameAndValue(Name(kRecoveryMap, kSt2086Primary), kSt2086PrimaryGreen);
+ writer.WriteAttributeNameAndValue(
+ Name(kRecoveryMap, kSt2086CoordinateX),
+ metadata.hdr10Metadata.st2086Metadata.greenPrimary.x);
+ writer.WriteAttributeNameAndValue(
+ Name(kRecoveryMap, kSt2086CoordinateY),
+ metadata.hdr10Metadata.st2086Metadata.greenPrimary.y);
+ writer.FinishWritingElement();
+
+ // blue
+ writer.StartWritingElement(Name(kRecoveryMap, kSt2086Coordinate));
+ writer.WriteAttributeNameAndValue(Name(kRecoveryMap, kSt2086Primary), kSt2086PrimaryBlue);
+ writer.WriteAttributeNameAndValue(
+ Name(kRecoveryMap, kSt2086CoordinateX),
+ metadata.hdr10Metadata.st2086Metadata.bluePrimary.x);
+ writer.WriteAttributeNameAndValue(
+ Name(kRecoveryMap, kSt2086CoordinateY),
+ metadata.hdr10Metadata.st2086Metadata.bluePrimary.y);
+ writer.FinishWritingElement();
+
+ // white
+ writer.StartWritingElement(Name(kRecoveryMap, kSt2086Coordinate));
+ writer.WriteAttributeNameAndValue(Name(kRecoveryMap, kSt2086Primary), kSt2086PrimaryWhite);
+ writer.WriteAttributeNameAndValue(
+ Name(kRecoveryMap, kSt2086CoordinateX),
+ metadata.hdr10Metadata.st2086Metadata.whitePoint.x);
+ writer.WriteAttributeNameAndValue(
+ Name(kRecoveryMap, kSt2086CoordinateY),
+ metadata.hdr10Metadata.st2086Metadata.whitePoint.y);
+ writer.FinishWritingElement();
+ }
+ writer.FinishWritingElementsToDepth(item_depth);
+ writer.StartWritingElements(kLiItem);
+ writer.WriteAttributeNameAndValue(kItemSemantic, kRecoveryMap);
+ writer.WriteAttributeNameAndValue(kItemMime, kImageJpeg);
+ writer.WriteAttributeNameAndValue(kItemLength, secondary_image_length);
+ writer.FinishWriting();
+
+ return ss.str();
+}
+
} // namespace android::recoverymap
\ No newline at end of file
diff --git a/libs/jpegrecoverymap/tests/recoverymap_test.cpp b/libs/jpegrecoverymap/tests/recoverymap_test.cpp
index ade33a0..6dea27f 100644
--- a/libs/jpegrecoverymap/tests/recoverymap_test.cpp
+++ b/libs/jpegrecoverymap/tests/recoverymap_test.cpp
@@ -15,6 +15,7 @@
*/
#include <jpegrecoverymap/recoverymap.h>
+#include <jpegrecoverymap/recoverymaputils.h>
#include <fcntl.h>
#include <fstream>
#include <gtest/gtest.h>
@@ -85,6 +86,7 @@
TEST_F(RecoveryMapTest, build) {
// Force all of the recovery map lib to be linked by calling all public functions.
RecoveryMap recovery_map;
+ recovery_map.encodeJPEGR(nullptr, static_cast<jpegr_transfer_function>(0), nullptr, 0, nullptr);
recovery_map.encodeJPEGR(nullptr, nullptr, static_cast<jpegr_transfer_function>(0),
nullptr, 0, nullptr);
recovery_map.encodeJPEGR(nullptr, nullptr, nullptr, static_cast<jpegr_transfer_function>(0),
@@ -93,6 +95,71 @@
recovery_map.decodeJPEGR(nullptr, nullptr, nullptr, false);
}
+TEST_F(RecoveryMapTest, writeXmpThenRead) {
+ jpegr_metadata metadata_expected;
+ metadata_expected.transferFunction = JPEGR_TF_HLG;
+ metadata_expected.rangeScalingFactor = 1.25;
+ int length_expected = 1000;
+ std::string xmp = generateXmp(1000, metadata_expected);
+
+ jpegr_metadata metadata_read;
+ EXPECT_TRUE(getMetadataFromXMP(reinterpret_cast<uint8_t*>(xmp[0]), xmp.size(), &metadata_read));
+ ASSERT_EQ(metadata_expected.transferFunction, metadata_read.transferFunction);
+ ASSERT_EQ(metadata_expected.rangeScalingFactor, metadata_read.rangeScalingFactor);
+
+}
+TEST_F(RecoveryMapTest, encodeFromP010ThenDecode) {
+ int ret;
+
+ // Load input files.
+ if (!loadFile(RAW_P010_IMAGE, mRawP010Image.data, nullptr)) {
+ FAIL() << "Load file " << RAW_P010_IMAGE << " failed";
+ }
+ mRawP010Image.width = RAW_P010_IMAGE_WIDTH;
+ mRawP010Image.height = RAW_P010_IMAGE_HEIGHT;
+ mRawP010Image.colorGamut = jpegr_color_gamut::JPEGR_COLORGAMUT_BT2100;
+
+ RecoveryMap recoveryMap;
+
+ jpegr_compressed_struct jpegR;
+ jpegR.maxLength = RAW_P010_IMAGE_WIDTH * RAW_P010_IMAGE_HEIGHT * sizeof(uint8_t);
+ jpegR.data = malloc(jpegR.maxLength);
+ ret = recoveryMap.encodeJPEGR(
+ &mRawP010Image, jpegr_transfer_function::JPEGR_TF_HLG, &jpegR, 90, nullptr);
+ if (ret != OK) {
+ FAIL() << "Error code is " << ret;
+ }
+ if (SAVE_ENCODING_RESULT) {
+ // Output image data to file
+ std::string filePath = "/sdcard/Documents/encoded_from_jpeg_input.jpgr";
+ std::ofstream imageFile(filePath.c_str(), std::ofstream::binary);
+ if (!imageFile.is_open()) {
+ ALOGE("%s: Unable to create file %s", __FUNCTION__, filePath.c_str());
+ }
+ imageFile.write((const char*)jpegR.data, jpegR.length);
+ }
+
+ jpegr_uncompressed_struct decodedJpegR;
+ int decodedJpegRSize = RAW_P010_IMAGE_WIDTH * RAW_P010_IMAGE_HEIGHT * 4;
+ decodedJpegR.data = malloc(decodedJpegRSize);
+ ret = recoveryMap.decodeJPEGR(&jpegR, &decodedJpegR);
+ if (ret != OK) {
+ FAIL() << "Error code is " << ret;
+ }
+ if (SAVE_DECODING_RESULT) {
+ // Output image data to file
+ std::string filePath = "/sdcard/Documents/decoded_from_jpeg_input.rgb10";
+ std::ofstream imageFile(filePath.c_str(), std::ofstream::binary);
+ if (!imageFile.is_open()) {
+ ALOGE("%s: Unable to create file %s", __FUNCTION__, filePath.c_str());
+ }
+ imageFile.write((const char*)decodedJpegR.data, decodedJpegRSize);
+ }
+
+ free(jpegR.data);
+ free(decodedJpegR.data);
+}
+
TEST_F(RecoveryMapTest, encodeFromJpegThenDecode) {
int ret;
diff --git a/libs/nativedisplay/ADisplay.cpp b/libs/nativedisplay/ADisplay.cpp
index 60328e4..bf0805b 100644
--- a/libs/nativedisplay/ADisplay.cpp
+++ b/libs/nativedisplay/ADisplay.cpp
@@ -117,15 +117,6 @@
#define CHECK_NOT_NULL(name) \
LOG_ALWAYS_FATAL_IF(name == nullptr, "nullptr passed as " #name " argument");
-namespace {
-
-sp<IBinder> getToken(ADisplay* display) {
- DisplayImpl* impl = reinterpret_cast<DisplayImpl*>(display);
- return SurfaceComposerClient::getPhysicalDisplayToken(impl->id);
-}
-
-} // namespace
-
namespace android {
int ADisplay_acquirePhysicalDisplays(ADisplay*** outDisplays) {
@@ -139,10 +130,9 @@
ui::DisplayConnectionType displayConnectionTypes[size];
int numModes = 0;
for (int i = 0; i < size; ++i) {
- const sp<IBinder> token = SurfaceComposerClient::getPhysicalDisplayToken(ids[i]);
-
ui::StaticDisplayInfo staticInfo;
- if (const status_t status = SurfaceComposerClient::getStaticDisplayInfo(token, &staticInfo);
+ if (const status_t status =
+ SurfaceComposerClient::getStaticDisplayInfo(ids[i].value, &staticInfo);
status != OK) {
return status;
}
@@ -150,7 +140,7 @@
ui::DynamicDisplayInfo dynamicInfo;
if (const status_t status =
- SurfaceComposerClient::getDynamicDisplayInfo(token, &dynamicInfo);
+ SurfaceComposerClient::getDynamicDisplayInfoFromId(ids[i].value, &dynamicInfo);
status != OK) {
return status;
}
@@ -260,14 +250,15 @@
int ADisplay_getCurrentConfig(ADisplay* display, ADisplayConfig** outConfig) {
CHECK_NOT_NULL(display);
- sp<IBinder> token = getToken(display);
ui::DynamicDisplayInfo info;
- if (const auto status = SurfaceComposerClient::getDynamicDisplayInfo(token, &info);
+ DisplayImpl* impl = reinterpret_cast<DisplayImpl*>(display);
+
+ if (const auto status =
+ SurfaceComposerClient::getDynamicDisplayInfoFromId(impl->id.value, &info);
status != OK) {
return status;
}
- DisplayImpl* impl = reinterpret_cast<DisplayImpl*>(display);
for (size_t i = 0; i < impl->numConfigs; i++) {
auto* config = impl->configs + i;
if (config->id == info.activeDisplayModeId) {
diff --git a/libs/nativewindow/AHardwareBuffer.cpp b/libs/nativewindow/AHardwareBuffer.cpp
index 180dce9..bbafbff 100644
--- a/libs/nativewindow/AHardwareBuffer.cpp
+++ b/libs/nativewindow/AHardwareBuffer.cpp
@@ -702,6 +702,14 @@
return ahardwarebuffer_format;
}
+int32_t AHardwareBuffer_getDataSpace(AHardwareBuffer* buffer) {
+ GraphicBuffer* gb = AHardwareBuffer_to_GraphicBuffer(buffer);
+ auto& mapper = GraphicBufferMapper::get();
+ ui::Dataspace dataspace = ui::Dataspace::UNKNOWN;
+ mapper.getDataspace(gb->handle, &dataspace);
+ return static_cast<int32_t>(dataspace);
+}
+
uint64_t AHardwareBuffer_convertToGrallocUsageBits(uint64_t usage) {
using android::hardware::graphics::common::V1_1::BufferUsage;
static_assert(AHARDWAREBUFFER_USAGE_CPU_READ_NEVER == (uint64_t)BufferUsage::CPU_READ_NEVER,
diff --git a/libs/nativewindow/ANativeWindow.cpp b/libs/nativewindow/ANativeWindow.cpp
index c345385..b7b2926 100644
--- a/libs/nativewindow/ANativeWindow.cpp
+++ b/libs/nativewindow/ANativeWindow.cpp
@@ -232,15 +232,6 @@
return native_window_set_frame_rate(window, frameRate, compatibility, changeFrameRateStrategy);
}
-int32_t ANativeWindow_clearFrameRate(ANativeWindow* window) {
- if (!window || !query(window, NATIVE_WINDOW_IS_VALID)) {
- return -EINVAL;
- }
- return native_window_set_frame_rate(window, 0,
- ANATIVEWINDOW_FRAME_RATE_COMPATIBILITY_DEFAULT,
- ANATIVEWINDOW_CHANGE_FRAME_RATE_ONLY_IF_SEAMLESS);
-}
-
/**************************************************************************************************
* vndk-stable
**************************************************************************************************/
diff --git a/libs/nativewindow/include-private/private/android/AHardwareBufferHelpers.h b/libs/nativewindow/include-private/private/android/AHardwareBufferHelpers.h
index ddfd1d1..6d3d295 100644
--- a/libs/nativewindow/include-private/private/android/AHardwareBufferHelpers.h
+++ b/libs/nativewindow/include-private/private/android/AHardwareBufferHelpers.h
@@ -52,6 +52,11 @@
// convert HAL format to AHardwareBuffer format (note: this is a no-op)
uint32_t AHardwareBuffer_convertToPixelFormat(uint32_t format);
+// retrieves a dataspace from the AHardwareBuffer metadata, if the device
+// support gralloc metadata. Returns UNKNOWN if gralloc metadata is not
+// supported.
+int32_t AHardwareBuffer_getDataSpace(AHardwareBuffer* buffer);
+
// convert AHardwareBuffer usage bits to HAL usage bits (note: this is a no-op)
uint64_t AHardwareBuffer_convertFromGrallocUsageBits(uint64_t usage);
diff --git a/libs/nativewindow/include/android/native_window.h b/libs/nativewindow/include/android/native_window.h
index a27e3dd..be6623e 100644
--- a/libs/nativewindow/include/android/native_window.h
+++ b/libs/nativewindow/include/android/native_window.h
@@ -372,8 +372,12 @@
*
* \return 0 for success, -EINVAL if the window value is invalid.
*/
-int32_t ANativeWindow_clearFrameRate(ANativeWindow* window)
- __INTRODUCED_IN(__ANDROID_API_U__);
+inline int32_t ANativeWindow_clearFrameRate(ANativeWindow* window)
+ __INTRODUCED_IN(__ANDROID_API_U__) {
+ return ANativeWindow_setFrameRateWithChangeStrategy(window, 0,
+ ANATIVEWINDOW_FRAME_RATE_COMPATIBILITY_DEFAULT,
+ ANATIVEWINDOW_CHANGE_FRAME_RATE_ONLY_IF_SEAMLESS);
+}
#ifdef __cplusplus
}
diff --git a/libs/nativewindow/libnativewindow.map.txt b/libs/nativewindow/libnativewindow.map.txt
index 76d23fa..c2fd6ef 100644
--- a/libs/nativewindow/libnativewindow.map.txt
+++ b/libs/nativewindow/libnativewindow.map.txt
@@ -51,7 +51,6 @@
ANativeWindow_setDequeueTimeout; # systemapi # introduced=30
ANativeWindow_setFrameRate; # introduced=30
ANativeWindow_setFrameRateWithChangeStrategy; # introduced=31
- ANativeWindow_clearFrameRate; # introduced=UpsideDownCake
ANativeWindow_setSharedBufferMode; # llndk
ANativeWindow_setSwapInterval; # llndk
ANativeWindow_setUsage; # llndk
@@ -71,6 +70,7 @@
android::AHardwareBuffer_convertToPixelFormat*;
android::AHardwareBuffer_convertFromGrallocUsageBits*;
android::AHardwareBuffer_convertToGrallocUsageBits*;
+ android::AHardwareBuffer_getDataSpace*;
android::AHardwareBuffer_to_GraphicBuffer*;
android::AHardwareBuffer_to_ANativeWindowBuffer*;
android::AHardwareBuffer_from_GraphicBuffer*;
diff --git a/libs/shaders/include/shaders/shaders.h b/libs/shaders/include/shaders/shaders.h
index 2a4a370..42b0cc1 100644
--- a/libs/shaders/include/shaders/shaders.h
+++ b/libs/shaders/include/shaders/shaders.h
@@ -68,6 +68,9 @@
// fakeInputDataspace is used to essentially masquerade the input dataspace to be the output
// dataspace for correct conversion to linear colors.
ui::Dataspace fakeInputDataspace = ui::Dataspace::UNKNOWN;
+
+ enum SkSLType { Shader, ColorFilter };
+ SkSLType type = Shader;
};
static inline bool operator==(const LinearEffect& lhs, const LinearEffect& rhs) {
@@ -96,6 +99,10 @@
// 2. Apply color transform matrices in linear space
std::string buildLinearEffectSkSL(const LinearEffect& linearEffect);
+// Generates a shader string that applies color transforms in linear space.
+// This is intended to be plugged into an SkColorFilter
+std::string buildLinearEffectSkSLForColorFilter(const LinearEffect& linearEffect);
+
// Generates a list of uniforms to set on the LinearEffect shader above.
std::vector<tonemap::ShaderUniform> buildLinearEffectUniforms(
const LinearEffect& linearEffect, const mat4& colorTransform, float maxDisplayLuminance,
diff --git a/libs/shaders/shaders.cpp b/libs/shaders/shaders.cpp
index f80e93f..a3c403e 100644
--- a/libs/shaders/shaders.cpp
+++ b/libs/shaders/shaders.cpp
@@ -386,12 +386,23 @@
}
}
-void generateEffectiveOOTF(bool undoPremultipliedAlpha, std::string& shader) {
- shader.append(R"(
- uniform shader child;
- half4 main(float2 xy) {
- float4 c = float4(child.eval(xy));
- )");
+void generateEffectiveOOTF(bool undoPremultipliedAlpha, LinearEffect::SkSLType type,
+ std::string& shader) {
+ switch (type) {
+ case LinearEffect::SkSLType::ColorFilter:
+ shader.append(R"(
+ half4 main(half4 inputColor) {
+ float4 c = float4(inputColor);
+ )");
+ break;
+ case LinearEffect::SkSLType::Shader:
+ shader.append(R"(
+ uniform shader child;
+ half4 main(float2 xy) {
+ float4 c = float4(child.eval(xy));
+ )");
+ break;
+ }
if (undoPremultipliedAlpha) {
shader.append(R"(
c.rgb = c.rgb / (c.a + 0.0019);
@@ -459,7 +470,7 @@
generateXYZTransforms(shaderString);
generateOOTF(linearEffect.inputDataspace, linearEffect.outputDataspace, shaderString);
generateOETF(linearEffect.outputDataspace, shaderString);
- generateEffectiveOOTF(linearEffect.undoPremultipliedAlpha, shaderString);
+ generateEffectiveOOTF(linearEffect.undoPremultipliedAlpha, linearEffect.type, shaderString);
return shaderString;
}
diff --git a/libs/ui/include/ui/DisplayMode.h b/libs/ui/include/ui/DisplayMode.h
index a2791a6..65a8769 100644
--- a/libs/ui/include/ui/DisplayMode.h
+++ b/libs/ui/include/ui/DisplayMode.h
@@ -19,6 +19,7 @@
#include <cstdint>
#include <type_traits>
+#include <ui/GraphicTypes.h>
#include <ui/Size.h>
#include <utils/Flattenable.h>
#include <utils/Timers.h>
@@ -34,6 +35,7 @@
ui::Size resolution;
float xDpi = 0;
float yDpi = 0;
+ std::vector<ui::Hdr> supportedHdrTypes;
float refreshRate = 0;
nsecs_t appVsyncOffset = 0;
diff --git a/libs/ui/include/ui/DynamicDisplayInfo.h b/libs/ui/include/ui/DynamicDisplayInfo.h
index 8c9fe4c..0b77754 100644
--- a/libs/ui/include/ui/DynamicDisplayInfo.h
+++ b/libs/ui/include/ui/DynamicDisplayInfo.h
@@ -35,6 +35,7 @@
// we can't use size_t because it may have different width
// in the client process.
ui::DisplayModeId activeDisplayModeId;
+ float renderFrameRate;
std::vector<ui::ColorMode> supportedColorModes;
ui::ColorMode activeColorMode;
diff --git a/libs/ui/include/ui/GraphicTypes.h b/libs/ui/include/ui/GraphicTypes.h
index 8661c36..1775d39 100644
--- a/libs/ui/include/ui/GraphicTypes.h
+++ b/libs/ui/include/ui/GraphicTypes.h
@@ -20,6 +20,7 @@
#include <aidl/android/hardware/graphics/common/ChromaSiting.h>
#include <aidl/android/hardware/graphics/common/Compression.h>
#include <aidl/android/hardware/graphics/common/Cta861_3.h>
+#include <aidl/android/hardware/graphics/common/Hdr.h>
#include <aidl/android/hardware/graphics/common/Interlaced.h>
#include <aidl/android/hardware/graphics/common/PlaneLayout.h>
#include <aidl/android/hardware/graphics/common/Smpte2086.h>
@@ -42,7 +43,6 @@
using android::hardware::graphics::common::V1_1::RenderIntent;
using android::hardware::graphics::common::V1_2::ColorMode;
using android::hardware::graphics::common::V1_2::Dataspace;
-using android::hardware::graphics::common::V1_2::Hdr;
using android::hardware::graphics::common::V1_2::PixelFormat;
/**
@@ -50,6 +50,7 @@
*/
using aidl::android::hardware::graphics::common::BlendMode;
using aidl::android::hardware::graphics::common::Cta861_3;
+using aidl::android::hardware::graphics::common::Hdr;
using aidl::android::hardware::graphics::common::PlaneLayout;
using aidl::android::hardware::graphics::common::Smpte2086;
diff --git a/libs/ui/include/ui/Rotation.h b/libs/ui/include/ui/Rotation.h
index 83d431d..c1d60f4 100644
--- a/libs/ui/include/ui/Rotation.h
+++ b/libs/ui/include/ui/Rotation.h
@@ -20,7 +20,14 @@
namespace android::ui {
-enum class Rotation { Rotation0 = 0, Rotation90 = 1, Rotation180 = 2, Rotation270 = 3 };
+enum class Rotation {
+ Rotation0 = 0,
+ Rotation90 = 1,
+ Rotation180 = 2,
+ Rotation270 = 3,
+
+ ftl_last = Rotation270
+};
// Equivalent to Surface.java constants.
constexpr auto ROTATION_0 = Rotation::Rotation0;
diff --git a/services/inputflinger/dispatcher/InputDispatcher.cpp b/services/inputflinger/dispatcher/InputDispatcher.cpp
index 466c51e..4aac377 100644
--- a/services/inputflinger/dispatcher/InputDispatcher.cpp
+++ b/services/inputflinger/dispatcher/InputDispatcher.cpp
@@ -148,21 +148,23 @@
}
bool isValidMotionAction(int32_t action, int32_t actionButton, int32_t pointerCount) {
- switch (action & AMOTION_EVENT_ACTION_MASK) {
+ switch (MotionEvent::getActionMasked(action)) {
case AMOTION_EVENT_ACTION_DOWN:
case AMOTION_EVENT_ACTION_UP:
- case AMOTION_EVENT_ACTION_CANCEL:
+ return pointerCount == 1;
case AMOTION_EVENT_ACTION_MOVE:
- case AMOTION_EVENT_ACTION_OUTSIDE:
case AMOTION_EVENT_ACTION_HOVER_ENTER:
case AMOTION_EVENT_ACTION_HOVER_MOVE:
case AMOTION_EVENT_ACTION_HOVER_EXIT:
+ return pointerCount >= 1;
+ case AMOTION_EVENT_ACTION_CANCEL:
+ case AMOTION_EVENT_ACTION_OUTSIDE:
case AMOTION_EVENT_ACTION_SCROLL:
return true;
case AMOTION_EVENT_ACTION_POINTER_DOWN:
case AMOTION_EVENT_ACTION_POINTER_UP: {
- int32_t index = getMotionEventActionPointerIndex(action);
- return index >= 0 && index < pointerCount;
+ const int32_t index = MotionEvent::getActionIndex(action);
+ return index >= 0 && index < pointerCount && pointerCount > 1;
}
case AMOTION_EVENT_ACTION_BUTTON_PRESS:
case AMOTION_EVENT_ACTION_BUTTON_RELEASE:
@@ -2096,7 +2098,7 @@
bool isSplit = shouldSplitTouch(tempTouchState, entry);
const bool switchedDevice = (oldState != nullptr) &&
- (tempTouchState.deviceId != entry.deviceId || tempTouchState.source != entry.source);
+ (oldState->deviceId != entry.deviceId || oldState->source != entry.source);
const bool isHoverAction = (maskedAction == AMOTION_EVENT_ACTION_HOVER_MOVE ||
maskedAction == AMOTION_EVENT_ACTION_HOVER_ENTER ||
@@ -2104,6 +2106,7 @@
const bool newGesture = (maskedAction == AMOTION_EVENT_ACTION_DOWN ||
maskedAction == AMOTION_EVENT_ACTION_SCROLL || isHoverAction);
const bool isFromMouse = isFromSource(entry.source, AINPUT_SOURCE_MOUSE);
+
if (newGesture) {
bool down = maskedAction == AMOTION_EVENT_ACTION_DOWN;
if (switchedDevice && tempTouchState.isDown() && !down && !isHoverAction) {
@@ -2995,6 +2998,8 @@
connection->getInputChannelName().c_str(), eventEntry->id);
ATRACE_NAME(message.c_str());
}
+ LOG_ALWAYS_FATAL_IF(!inputTarget.flags.any(InputTarget::DISPATCH_MASK),
+ "No dispatch flags are set for %s", eventEntry->getDescription().c_str());
const bool wasEmpty = connection->outboundQueue.empty();
@@ -4053,10 +4058,9 @@
args->pointerCoords[i].getAxisValue(AMOTION_EVENT_AXIS_ORIENTATION));
}
}
- if (!validateMotionEvent(args->action, args->actionButton, args->pointerCount,
- args->pointerProperties)) {
- return;
- }
+ LOG_ALWAYS_FATAL_IF(!validateMotionEvent(args->action, args->actionButton, args->pointerCount,
+ args->pointerProperties),
+ "Invalid event: %s", args->dump().c_str());
uint32_t policyFlags = args->policyFlags;
policyFlags |= POLICY_FLAG_TRUSTED;
@@ -4981,10 +4985,6 @@
}
}
}
-
- if (DEBUG_FOCUS) {
- logDispatchStateLocked();
- }
} // release lock
// Wake up poll loop since it may need to make new input dispatching choices.
@@ -5015,10 +5015,6 @@
} else {
changed = false;
}
-
- if (DEBUG_FOCUS) {
- logDispatchStateLocked();
- }
} // release lock
if (changed) {
@@ -5193,10 +5189,6 @@
synthesizeCancelationEventsForConnectionLocked(fromConnection, options);
synthesizePointerDownEventsForConnectionLocked(downTimeInTarget, toConnection);
}
-
- if (DEBUG_FOCUS) {
- logDispatchStateLocked();
- }
} // release lock
// Wake up poll loop since it may need to make new input dispatching choices.
diff --git a/services/inputflinger/dispatcher/TouchState.cpp b/services/inputflinger/dispatcher/TouchState.cpp
index 114e0bf..c21af9e 100644
--- a/services/inputflinger/dispatcher/TouchState.cpp
+++ b/services/inputflinger/dispatcher/TouchState.cpp
@@ -34,8 +34,7 @@
void TouchState::addOrUpdateWindow(const sp<WindowInfoHandle>& windowHandle,
ftl::Flags<InputTarget::Flags> targetFlags, BitSet32 pointerIds,
std::optional<nsecs_t> eventTime) {
- for (size_t i = 0; i < windows.size(); i++) {
- TouchedWindow& touchedWindow = windows[i];
+ for (TouchedWindow& touchedWindow : windows) {
if (touchedWindow.windowHandle == windowHandle) {
touchedWindow.targetFlags |= targetFlags;
if (targetFlags.test(InputTarget::Flags::DISPATCH_AS_SLIPPERY_EXIT)) {
diff --git a/services/inputflinger/include/InputReaderBase.h b/services/inputflinger/include/InputReaderBase.h
index 3b0f2ac..b8a6dad 100644
--- a/services/inputflinger/include/InputReaderBase.h
+++ b/services/inputflinger/include/InputReaderBase.h
@@ -23,6 +23,7 @@
#include <input/VelocityControl.h>
#include <input/VelocityTracker.h>
#include <stddef.h>
+#include <ui/Rotation.h>
#include <unistd.h>
#include <utils/Errors.h>
#include <utils/RefBase.h>
@@ -87,6 +88,9 @@
virtual int32_t getSwitchState(int32_t deviceId, uint32_t sourceMask,
int32_t sw) = 0;
+ virtual void addKeyRemapping(int32_t deviceId, int32_t fromKeyCode,
+ int32_t toKeyCode) const = 0;
+
virtual int32_t getKeyCodeForKeyLocation(int32_t deviceId, int32_t locationKeyCode) const = 0;
/* Toggle Caps Lock */
@@ -395,7 +399,7 @@
/* Gets the affine calibration associated with the specified device. */
virtual TouchAffineTransformation getTouchAffineTransformation(
- const std::string& inputDeviceDescriptor, int32_t surfaceRotation) = 0;
+ const std::string& inputDeviceDescriptor, ui::Rotation surfaceRotation) = 0;
/* Notifies the input reader policy that a stylus gesture has started. */
virtual void notifyStylusGestureStarted(int32_t deviceId, nsecs_t eventTime) = 0;
};
diff --git a/services/inputflinger/reader/Android.bp b/services/inputflinger/reader/Android.bp
index 46e86de..f37f0fa 100644
--- a/services/inputflinger/reader/Android.bp
+++ b/services/inputflinger/reader/Android.bp
@@ -50,7 +50,9 @@
"mapper/SensorInputMapper.cpp",
"mapper/SingleTouchInputMapper.cpp",
"mapper/SwitchInputMapper.cpp",
+ "mapper/TouchCursorInputMapperCommon.cpp",
"mapper/TouchInputMapper.cpp",
+ "mapper/TouchpadInputMapper.cpp",
"mapper/VibratorInputMapper.cpp",
"mapper/accumulator/CursorButtonAccumulator.cpp",
"mapper/accumulator/CursorScrollAccumulator.cpp",
@@ -72,6 +74,7 @@
"libcutils",
"libjsoncpp",
"liblog",
+ "libPlatformProperties",
"libstatslog",
"libutils",
],
@@ -88,7 +91,6 @@
target: {
android: {
shared_libs: [
- "libPlatformProperties",
"libinput",
],
},
@@ -115,6 +117,9 @@
"libchrome-gestures_headers",
"libinputreader_headers",
],
+ whole_static_libs: [
+ "libchrome-gestures",
+ ],
}
cc_library_shared {
@@ -131,6 +136,7 @@
// This should consist only of dependencies from inputflinger. Other dependencies should be
// in cc_defaults so that they are included in the tests.
"libinputflinger_base",
+ "libjsoncpp",
],
export_header_lib_headers: [
"libinputreader_headers",
@@ -145,5 +151,6 @@
},
static_libs: [
"libc++fs",
+ "libchrome-gestures",
],
}
diff --git a/services/inputflinger/reader/EventHub.cpp b/services/inputflinger/reader/EventHub.cpp
index 0aaef53..e26bc8c 100644
--- a/services/inputflinger/reader/EventHub.cpp
+++ b/services/inputflinger/reader/EventHub.cpp
@@ -952,15 +952,19 @@
device->getKeyCharacterMap()->mapKey(scanCodes[0], 0 /*usageCode*/, &outKeyCode);
switch (mapKeyRes) {
case OK:
- return outKeyCode;
+ break;
case NAME_NOT_FOUND:
// key character map doesn't re-map this scanCode, hence the keyCode remains the same
- return locationKeyCode;
+ outKeyCode = locationKeyCode;
+ break;
default:
ALOGW("Failed to get key code for key location: Key character map returned error %s",
statusToString(mapKeyRes).c_str());
- return AKEYCODE_UNKNOWN;
+ outKeyCode = AKEYCODE_UNKNOWN;
+ break;
}
+ // Remap if there is a Key remapping added to the KCM and return the remapped key
+ return device->getKeyCharacterMap()->applyKeyRemapping(outKeyCode);
}
int32_t EventHub::getSwitchState(int32_t deviceId, int32_t sw) const {
@@ -1023,6 +1027,18 @@
return false;
}
+void EventHub::addKeyRemapping(int32_t deviceId, int32_t fromKeyCode, int32_t toKeyCode) const {
+ std::scoped_lock _l(mLock);
+ Device* device = getDeviceLocked(deviceId);
+ if (device == nullptr) {
+ return;
+ }
+ const std::shared_ptr<KeyCharacterMap> kcm = device->getKeyCharacterMap();
+ if (kcm) {
+ kcm->addKeyRemapping(fromKeyCode, toKeyCode);
+ }
+}
+
status_t EventHub::mapKey(int32_t deviceId, int32_t scanCode, int32_t usageCode, int32_t metaState,
int32_t* outKeycode, int32_t* outMetaState, uint32_t* outFlags) const {
std::scoped_lock _l(mLock);
@@ -1048,7 +1064,13 @@
if (status == NO_ERROR) {
if (kcm) {
- kcm->tryRemapKey(*outKeycode, metaState, outKeycode, outMetaState);
+ // Remap keys based on user-defined key remappings and key behavior defined in the
+ // corresponding kcm file
+ *outKeycode = kcm->applyKeyRemapping(*outKeycode);
+
+ // Remap keys based on Key behavior defined in KCM file
+ std::tie(*outKeycode, *outMetaState) =
+ kcm->applyKeyBehavior(*outKeycode, metaState);
} else {
*outMetaState = metaState;
}
@@ -2215,6 +2237,10 @@
// a touch screen.
if (device->keyBitmask.test(BTN_TOUCH) || !haveGamepadButtons) {
device->classes |= (InputDeviceClass::TOUCH | InputDeviceClass::TOUCH_MT);
+ if (device->propBitmask.test(INPUT_PROP_POINTER) &&
+ !device->keyBitmask.any(BTN_TOOL_PEN, BTN_TOOL_FINGER) && !haveStylusButtons) {
+ device->classes |= InputDeviceClass::TOUCHPAD;
+ }
}
// Is this an old style single-touch driver?
} else if (device->keyBitmask.test(BTN_TOUCH) && device->absBitmask.test(ABS_X) &&
diff --git a/services/inputflinger/reader/InputDevice.cpp b/services/inputflinger/reader/InputDevice.cpp
index e6ab872..11b5209 100644
--- a/services/inputflinger/reader/InputDevice.cpp
+++ b/services/inputflinger/reader/InputDevice.cpp
@@ -20,6 +20,7 @@
#include <algorithm>
+#include <android/sysprop/InputProperties.sysprop.h>
#include <ftl/flags.h>
#include "CursorInputMapper.h"
@@ -33,6 +34,7 @@
#include "SensorInputMapper.h"
#include "SingleTouchInputMapper.h"
#include "SwitchInputMapper.h"
+#include "TouchpadInputMapper.h"
#include "VibratorInputMapper.h"
using android::hardware::input::InputDeviceCountryCode;
@@ -208,7 +210,12 @@
}
// Touchscreens and touchpad devices.
- if (classes.test(InputDeviceClass::TOUCH_MT)) {
+ static const bool ENABLE_TOUCHPAD_GESTURES_LIBRARY =
+ sysprop::InputProperties::enable_touchpad_gestures_library().value_or(false);
+ if (ENABLE_TOUCHPAD_GESTURES_LIBRARY && classes.test(InputDeviceClass::TOUCHPAD) &&
+ classes.test(InputDeviceClass::TOUCH_MT)) {
+ mappers.push_back(std::make_unique<TouchpadInputMapper>(*contextPtr));
+ } else if (classes.test(InputDeviceClass::TOUCH_MT)) {
mappers.push_back(std::make_unique<MultiTouchInputMapper>(*contextPtr));
} else if (classes.test(InputDeviceClass::TOUCH)) {
mappers.push_back(std::make_unique<SingleTouchInputMapper>(*contextPtr));
@@ -618,6 +625,12 @@
});
}
+void InputDevice::addKeyRemapping(int32_t fromKeyCode, int32_t toKeyCode) {
+ for_each_subdevice([fromKeyCode, toKeyCode](auto& context) {
+ context.addKeyRemapping(fromKeyCode, toKeyCode);
+ });
+}
+
void InputDevice::bumpGeneration() {
mGeneration = mContext->bumpGeneration();
}
diff --git a/services/inputflinger/reader/InputReader.cpp b/services/inputflinger/reader/InputReader.cpp
index f04a646..57f679c 100644
--- a/services/inputflinger/reader/InputReader.cpp
+++ b/services/inputflinger/reader/InputReader.cpp
@@ -650,6 +650,15 @@
return result;
}
+void InputReader::addKeyRemapping(int32_t deviceId, int32_t fromKeyCode, int32_t toKeyCode) const {
+ std::scoped_lock _l(mLock);
+
+ InputDevice* device = findInputDeviceLocked(deviceId);
+ if (device != nullptr) {
+ device->addKeyRemapping(fromKeyCode, toKeyCode);
+ }
+}
+
int32_t InputReader::getKeyCodeForKeyLocation(int32_t deviceId, int32_t locationKeyCode) const {
std::scoped_lock _l(mLock);
diff --git a/services/inputflinger/reader/include/EventHub.h b/services/inputflinger/reader/include/EventHub.h
index 8e5f15f..8a844b2 100644
--- a/services/inputflinger/reader/include/EventHub.h
+++ b/services/inputflinger/reader/include/EventHub.h
@@ -94,7 +94,7 @@
/* The input device is a cursor device such as a trackball or mouse. */
CURSOR = 0x00000008,
- /* The input device is a multi-touch touchscreen. */
+ /* The input device is a multi-touch touchscreen or touchpad. */
TOUCH_MT = 0x00000010,
/* The input device is a directional pad (implies keyboard, has DPAD keys). */
@@ -130,6 +130,9 @@
/* The input device has sysfs controllable lights */
LIGHT = 0x00008000,
+ /* The input device is a touchpad, requiring an on-screen cursor. */
+ TOUCHPAD = 0x00010000,
+
/* The input device is virtual (not a real device, not part of UI configuration). */
VIRTUAL = 0x40000000,
@@ -259,6 +262,9 @@
virtual bool hasMscEvent(int32_t deviceId, int mscEvent) const = 0;
+ virtual void addKeyRemapping(int32_t deviceId, int32_t fromKeyCode,
+ int32_t toKeyCode) const = 0;
+
virtual status_t mapKey(int32_t deviceId, int32_t scanCode, int32_t usageCode,
int32_t metaState, int32_t* outKeycode, int32_t* outMetaState,
uint32_t* outFlags) const = 0;
@@ -457,6 +463,9 @@
bool hasMscEvent(int32_t deviceId, int mscEvent) const override final;
+ void addKeyRemapping(int32_t deviceId, int32_t fromKeyCode,
+ int32_t toKeyCode) const override final;
+
status_t mapKey(int32_t deviceId, int32_t scanCode, int32_t usageCode, int32_t metaState,
int32_t* outKeycode, int32_t* outMetaState,
uint32_t* outFlags) const override final;
diff --git a/services/inputflinger/reader/include/InputDevice.h b/services/inputflinger/reader/include/InputDevice.h
index 439123b..6fa21e5 100644
--- a/services/inputflinger/reader/include/InputDevice.h
+++ b/services/inputflinger/reader/include/InputDevice.h
@@ -114,6 +114,8 @@
int32_t getMetaState();
void updateMetaState(int32_t keyCode);
+ void addKeyRemapping(int32_t fromKeyCode, int32_t toKeyCode);
+
void bumpGeneration();
[[nodiscard]] NotifyDeviceResetArgs notifyReset(nsecs_t when);
@@ -278,6 +280,10 @@
inline bool hasMscEvent(int mscEvent) const { return mEventHub->hasMscEvent(mId, mscEvent); }
+ inline void addKeyRemapping(int32_t fromKeyCode, int32_t toKeyCode) const {
+ mEventHub->addKeyRemapping(mId, fromKeyCode, toKeyCode);
+ }
+
inline status_t mapKey(int32_t scanCode, int32_t usageCode, int32_t metaState,
int32_t* outKeycode, int32_t* outMetaState, uint32_t* outFlags) const {
return mEventHub->mapKey(mId, scanCode, usageCode, metaState, outKeycode, outMetaState,
diff --git a/services/inputflinger/reader/include/InputReader.h b/services/inputflinger/reader/include/InputReader.h
index 4f2503a..e9c989a 100644
--- a/services/inputflinger/reader/include/InputReader.h
+++ b/services/inputflinger/reader/include/InputReader.h
@@ -68,6 +68,8 @@
int32_t getKeyCodeState(int32_t deviceId, uint32_t sourceMask, int32_t keyCode) override;
int32_t getSwitchState(int32_t deviceId, uint32_t sourceMask, int32_t sw) override;
+ void addKeyRemapping(int32_t deviceId, int32_t fromKeyCode, int32_t toKeyCode) const override;
+
int32_t getKeyCodeForKeyLocation(int32_t deviceId, int32_t locationKeyCode) const override;
void toggleCapsLockState(int32_t deviceId) override;
diff --git a/services/inputflinger/reader/mapper/CursorInputMapper.cpp b/services/inputflinger/reader/mapper/CursorInputMapper.cpp
index a1a2af9..13e4d0c 100644
--- a/services/inputflinger/reader/mapper/CursorInputMapper.cpp
+++ b/services/inputflinger/reader/mapper/CursorInputMapper.cpp
@@ -227,7 +227,7 @@
mDisplayId = mPointerController->getDisplayId();
}
- mOrientation = DISPLAY_ORIENTATION_0;
+ mOrientation = ui::ROTATION_0;
const bool isOrientedDevice =
(mParameters.orientationAware && mParameters.hasAssociatedDisplay);
// InputReader works in the un-rotated display coordinate space, so we don't need to do
diff --git a/services/inputflinger/reader/mapper/CursorInputMapper.h b/services/inputflinger/reader/mapper/CursorInputMapper.h
index 20746e5..939cceb 100644
--- a/services/inputflinger/reader/mapper/CursorInputMapper.h
+++ b/services/inputflinger/reader/mapper/CursorInputMapper.h
@@ -22,6 +22,7 @@
#include <PointerControllerInterface.h>
#include <input/VelocityControl.h>
+#include <ui/Rotation.h>
namespace android {
@@ -115,7 +116,7 @@
// ADISPLAY_ID_NONE to target the focused display. If there is no display target (i.e.
// std::nullopt), all events will be ignored.
std::optional<int32_t> mDisplayId;
- int32_t mOrientation;
+ ui::Rotation mOrientation;
std::shared_ptr<PointerControllerInterface> mPointerController;
diff --git a/services/inputflinger/reader/mapper/KeyboardInputMapper.cpp b/services/inputflinger/reader/mapper/KeyboardInputMapper.cpp
index da9413e..44f0dfe 100644
--- a/services/inputflinger/reader/mapper/KeyboardInputMapper.cpp
+++ b/services/inputflinger/reader/mapper/KeyboardInputMapper.cpp
@@ -20,11 +20,13 @@
#include "KeyboardInputMapper.h"
+#include <ui/Rotation.h>
+
namespace android {
// --- Static Definitions ---
-static int32_t rotateKeyCode(int32_t keyCode, int32_t orientation) {
+static int32_t rotateKeyCode(int32_t keyCode, ui::Rotation orientation) {
static constexpr int32_t KEYCODE_ROTATION_MAP[][4] = {
// key codes enumerated counter-clockwise with the original (unrotated) key first
// no rotation, 90 degree rotation, 180 degree rotation, 270 degree rotation
@@ -42,11 +44,10 @@
AKEYCODE_SYSTEM_NAVIGATION_RIGHT, AKEYCODE_SYSTEM_NAVIGATION_UP},
};
- LOG_ALWAYS_FATAL_IF(orientation < 0 || orientation > 3, "Invalid orientation: %d", orientation);
- if (orientation != DISPLAY_ORIENTATION_0) {
+ if (orientation != ui::ROTATION_0) {
for (const auto& rotation : KEYCODE_ROTATION_MAP) {
- if (rotation[DISPLAY_ORIENTATION_0] == keyCode) {
- return rotation[orientation];
+ if (rotation[static_cast<size_t>(ui::ROTATION_0)] == keyCode) {
+ return rotation[static_cast<size_t>(orientation)];
}
}
}
@@ -100,11 +101,11 @@
return mSource;
}
-int32_t KeyboardInputMapper::getOrientation() {
+ui::Rotation KeyboardInputMapper::getOrientation() {
if (mViewport) {
return mViewport->orientation;
}
- return DISPLAY_ORIENTATION_0;
+ return ui::ROTATION_0;
}
int32_t KeyboardInputMapper::getDisplayId() {
diff --git a/services/inputflinger/reader/mapper/KeyboardInputMapper.h b/services/inputflinger/reader/mapper/KeyboardInputMapper.h
index 11d5ad2..0526fd8 100644
--- a/services/inputflinger/reader/mapper/KeyboardInputMapper.h
+++ b/services/inputflinger/reader/mapper/KeyboardInputMapper.h
@@ -82,7 +82,7 @@
void configureParameters();
void dumpParameters(std::string& dump) const;
- int32_t getOrientation();
+ ui::Rotation getOrientation();
int32_t getDisplayId();
[[nodiscard]] std::list<NotifyArgs> processKey(nsecs_t when, nsecs_t readTime, bool down,
diff --git a/services/inputflinger/reader/mapper/MultiTouchInputMapper.cpp b/services/inputflinger/reader/mapper/MultiTouchInputMapper.cpp
index b193dff..633efc6 100644
--- a/services/inputflinger/reader/mapper/MultiTouchInputMapper.cpp
+++ b/services/inputflinger/reader/mapper/MultiTouchInputMapper.cpp
@@ -16,10 +16,8 @@
#include "../Macros.h"
-#include "MultiTouchInputMapper.h"
-#if defined(__ANDROID__)
#include <android/sysprop/InputProperties.sysprop.h>
-#endif
+#include "MultiTouchInputMapper.h"
namespace android {
@@ -218,12 +216,7 @@
bool MultiTouchInputMapper::shouldSimulateStylusWithTouch() const {
static const bool SIMULATE_STYLUS_WITH_TOUCH =
-#if defined(__ANDROID__)
sysprop::InputProperties::simulate_stylus_with_touch().value_or(false);
-#else
- // Disable this developer feature where sysproperties are not available
- false;
-#endif
return SIMULATE_STYLUS_WITH_TOUCH &&
mParameters.deviceType == Parameters::DeviceType::TOUCH_SCREEN;
}
diff --git a/services/inputflinger/reader/mapper/RotaryEncoderInputMapper.cpp b/services/inputflinger/reader/mapper/RotaryEncoderInputMapper.cpp
index 06d4dc3..19a79d7 100644
--- a/services/inputflinger/reader/mapper/RotaryEncoderInputMapper.cpp
+++ b/services/inputflinger/reader/mapper/RotaryEncoderInputMapper.cpp
@@ -25,7 +25,7 @@
namespace android {
RotaryEncoderInputMapper::RotaryEncoderInputMapper(InputDeviceContext& deviceContext)
- : InputMapper(deviceContext), mOrientation(DISPLAY_ORIENTATION_0) {
+ : InputMapper(deviceContext), mOrientation(ui::ROTATION_0) {
mSource = AINPUT_SOURCE_ROTARY_ENCODER;
}
@@ -73,7 +73,7 @@
if (internalViewport) {
mOrientation = internalViewport->orientation;
} else {
- mOrientation = DISPLAY_ORIENTATION_0;
+ mOrientation = ui::ROTATION_0;
}
}
return out;
@@ -107,7 +107,7 @@
// This is not a pointer, so it's not associated with a display.
int32_t displayId = ADISPLAY_ID_NONE;
- if (mOrientation == DISPLAY_ORIENTATION_180) {
+ if (mOrientation == ui::ROTATION_180) {
scroll = -scroll;
}
diff --git a/services/inputflinger/reader/mapper/RotaryEncoderInputMapper.h b/services/inputflinger/reader/mapper/RotaryEncoderInputMapper.h
index f4352e7..cb5fd88 100644
--- a/services/inputflinger/reader/mapper/RotaryEncoderInputMapper.h
+++ b/services/inputflinger/reader/mapper/RotaryEncoderInputMapper.h
@@ -16,6 +16,8 @@
#pragma once
+#include <ui/Rotation.h>
+
#include "CursorScrollAccumulator.h"
#include "InputMapper.h"
@@ -40,7 +42,7 @@
int32_t mSource;
float mScalingFactor;
- int32_t mOrientation;
+ ui::Rotation mOrientation;
[[nodiscard]] std::list<NotifyArgs> sync(nsecs_t when, nsecs_t readTime);
};
diff --git a/services/inputflinger/reader/mapper/TouchCursorInputMapperCommon.cpp b/services/inputflinger/reader/mapper/TouchCursorInputMapperCommon.cpp
new file mode 100644
index 0000000..c12e95d
--- /dev/null
+++ b/services/inputflinger/reader/mapper/TouchCursorInputMapperCommon.cpp
@@ -0,0 +1,127 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <input/DisplayViewport.h>
+#include <stdint.h>
+#include <ui/Rotation.h>
+
+#include "EventHub.h"
+#include "InputListener.h"
+#include "InputReaderContext.h"
+
+namespace android {
+
+namespace {
+
+[[nodiscard]] std::list<NotifyArgs> synthesizeButtonKey(
+ InputReaderContext* context, int32_t action, nsecs_t when, nsecs_t readTime,
+ int32_t deviceId, uint32_t source, int32_t displayId, uint32_t policyFlags,
+ int32_t lastButtonState, int32_t currentButtonState, int32_t buttonState, int32_t keyCode) {
+ std::list<NotifyArgs> out;
+ if ((action == AKEY_EVENT_ACTION_DOWN && !(lastButtonState & buttonState) &&
+ (currentButtonState & buttonState)) ||
+ (action == AKEY_EVENT_ACTION_UP && (lastButtonState & buttonState) &&
+ !(currentButtonState & buttonState))) {
+ out.push_back(NotifyKeyArgs(context->getNextId(), when, readTime, deviceId, source,
+ displayId, policyFlags, action, 0, keyCode, 0,
+ context->getGlobalMetaState(), when));
+ }
+ return out;
+}
+
+} // namespace
+
+ui::Rotation getInverseRotation(ui::Rotation orientation) {
+ switch (orientation) {
+ case ui::ROTATION_90:
+ return ui::ROTATION_270;
+ case ui::ROTATION_270:
+ return ui::ROTATION_90;
+ default:
+ return orientation;
+ }
+}
+
+void rotateDelta(ui::Rotation orientation, float* deltaX, float* deltaY) {
+ float temp;
+ switch (orientation) {
+ case ui::ROTATION_90:
+ temp = *deltaX;
+ *deltaX = *deltaY;
+ *deltaY = -temp;
+ break;
+
+ case ui::ROTATION_180:
+ *deltaX = -*deltaX;
+ *deltaY = -*deltaY;
+ break;
+
+ case ui::ROTATION_270:
+ temp = *deltaX;
+ *deltaX = -*deltaY;
+ *deltaY = temp;
+ break;
+
+ default:
+ break;
+ }
+}
+
+bool isPointerDown(int32_t buttonState) {
+ return buttonState &
+ (AMOTION_EVENT_BUTTON_PRIMARY | AMOTION_EVENT_BUTTON_SECONDARY |
+ AMOTION_EVENT_BUTTON_TERTIARY);
+}
+
+[[nodiscard]] std::list<NotifyArgs> synthesizeButtonKeys(
+ InputReaderContext* context, int32_t action, nsecs_t when, nsecs_t readTime,
+ int32_t deviceId, uint32_t source, int32_t displayId, uint32_t policyFlags,
+ int32_t lastButtonState, int32_t currentButtonState) {
+ std::list<NotifyArgs> out;
+ out += synthesizeButtonKey(context, action, when, readTime, deviceId, source, displayId,
+ policyFlags, lastButtonState, currentButtonState,
+ AMOTION_EVENT_BUTTON_BACK, AKEYCODE_BACK);
+ out += synthesizeButtonKey(context, action, when, readTime, deviceId, source, displayId,
+ policyFlags, lastButtonState, currentButtonState,
+ AMOTION_EVENT_BUTTON_FORWARD, AKEYCODE_FORWARD);
+ return out;
+}
+
+std::tuple<nsecs_t /*eventTime*/, nsecs_t /*readTime*/> applyBluetoothTimestampSmoothening(
+ const InputDeviceIdentifier& identifier, nsecs_t currentEventTime, nsecs_t readTime,
+ nsecs_t lastEventTime) {
+ if (identifier.bus != BUS_BLUETOOTH) {
+ return {currentEventTime, readTime};
+ }
+
+ // Assume the fastest rate at which a Bluetooth touch device can report input events is one
+ // every 4 milliseconds, or 250 Hz. Timestamps for successive events from a Bluetooth device
+ // will be separated by at least this amount.
+ constexpr static nsecs_t MIN_BLUETOOTH_TIMESTAMP_DELTA = ms2ns(4);
+ // We define a maximum smoothing time delta so that we don't generate events too far into the
+ // future.
+ constexpr static nsecs_t MAX_BLUETOOTH_SMOOTHING_DELTA = ms2ns(32);
+ const nsecs_t smoothenedEventTime =
+ std::min(std::max(currentEventTime, lastEventTime + MIN_BLUETOOTH_TIMESTAMP_DELTA),
+ currentEventTime + MAX_BLUETOOTH_SMOOTHING_DELTA);
+ // If we are modifying the event time, treat this event as a synthetically generated event for
+ // latency tracking purposes and use the event time as the read time (zero read latency).
+ const nsecs_t smoothenedReadTime =
+ smoothenedEventTime != currentEventTime ? currentEventTime : readTime;
+ return {smoothenedEventTime, smoothenedReadTime};
+}
+
+} // namespace android
diff --git a/services/inputflinger/reader/mapper/TouchCursorInputMapperCommon.h b/services/inputflinger/reader/mapper/TouchCursorInputMapperCommon.h
index d8a4d34..3023e68 100644
--- a/services/inputflinger/reader/mapper/TouchCursorInputMapperCommon.h
+++ b/services/inputflinger/reader/mapper/TouchCursorInputMapperCommon.h
@@ -18,6 +18,7 @@
#include <input/DisplayViewport.h>
#include <stdint.h>
+#include <ui/Rotation.h>
#include "EventHub.h"
#include "InputListener.h"
@@ -25,81 +26,18 @@
namespace android {
-// --- Static Definitions ---
+ui::Rotation getInverseRotation(ui::Rotation orientation);
-static int32_t getInverseRotation(int32_t orientation) {
- switch (orientation) {
- case DISPLAY_ORIENTATION_90:
- return DISPLAY_ORIENTATION_270;
- case DISPLAY_ORIENTATION_270:
- return DISPLAY_ORIENTATION_90;
- default:
- return orientation;
- }
-}
-
-static void rotateDelta(int32_t orientation, float* deltaX, float* deltaY) {
- float temp;
- switch (orientation) {
- case DISPLAY_ORIENTATION_90:
- temp = *deltaX;
- *deltaX = *deltaY;
- *deltaY = -temp;
- break;
-
- case DISPLAY_ORIENTATION_180:
- *deltaX = -*deltaX;
- *deltaY = -*deltaY;
- break;
-
- case DISPLAY_ORIENTATION_270:
- temp = *deltaX;
- *deltaX = -*deltaY;
- *deltaY = temp;
- break;
-
- default:
- break;
- }
-}
+void rotateDelta(ui::Rotation orientation, float* deltaX, float* deltaY);
// Returns true if the pointer should be reported as being down given the specified
// button states. This determines whether the event is reported as a touch event.
-static bool isPointerDown(int32_t buttonState) {
- return buttonState &
- (AMOTION_EVENT_BUTTON_PRIMARY | AMOTION_EVENT_BUTTON_SECONDARY |
- AMOTION_EVENT_BUTTON_TERTIARY);
-}
+bool isPointerDown(int32_t buttonState);
-[[nodiscard]] static std::list<NotifyArgs> synthesizeButtonKey(
+[[nodiscard]] std::list<NotifyArgs> synthesizeButtonKeys(
InputReaderContext* context, int32_t action, nsecs_t when, nsecs_t readTime,
int32_t deviceId, uint32_t source, int32_t displayId, uint32_t policyFlags,
- int32_t lastButtonState, int32_t currentButtonState, int32_t buttonState, int32_t keyCode) {
- std::list<NotifyArgs> out;
- if ((action == AKEY_EVENT_ACTION_DOWN && !(lastButtonState & buttonState) &&
- (currentButtonState & buttonState)) ||
- (action == AKEY_EVENT_ACTION_UP && (lastButtonState & buttonState) &&
- !(currentButtonState & buttonState))) {
- out.push_back(NotifyKeyArgs(context->getNextId(), when, readTime, deviceId, source,
- displayId, policyFlags, action, 0, keyCode, 0,
- context->getGlobalMetaState(), when));
- }
- return out;
-}
-
-[[nodiscard]] static std::list<NotifyArgs> synthesizeButtonKeys(
- InputReaderContext* context, int32_t action, nsecs_t when, nsecs_t readTime,
- int32_t deviceId, uint32_t source, int32_t displayId, uint32_t policyFlags,
- int32_t lastButtonState, int32_t currentButtonState) {
- std::list<NotifyArgs> out;
- out += synthesizeButtonKey(context, action, when, readTime, deviceId, source, displayId,
- policyFlags, lastButtonState, currentButtonState,
- AMOTION_EVENT_BUTTON_BACK, AKEYCODE_BACK);
- out += synthesizeButtonKey(context, action, when, readTime, deviceId, source, displayId,
- policyFlags, lastButtonState, currentButtonState,
- AMOTION_EVENT_BUTTON_FORWARD, AKEYCODE_FORWARD);
- return out;
-}
+ int32_t lastButtonState, int32_t currentButtonState);
// For devices connected over Bluetooth, although they may produce events at a consistent rate,
// the events might end up reaching Android in a "batched" manner through the Bluetooth
@@ -110,28 +48,8 @@
// coordinates result in extremely large instantaneous velocities, which can negatively impact
// user experience. To avoid this, we augment the timestamps so that subsequent event timestamps
// differ by at least a minimum delta value.
-static std::tuple<nsecs_t /*eventTime*/, nsecs_t /*readTime*/> applyBluetoothTimestampSmoothening(
+std::tuple<nsecs_t /*eventTime*/, nsecs_t /*readTime*/> applyBluetoothTimestampSmoothening(
const InputDeviceIdentifier& identifier, nsecs_t currentEventTime, nsecs_t readTime,
- nsecs_t lastEventTime) {
- if (identifier.bus != BUS_BLUETOOTH) {
- return {currentEventTime, readTime};
- }
-
- // Assume the fastest rate at which a Bluetooth touch device can report input events is one
- // every 4 milliseconds, or 250 Hz. Timestamps for successive events from a Bluetooth device
- // will be separated by at least this amount.
- constexpr static nsecs_t MIN_BLUETOOTH_TIMESTAMP_DELTA = ms2ns(4);
- // We define a maximum smoothing time delta so that we don't generate events too far into the
- // future.
- constexpr static nsecs_t MAX_BLUETOOTH_SMOOTHING_DELTA = ms2ns(32);
- const nsecs_t smoothenedEventTime =
- std::min(std::max(currentEventTime, lastEventTime + MIN_BLUETOOTH_TIMESTAMP_DELTA),
- currentEventTime + MAX_BLUETOOTH_SMOOTHING_DELTA);
- // If we are modifying the event time, treat this event as a synthetically generated event for
- // latency tracking purposes and use the event time as the read time (zero read latency).
- const nsecs_t smoothenedReadTime =
- smoothenedEventTime != currentEventTime ? currentEventTime : readTime;
- return {smoothenedEventTime, smoothenedReadTime};
-}
+ nsecs_t lastEventTime);
} // namespace android
diff --git a/services/inputflinger/reader/mapper/TouchInputMapper.cpp b/services/inputflinger/reader/mapper/TouchInputMapper.cpp
index 5631a10..cefc44e 100644
--- a/services/inputflinger/reader/mapper/TouchInputMapper.cpp
+++ b/services/inputflinger/reader/mapper/TouchInputMapper.cpp
@@ -27,6 +27,7 @@
#include "CursorScrollAccumulator.h"
#include "TouchButtonAccumulator.h"
#include "TouchCursorInputMapperCommon.h"
+#include "ui/Rotation.h"
namespace android {
@@ -81,16 +82,14 @@
}
static std::tuple<ui::Size /*displayBounds*/, Rect /*physicalFrame*/> getNaturalDisplayInfo(
- const DisplayViewport& viewport, int32_t naturalOrientation) {
- const auto rotation = ui::toRotation(naturalOrientation);
-
+ const DisplayViewport& viewport, ui::Rotation naturalOrientation) {
ui::Size rotatedDisplaySize{viewport.deviceWidth, viewport.deviceHeight};
- if (rotation == ui::ROTATION_90 || rotation == ui::ROTATION_270) {
+ if (naturalOrientation == ui::ROTATION_90 || naturalOrientation == ui::ROTATION_270) {
std::swap(rotatedDisplaySize.width, rotatedDisplaySize.height);
}
- ui::Transform rotate(ui::Transform::toRotationFlags(rotation), rotatedDisplaySize.width,
- rotatedDisplaySize.height);
+ ui::Transform rotate(ui::Transform::toRotationFlags(naturalOrientation),
+ rotatedDisplaySize.width, rotatedDisplaySize.height);
Rect physicalFrame{viewport.physicalLeft, viewport.physicalTop, viewport.physicalRight,
viewport.physicalBottom};
@@ -133,7 +132,7 @@
mTouchButtonAccumulator(deviceContext),
mSource(0),
mDeviceMode(DeviceMode::DISABLED),
- mInputDeviceOrientation(DISPLAY_ORIENTATION_0) {}
+ mInputDeviceOrientation(ui::ROTATION_0) {}
TouchInputMapper::~TouchInputMapper() {}
@@ -424,18 +423,18 @@
getDeviceContext().getConfiguration().tryGetProperty("touch.orientationAware",
mParameters.orientationAware);
- mParameters.orientation = Parameters::Orientation::ORIENTATION_0;
+ mParameters.orientation = ui::ROTATION_0;
std::string orientationString;
if (getDeviceContext().getConfiguration().tryGetProperty("touch.orientation",
orientationString)) {
if (mParameters.deviceType != Parameters::DeviceType::TOUCH_SCREEN) {
ALOGW("The configuration 'touch.orientation' is only supported for touchscreens.");
} else if (orientationString == "ORIENTATION_90") {
- mParameters.orientation = Parameters::Orientation::ORIENTATION_90;
+ mParameters.orientation = ui::ROTATION_90;
} else if (orientationString == "ORIENTATION_180") {
- mParameters.orientation = Parameters::Orientation::ORIENTATION_180;
+ mParameters.orientation = ui::ROTATION_180;
} else if (orientationString == "ORIENTATION_270") {
- mParameters.orientation = Parameters::Orientation::ORIENTATION_270;
+ mParameters.orientation = ui::ROTATION_270;
} else if (orientationString != "ORIENTATION_0") {
ALOGW("Invalid value for touch.orientation: '%s'", orientationString.c_str());
}
@@ -812,8 +811,8 @@
// Note that the maximum value reported is an inclusive maximum value so it is one
// unit less than the total width or height of the display.
switch (mInputDeviceOrientation) {
- case DISPLAY_ORIENTATION_90:
- case DISPLAY_ORIENTATION_270:
+ case ui::ROTATION_90:
+ case ui::ROTATION_270:
mOrientedXPrecision = mYPrecision;
mOrientedYPrecision = mXPrecision;
@@ -923,8 +922,8 @@
// Apply the inverse of the input device orientation so that the input device is
// configured in the same orientation as the viewport. The input device orientation will
// be re-applied by mInputDeviceOrientation.
- const int32_t naturalDeviceOrientation =
- (mViewport.orientation - static_cast<int32_t>(mParameters.orientation) + 4) % 4;
+ const ui::Rotation naturalDeviceOrientation =
+ mViewport.orientation - mParameters.orientation;
std::tie(mDisplayBounds, mPhysicalFrameInDisplay) =
getNaturalDisplayInfo(mViewport, naturalDeviceOrientation);
@@ -935,7 +934,7 @@
// when the display rotation is applied later as a part of the per-window transform, we
// get the expected screen coordinates.
mInputDeviceOrientation = mParameters.orientationAware
- ? DISPLAY_ORIENTATION_0
+ ? ui::ROTATION_0
: getInverseRotation(mViewport.orientation);
// For orientation-aware devices that work in the un-rotated coordinate space, the
// viewport update should be skipped if it is only a change in the orientation.
@@ -943,12 +942,11 @@
mDisplayBounds == oldDisplayBounds && viewportOrientationChanged;
// Apply the input device orientation for the device.
- mInputDeviceOrientation =
- (mInputDeviceOrientation + static_cast<int32_t>(mParameters.orientation)) % 4;
+ mInputDeviceOrientation = mInputDeviceOrientation + mParameters.orientation;
} else {
mDisplayBounds = rawSize;
mPhysicalFrameInDisplay = Rect{mDisplayBounds};
- mInputDeviceOrientation = DISPLAY_ORIENTATION_0;
+ mInputDeviceOrientation = ui::ROTATION_0;
}
}
@@ -2349,7 +2347,7 @@
float left, top, right, bottom;
switch (mInputDeviceOrientation) {
- case DISPLAY_ORIENTATION_90:
+ case ui::ROTATION_90:
left = float(rawTop - mRawPointerAxes.y.minValue) * mYScale;
right = float(rawBottom - mRawPointerAxes.y.minValue) * mYScale;
bottom = float(mRawPointerAxes.x.maxValue - rawLeft) * mXScale;
@@ -2360,7 +2358,7 @@
(mOrientedRanges.orientation->max - mOrientedRanges.orientation->min);
}
break;
- case DISPLAY_ORIENTATION_180:
+ case ui::ROTATION_180:
left = float(mRawPointerAxes.x.maxValue - rawRight) * mXScale;
right = float(mRawPointerAxes.x.maxValue - rawLeft) * mXScale;
bottom = float(mRawPointerAxes.y.maxValue - rawTop) * mYScale;
@@ -2371,7 +2369,7 @@
(mOrientedRanges.orientation->max - mOrientedRanges.orientation->min);
}
break;
- case DISPLAY_ORIENTATION_270:
+ case ui::ROTATION_270:
left = float(mRawPointerAxes.y.maxValue - rawBottom) * mYScale;
right = float(mRawPointerAxes.y.maxValue - rawTop) * mYScale;
bottom = float(rawRight - mRawPointerAxes.x.minValue) * mXScale;
@@ -3805,19 +3803,19 @@
// 180 - reverse x, y.
// 270 - swap x/y and reverse x.
switch (mInputDeviceOrientation) {
- case DISPLAY_ORIENTATION_0:
+ case ui::ROTATION_0:
x = xScaled;
y = yScaled;
break;
- case DISPLAY_ORIENTATION_90:
+ case ui::ROTATION_90:
y = xScaledMax;
x = yScaled;
break;
- case DISPLAY_ORIENTATION_180:
+ case ui::ROTATION_180:
x = xScaledMax;
y = yScaledMax;
break;
- case DISPLAY_ORIENTATION_270:
+ case ui::ROTATION_270:
y = xScaled;
x = yScaledMax;
break;
diff --git a/services/inputflinger/reader/mapper/TouchInputMapper.h b/services/inputflinger/reader/mapper/TouchInputMapper.h
index 3962b2a..34ba625 100644
--- a/services/inputflinger/reader/mapper/TouchInputMapper.h
+++ b/services/inputflinger/reader/mapper/TouchInputMapper.h
@@ -17,6 +17,7 @@
#pragma once
#include <stdint.h>
+#include <ui/Rotation.h>
#include "CursorButtonAccumulator.h"
#include "CursorScrollAccumulator.h"
@@ -218,15 +219,7 @@
bool associatedDisplayIsExternal;
bool orientationAware;
- enum class Orientation : int32_t {
- ORIENTATION_0 = DISPLAY_ORIENTATION_0,
- ORIENTATION_90 = DISPLAY_ORIENTATION_90,
- ORIENTATION_180 = DISPLAY_ORIENTATION_180,
- ORIENTATION_270 = DISPLAY_ORIENTATION_270,
-
- ftl_last = ORIENTATION_270
- };
- Orientation orientation;
+ ui::Rotation orientation;
bool hasButtonUnderPad;
std::string uniqueDisplayId;
@@ -424,7 +417,7 @@
// The orientation of the input device relative to that of the display panel. It specifies
// the rotation of the input device coordinates required to produce the display panel
// orientation, so it will depend on whether the device is orientation aware.
- int32_t mInputDeviceOrientation;
+ ui::Rotation mInputDeviceOrientation;
// Translation and scaling factors, orientation-independent.
float mXScale;
diff --git a/services/inputflinger/reader/mapper/TouchpadInputMapper.cpp b/services/inputflinger/reader/mapper/TouchpadInputMapper.cpp
new file mode 100644
index 0000000..de6e4b0
--- /dev/null
+++ b/services/inputflinger/reader/mapper/TouchpadInputMapper.cpp
@@ -0,0 +1,365 @@
+/*
+ * 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 "../Macros.h"
+
+#include <chrono>
+
+#include <android/input.h>
+#include <log/log_main.h>
+#include "TouchCursorInputMapperCommon.h"
+#include "TouchpadInputMapper.h"
+
+namespace android {
+
+namespace {
+
+short getMaxTouchCount(const InputDeviceContext& context) {
+ if (context.hasKeyCode(BTN_TOOL_QUINTTAP)) return 5;
+ if (context.hasKeyCode(BTN_TOOL_QUADTAP)) return 4;
+ if (context.hasKeyCode(BTN_TOOL_TRIPLETAP)) return 3;
+ if (context.hasKeyCode(BTN_TOOL_DOUBLETAP)) return 2;
+ if (context.hasKeyCode(BTN_TOOL_FINGER)) return 1;
+ return 0;
+}
+
+HardwareProperties createHardwareProperties(const InputDeviceContext& context) {
+ HardwareProperties props;
+ RawAbsoluteAxisInfo absMtPositionX;
+ context.getAbsoluteAxisInfo(ABS_MT_POSITION_X, &absMtPositionX);
+ props.left = absMtPositionX.minValue;
+ props.right = absMtPositionX.maxValue;
+ props.res_x = absMtPositionX.resolution;
+
+ RawAbsoluteAxisInfo absMtPositionY;
+ context.getAbsoluteAxisInfo(ABS_MT_POSITION_Y, &absMtPositionY);
+ props.top = absMtPositionY.minValue;
+ props.bottom = absMtPositionY.maxValue;
+ props.res_y = absMtPositionY.resolution;
+
+ RawAbsoluteAxisInfo absMtOrientation;
+ context.getAbsoluteAxisInfo(ABS_MT_ORIENTATION, &absMtOrientation);
+ props.orientation_minimum = absMtOrientation.minValue;
+ props.orientation_maximum = absMtOrientation.maxValue;
+
+ RawAbsoluteAxisInfo absMtSlot;
+ context.getAbsoluteAxisInfo(ABS_MT_SLOT, &absMtSlot);
+ props.max_finger_cnt = absMtSlot.maxValue - absMtSlot.minValue + 1;
+ props.max_touch_cnt = getMaxTouchCount(context);
+
+ // T5R2 ("Track 5, Report 2") is a feature of some old Synaptics touchpads that could track 5
+ // fingers but only report the coordinates of 2 of them. We don't know of any external touchpads
+ // that did this, so assume false.
+ props.supports_t5r2 = false;
+
+ props.support_semi_mt = context.hasInputProperty(INPUT_PROP_SEMI_MT);
+ props.is_button_pad = context.hasInputProperty(INPUT_PROP_BUTTONPAD);
+
+ // Mouse-only properties, which will always be false.
+ props.has_wheel = false;
+ props.wheel_is_hi_res = false;
+
+ // Linux Kernel haptic touchpad support isn't merged yet, so for now assume that no touchpads
+ // are haptic.
+ props.is_haptic_pad = false;
+ return props;
+}
+
+void gestureInterpreterCallback(void* clientData, const Gesture* gesture) {
+ TouchpadInputMapper* mapper = static_cast<TouchpadInputMapper*>(clientData);
+ mapper->consumeGesture(gesture);
+}
+
+uint32_t gesturesButtonToMotionEventButton(uint32_t gesturesButton) {
+ switch (gesturesButton) {
+ case GESTURES_BUTTON_LEFT:
+ return AMOTION_EVENT_BUTTON_PRIMARY;
+ case GESTURES_BUTTON_MIDDLE:
+ return AMOTION_EVENT_BUTTON_TERTIARY;
+ case GESTURES_BUTTON_RIGHT:
+ return AMOTION_EVENT_BUTTON_SECONDARY;
+ case GESTURES_BUTTON_BACK:
+ return AMOTION_EVENT_BUTTON_BACK;
+ case GESTURES_BUTTON_FORWARD:
+ return AMOTION_EVENT_BUTTON_FORWARD;
+ default:
+ return 0;
+ }
+}
+
+} // namespace
+
+TouchpadInputMapper::TouchpadInputMapper(InputDeviceContext& deviceContext)
+ : InputMapper(deviceContext),
+ mGestureInterpreter(NewGestureInterpreter(), DeleteGestureInterpreter),
+ mPointerController(getContext()->getPointerController(getDeviceId())),
+ mTouchButtonAccumulator(deviceContext) {
+ mGestureInterpreter->Initialize(GESTURES_DEVCLASS_TOUCHPAD);
+ mGestureInterpreter->SetHardwareProperties(createHardwareProperties(deviceContext));
+ // Even though we don't explicitly delete copy/move semantics, it's safe to
+ // give away a pointer to TouchpadInputMapper here because
+ // 1) mGestureInterpreter's lifecycle is determined by TouchpadInputMapper, and
+ // 2) TouchpadInputMapper is stored as a unique_ptr and not moved.
+ mGestureInterpreter->SetCallback(gestureInterpreterCallback, this);
+ // TODO(b/251196347): set a property provider, so we can change gesture properties.
+ // TODO(b/251196347): set a timer provider, so the library can use timers.
+
+ RawAbsoluteAxisInfo slotAxisInfo;
+ getAbsoluteAxisInfo(ABS_MT_SLOT, &slotAxisInfo);
+ if (!slotAxisInfo.valid || slotAxisInfo.maxValue <= 0) {
+ ALOGW("Touchpad \"%s\" doesn't have a valid ABS_MT_SLOT axis, and probably won't work "
+ "properly.",
+ getDeviceName().c_str());
+ }
+ mMotionAccumulator.configure(getDeviceContext(), slotAxisInfo.maxValue + 1, true);
+ mTouchButtonAccumulator.configure();
+}
+
+TouchpadInputMapper::~TouchpadInputMapper() {
+ if (mPointerController != nullptr) {
+ mPointerController->fade(PointerControllerInterface::Transition::IMMEDIATE);
+ }
+}
+
+uint32_t TouchpadInputMapper::getSources() const {
+ return AINPUT_SOURCE_MOUSE | AINPUT_SOURCE_TOUCHPAD;
+}
+
+std::list<NotifyArgs> TouchpadInputMapper::reset(nsecs_t when) {
+ mCursorButtonAccumulator.reset(getDeviceContext());
+ mTouchButtonAccumulator.reset();
+ mMscTimestamp = 0;
+
+ mButtonState = 0;
+ return InputMapper::reset(when);
+}
+
+std::list<NotifyArgs> TouchpadInputMapper::process(const RawEvent* rawEvent) {
+ std::list<NotifyArgs> out = {};
+ if (rawEvent->type == EV_SYN && rawEvent->code == SYN_REPORT) {
+ out = sync(rawEvent->when, rawEvent->readTime);
+ }
+ if (rawEvent->type == EV_MSC && rawEvent->code == MSC_TIMESTAMP) {
+ mMscTimestamp = rawEvent->value;
+ }
+ mCursorButtonAccumulator.process(rawEvent);
+ mMotionAccumulator.process(rawEvent);
+ mTouchButtonAccumulator.process(rawEvent);
+ return out;
+}
+
+std::list<NotifyArgs> TouchpadInputMapper::sync(nsecs_t when, nsecs_t readTime) {
+ HardwareState hwState;
+ // The gestures library uses doubles to represent timestamps in seconds.
+ hwState.timestamp = std::chrono::duration<stime_t>(std::chrono::nanoseconds(when)).count();
+ hwState.msc_timestamp =
+ std::chrono::duration<stime_t>(std::chrono::microseconds(mMscTimestamp)).count();
+
+ hwState.buttons_down = 0;
+ if (mCursorButtonAccumulator.isLeftPressed()) {
+ hwState.buttons_down |= GESTURES_BUTTON_LEFT;
+ }
+ if (mCursorButtonAccumulator.isMiddlePressed()) {
+ hwState.buttons_down |= GESTURES_BUTTON_MIDDLE;
+ }
+ if (mCursorButtonAccumulator.isRightPressed()) {
+ hwState.buttons_down |= GESTURES_BUTTON_RIGHT;
+ }
+ if (mCursorButtonAccumulator.isBackPressed() || mCursorButtonAccumulator.isSidePressed()) {
+ hwState.buttons_down |= GESTURES_BUTTON_BACK;
+ }
+ if (mCursorButtonAccumulator.isForwardPressed() || mCursorButtonAccumulator.isExtraPressed()) {
+ hwState.buttons_down |= GESTURES_BUTTON_FORWARD;
+ }
+
+ std::vector<FingerState> fingers;
+ for (size_t i = 0; i < mMotionAccumulator.getSlotCount(); i++) {
+ MultiTouchMotionAccumulator::Slot slot = mMotionAccumulator.getSlot(i);
+ if (slot.isInUse()) {
+ FingerState& fingerState = fingers.emplace_back();
+ fingerState = {};
+ fingerState.touch_major = slot.getTouchMajor();
+ fingerState.touch_minor = slot.getTouchMinor();
+ fingerState.width_major = slot.getToolMajor();
+ fingerState.width_minor = slot.getToolMinor();
+ fingerState.pressure = slot.getPressure();
+ fingerState.orientation = slot.getOrientation();
+ fingerState.position_x = slot.getX();
+ fingerState.position_y = slot.getY();
+ fingerState.tracking_id = slot.getTrackingId();
+ }
+ }
+ hwState.fingers = fingers.data();
+ hwState.finger_cnt = fingers.size();
+ hwState.touch_cnt = mTouchButtonAccumulator.getTouchCount();
+
+ mProcessing = true;
+ mGestureInterpreter->PushHardwareState(&hwState);
+ mProcessing = false;
+
+ std::list<NotifyArgs> out = processGestures(when, readTime);
+
+ mMotionAccumulator.finishSync();
+ mMscTimestamp = 0;
+ return out;
+}
+
+void TouchpadInputMapper::consumeGesture(const Gesture* gesture) {
+ ALOGD("Gesture ready: %s", gesture->String().c_str());
+ if (!mProcessing) {
+ ALOGE("Received gesture outside of the normal processing flow; ignoring it.");
+ return;
+ }
+ mGesturesToProcess.push_back(*gesture);
+}
+
+std::list<NotifyArgs> TouchpadInputMapper::processGestures(nsecs_t when, nsecs_t readTime) {
+ std::list<NotifyArgs> out = {};
+ for (Gesture& gesture : mGesturesToProcess) {
+ switch (gesture.type) {
+ case kGestureTypeMove:
+ out.push_back(handleMove(when, readTime, gesture));
+ break;
+ case kGestureTypeButtonsChange:
+ out += handleButtonsChange(when, readTime, gesture);
+ break;
+ default:
+ // TODO(b/251196347): handle more gesture types.
+ break;
+ }
+ }
+ mGesturesToProcess.clear();
+ return out;
+}
+
+NotifyArgs TouchpadInputMapper::handleMove(nsecs_t when, nsecs_t readTime, const Gesture& gesture) {
+ PointerProperties props;
+ props.clear();
+ props.id = 0;
+ props.toolType = AMOTION_EVENT_TOOL_TYPE_FINGER;
+
+ mPointerController->setPresentation(PointerControllerInterface::Presentation::POINTER);
+ mPointerController->move(gesture.details.move.dx, gesture.details.move.dy);
+ mPointerController->unfade(PointerControllerInterface::Transition::IMMEDIATE);
+ float xCursorPosition, yCursorPosition;
+ mPointerController->getPosition(&xCursorPosition, &yCursorPosition);
+
+ PointerCoords coords;
+ coords.clear();
+ coords.setAxisValue(AMOTION_EVENT_AXIS_X, xCursorPosition);
+ coords.setAxisValue(AMOTION_EVENT_AXIS_Y, yCursorPosition);
+ coords.setAxisValue(AMOTION_EVENT_AXIS_RELATIVE_X, gesture.details.move.dx);
+ coords.setAxisValue(AMOTION_EVENT_AXIS_RELATIVE_Y, gesture.details.move.dy);
+ const bool down = isPointerDown(mButtonState);
+ coords.setAxisValue(AMOTION_EVENT_AXIS_PRESSURE, down ? 1.0f : 0.0f);
+
+ const int32_t action = down ? AMOTION_EVENT_ACTION_MOVE : AMOTION_EVENT_ACTION_HOVER_MOVE;
+ return makeMotionArgs(when, readTime, action, /* actionButton= */ 0, mButtonState,
+ /* pointerCount= */ 1, &props, &coords, xCursorPosition, yCursorPosition);
+}
+
+std::list<NotifyArgs> TouchpadInputMapper::handleButtonsChange(nsecs_t when, nsecs_t readTime,
+ const Gesture& gesture) {
+ std::list<NotifyArgs> out = {};
+
+ mPointerController->setPresentation(PointerControllerInterface::Presentation::POINTER);
+ mPointerController->unfade(PointerControllerInterface::Transition::IMMEDIATE);
+
+ PointerProperties props;
+ props.clear();
+ props.id = 0;
+ props.toolType = AMOTION_EVENT_TOOL_TYPE_FINGER;
+
+ float xCursorPosition, yCursorPosition;
+ mPointerController->getPosition(&xCursorPosition, &yCursorPosition);
+
+ PointerCoords coords;
+ coords.clear();
+ coords.setAxisValue(AMOTION_EVENT_AXIS_X, xCursorPosition);
+ coords.setAxisValue(AMOTION_EVENT_AXIS_Y, yCursorPosition);
+ coords.setAxisValue(AMOTION_EVENT_AXIS_RELATIVE_X, 0);
+ coords.setAxisValue(AMOTION_EVENT_AXIS_RELATIVE_Y, 0);
+ const uint32_t buttonsPressed = gesture.details.buttons.down;
+ bool pointerDown = isPointerDown(mButtonState) ||
+ buttonsPressed &
+ (GESTURES_BUTTON_LEFT | GESTURES_BUTTON_MIDDLE | GESTURES_BUTTON_RIGHT);
+ coords.setAxisValue(AMOTION_EVENT_AXIS_PRESSURE, pointerDown ? 1.0f : 0.0f);
+
+ uint32_t newButtonState = mButtonState;
+ std::list<NotifyArgs> pressEvents = {};
+ for (uint32_t button = 1; button <= GESTURES_BUTTON_FORWARD; button <<= 1) {
+ if (buttonsPressed & button) {
+ uint32_t actionButton = gesturesButtonToMotionEventButton(button);
+ newButtonState |= actionButton;
+ pressEvents.push_back(makeMotionArgs(when, readTime, AMOTION_EVENT_ACTION_BUTTON_PRESS,
+ actionButton, newButtonState,
+ /* pointerCount= */ 1, &props, &coords,
+ xCursorPosition, yCursorPosition));
+ }
+ }
+ if (!isPointerDown(mButtonState) && isPointerDown(newButtonState)) {
+ mDownTime = when;
+ out.push_back(makeMotionArgs(when, readTime, AMOTION_EVENT_ACTION_DOWN,
+ /* actionButton= */ 0, newButtonState, /* pointerCount= */ 1,
+ &props, &coords, xCursorPosition, yCursorPosition));
+ }
+ out.splice(out.end(), pressEvents);
+
+ // The same button may be in both down and up in the same gesture, in which case we should treat
+ // it as having gone down and then up. So, we treat a single button change gesture as two state
+ // 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);
+ newButtonState &= ~actionButton;
+ out.push_back(makeMotionArgs(when, readTime, AMOTION_EVENT_ACTION_BUTTON_RELEASE,
+ actionButton, newButtonState, /* pointerCount= */ 1,
+ &props, &coords, xCursorPosition, yCursorPosition));
+ }
+ }
+ if (isPointerDown(mButtonState) && !isPointerDown(newButtonState)) {
+ coords.setAxisValue(AMOTION_EVENT_AXIS_PRESSURE, 0.0f);
+ out.push_back(makeMotionArgs(when, readTime, AMOTION_EVENT_ACTION_UP, /* actionButton= */ 0,
+ newButtonState, /* pointerCount= */ 1, &props, &coords,
+ xCursorPosition, yCursorPosition));
+ }
+ mButtonState = newButtonState;
+ return out;
+}
+
+NotifyMotionArgs TouchpadInputMapper::makeMotionArgs(nsecs_t when, nsecs_t readTime, int32_t action,
+ int32_t actionButton, int32_t buttonState,
+ uint32_t pointerCount,
+ const PointerProperties* pointerProperties,
+ const PointerCoords* pointerCoords,
+ float xCursorPosition, float yCursorPosition) {
+ // TODO(b/260226362): consider what the appropriate source for these events is.
+ const uint32_t source = AINPUT_SOURCE_MOUSE;
+
+ return NotifyMotionArgs(getContext()->getNextId(), when, readTime, getDeviceId(), source,
+ mPointerController->getDisplayId(), /* policyFlags= */ 0, action,
+ /* actionButton= */ actionButton, /* flags= */ 0,
+ getContext()->getGlobalMetaState(), buttonState,
+ MotionClassification::NONE, AMOTION_EVENT_EDGE_FLAG_NONE, pointerCount,
+ pointerProperties, pointerCoords,
+ /* xPrecision= */ 1.0f, /* yPrecision= */ 1.0f, xCursorPosition,
+ yCursorPosition, /* downTime= */ mDownTime, /* videoFrames= */ {});
+}
+
+} // namespace android
diff --git a/services/inputflinger/reader/mapper/TouchpadInputMapper.h b/services/inputflinger/reader/mapper/TouchpadInputMapper.h
new file mode 100644
index 0000000..fe6b1fe
--- /dev/null
+++ b/services/inputflinger/reader/mapper/TouchpadInputMapper.h
@@ -0,0 +1,78 @@
+/*
+ * 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 <memory>
+
+#include <PointerControllerInterface.h>
+
+#include "EventHub.h"
+#include "InputDevice.h"
+#include "InputMapper.h"
+#include "NotifyArgs.h"
+#include "accumulator/CursorButtonAccumulator.h"
+#include "accumulator/MultiTouchMotionAccumulator.h"
+#include "accumulator/TouchButtonAccumulator.h"
+
+#include "include/gestures.h"
+
+namespace android {
+
+class TouchpadInputMapper : public InputMapper {
+public:
+ explicit TouchpadInputMapper(InputDeviceContext& deviceContext);
+ ~TouchpadInputMapper();
+
+ uint32_t getSources() const override;
+ [[nodiscard]] std::list<NotifyArgs> reset(nsecs_t when) override;
+ [[nodiscard]] std::list<NotifyArgs> process(const RawEvent* rawEvent) override;
+
+ void consumeGesture(const Gesture* gesture);
+
+private:
+ [[nodiscard]] std::list<NotifyArgs> sync(nsecs_t when, nsecs_t readTime);
+ [[nodiscard]] std::list<NotifyArgs> processGestures(nsecs_t when, nsecs_t readTime);
+ NotifyArgs handleMove(nsecs_t when, nsecs_t readTime, const Gesture& gesture);
+ [[nodiscard]] std::list<NotifyArgs> handleButtonsChange(nsecs_t when, nsecs_t readTime,
+ const Gesture& gesture);
+
+ NotifyMotionArgs makeMotionArgs(nsecs_t when, nsecs_t readTime, int32_t action,
+ int32_t actionButton, int32_t buttonState,
+ uint32_t pointerCount,
+ const PointerProperties* pointerProperties,
+ const PointerCoords* pointerCoords, float xCursorPosition,
+ float yCursorPosition);
+
+ std::unique_ptr<gestures::GestureInterpreter, void (*)(gestures::GestureInterpreter*)>
+ mGestureInterpreter;
+ std::shared_ptr<PointerControllerInterface> mPointerController;
+
+ CursorButtonAccumulator mCursorButtonAccumulator;
+ MultiTouchMotionAccumulator mMotionAccumulator;
+ TouchButtonAccumulator mTouchButtonAccumulator;
+ int32_t mMscTimestamp = 0;
+
+ bool mProcessing = false;
+ std::vector<Gesture> mGesturesToProcess;
+
+ // The current button state according to the gestures library, but converted into MotionEvent
+ // button values (AMOTION_EVENT_BUTTON_...).
+ uint32_t mButtonState = 0;
+ nsecs_t mDownTime = 0;
+};
+
+} // namespace android
diff --git a/services/inputflinger/reader/mapper/accumulator/CursorButtonAccumulator.h b/services/inputflinger/reader/mapper/accumulator/CursorButtonAccumulator.h
index ed4c789..1380604 100644
--- a/services/inputflinger/reader/mapper/accumulator/CursorButtonAccumulator.h
+++ b/services/inputflinger/reader/mapper/accumulator/CursorButtonAccumulator.h
@@ -32,6 +32,14 @@
void process(const RawEvent* rawEvent);
uint32_t getButtonState() const;
+ inline bool isLeftPressed() const { return mBtnLeft; }
+ inline bool isRightPressed() const { return mBtnRight; }
+ inline bool isMiddlePressed() const { return mBtnMiddle; }
+ inline bool isBackPressed() const { return mBtnBack; }
+ inline bool isSidePressed() const { return mBtnSide; }
+ inline bool isForwardPressed() const { return mBtnForward; }
+ inline bool isExtraPressed() const { return mBtnExtra; }
+ inline bool isTaskPressed() const { return mBtnTask; }
private:
bool mBtnLeft;
diff --git a/services/inputflinger/reader/mapper/accumulator/TouchButtonAccumulator.cpp b/services/inputflinger/reader/mapper/accumulator/TouchButtonAccumulator.cpp
index bc23a8e..6601702 100644
--- a/services/inputflinger/reader/mapper/accumulator/TouchButtonAccumulator.cpp
+++ b/services/inputflinger/reader/mapper/accumulator/TouchButtonAccumulator.cpp
@@ -48,6 +48,7 @@
mBtnToolDoubleTap = mDeviceContext.isKeyPressed(BTN_TOOL_DOUBLETAP);
mBtnToolTripleTap = mDeviceContext.isKeyPressed(BTN_TOOL_TRIPLETAP);
mBtnToolQuadTap = mDeviceContext.isKeyPressed(BTN_TOOL_QUADTAP);
+ mBtnToolQuintTap = mDeviceContext.isKeyPressed(BTN_TOOL_QUINTTAP);
mHidUsageAccumulator.reset();
}
@@ -100,6 +101,9 @@
case BTN_TOOL_QUADTAP:
mBtnToolQuadTap = rawEvent->value;
break;
+ case BTN_TOOL_QUINTTAP:
+ mBtnToolQuintTap = rawEvent->value;
+ break;
default:
processMappedKey(rawEvent->code, rawEvent->value);
}
@@ -147,7 +151,8 @@
if (mBtnToolPen || mBtnToolBrush || mBtnToolPencil || mBtnToolAirbrush) {
return AMOTION_EVENT_TOOL_TYPE_STYLUS;
}
- if (mBtnToolFinger || mBtnToolDoubleTap || mBtnToolTripleTap || mBtnToolQuadTap) {
+ if (mBtnToolFinger || mBtnToolDoubleTap || mBtnToolTripleTap || mBtnToolQuadTap ||
+ mBtnToolQuintTap) {
return AMOTION_EVENT_TOOL_TYPE_FINGER;
}
return AMOTION_EVENT_TOOL_TYPE_UNKNOWN;
@@ -156,7 +161,7 @@
bool TouchButtonAccumulator::isToolActive() const {
return mBtnTouch || mBtnToolFinger || mBtnToolPen || mBtnToolRubber || mBtnToolBrush ||
mBtnToolPencil || mBtnToolAirbrush || mBtnToolMouse || mBtnToolLens ||
- mBtnToolDoubleTap || mBtnToolTripleTap || mBtnToolQuadTap;
+ mBtnToolDoubleTap || mBtnToolTripleTap || mBtnToolQuadTap || mBtnToolQuintTap;
}
bool TouchButtonAccumulator::isHovering() const {
@@ -171,4 +176,15 @@
return mHaveBtnTouch;
}
+int TouchButtonAccumulator::getTouchCount() const {
+ if (mBtnTouch) {
+ if (mBtnToolQuintTap) return 5;
+ if (mBtnToolQuadTap) return 4;
+ if (mBtnToolTripleTap) return 3;
+ if (mBtnToolDoubleTap) return 2;
+ if (mBtnToolFinger) return 1;
+ }
+ return 0;
+}
+
} // namespace android
diff --git a/services/inputflinger/reader/mapper/accumulator/TouchButtonAccumulator.h b/services/inputflinger/reader/mapper/accumulator/TouchButtonAccumulator.h
index c2de23c..2e70e2e 100644
--- a/services/inputflinger/reader/mapper/accumulator/TouchButtonAccumulator.h
+++ b/services/inputflinger/reader/mapper/accumulator/TouchButtonAccumulator.h
@@ -41,6 +41,7 @@
bool isHovering() const;
bool hasStylus() const;
bool hasButtonTouch() const;
+ int getTouchCount() const;
private:
bool mHaveBtnTouch{};
@@ -60,6 +61,7 @@
bool mBtnToolDoubleTap{};
bool mBtnToolTripleTap{};
bool mBtnToolQuadTap{};
+ bool mBtnToolQuintTap{};
HidUsageAccumulator mHidUsageAccumulator{};
diff --git a/services/inputflinger/tests/Android.bp b/services/inputflinger/tests/Android.bp
index 2e5bec9..53d821f 100644
--- a/services/inputflinger/tests/Android.bp
+++ b/services/inputflinger/tests/Android.bp
@@ -44,10 +44,12 @@
"FakeInputReaderPolicy.cpp",
"FakePointerController.cpp",
"FocusResolver_test.cpp",
+ "InputMapperTest.cpp",
"InputProcessor_test.cpp",
"InputProcessorConverter_test.cpp",
"InputDispatcher_test.cpp",
"InputReader_test.cpp",
+ "InstrumentedInputReader.cpp",
"LatencyTracker_test.cpp",
"NotifyArgs_test.cpp",
"PreferStylusOverTouch_test.cpp",
diff --git a/services/inputflinger/tests/FakeEventHub.cpp b/services/inputflinger/tests/FakeEventHub.cpp
index f6cf1cc..289a780 100644
--- a/services/inputflinger/tests/FakeEventHub.cpp
+++ b/services/inputflinger/tests/FakeEventHub.cpp
@@ -154,6 +154,11 @@
getDevice(deviceId)->keyCodeMapping.insert_or_assign(fromKeyCode, toKeyCode);
}
+void FakeEventHub::addKeyRemapping(int32_t deviceId, int32_t fromKeyCode, int32_t toKeyCode) const {
+ Device* device = getDevice(deviceId);
+ device->keyRemapping.insert_or_assign(fromKeyCode, toKeyCode);
+}
+
void FakeEventHub::addLed(int32_t deviceId, int32_t led, bool initialState) {
getDevice(deviceId)->leds.add(led, initialState);
}
@@ -299,7 +304,8 @@
const KeyInfo* key = getKey(device, scanCode, usageCode);
if (key) {
if (outKeycode) {
- *outKeycode = key->keyCode;
+ auto it = device->keyRemapping.find(key->keyCode);
+ *outKeycode = it != device->keyRemapping.end() ? it->second : key->keyCode;
}
if (outFlags) {
*outFlags = key->flags;
diff --git a/services/inputflinger/tests/FakeEventHub.h b/services/inputflinger/tests/FakeEventHub.h
index 21cb2f1..fb3c859 100644
--- a/services/inputflinger/tests/FakeEventHub.h
+++ b/services/inputflinger/tests/FakeEventHub.h
@@ -59,6 +59,7 @@
KeyedVector<int32_t, int32_t> absoluteAxisValue;
KeyedVector<int32_t, KeyInfo> keysByScanCode;
KeyedVector<int32_t, KeyInfo> keysByUsageCode;
+ std::unordered_map<int32_t, int32_t> keyRemapping;
KeyedVector<int32_t, bool> leds;
// fake mapping which would normally come from keyCharacterMap
std::unordered_map<int32_t, int32_t> keyCodeMapping;
@@ -132,6 +133,7 @@
void addKey(int32_t deviceId, int32_t scanCode, int32_t usageCode, int32_t keyCode,
uint32_t flags);
void addKeyCodeMapping(int32_t deviceId, int32_t fromKeyCode, int32_t toKeyCode);
+ void addKeyRemapping(int32_t deviceId, int32_t fromKeyCode, int32_t toKeyCode) const;
void addVirtualKeyDefinition(int32_t deviceId, const VirtualKeyDefinition& definition);
void addSensorAxis(int32_t deviceId, int32_t absCode, InputDeviceSensorType sensorType,
diff --git a/services/inputflinger/tests/FakeInputReaderPolicy.cpp b/services/inputflinger/tests/FakeInputReaderPolicy.cpp
index 5c6a1b8..3af4298 100644
--- a/services/inputflinger/tests/FakeInputReaderPolicy.cpp
+++ b/services/inputflinger/tests/FakeInputReaderPolicy.cpp
@@ -20,6 +20,7 @@
#include <gtest/gtest.h>
#include "TestConstants.h"
+#include "ui/Rotation.h"
namespace android {
@@ -76,12 +77,11 @@
}
void FakeInputReaderPolicy::addDisplayViewport(int32_t displayId, int32_t width, int32_t height,
- int32_t orientation, bool isActive,
+ ui::Rotation orientation, bool isActive,
const std::string& uniqueId,
std::optional<uint8_t> physicalPort,
ViewportType type) {
- const bool isRotated =
- (orientation == DISPLAY_ORIENTATION_90 || orientation == DISPLAY_ORIENTATION_270);
+ const bool isRotated = orientation == ui::ROTATION_90 || orientation == ui::ROTATION_270;
DisplayViewport v;
v.displayId = displayId;
v.orientation = orientation;
@@ -153,7 +153,7 @@
}
TouchAffineTransformation FakeInputReaderPolicy::getTouchAffineTransformation(
- const std::string& inputDeviceDescriptor, int32_t surfaceRotation) {
+ const std::string& inputDeviceDescriptor, ui::Rotation surfaceRotation) {
return transform;
}
diff --git a/services/inputflinger/tests/FakeInputReaderPolicy.h b/services/inputflinger/tests/FakeInputReaderPolicy.h
index 65fe08f..c16cda4 100644
--- a/services/inputflinger/tests/FakeInputReaderPolicy.h
+++ b/services/inputflinger/tests/FakeInputReaderPolicy.h
@@ -49,8 +49,8 @@
std::optional<DisplayViewport> getDisplayViewportByType(ViewportType type) const;
std::optional<DisplayViewport> getDisplayViewportByPort(uint8_t displayPort) const;
void addDisplayViewport(DisplayViewport viewport);
- void addDisplayViewport(int32_t displayId, int32_t width, int32_t height, int32_t orientation,
- bool isActive, const std::string& uniqueId,
+ void addDisplayViewport(int32_t displayId, int32_t width, int32_t height,
+ ui::Rotation orientation, bool isActive, const std::string& uniqueId,
std::optional<uint8_t> physicalPort, ViewportType type);
bool updateViewport(const DisplayViewport& viewport);
void addExcludedDeviceName(const std::string& deviceName);
@@ -63,7 +63,7 @@
const InputReaderConfiguration* getReaderConfiguration() const;
const std::vector<InputDeviceInfo>& getInputDevices() const;
TouchAffineTransformation getTouchAffineTransformation(const std::string& inputDeviceDescriptor,
- int32_t surfaceRotation);
+ ui::Rotation surfaceRotation);
void setTouchAffineTransformation(const TouchAffineTransformation t);
PointerCaptureRequest setPointerCapture(bool enabled);
void setShowTouches(bool enabled);
diff --git a/services/inputflinger/tests/InputDispatcher_test.cpp b/services/inputflinger/tests/InputDispatcher_test.cpp
index f3239ca..41c174a 100644
--- a/services/inputflinger/tests/InputDispatcher_test.cpp
+++ b/services/inputflinger/tests/InputDispatcher_test.cpp
@@ -110,6 +110,13 @@
*result_listener << "expected action " << MotionEvent::actionToString(action)
<< ", but got " << MotionEvent::actionToString(arg.getAction());
}
+ if (action == AMOTION_EVENT_ACTION_DOWN) {
+ if (!matches) {
+ *result_listener << "; ";
+ }
+ *result_listener << "downTime should match eventTime for ACTION_DOWN events";
+ matches &= arg.getDownTime() == arg.getEventTime();
+ }
if (action == AMOTION_EVENT_ACTION_CANCEL) {
if (!matches) {
*result_listener << "; ";
@@ -120,6 +127,10 @@
return matches;
}
+MATCHER_P(WithDownTime, downTime, "InputEvent with specified downTime") {
+ return arg.getDownTime() == downTime;
+}
+
MATCHER_P(WithSource, source, "InputEvent with specified source") {
*result_listener << "expected source " << inputEventSourceToString(source) << ", but got "
<< inputEventSourceToString(arg.getSource());
@@ -1210,6 +1221,7 @@
void consumeMotionOutsideWithZeroedCoords(int32_t expectedDisplayId = ADISPLAY_ID_DEFAULT,
int32_t expectedFlags = 0) {
InputEvent* event = consume();
+ ASSERT_NE(nullptr, event);
ASSERT_EQ(AINPUT_EVENT_TYPE_MOTION, event->getType());
const MotionEvent& motionEvent = static_cast<MotionEvent&>(*event);
EXPECT_EQ(AMOTION_EVENT_ACTION_OUTSIDE, motionEvent.getActionMasked());
@@ -1231,7 +1243,7 @@
void consumeMotionEvent(const ::testing::Matcher<MotionEvent>& matcher) {
MotionEvent* motionEvent = consumeMotion();
- ASSERT_NE(nullptr, motionEvent) << "Did not get a motion event";
+ ASSERT_NE(nullptr, motionEvent) << "Did not get a motion event, but expected " << matcher;
ASSERT_THAT(*motionEvent, matcher);
}
@@ -2056,6 +2068,7 @@
mDispatcher->waitForIdle();
InputEvent* inputEvent1 = window1->consume();
+ ASSERT_NE(inputEvent1, nullptr);
window2->assertNoEvents();
MotionEvent& motionEvent1 = static_cast<MotionEvent&>(*inputEvent1);
nsecs_t downTimeForWindow1 = motionEvent1.getDownTime();
@@ -2065,6 +2078,7 @@
mDispatcher->notifyMotion(&(args = generateTouchArgs(POINTER_1_DOWN, {{50, 50}, {150, 50}})));
mDispatcher->waitForIdle();
InputEvent* inputEvent2 = window2->consume();
+ ASSERT_NE(inputEvent2, nullptr);
MotionEvent& motionEvent2 = static_cast<MotionEvent&>(*inputEvent2);
nsecs_t downTimeForWindow2 = motionEvent2.getDownTime();
ASSERT_NE(downTimeForWindow1, downTimeForWindow2);
@@ -2074,17 +2088,13 @@
mDispatcher->notifyMotion(
&(args = generateTouchArgs(AMOTION_EVENT_ACTION_MOVE, {{50, 50}, {151, 51}})));
mDispatcher->waitForIdle();
- InputEvent* inputEvent3 = window2->consume();
- MotionEvent& motionEvent3 = static_cast<MotionEvent&>(*inputEvent3);
- ASSERT_EQ(motionEvent3.getDownTime(), downTimeForWindow2);
+ window2->consumeMotionEvent(WithDownTime(downTimeForWindow2));
// Now add new touch down on the second window
mDispatcher->notifyMotion(
&(args = generateTouchArgs(POINTER_2_DOWN, {{50, 50}, {151, 51}, {150, 50}})));
mDispatcher->waitForIdle();
- InputEvent* inputEvent4 = window2->consume();
- MotionEvent& motionEvent4 = static_cast<MotionEvent&>(*inputEvent4);
- ASSERT_EQ(motionEvent4.getDownTime(), downTimeForWindow2);
+ window2->consumeMotionEvent(WithDownTime(downTimeForWindow2));
// TODO(b/232530217): do not send the unnecessary MOVE event and delete the next line
window1->consumeMotionMove();
@@ -2094,16 +2104,12 @@
mDispatcher->notifyMotion(
&(args = generateTouchArgs(AMOTION_EVENT_ACTION_MOVE, {{51, 51}, {151, 51}})));
mDispatcher->waitForIdle();
- InputEvent* inputEvent5 = window1->consume();
- MotionEvent& motionEvent5 = static_cast<MotionEvent&>(*inputEvent5);
- ASSERT_EQ(motionEvent5.getDownTime(), downTimeForWindow1);
+ window1->consumeMotionEvent(WithDownTime(downTimeForWindow1));
mDispatcher->notifyMotion(&(
args = generateTouchArgs(POINTER_3_DOWN, {{51, 51}, {151, 51}, {150, 50}, {50, 50}})));
mDispatcher->waitForIdle();
- InputEvent* inputEvent6 = window1->consume();
- MotionEvent& motionEvent6 = static_cast<MotionEvent&>(*inputEvent6);
- ASSERT_EQ(motionEvent6.getDownTime(), downTimeForWindow1);
+ window1->consumeMotionEvent(WithDownTime(downTimeForWindow1));
}
TEST_F(InputDispatcherTest, HoverMoveEnterMouseClickAndHoverMoveExit) {
@@ -2128,10 +2134,8 @@
.x(900)
.y(400))
.build()));
- windowRight->consumeEvent(AINPUT_EVENT_TYPE_MOTION, AMOTION_EVENT_ACTION_HOVER_ENTER,
- ADISPLAY_ID_DEFAULT, 0 /* expectedFlag */);
- windowRight->consumeEvent(AINPUT_EVENT_TYPE_MOTION, AMOTION_EVENT_ACTION_HOVER_MOVE,
- ADISPLAY_ID_DEFAULT, 0 /* expectedFlag */);
+ windowRight->consumeMotionEvent(WithMotionAction(AMOTION_EVENT_ACTION_HOVER_ENTER));
+ windowRight->consumeMotionEvent(WithMotionAction(AMOTION_EVENT_ACTION_HOVER_MOVE));
// Move cursor into left window
ASSERT_EQ(InputEventInjectionResult::SUCCEEDED,
@@ -2142,12 +2146,9 @@
.x(300)
.y(400))
.build()));
- windowRight->consumeEvent(AINPUT_EVENT_TYPE_MOTION, AMOTION_EVENT_ACTION_HOVER_EXIT,
- ADISPLAY_ID_DEFAULT, 0 /* expectedFlag */);
- windowLeft->consumeEvent(AINPUT_EVENT_TYPE_MOTION, AMOTION_EVENT_ACTION_HOVER_ENTER,
- ADISPLAY_ID_DEFAULT, 0 /* expectedFlag */);
- windowLeft->consumeEvent(AINPUT_EVENT_TYPE_MOTION, AMOTION_EVENT_ACTION_HOVER_MOVE,
- ADISPLAY_ID_DEFAULT, 0 /* expectedFlag */);
+ windowRight->consumeMotionEvent(WithMotionAction(AMOTION_EVENT_ACTION_HOVER_EXIT));
+ windowLeft->consumeMotionEvent(WithMotionAction(AMOTION_EVENT_ACTION_HOVER_ENTER));
+ windowLeft->consumeMotionEvent(WithMotionAction(AMOTION_EVENT_ACTION_HOVER_MOVE));
// Inject a series of mouse events for a mouse click
ASSERT_EQ(InputEventInjectionResult::SUCCEEDED,
@@ -2170,8 +2171,7 @@
.x(300)
.y(400))
.build()));
- windowLeft->consumeEvent(AINPUT_EVENT_TYPE_MOTION, AMOTION_EVENT_ACTION_BUTTON_PRESS,
- ADISPLAY_ID_DEFAULT, 0 /* expectedFlag */);
+ windowLeft->consumeMotionEvent(WithMotionAction(AMOTION_EVENT_ACTION_BUTTON_PRESS));
ASSERT_EQ(InputEventInjectionResult::SUCCEEDED,
injectMotionEvent(mDispatcher,
@@ -2183,8 +2183,7 @@
.x(300)
.y(400))
.build()));
- windowLeft->consumeEvent(AINPUT_EVENT_TYPE_MOTION, AMOTION_EVENT_ACTION_BUTTON_RELEASE,
- ADISPLAY_ID_DEFAULT, 0 /* expectedFlag */);
+ windowLeft->consumeMotionEvent(WithMotionAction(AMOTION_EVENT_ACTION_BUTTON_RELEASE));
ASSERT_EQ(InputEventInjectionResult::SUCCEEDED,
injectMotionEvent(mDispatcher,
@@ -2205,12 +2204,47 @@
.x(900)
.y(400))
.build()));
- windowLeft->consumeEvent(AINPUT_EVENT_TYPE_MOTION, AMOTION_EVENT_ACTION_HOVER_EXIT,
- ADISPLAY_ID_DEFAULT, 0 /* expectedFlag */);
- windowRight->consumeEvent(AINPUT_EVENT_TYPE_MOTION, AMOTION_EVENT_ACTION_HOVER_ENTER,
- ADISPLAY_ID_DEFAULT, 0 /* expectedFlag */);
- windowRight->consumeEvent(AINPUT_EVENT_TYPE_MOTION, AMOTION_EVENT_ACTION_HOVER_MOVE,
- ADISPLAY_ID_DEFAULT, 0 /* expectedFlag */);
+ windowLeft->consumeMotionEvent(WithMotionAction(AMOTION_EVENT_ACTION_HOVER_EXIT));
+ windowRight->consumeMotionEvent(WithMotionAction(AMOTION_EVENT_ACTION_HOVER_ENTER));
+ windowRight->consumeMotionEvent(WithMotionAction(AMOTION_EVENT_ACTION_HOVER_MOVE));
+
+ // No more events
+ windowLeft->assertNoEvents();
+ windowRight->assertNoEvents();
+}
+
+TEST_F(InputDispatcherTest, HoverWithSpyWindows) {
+ std::shared_ptr<FakeApplicationHandle> application = std::make_shared<FakeApplicationHandle>();
+
+ sp<FakeWindowHandle> spyWindow =
+ sp<FakeWindowHandle>::make(application, mDispatcher, "Spy", ADISPLAY_ID_DEFAULT);
+ spyWindow->setFrame(Rect(0, 0, 600, 800));
+ spyWindow->setTrustedOverlay(true);
+ spyWindow->setSpy(true);
+ sp<FakeWindowHandle> window =
+ sp<FakeWindowHandle>::make(application, mDispatcher, "Window", ADISPLAY_ID_DEFAULT);
+ window->setFrame(Rect(0, 0, 600, 800));
+
+ mDispatcher->setFocusedApplication(ADISPLAY_ID_DEFAULT, application);
+ mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {spyWindow, window}}});
+
+ // Send mouse cursor to the window
+ ASSERT_EQ(InputEventInjectionResult::SUCCEEDED,
+ injectMotionEvent(mDispatcher,
+ MotionEventBuilder(AMOTION_EVENT_ACTION_HOVER_ENTER,
+ AINPUT_SOURCE_MOUSE)
+ .pointer(PointerBuilder(0, AMOTION_EVENT_TOOL_TYPE_MOUSE)
+ .x(100)
+ .y(100))
+ .build()));
+
+ window->consumeMotionEvent(AllOf(WithMotionAction(AMOTION_EVENT_ACTION_HOVER_ENTER),
+ WithSource(AINPUT_SOURCE_MOUSE)));
+ spyWindow->consumeMotionEvent(AllOf(WithMotionAction(AMOTION_EVENT_ACTION_HOVER_ENTER),
+ WithSource(AINPUT_SOURCE_MOUSE)));
+
+ window->assertNoEvents();
+ spyWindow->assertNoEvents();
}
// This test is different from the test above that HOVER_ENTER and HOVER_EXIT events are injected
@@ -2233,8 +2267,7 @@
.x(300)
.y(400))
.build()));
- window->consumeEvent(AINPUT_EVENT_TYPE_MOTION, AMOTION_EVENT_ACTION_HOVER_ENTER,
- ADISPLAY_ID_DEFAULT, 0 /* expectedFlag */);
+ window->consumeMotionEvent(WithMotionAction(AMOTION_EVENT_ACTION_HOVER_ENTER));
// Inject a series of mouse events for a mouse click
ASSERT_EQ(InputEventInjectionResult::SUCCEEDED,
@@ -2257,8 +2290,7 @@
.x(300)
.y(400))
.build()));
- window->consumeEvent(AINPUT_EVENT_TYPE_MOTION, AMOTION_EVENT_ACTION_BUTTON_PRESS,
- ADISPLAY_ID_DEFAULT, 0 /* expectedFlag */);
+ window->consumeMotionEvent(WithMotionAction(AMOTION_EVENT_ACTION_BUTTON_PRESS));
ASSERT_EQ(InputEventInjectionResult::SUCCEEDED,
injectMotionEvent(mDispatcher,
@@ -2270,8 +2302,7 @@
.x(300)
.y(400))
.build()));
- window->consumeEvent(AINPUT_EVENT_TYPE_MOTION, AMOTION_EVENT_ACTION_BUTTON_RELEASE,
- ADISPLAY_ID_DEFAULT, 0 /* expectedFlag */);
+ window->consumeMotionEvent(WithMotionAction(AMOTION_EVENT_ACTION_BUTTON_RELEASE));
ASSERT_EQ(InputEventInjectionResult::SUCCEEDED,
injectMotionEvent(mDispatcher,
@@ -2291,8 +2322,7 @@
.x(300)
.y(400))
.build()));
- window->consumeEvent(AINPUT_EVENT_TYPE_MOTION, AMOTION_EVENT_ACTION_HOVER_EXIT,
- ADISPLAY_ID_DEFAULT, 0 /* expectedFlag */);
+ window->consumeMotionEvent(WithMotionAction(AMOTION_EVENT_ACTION_HOVER_EXIT));
}
/**
@@ -2362,14 +2392,13 @@
.x(300)
.y(600))
.build()));
- windowDefaultDisplay->consumeEvent(AINPUT_EVENT_TYPE_MOTION, AMOTION_EVENT_ACTION_HOVER_ENTER,
- ADISPLAY_ID_DEFAULT, 0 /* expectedFlag */);
- windowDefaultDisplay->consumeEvent(AINPUT_EVENT_TYPE_MOTION, AMOTION_EVENT_ACTION_HOVER_MOVE,
- ADISPLAY_ID_DEFAULT, 0 /* expectedFlag */);
+ windowDefaultDisplay->consumeMotionEvent(WithMotionAction(AMOTION_EVENT_ACTION_HOVER_ENTER));
+ windowDefaultDisplay->consumeMotionEvent(WithMotionAction(AMOTION_EVENT_ACTION_HOVER_MOVE));
// Remove all windows in secondary display and check that no event happens on window in
// primary display.
- mDispatcher->setInputWindows({{SECOND_DISPLAY_ID, {}}});
+ mDispatcher->setInputWindows(
+ {{ADISPLAY_ID_DEFAULT, {windowDefaultDisplay}}, {SECOND_DISPLAY_ID, {}}});
windowDefaultDisplay->assertNoEvents();
// Move cursor position in window in default display and check that only hover move
@@ -2385,8 +2414,9 @@
.x(400)
.y(700))
.build()));
- windowDefaultDisplay->consumeEvent(AINPUT_EVENT_TYPE_MOTION, AMOTION_EVENT_ACTION_HOVER_MOVE,
- ADISPLAY_ID_DEFAULT, 0 /* expectedFlag */);
+ windowDefaultDisplay->consumeMotionEvent(
+ AllOf(WithMotionAction(AMOTION_EVENT_ACTION_HOVER_MOVE),
+ WithSource(AINPUT_SOURCE_MOUSE)));
windowDefaultDisplay->assertNoEvents();
}
@@ -4669,6 +4699,7 @@
const MotionEvent& motionEvent = static_cast<const MotionEvent&>(*event);
assertMotionAction(expectedAction, motionEvent.getAction());
+ ASSERT_EQ(points.size(), motionEvent.getPointerCount());
for (size_t i = 0; i < points.size(); i++) {
float expectedX = points[i].x;
diff --git a/services/inputflinger/tests/InputMapperTest.cpp b/services/inputflinger/tests/InputMapperTest.cpp
new file mode 100644
index 0000000..3cd7c1b
--- /dev/null
+++ b/services/inputflinger/tests/InputMapperTest.cpp
@@ -0,0 +1,176 @@
+/*
+ * 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 "InputMapperTest.h"
+
+#include <InputReaderBase.h>
+#include <gtest/gtest.h>
+#include <ui/Rotation.h>
+
+namespace android {
+
+const char* InputMapperTest::DEVICE_NAME = "device";
+const char* InputMapperTest::DEVICE_LOCATION = "USB1";
+const ftl::Flags<InputDeviceClass> InputMapperTest::DEVICE_CLASSES =
+ ftl::Flags<InputDeviceClass>(0); // not needed for current tests
+
+void InputMapperTest::SetUp(ftl::Flags<InputDeviceClass> classes, int bus) {
+ mFakeEventHub = std::make_unique<FakeEventHub>();
+ mFakePolicy = sp<FakeInputReaderPolicy>::make();
+ mFakeListener = std::make_unique<TestInputListener>();
+ mReader = std::make_unique<InstrumentedInputReader>(mFakeEventHub, mFakePolicy, *mFakeListener);
+ mDevice = newDevice(DEVICE_ID, DEVICE_NAME, DEVICE_LOCATION, EVENTHUB_ID, classes, bus);
+ // Consume the device reset notification generated when adding a new device.
+ mFakeListener->assertNotifyDeviceResetWasCalled();
+}
+
+void InputMapperTest::SetUp() {
+ SetUp(DEVICE_CLASSES);
+}
+
+void InputMapperTest::TearDown() {
+ mFakeListener.reset();
+ mFakePolicy.clear();
+}
+
+void InputMapperTest::addConfigurationProperty(const char* key, const char* value) {
+ mFakeEventHub->addConfigurationProperty(EVENTHUB_ID, key, value);
+}
+
+std::list<NotifyArgs> InputMapperTest::configureDevice(uint32_t changes) {
+ if (!changes ||
+ (changes &
+ (InputReaderConfiguration::CHANGE_DISPLAY_INFO |
+ InputReaderConfiguration::CHANGE_POINTER_CAPTURE))) {
+ mReader->requestRefreshConfiguration(changes);
+ mReader->loopOnce();
+ }
+ std::list<NotifyArgs> out =
+ mDevice->configure(ARBITRARY_TIME, mFakePolicy->getReaderConfiguration(), changes);
+ // Loop the reader to flush the input listener queue.
+ for (const NotifyArgs& args : out) {
+ mFakeListener->notify(args);
+ }
+ mReader->loopOnce();
+ return out;
+}
+
+std::shared_ptr<InputDevice> InputMapperTest::newDevice(int32_t deviceId, const std::string& name,
+ const std::string& location,
+ int32_t eventHubId,
+ ftl::Flags<InputDeviceClass> classes,
+ int bus) {
+ InputDeviceIdentifier identifier;
+ identifier.name = name;
+ identifier.location = location;
+ identifier.bus = bus;
+ std::shared_ptr<InputDevice> device =
+ std::make_shared<InputDevice>(mReader->getContext(), deviceId, DEVICE_GENERATION,
+ identifier);
+ mReader->pushNextDevice(device);
+ mFakeEventHub->addDevice(eventHubId, name, classes, bus);
+ mReader->loopOnce();
+ return device;
+}
+
+void InputMapperTest::setDisplayInfoAndReconfigure(int32_t displayId, int32_t width, int32_t height,
+ ui::Rotation orientation,
+ const std::string& uniqueId,
+ std::optional<uint8_t> physicalPort,
+ ViewportType viewportType) {
+ mFakePolicy->addDisplayViewport(displayId, width, height, orientation, /* isActive= */ true,
+ uniqueId, physicalPort, viewportType);
+ configureDevice(InputReaderConfiguration::CHANGE_DISPLAY_INFO);
+}
+
+void InputMapperTest::clearViewports() {
+ mFakePolicy->clearViewports();
+}
+
+std::list<NotifyArgs> InputMapperTest::process(InputMapper& mapper, nsecs_t when, nsecs_t readTime,
+ int32_t type, int32_t code, int32_t value) {
+ RawEvent event;
+ event.when = when;
+ event.readTime = readTime;
+ event.deviceId = mapper.getDeviceContext().getEventHubId();
+ event.type = type;
+ event.code = code;
+ event.value = value;
+ std::list<NotifyArgs> processArgList = mapper.process(&event);
+ for (const NotifyArgs& args : processArgList) {
+ mFakeListener->notify(args);
+ }
+ // Loop the reader to flush the input listener queue.
+ mReader->loopOnce();
+ return processArgList;
+}
+
+void InputMapperTest::resetMapper(InputMapper& mapper, nsecs_t when) {
+ const auto resetArgs = mapper.reset(when);
+ for (const auto args : resetArgs) {
+ mFakeListener->notify(args);
+ }
+ // Loop the reader to flush the input listener queue.
+ mReader->loopOnce();
+}
+
+std::list<NotifyArgs> InputMapperTest::handleTimeout(InputMapper& mapper, nsecs_t when) {
+ std::list<NotifyArgs> generatedArgs = mapper.timeoutExpired(when);
+ for (const NotifyArgs& args : generatedArgs) {
+ mFakeListener->notify(args);
+ }
+ // Loop the reader to flush the input listener queue.
+ mReader->loopOnce();
+ return generatedArgs;
+}
+
+void InputMapperTest::assertMotionRange(const InputDeviceInfo& info, int32_t axis, uint32_t source,
+ float min, float max, float flat, float fuzz) {
+ const InputDeviceInfo::MotionRange* range = info.getMotionRange(axis, source);
+ ASSERT_TRUE(range != nullptr) << "Axis: " << axis << " Source: " << source;
+ ASSERT_EQ(axis, range->axis) << "Axis: " << axis << " Source: " << source;
+ ASSERT_EQ(source, range->source) << "Axis: " << axis << " Source: " << source;
+ ASSERT_NEAR(min, range->min, EPSILON) << "Axis: " << axis << " Source: " << source;
+ ASSERT_NEAR(max, range->max, EPSILON) << "Axis: " << axis << " Source: " << source;
+ ASSERT_NEAR(flat, range->flat, EPSILON) << "Axis: " << axis << " Source: " << source;
+ ASSERT_NEAR(fuzz, range->fuzz, EPSILON) << "Axis: " << axis << " Source: " << source;
+}
+
+void InputMapperTest::assertPointerCoords(const PointerCoords& coords, float x, float y,
+ float pressure, float size, float touchMajor,
+ float touchMinor, float toolMajor, float toolMinor,
+ float orientation, float distance,
+ float scaledAxisEpsilon) {
+ ASSERT_NEAR(x, coords.getAxisValue(AMOTION_EVENT_AXIS_X), scaledAxisEpsilon);
+ ASSERT_NEAR(y, coords.getAxisValue(AMOTION_EVENT_AXIS_Y), scaledAxisEpsilon);
+ ASSERT_NEAR(pressure, coords.getAxisValue(AMOTION_EVENT_AXIS_PRESSURE), EPSILON);
+ ASSERT_NEAR(size, coords.getAxisValue(AMOTION_EVENT_AXIS_SIZE), EPSILON);
+ ASSERT_NEAR(touchMajor, coords.getAxisValue(AMOTION_EVENT_AXIS_TOUCH_MAJOR), scaledAxisEpsilon);
+ ASSERT_NEAR(touchMinor, coords.getAxisValue(AMOTION_EVENT_AXIS_TOUCH_MINOR), scaledAxisEpsilon);
+ ASSERT_NEAR(toolMajor, coords.getAxisValue(AMOTION_EVENT_AXIS_TOOL_MAJOR), scaledAxisEpsilon);
+ ASSERT_NEAR(toolMinor, coords.getAxisValue(AMOTION_EVENT_AXIS_TOOL_MINOR), scaledAxisEpsilon);
+ ASSERT_NEAR(orientation, coords.getAxisValue(AMOTION_EVENT_AXIS_ORIENTATION), EPSILON);
+ ASSERT_NEAR(distance, coords.getAxisValue(AMOTION_EVENT_AXIS_DISTANCE), EPSILON);
+}
+
+void InputMapperTest::assertPosition(const FakePointerController& controller, float x, float y) {
+ float actualX, actualY;
+ controller.getPosition(&actualX, &actualY);
+ ASSERT_NEAR(x, actualX, 1);
+ ASSERT_NEAR(y, actualY, 1);
+}
+
+} // namespace android
diff --git a/services/inputflinger/tests/InputMapperTest.h b/services/inputflinger/tests/InputMapperTest.h
new file mode 100644
index 0000000..b3401c3
--- /dev/null
+++ b/services/inputflinger/tests/InputMapperTest.h
@@ -0,0 +1,95 @@
+/*
+ * 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 <list>
+#include <memory>
+
+#include <InputDevice.h>
+#include <InputMapper.h>
+#include <NotifyArgs.h>
+#include <ftl/flags.h>
+#include <utils/StrongPointer.h>
+
+#include "FakeEventHub.h"
+#include "FakeInputReaderPolicy.h"
+#include "InstrumentedInputReader.h"
+#include "TestConstants.h"
+#include "TestInputListener.h"
+
+namespace android {
+
+class InputMapperTest : public testing::Test {
+protected:
+ static const char* DEVICE_NAME;
+ static const char* DEVICE_LOCATION;
+ static constexpr int32_t DEVICE_ID = END_RESERVED_ID + 1000;
+ static constexpr int32_t DEVICE_GENERATION = 2;
+ static constexpr int32_t DEVICE_CONTROLLER_NUMBER = 0;
+ static const ftl::Flags<InputDeviceClass> DEVICE_CLASSES;
+ static constexpr int32_t EVENTHUB_ID = 1;
+
+ std::shared_ptr<FakeEventHub> mFakeEventHub;
+ sp<FakeInputReaderPolicy> mFakePolicy;
+ std::unique_ptr<TestInputListener> mFakeListener;
+ std::unique_ptr<InstrumentedInputReader> mReader;
+ std::shared_ptr<InputDevice> mDevice;
+
+ virtual void SetUp(ftl::Flags<InputDeviceClass> classes, int bus = 0);
+ void SetUp() override;
+ void TearDown() override;
+
+ void addConfigurationProperty(const char* key, const char* value);
+ std::list<NotifyArgs> configureDevice(uint32_t changes);
+ std::shared_ptr<InputDevice> newDevice(int32_t deviceId, const std::string& name,
+ const std::string& location, int32_t eventHubId,
+ ftl::Flags<InputDeviceClass> classes, int bus = 0);
+ template <class T, typename... Args>
+ T& addMapperAndConfigure(Args... args) {
+ T& mapper = mDevice->addMapper<T>(EVENTHUB_ID, args...);
+ configureDevice(0);
+ std::list<NotifyArgs> resetArgList = mDevice->reset(ARBITRARY_TIME);
+ resetArgList += mapper.reset(ARBITRARY_TIME);
+ // Loop the reader to flush the input listener queue.
+ for (const NotifyArgs& loopArgs : resetArgList) {
+ mFakeListener->notify(loopArgs);
+ }
+ mReader->loopOnce();
+ return mapper;
+ }
+
+ void setDisplayInfoAndReconfigure(int32_t displayId, int32_t width, int32_t height,
+ ui::Rotation orientation, const std::string& uniqueId,
+ std::optional<uint8_t> physicalPort,
+ ViewportType viewportType);
+ void clearViewports();
+ std::list<NotifyArgs> process(InputMapper& mapper, nsecs_t when, nsecs_t readTime, int32_t type,
+ int32_t code, int32_t value);
+ void resetMapper(InputMapper& mapper, nsecs_t when);
+
+ std::list<NotifyArgs> handleTimeout(InputMapper& mapper, nsecs_t when);
+
+ static void assertMotionRange(const InputDeviceInfo& info, int32_t axis, uint32_t source,
+ float min, float max, float flat, float fuzz);
+ static void assertPointerCoords(const PointerCoords& coords, float x, float y, float pressure,
+ float size, float touchMajor, float touchMinor, float toolMajor,
+ float toolMinor, float orientation, float distance,
+ float scaledAxisEpsilon = 1.f);
+ static void assertPosition(const FakePointerController& controller, float x, float y);
+};
+
+} // namespace android
diff --git a/services/inputflinger/tests/InputReader_test.cpp b/services/inputflinger/tests/InputReader_test.cpp
index c72d01f..4cc48f6 100644
--- a/services/inputflinger/tests/InputReader_test.cpp
+++ b/services/inputflinger/tests/InputReader_test.cpp
@@ -36,13 +36,17 @@
#include <UinputDevice.h>
#include <VibratorInputMapper.h>
#include <android-base/thread_annotations.h>
+#include <ftl/enum.h>
#include <gtest/gtest.h>
#include <gui/constants.h>
+#include <ui/Rotation.h>
#include <thread>
#include "FakeEventHub.h"
#include "FakeInputReaderPolicy.h"
#include "FakePointerController.h"
+#include "InputMapperTest.h"
+#include "InstrumentedInputReader.h"
#include "TestConstants.h"
#include "android/hardware/input/InputDeviceCountryCode.h"
#include "input/DisplayViewport.h"
@@ -89,9 +93,6 @@
static constexpr int32_t ACTION_POINTER_1_UP =
AMOTION_EVENT_ACTION_POINTER_UP | (1 << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT);
-// Error tolerance for floating point assertions.
-static const float EPSILON = 0.001f;
-
// Minimum timestamp separation between subsequent input events from a Bluetooth device.
static constexpr nsecs_t MIN_BLUETOOTH_TIMESTAMP_DELTA = ms2ns(4);
// Maximum smoothing time delta so that we don't generate events too far into the future.
@@ -111,12 +112,12 @@
{"green", LightColor::GREEN},
{"blue", LightColor::BLUE}};
-static int32_t getInverseRotation(int32_t orientation) {
+static ui::Rotation getInverseRotation(ui::Rotation orientation) {
switch (orientation) {
- case DISPLAY_ORIENTATION_90:
- return DISPLAY_ORIENTATION_270;
- case DISPLAY_ORIENTATION_270:
- return DISPLAY_ORIENTATION_90;
+ case ui::ROTATION_90:
+ return ui::ROTATION_270;
+ case ui::ROTATION_270:
+ return ui::ROTATION_90;
default:
return orientation;
}
@@ -343,117 +344,6 @@
}
};
-
-// --- InstrumentedInputReader ---
-
-class InstrumentedInputReader : public InputReader {
- std::queue<std::shared_ptr<InputDevice>> mNextDevices;
-
-public:
- InstrumentedInputReader(std::shared_ptr<EventHubInterface> eventHub,
- const sp<InputReaderPolicyInterface>& policy,
- InputListenerInterface& listener)
- : InputReader(eventHub, policy, listener), mFakeContext(this) {}
-
- virtual ~InstrumentedInputReader() {}
-
- void pushNextDevice(std::shared_ptr<InputDevice> device) { mNextDevices.push(device); }
-
- std::shared_ptr<InputDevice> newDevice(int32_t deviceId, const std::string& name,
- const std::string& location = "") {
- InputDeviceIdentifier identifier;
- identifier.name = name;
- identifier.location = location;
- int32_t generation = deviceId + 1;
- return std::make_shared<InputDevice>(&mFakeContext, deviceId, generation, identifier);
- }
-
- // Make the protected loopOnce method accessible to tests.
- using InputReader::loopOnce;
-
-protected:
- virtual std::shared_ptr<InputDevice> createDeviceLocked(int32_t eventHubId,
- const InputDeviceIdentifier& identifier)
- REQUIRES(mLock) {
- if (!mNextDevices.empty()) {
- std::shared_ptr<InputDevice> device(std::move(mNextDevices.front()));
- mNextDevices.pop();
- return device;
- }
- return InputReader::createDeviceLocked(eventHubId, identifier);
- }
-
- // --- FakeInputReaderContext ---
- class FakeInputReaderContext : public ContextImpl {
- int32_t mGlobalMetaState;
- bool mUpdateGlobalMetaStateWasCalled;
- int32_t mGeneration;
- std::optional<nsecs_t> mRequestedTimeout;
- std::vector<InputDeviceInfo> mExternalStylusDevices;
-
- public:
- FakeInputReaderContext(InputReader* reader)
- : ContextImpl(reader),
- mGlobalMetaState(0),
- mUpdateGlobalMetaStateWasCalled(false),
- mGeneration(1) {}
-
- virtual ~FakeInputReaderContext() {}
-
- void assertUpdateGlobalMetaStateWasCalled() {
- ASSERT_TRUE(mUpdateGlobalMetaStateWasCalled)
- << "Expected updateGlobalMetaState() to have been called.";
- mUpdateGlobalMetaStateWasCalled = false;
- }
-
- void setGlobalMetaState(int32_t state) { mGlobalMetaState = state; }
-
- uint32_t getGeneration() { return mGeneration; }
-
- void updateGlobalMetaState() override {
- mUpdateGlobalMetaStateWasCalled = true;
- ContextImpl::updateGlobalMetaState();
- }
-
- int32_t getGlobalMetaState() override {
- return mGlobalMetaState | ContextImpl::getGlobalMetaState();
- }
-
- int32_t bumpGeneration() override {
- mGeneration = ContextImpl::bumpGeneration();
- return mGeneration;
- }
-
- void requestTimeoutAtTime(nsecs_t when) override { mRequestedTimeout = when; }
-
- void assertTimeoutWasRequested(nsecs_t when) {
- ASSERT_TRUE(mRequestedTimeout) << "Expected timeout at time " << when
- << " but there was no timeout requested.";
- ASSERT_EQ(when, *mRequestedTimeout);
- mRequestedTimeout.reset();
- }
-
- void assertTimeoutWasNotRequested() {
- ASSERT_FALSE(mRequestedTimeout) << "Expected no timeout to have been requested,"
- " but one was requested at time "
- << *mRequestedTimeout;
- }
-
- void getExternalStylusDevices(std::vector<InputDeviceInfo>& outDevices) override {
- outDevices = mExternalStylusDevices;
- }
-
- void setExternalStylusDevices(std::vector<InputDeviceInfo>&& devices) {
- mExternalStylusDevices = devices;
- }
- } mFakeContext;
-
- friend class InputReaderTest;
-
-public:
- FakeInputReaderContext* getContext() { return &mFakeContext; }
-};
-
// --- InputReaderPolicyTest ---
class InputReaderPolicyTest : public testing::Test {
protected:
@@ -479,9 +369,8 @@
ASSERT_FALSE(internalViewport);
// Add an internal viewport, then clear it
- mFakePolicy->addDisplayViewport(DISPLAY_ID, DISPLAY_WIDTH, DISPLAY_HEIGHT,
- DISPLAY_ORIENTATION_0, true /*isActive*/, uniqueId, NO_PORT,
- ViewportType::INTERNAL);
+ mFakePolicy->addDisplayViewport(DISPLAY_ID, DISPLAY_WIDTH, DISPLAY_HEIGHT, ui::ROTATION_0,
+ true /*isActive*/, uniqueId, NO_PORT, ViewportType::INTERNAL);
// Check matching by uniqueId
internalViewport = mFakePolicy->getDisplayViewportByUniqueId(uniqueId);
@@ -510,21 +399,21 @@
constexpr int32_t virtualDisplayId2 = 3;
// Add an internal viewport
- mFakePolicy->addDisplayViewport(DISPLAY_ID, DISPLAY_WIDTH, DISPLAY_HEIGHT,
- DISPLAY_ORIENTATION_0, true /*isActive*/, internalUniqueId,
- NO_PORT, ViewportType::INTERNAL);
+ mFakePolicy->addDisplayViewport(DISPLAY_ID, DISPLAY_WIDTH, DISPLAY_HEIGHT, ui::ROTATION_0,
+ true /*isActive*/, internalUniqueId, NO_PORT,
+ ViewportType::INTERNAL);
// Add an external viewport
- mFakePolicy->addDisplayViewport(DISPLAY_ID, DISPLAY_WIDTH, DISPLAY_HEIGHT,
- DISPLAY_ORIENTATION_0, true /*isActive*/, externalUniqueId,
- NO_PORT, ViewportType::EXTERNAL);
+ mFakePolicy->addDisplayViewport(DISPLAY_ID, DISPLAY_WIDTH, DISPLAY_HEIGHT, ui::ROTATION_0,
+ true /*isActive*/, externalUniqueId, NO_PORT,
+ ViewportType::EXTERNAL);
// Add an virtual viewport
mFakePolicy->addDisplayViewport(virtualDisplayId1, DISPLAY_WIDTH, DISPLAY_HEIGHT,
- DISPLAY_ORIENTATION_0, true /*isActive*/, virtualUniqueId1,
- NO_PORT, ViewportType::VIRTUAL);
+ ui::ROTATION_0, true /*isActive*/, virtualUniqueId1, NO_PORT,
+ ViewportType::VIRTUAL);
// Add another virtual viewport
mFakePolicy->addDisplayViewport(virtualDisplayId2, DISPLAY_WIDTH, DISPLAY_HEIGHT,
- DISPLAY_ORIENTATION_0, true /*isActive*/, virtualUniqueId2,
- NO_PORT, ViewportType::VIRTUAL);
+ ui::ROTATION_0, true /*isActive*/, virtualUniqueId2, NO_PORT,
+ ViewportType::VIRTUAL);
// Check matching by type for internal
std::optional<DisplayViewport> internalViewport =
@@ -572,13 +461,11 @@
for (const ViewportType& type : types) {
mFakePolicy->clearViewports();
// Add a viewport
- mFakePolicy->addDisplayViewport(displayId1, DISPLAY_WIDTH, DISPLAY_HEIGHT,
- DISPLAY_ORIENTATION_0, true /*isActive*/, uniqueId1,
- NO_PORT, type);
+ mFakePolicy->addDisplayViewport(displayId1, DISPLAY_WIDTH, DISPLAY_HEIGHT, ui::ROTATION_0,
+ true /*isActive*/, uniqueId1, NO_PORT, type);
// Add another viewport
- mFakePolicy->addDisplayViewport(displayId2, DISPLAY_WIDTH, DISPLAY_HEIGHT,
- DISPLAY_ORIENTATION_0, true /*isActive*/, uniqueId2,
- NO_PORT, type);
+ mFakePolicy->addDisplayViewport(displayId2, DISPLAY_WIDTH, DISPLAY_HEIGHT, ui::ROTATION_0,
+ true /*isActive*/, uniqueId2, NO_PORT, type);
// Check that correct display viewport was returned by comparing the display IDs.
std::optional<DisplayViewport> viewport1 =
@@ -618,10 +505,10 @@
// Add the default display first and ensure it gets returned.
mFakePolicy->clearViewports();
mFakePolicy->addDisplayViewport(ADISPLAY_ID_DEFAULT, DISPLAY_WIDTH, DISPLAY_HEIGHT,
- DISPLAY_ORIENTATION_0, true /*isActive*/, uniqueId1, NO_PORT,
+ ui::ROTATION_0, true /*isActive*/, uniqueId1, NO_PORT,
ViewportType::INTERNAL);
mFakePolicy->addDisplayViewport(nonDefaultDisplayId, DISPLAY_WIDTH, DISPLAY_HEIGHT,
- DISPLAY_ORIENTATION_0, true /*isActive*/, uniqueId2, NO_PORT,
+ ui::ROTATION_0, true /*isActive*/, uniqueId2, NO_PORT,
ViewportType::INTERNAL);
std::optional<DisplayViewport> viewport =
@@ -633,10 +520,10 @@
// Add the default display second to make sure order doesn't matter.
mFakePolicy->clearViewports();
mFakePolicy->addDisplayViewport(nonDefaultDisplayId, DISPLAY_WIDTH, DISPLAY_HEIGHT,
- DISPLAY_ORIENTATION_0, true /*isActive*/, uniqueId2, NO_PORT,
+ ui::ROTATION_0, true /*isActive*/, uniqueId2, NO_PORT,
ViewportType::INTERNAL);
mFakePolicy->addDisplayViewport(ADISPLAY_ID_DEFAULT, DISPLAY_WIDTH, DISPLAY_HEIGHT,
- DISPLAY_ORIENTATION_0, true /*isActive*/, uniqueId1, NO_PORT,
+ ui::ROTATION_0, true /*isActive*/, uniqueId1, NO_PORT,
ViewportType::INTERNAL);
viewport = mFakePolicy->getDisplayViewportByType(ViewportType::INTERNAL);
@@ -660,13 +547,11 @@
mFakePolicy->clearViewports();
// Add a viewport that's associated with some display port that's not of interest.
- mFakePolicy->addDisplayViewport(displayId1, DISPLAY_WIDTH, DISPLAY_HEIGHT,
- DISPLAY_ORIENTATION_0, true /*isActive*/, uniqueId1, hdmi3,
- type);
+ mFakePolicy->addDisplayViewport(displayId1, DISPLAY_WIDTH, DISPLAY_HEIGHT, ui::ROTATION_0,
+ true /*isActive*/, uniqueId1, hdmi3, type);
// Add another viewport, connected to HDMI1 port
- mFakePolicy->addDisplayViewport(displayId2, DISPLAY_WIDTH, DISPLAY_HEIGHT,
- DISPLAY_ORIENTATION_0, true /*isActive*/, uniqueId2, hdmi1,
- type);
+ mFakePolicy->addDisplayViewport(displayId2, DISPLAY_WIDTH, DISPLAY_HEIGHT, ui::ROTATION_0,
+ true /*isActive*/, uniqueId2, hdmi1, type);
// Check that correct display viewport was returned by comparing the display ports.
std::optional<DisplayViewport> hdmi1Viewport = mFakePolicy->getDisplayViewportByPort(hdmi1);
@@ -1119,11 +1004,10 @@
// Add default and second display.
mFakePolicy->clearViewports();
- mFakePolicy->addDisplayViewport(DISPLAY_ID, DISPLAY_WIDTH, DISPLAY_HEIGHT,
- DISPLAY_ORIENTATION_0, true /*isActive*/, "local:0", NO_PORT,
- ViewportType::INTERNAL);
+ mFakePolicy->addDisplayViewport(DISPLAY_ID, DISPLAY_WIDTH, DISPLAY_HEIGHT, ui::ROTATION_0,
+ true /*isActive*/, "local:0", NO_PORT, ViewportType::INTERNAL);
mFakePolicy->addDisplayViewport(SECONDARY_DISPLAY_ID, DISPLAY_WIDTH, DISPLAY_HEIGHT,
- DISPLAY_ORIENTATION_0, true /*isActive*/, "local:1", hdmi1,
+ ui::ROTATION_0, true /*isActive*/, "local:1", hdmi1,
ViewportType::EXTERNAL);
mReader->requestRefreshConfiguration(InputReaderConfiguration::CHANGE_DISPLAY_INFO);
mReader->loopOnce();
@@ -1582,9 +1466,8 @@
#endif
InputReaderIntegrationTest::SetUp();
// At least add an internal display.
- setDisplayInfoAndReconfigure(DISPLAY_ID, DISPLAY_WIDTH, DISPLAY_HEIGHT,
- DISPLAY_ORIENTATION_0, UNIQUE_ID, NO_PORT,
- ViewportType::INTERNAL);
+ setDisplayInfoAndReconfigure(DISPLAY_ID, DISPLAY_WIDTH, DISPLAY_HEIGHT, ui::ROTATION_0,
+ UNIQUE_ID, NO_PORT, ViewportType::INTERNAL);
mDevice = createUinputDevice<UinputTouchScreen>(Rect(0, 0, DISPLAY_WIDTH, DISPLAY_HEIGHT));
ASSERT_NO_FATAL_FAILURE(mFakePolicy->assertInputDevicesChanged());
@@ -1595,7 +1478,7 @@
}
void setDisplayInfoAndReconfigure(int32_t displayId, int32_t width, int32_t height,
- int32_t orientation, const std::string& uniqueId,
+ ui::Rotation orientation, const std::string& uniqueId,
std::optional<uint8_t> physicalPort,
ViewportType viewportType) {
mFakePolicy->addDisplayViewport(displayId, width, height, orientation, true /*isActive*/,
@@ -2533,7 +2416,7 @@
// Prepare displays.
mFakePolicy->addDisplayViewport(SECONDARY_DISPLAY_ID, DISPLAY_WIDTH, DISPLAY_HEIGHT,
- DISPLAY_ORIENTATION_0, true /*isActive*/, UNIQUE_ID, hdmi,
+ ui::ROTATION_0, true /*isActive*/, UNIQUE_ID, hdmi,
ViewportType::INTERNAL);
unused += mDevice->configure(ARBITRARY_TIME, mFakePolicy->getReaderConfiguration(),
InputReaderConfiguration::CHANGE_DISPLAY_INFO);
@@ -2568,7 +2451,7 @@
// Device should be enabled when a display is found.
mFakePolicy->addDisplayViewport(SECONDARY_DISPLAY_ID, DISPLAY_WIDTH, DISPLAY_HEIGHT,
- DISPLAY_ORIENTATION_0, /* isActive= */ true, DISPLAY_UNIQUE_ID,
+ ui::ROTATION_0, /* isActive= */ true, DISPLAY_UNIQUE_ID,
NO_PORT, ViewportType::INTERNAL);
unused += mDevice->configure(ARBITRARY_TIME, mFakePolicy->getReaderConfiguration(),
InputReaderConfiguration::CHANGE_DISPLAY_INFO);
@@ -2594,7 +2477,7 @@
mFakePolicy->addInputUniqueIdAssociation(DEVICE_LOCATION, DISPLAY_UNIQUE_ID);
mFakePolicy->addDisplayViewport(SECONDARY_DISPLAY_ID, DISPLAY_WIDTH, DISPLAY_HEIGHT,
- DISPLAY_ORIENTATION_0, /* isActive= */ true, DISPLAY_UNIQUE_ID,
+ ui::ROTATION_0, /* isActive= */ true, DISPLAY_UNIQUE_ID,
NO_PORT, ViewportType::INTERNAL);
unused += mDevice->configure(ARBITRARY_TIME, mFakePolicy->getReaderConfiguration(),
InputReaderConfiguration::CHANGE_DISPLAY_INFO);
@@ -2622,194 +2505,6 @@
ASSERT_EQ(DEVICE_BLUETOOTH_ADDRESS, *address);
}
-// --- InputMapperTest ---
-
-class InputMapperTest : public testing::Test {
-protected:
- static const char* DEVICE_NAME;
- static const char* DEVICE_LOCATION;
- static const int32_t DEVICE_ID;
- static const int32_t DEVICE_GENERATION;
- static const int32_t DEVICE_CONTROLLER_NUMBER;
- static const ftl::Flags<InputDeviceClass> DEVICE_CLASSES;
- static const int32_t EVENTHUB_ID;
-
- std::shared_ptr<FakeEventHub> mFakeEventHub;
- sp<FakeInputReaderPolicy> mFakePolicy;
- std::unique_ptr<TestInputListener> mFakeListener;
- std::unique_ptr<InstrumentedInputReader> mReader;
- std::shared_ptr<InputDevice> mDevice;
-
- virtual void SetUp(ftl::Flags<InputDeviceClass> classes, int bus = 0) {
- mFakeEventHub = std::make_unique<FakeEventHub>();
- mFakePolicy = sp<FakeInputReaderPolicy>::make();
- mFakeListener = std::make_unique<TestInputListener>();
- mReader = std::make_unique<InstrumentedInputReader>(mFakeEventHub, mFakePolicy,
- *mFakeListener);
- mDevice = newDevice(DEVICE_ID, DEVICE_NAME, DEVICE_LOCATION, EVENTHUB_ID, classes, bus);
- // Consume the device reset notification generated when adding a new device.
- mFakeListener->assertNotifyDeviceResetWasCalled();
- }
-
- void SetUp() override {
- SetUp(DEVICE_CLASSES);
- }
-
- void TearDown() override {
- mFakeListener.reset();
- mFakePolicy.clear();
- }
-
- void addConfigurationProperty(const char* key, const char* value) {
- mFakeEventHub->addConfigurationProperty(EVENTHUB_ID, key, value);
- }
-
- std::list<NotifyArgs> configureDevice(uint32_t changes) {
- if (!changes ||
- (changes &
- (InputReaderConfiguration::CHANGE_DISPLAY_INFO |
- InputReaderConfiguration::CHANGE_POINTER_CAPTURE))) {
- mReader->requestRefreshConfiguration(changes);
- mReader->loopOnce();
- }
- std::list<NotifyArgs> out =
- mDevice->configure(ARBITRARY_TIME, mFakePolicy->getReaderConfiguration(), changes);
- // Loop the reader to flush the input listener queue.
- for (const NotifyArgs& args : out) {
- mFakeListener->notify(args);
- }
- mReader->loopOnce();
- return out;
- }
-
- std::shared_ptr<InputDevice> newDevice(int32_t deviceId, const std::string& name,
- const std::string& location, int32_t eventHubId,
- ftl::Flags<InputDeviceClass> classes, int bus = 0) {
- InputDeviceIdentifier identifier;
- identifier.name = name;
- identifier.location = location;
- identifier.bus = bus;
- std::shared_ptr<InputDevice> device =
- std::make_shared<InputDevice>(mReader->getContext(), deviceId, DEVICE_GENERATION,
- identifier);
- mReader->pushNextDevice(device);
- mFakeEventHub->addDevice(eventHubId, name, classes, bus);
- mReader->loopOnce();
- return device;
- }
-
- template <class T, typename... Args>
- T& addMapperAndConfigure(Args... args) {
- T& mapper = mDevice->addMapper<T>(EVENTHUB_ID, args...);
- configureDevice(0);
- std::list<NotifyArgs> resetArgList = mDevice->reset(ARBITRARY_TIME);
- resetArgList += mapper.reset(ARBITRARY_TIME);
- // Loop the reader to flush the input listener queue.
- for (const NotifyArgs& loopArgs : resetArgList) {
- mFakeListener->notify(loopArgs);
- }
- mReader->loopOnce();
- return mapper;
- }
-
- void setDisplayInfoAndReconfigure(int32_t displayId, int32_t width, int32_t height,
- int32_t orientation, const std::string& uniqueId,
- std::optional<uint8_t> physicalPort, ViewportType viewportType) {
- mFakePolicy->addDisplayViewport(displayId, width, height, orientation, true /*isActive*/,
- uniqueId, physicalPort, viewportType);
- configureDevice(InputReaderConfiguration::CHANGE_DISPLAY_INFO);
- }
-
- void clearViewports() {
- mFakePolicy->clearViewports();
- }
-
- std::list<NotifyArgs> process(InputMapper& mapper, nsecs_t when, nsecs_t readTime, int32_t type,
- int32_t code, int32_t value) {
- RawEvent event;
- event.when = when;
- event.readTime = readTime;
- event.deviceId = mapper.getDeviceContext().getEventHubId();
- event.type = type;
- event.code = code;
- event.value = value;
- std::list<NotifyArgs> processArgList = mapper.process(&event);
- for (const NotifyArgs& args : processArgList) {
- mFakeListener->notify(args);
- }
- // Loop the reader to flush the input listener queue.
- mReader->loopOnce();
- return processArgList;
- }
-
- void resetMapper(InputMapper& mapper, nsecs_t when) {
- const auto resetArgs = mapper.reset(when);
- for (const auto args : resetArgs) {
- mFakeListener->notify(args);
- }
- // Loop the reader to flush the input listener queue.
- mReader->loopOnce();
- }
-
- std::list<NotifyArgs> handleTimeout(InputMapper& mapper, nsecs_t when) {
- std::list<NotifyArgs> generatedArgs = mapper.timeoutExpired(when);
- for (const NotifyArgs& args : generatedArgs) {
- mFakeListener->notify(args);
- }
- // Loop the reader to flush the input listener queue.
- mReader->loopOnce();
- return generatedArgs;
- }
-
- static void assertMotionRange(const InputDeviceInfo& info,
- int32_t axis, uint32_t source, float min, float max, float flat, float fuzz) {
- const InputDeviceInfo::MotionRange* range = info.getMotionRange(axis, source);
- ASSERT_TRUE(range != nullptr) << "Axis: " << axis << " Source: " << source;
- ASSERT_EQ(axis, range->axis) << "Axis: " << axis << " Source: " << source;
- ASSERT_EQ(source, range->source) << "Axis: " << axis << " Source: " << source;
- ASSERT_NEAR(min, range->min, EPSILON) << "Axis: " << axis << " Source: " << source;
- ASSERT_NEAR(max, range->max, EPSILON) << "Axis: " << axis << " Source: " << source;
- ASSERT_NEAR(flat, range->flat, EPSILON) << "Axis: " << axis << " Source: " << source;
- ASSERT_NEAR(fuzz, range->fuzz, EPSILON) << "Axis: " << axis << " Source: " << source;
- }
-
- static void assertPointerCoords(const PointerCoords& coords, float x, float y, float pressure,
- float size, float touchMajor, float touchMinor, float toolMajor,
- float toolMinor, float orientation, float distance,
- float scaledAxisEpsilon = 1.f) {
- ASSERT_NEAR(x, coords.getAxisValue(AMOTION_EVENT_AXIS_X), scaledAxisEpsilon);
- ASSERT_NEAR(y, coords.getAxisValue(AMOTION_EVENT_AXIS_Y), scaledAxisEpsilon);
- ASSERT_NEAR(pressure, coords.getAxisValue(AMOTION_EVENT_AXIS_PRESSURE), EPSILON);
- ASSERT_NEAR(size, coords.getAxisValue(AMOTION_EVENT_AXIS_SIZE), EPSILON);
- ASSERT_NEAR(touchMajor, coords.getAxisValue(AMOTION_EVENT_AXIS_TOUCH_MAJOR),
- scaledAxisEpsilon);
- ASSERT_NEAR(touchMinor, coords.getAxisValue(AMOTION_EVENT_AXIS_TOUCH_MINOR),
- scaledAxisEpsilon);
- ASSERT_NEAR(toolMajor, coords.getAxisValue(AMOTION_EVENT_AXIS_TOOL_MAJOR),
- scaledAxisEpsilon);
- ASSERT_NEAR(toolMinor, coords.getAxisValue(AMOTION_EVENT_AXIS_TOOL_MINOR),
- scaledAxisEpsilon);
- ASSERT_NEAR(orientation, coords.getAxisValue(AMOTION_EVENT_AXIS_ORIENTATION), EPSILON);
- ASSERT_NEAR(distance, coords.getAxisValue(AMOTION_EVENT_AXIS_DISTANCE), EPSILON);
- }
-
- static void assertPosition(const FakePointerController& controller, float x, float y) {
- float actualX, actualY;
- controller.getPosition(&actualX, &actualY);
- ASSERT_NEAR(x, actualX, 1);
- ASSERT_NEAR(y, actualY, 1);
- }
-};
-
-const char* InputMapperTest::DEVICE_NAME = "device";
-const char* InputMapperTest::DEVICE_LOCATION = "USB1";
-const int32_t InputMapperTest::DEVICE_ID = END_RESERVED_ID + 1000;
-const int32_t InputMapperTest::DEVICE_GENERATION = 2;
-const int32_t InputMapperTest::DEVICE_CONTROLLER_NUMBER = 0;
-const ftl::Flags<InputDeviceClass> InputMapperTest::DEVICE_CLASSES =
- ftl::Flags<InputDeviceClass>(0); // not needed for current tests
-const int32_t InputMapperTest::EVENTHUB_ID = 1;
-
// --- SwitchInputMapperTest ---
class SwitchInputMapperTest : public InputMapperTest {
@@ -3069,7 +2764,7 @@
protected:
const std::string UNIQUE_ID = "local:0";
- void prepareDisplay(int32_t orientation);
+ void prepareDisplay(ui::Rotation orientation);
void testDPadKeyRotation(KeyboardInputMapper& mapper, int32_t originalScanCode,
int32_t originalKeyCode, int32_t rotatedKeyCode,
@@ -3079,7 +2774,7 @@
/* Similar to setDisplayInfoAndReconfigure, but pre-populates all parameters except for the
* orientation.
*/
-void KeyboardInputMapperTest::prepareDisplay(int32_t orientation) {
+void KeyboardInputMapperTest::prepareDisplay(ui::Rotation orientation) {
setDisplayInfoAndReconfigure(DISPLAY_ID, DISPLAY_WIDTH, DISPLAY_HEIGHT, orientation, UNIQUE_ID,
NO_PORT, ViewportType::INTERNAL);
}
@@ -3217,6 +2912,27 @@
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);
+ mFakeEventHub->addKeyRemapping(EVENTHUB_ID, AKEYCODE_A, AKEYCODE_B);
+
+ KeyboardInputMapper& mapper =
+ addMapperAndConfigure<KeyboardInputMapper>(AINPUT_SOURCE_KEYBOARD,
+ AINPUT_KEYBOARD_TYPE_ALPHABETIC);
+
+ // 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.
*/
@@ -3291,7 +3007,7 @@
addMapperAndConfigure<KeyboardInputMapper>(AINPUT_SOURCE_KEYBOARD,
AINPUT_KEYBOARD_TYPE_ALPHABETIC);
- prepareDisplay(DISPLAY_ORIENTATION_90);
+ prepareDisplay(ui::ROTATION_90);
ASSERT_NO_FATAL_FAILURE(testDPadKeyRotation(mapper,
KEY_UP, AKEYCODE_DPAD_UP, AKEYCODE_DPAD_UP));
ASSERT_NO_FATAL_FAILURE(testDPadKeyRotation(mapper,
@@ -3313,7 +3029,7 @@
addMapperAndConfigure<KeyboardInputMapper>(AINPUT_SOURCE_KEYBOARD,
AINPUT_KEYBOARD_TYPE_ALPHABETIC);
- prepareDisplay(DISPLAY_ORIENTATION_0);
+ 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,
@@ -3324,7 +3040,7 @@
AKEYCODE_DPAD_LEFT, DISPLAY_ID));
clearViewports();
- prepareDisplay(DISPLAY_ORIENTATION_90);
+ 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,
@@ -3335,7 +3051,7 @@
AKEYCODE_DPAD_DOWN, DISPLAY_ID));
clearViewports();
- prepareDisplay(DISPLAY_ORIENTATION_180);
+ 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,
@@ -3346,7 +3062,7 @@
AKEYCODE_DPAD_RIGHT, DISPLAY_ID));
clearViewports();
- prepareDisplay(DISPLAY_ORIENTATION_270);
+ 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,
@@ -3360,7 +3076,7 @@
// in the key up as we did in the key down.
NotifyKeyArgs args;
clearViewports();
- prepareDisplay(DISPLAY_ORIENTATION_270);
+ 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);
@@ -3368,7 +3084,7 @@
ASSERT_EQ(AKEYCODE_DPAD_RIGHT, args.keyCode);
clearViewports();
- prepareDisplay(DISPLAY_ORIENTATION_180);
+ 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);
@@ -3393,7 +3109,7 @@
ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyKeyWasCalled(&args));
ASSERT_EQ(ADISPLAY_ID_NONE, args.displayId);
- prepareDisplay(DISPLAY_ORIENTATION_0);
+ 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);
@@ -3415,7 +3131,7 @@
// Display id should be ADISPLAY_ID_NONE without any display configuration.
// ^--- already checked by the previous test
- setDisplayInfoAndReconfigure(DISPLAY_ID, DISPLAY_WIDTH, DISPLAY_HEIGHT, DISPLAY_ORIENTATION_0,
+ 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));
@@ -3425,7 +3141,7 @@
constexpr int32_t newDisplayId = 2;
clearViewports();
- setDisplayInfoAndReconfigure(newDisplayId, DISPLAY_WIDTH, DISPLAY_HEIGHT, DISPLAY_ORIENTATION_0,
+ 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));
@@ -3635,9 +3351,9 @@
// Prepare second display.
constexpr int32_t newDisplayId = 2;
- setDisplayInfoAndReconfigure(DISPLAY_ID, DISPLAY_WIDTH, DISPLAY_HEIGHT, DISPLAY_ORIENTATION_0,
+ setDisplayInfoAndReconfigure(DISPLAY_ID, DISPLAY_WIDTH, DISPLAY_HEIGHT, ui::ROTATION_0,
UNIQUE_ID, hdmi1, ViewportType::INTERNAL);
- setDisplayInfoAndReconfigure(newDisplayId, DISPLAY_WIDTH, DISPLAY_HEIGHT, DISPLAY_ORIENTATION_0,
+ 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(),
@@ -3968,14 +3684,14 @@
void testMotionRotation(CursorInputMapper& mapper, int32_t originalX, int32_t originalY,
int32_t rotatedX, int32_t rotatedY);
- void prepareDisplay(int32_t orientation) {
+ void prepareDisplay(ui::Rotation orientation) {
setDisplayInfoAndReconfigure(DISPLAY_ID, DISPLAY_WIDTH, DISPLAY_HEIGHT, orientation,
DISPLAY_UNIQUE_ID, NO_PORT, ViewportType::INTERNAL);
}
void prepareSecondaryDisplay() {
setDisplayInfoAndReconfigure(SECONDARY_DISPLAY_ID, DISPLAY_WIDTH, DISPLAY_HEIGHT,
- DISPLAY_ORIENTATION_0, SECONDARY_DISPLAY_UNIQUE_ID, NO_PORT,
+ ui::ROTATION_0, SECONDARY_DISPLAY_UNIQUE_ID, NO_PORT,
ViewportType::EXTERNAL);
}
@@ -4260,7 +3976,7 @@
addConfigurationProperty("cursor.orientationAware", "1");
CursorInputMapper& mapper = addMapperAndConfigure<CursorInputMapper>();
- prepareDisplay(DISPLAY_ORIENTATION_90);
+ prepareDisplay(ui::ROTATION_90);
ASSERT_NO_FATAL_FAILURE(testMotionRotation(mapper, 0, 1, 0, 1));
ASSERT_NO_FATAL_FAILURE(testMotionRotation(mapper, 1, 1, 1, 1));
ASSERT_NO_FATAL_FAILURE(testMotionRotation(mapper, 1, 0, 1, 0));
@@ -4279,7 +3995,7 @@
CursorInputMapper& mapper = addMapperAndConfigure<CursorInputMapper>();
clearViewports();
- prepareDisplay(DISPLAY_ORIENTATION_0);
+ prepareDisplay(ui::ROTATION_0);
ASSERT_NO_FATAL_FAILURE(testMotionRotation(mapper, 0, 1, 0, 1));
ASSERT_NO_FATAL_FAILURE(testMotionRotation(mapper, 1, 1, 1, 1));
ASSERT_NO_FATAL_FAILURE(testMotionRotation(mapper, 1, 0, 1, 0));
@@ -4290,7 +4006,7 @@
ASSERT_NO_FATAL_FAILURE(testMotionRotation(mapper, -1, 1, -1, 1));
clearViewports();
- prepareDisplay(DISPLAY_ORIENTATION_90);
+ prepareDisplay(ui::ROTATION_90);
ASSERT_NO_FATAL_FAILURE(testMotionRotation(mapper, 0, 1, -1, 0));
ASSERT_NO_FATAL_FAILURE(testMotionRotation(mapper, 1, 1, -1, 1));
ASSERT_NO_FATAL_FAILURE(testMotionRotation(mapper, 1, 0, 0, 1));
@@ -4301,7 +4017,7 @@
ASSERT_NO_FATAL_FAILURE(testMotionRotation(mapper, -1, 1, -1, -1));
clearViewports();
- prepareDisplay(DISPLAY_ORIENTATION_180);
+ prepareDisplay(ui::ROTATION_180);
ASSERT_NO_FATAL_FAILURE(testMotionRotation(mapper, 0, 1, 0, -1));
ASSERT_NO_FATAL_FAILURE(testMotionRotation(mapper, 1, 1, -1, -1));
ASSERT_NO_FATAL_FAILURE(testMotionRotation(mapper, 1, 0, -1, 0));
@@ -4312,7 +4028,7 @@
ASSERT_NO_FATAL_FAILURE(testMotionRotation(mapper, -1, 1, 1, -1));
clearViewports();
- prepareDisplay(DISPLAY_ORIENTATION_270);
+ prepareDisplay(ui::ROTATION_270);
ASSERT_NO_FATAL_FAILURE(testMotionRotation(mapper, 0, 1, 1, 0));
ASSERT_NO_FATAL_FAILURE(testMotionRotation(mapper, 1, 1, 1, -1));
ASSERT_NO_FATAL_FAILURE(testMotionRotation(mapper, 1, 0, 0, -1));
@@ -4776,7 +4492,7 @@
ASSERT_EQ(DEVICE_ID, resetArgs.deviceId);
// Ensure the display is rotated.
- prepareDisplay(DISPLAY_ORIENTATION_90);
+ prepareDisplay(ui::ROTATION_90);
NotifyMotionArgs args;
@@ -4812,7 +4528,7 @@
CursorInputMapper& mapper = addMapperAndConfigure<CursorInputMapper>();
// Set up the default display.
- prepareDisplay(DISPLAY_ORIENTATION_90);
+ prepareDisplay(ui::ROTATION_90);
// Set up the secondary display as the display on which the pointer should be shown.
// The InputDevice is not associated with any display.
@@ -4839,7 +4555,7 @@
CursorInputMapper& mapper = addMapperAndConfigure<CursorInputMapper>();
// Set up the default display.
- prepareDisplay(DISPLAY_ORIENTATION_90);
+ prepareDisplay(ui::ROTATION_90);
// Set up the secondary display as the display on which the pointer should be shown,
// and associate the InputDevice with the secondary display.
@@ -4866,7 +4582,7 @@
CursorInputMapper& mapper = addMapperAndConfigure<CursorInputMapper>();
// Set up the default display as the display on which the pointer should be shown.
- prepareDisplay(DISPLAY_ORIENTATION_90);
+ prepareDisplay(ui::ROTATION_90);
mFakePolicy->setDefaultPointerDisplayId(DISPLAY_ID);
// Associate the InputDevice with the secondary display.
@@ -5033,9 +4749,9 @@
TOOL_TYPE = 1 << 10,
};
- void prepareDisplay(int32_t orientation, std::optional<uint8_t> port = NO_PORT);
+ void prepareDisplay(ui::Rotation orientation, std::optional<uint8_t> port = NO_PORT);
void prepareSecondaryDisplay(ViewportType type, std::optional<uint8_t> port = NO_PORT);
- void prepareVirtualDisplay(int32_t orientation);
+ void prepareVirtualDisplay(ui::Rotation orientation);
void prepareVirtualKeys();
void prepareLocationCalibration();
int32_t toRawX(float displayX);
@@ -5089,17 +4805,17 @@
{ KEY_MENU, DISPLAY_HEIGHT - 60, DISPLAY_WIDTH + 15, 20, 20 },
};
-void TouchInputMapperTest::prepareDisplay(int32_t orientation, std::optional<uint8_t> port) {
+void TouchInputMapperTest::prepareDisplay(ui::Rotation orientation, std::optional<uint8_t> port) {
setDisplayInfoAndReconfigure(DISPLAY_ID, DISPLAY_WIDTH, DISPLAY_HEIGHT, orientation, UNIQUE_ID,
port, ViewportType::INTERNAL);
}
void TouchInputMapperTest::prepareSecondaryDisplay(ViewportType type, std::optional<uint8_t> port) {
setDisplayInfoAndReconfigure(SECONDARY_DISPLAY_ID, DISPLAY_WIDTH, DISPLAY_HEIGHT,
- DISPLAY_ORIENTATION_0, SECONDARY_UNIQUE_ID, port, type);
+ ui::ROTATION_0, SECONDARY_UNIQUE_ID, port, type);
}
-void TouchInputMapperTest::prepareVirtualDisplay(int32_t orientation) {
+void TouchInputMapperTest::prepareVirtualDisplay(ui::Rotation orientation) {
setDisplayInfoAndReconfigure(VIRTUAL_DISPLAY_ID, VIRTUAL_DISPLAY_WIDTH, VIRTUAL_DISPLAY_HEIGHT,
orientation, VIRTUAL_DISPLAY_UNIQUE_ID, NO_PORT,
ViewportType::VIRTUAL);
@@ -5266,7 +4982,7 @@
TEST_F(SingleTouchInputMapperTest, GetKeyCodeState) {
addConfigurationProperty("touch.deviceType", "touchScreen");
- prepareDisplay(DISPLAY_ORIENTATION_0);
+ prepareDisplay(ui::ROTATION_0);
prepareButtons();
prepareAxes(POSITION);
prepareVirtualKeys();
@@ -5294,7 +5010,7 @@
TEST_F(SingleTouchInputMapperTest, GetScanCodeState) {
addConfigurationProperty("touch.deviceType", "touchScreen");
- prepareDisplay(DISPLAY_ORIENTATION_0);
+ prepareDisplay(ui::ROTATION_0);
prepareButtons();
prepareAxes(POSITION);
prepareVirtualKeys();
@@ -5322,7 +5038,7 @@
TEST_F(SingleTouchInputMapperTest, MarkSupportedKeyCodes) {
addConfigurationProperty("touch.deviceType", "touchScreen");
- prepareDisplay(DISPLAY_ORIENTATION_0);
+ prepareDisplay(ui::ROTATION_0);
prepareButtons();
prepareAxes(POSITION);
prepareVirtualKeys();
@@ -5337,7 +5053,7 @@
TEST_F(SingleTouchInputMapperTest, Process_WhenVirtualKeyIsPressedAndReleasedNormally_SendsKeyDownAndKeyUp) {
addConfigurationProperty("touch.deviceType", "touchScreen");
- prepareDisplay(DISPLAY_ORIENTATION_0);
+ prepareDisplay(ui::ROTATION_0);
prepareButtons();
prepareAxes(POSITION);
prepareVirtualKeys();
@@ -5387,7 +5103,7 @@
TEST_F(SingleTouchInputMapperTest, Process_WhenVirtualKeyIsPressedAndMovedOutOfBounds_SendsKeyDownAndKeyCancel) {
addConfigurationProperty("touch.deviceType", "touchScreen");
- prepareDisplay(DISPLAY_ORIENTATION_0);
+ prepareDisplay(ui::ROTATION_0);
prepareButtons();
prepareAxes(POSITION);
prepareVirtualKeys();
@@ -5508,7 +5224,7 @@
TEST_F(SingleTouchInputMapperTest, Process_WhenTouchStartsOutsideDisplayAndMovesIn_SendsDownAsTouchEntersDisplay) {
addConfigurationProperty("touch.deviceType", "touchScreen");
- prepareDisplay(DISPLAY_ORIENTATION_0);
+ prepareDisplay(ui::ROTATION_0);
prepareButtons();
prepareAxes(POSITION);
prepareVirtualKeys();
@@ -5583,7 +5299,7 @@
addConfigurationProperty("touch.deviceType", "touchScreen");
addConfigurationProperty("touch.displayId", VIRTUAL_DISPLAY_UNIQUE_ID);
- prepareVirtualDisplay(DISPLAY_ORIENTATION_0);
+ prepareVirtualDisplay(ui::ROTATION_0);
prepareButtons();
prepareAxes(POSITION);
prepareVirtualKeys();
@@ -5679,7 +5395,7 @@
TEST_F(SingleTouchInputMapperTest, Process_NormalSingleTouchGesture) {
addConfigurationProperty("touch.deviceType", "touchScreen");
- prepareDisplay(DISPLAY_ORIENTATION_0);
+ prepareDisplay(ui::ROTATION_0);
prepareButtons();
prepareAxes(POSITION);
prepareVirtualKeys();
@@ -5778,7 +5494,7 @@
NotifyMotionArgs args;
// Rotation 90.
- prepareDisplay(DISPLAY_ORIENTATION_90);
+ prepareDisplay(ui::ROTATION_90);
processDown(mapper, toRawX(50), toRawY(75));
processSync(mapper);
@@ -5804,7 +5520,7 @@
// Rotation 0.
clearViewports();
- prepareDisplay(DISPLAY_ORIENTATION_0);
+ prepareDisplay(ui::ROTATION_0);
processDown(mapper, toRawX(50), toRawY(75));
processSync(mapper);
@@ -5818,7 +5534,7 @@
// Rotation 90.
clearViewports();
- prepareDisplay(DISPLAY_ORIENTATION_90);
+ prepareDisplay(ui::ROTATION_90);
processDown(mapper, toRawX(75), RAW_Y_MAX - toRawY(50) + RAW_Y_MIN);
processSync(mapper);
@@ -5832,7 +5548,7 @@
// Rotation 180.
clearViewports();
- prepareDisplay(DISPLAY_ORIENTATION_180);
+ prepareDisplay(ui::ROTATION_180);
processDown(mapper, RAW_X_MAX - toRawX(50) + RAW_X_MIN, RAW_Y_MAX - toRawY(75) + RAW_Y_MIN);
processSync(mapper);
@@ -5846,7 +5562,7 @@
// Rotation 270.
clearViewports();
- prepareDisplay(DISPLAY_ORIENTATION_270);
+ prepareDisplay(ui::ROTATION_270);
processDown(mapper, RAW_X_MAX - toRawX(75) + RAW_X_MIN, toRawY(50));
processSync(mapper);
@@ -5866,7 +5582,7 @@
addConfigurationProperty("touch.orientationAware", "1");
addConfigurationProperty("touch.orientation", "ORIENTATION_0");
clearViewports();
- prepareDisplay(DISPLAY_ORIENTATION_0);
+ prepareDisplay(ui::ROTATION_0);
auto& mapper = addMapperAndConfigure<SingleTouchInputMapper>();
NotifyMotionArgs args;
@@ -5890,7 +5606,7 @@
addConfigurationProperty("touch.orientationAware", "1");
addConfigurationProperty("touch.orientation", "ORIENTATION_90");
clearViewports();
- prepareDisplay(DISPLAY_ORIENTATION_0);
+ prepareDisplay(ui::ROTATION_0);
auto& mapper = addMapperAndConfigure<SingleTouchInputMapper>();
NotifyMotionArgs args;
@@ -5914,7 +5630,7 @@
addConfigurationProperty("touch.orientationAware", "1");
addConfigurationProperty("touch.orientation", "ORIENTATION_180");
clearViewports();
- prepareDisplay(DISPLAY_ORIENTATION_0);
+ prepareDisplay(ui::ROTATION_0);
auto& mapper = addMapperAndConfigure<SingleTouchInputMapper>();
NotifyMotionArgs args;
@@ -5938,7 +5654,7 @@
addConfigurationProperty("touch.orientationAware", "1");
addConfigurationProperty("touch.orientation", "ORIENTATION_270");
clearViewports();
- prepareDisplay(DISPLAY_ORIENTATION_0);
+ prepareDisplay(ui::ROTATION_0);
auto& mapper = addMapperAndConfigure<SingleTouchInputMapper>();
NotifyMotionArgs args;
@@ -5969,7 +5685,7 @@
// Orientation 90, Rotation 0.
clearViewports();
- prepareDisplay(DISPLAY_ORIENTATION_0);
+ prepareDisplay(ui::ROTATION_0);
processDown(mapper, RAW_X_MAX - toRotatedRawX(75) + RAW_X_MIN, toRotatedRawY(50));
processSync(mapper);
@@ -5983,7 +5699,7 @@
// Orientation 90, Rotation 90.
clearViewports();
- prepareDisplay(DISPLAY_ORIENTATION_90);
+ prepareDisplay(ui::ROTATION_90);
processDown(mapper, toRotatedRawX(50), toRotatedRawY(75));
processSync(mapper);
@@ -5997,7 +5713,7 @@
// Orientation 90, Rotation 180.
clearViewports();
- prepareDisplay(DISPLAY_ORIENTATION_180);
+ prepareDisplay(ui::ROTATION_180);
processDown(mapper, toRotatedRawX(75), RAW_Y_MAX - toRotatedRawY(50) + RAW_Y_MIN);
processSync(mapper);
@@ -6011,7 +5727,7 @@
// Orientation 90, Rotation 270.
clearViewports();
- prepareDisplay(DISPLAY_ORIENTATION_270);
+ prepareDisplay(ui::ROTATION_270);
processDown(mapper, RAW_X_MAX - toRotatedRawX(50) + RAW_X_MIN,
RAW_Y_MAX - toRotatedRawY(75) + RAW_Y_MIN);
processSync(mapper);
@@ -6027,7 +5743,7 @@
TEST_F(SingleTouchInputMapperTest, Process_AllAxes_DefaultCalibration) {
addConfigurationProperty("touch.deviceType", "touchScreen");
- prepareDisplay(DISPLAY_ORIENTATION_0);
+ prepareDisplay(ui::ROTATION_0);
prepareButtons();
prepareAxes(POSITION | PRESSURE | TOOL | DISTANCE | TILT);
SingleTouchInputMapper& mapper = addMapperAndConfigure<SingleTouchInputMapper>();
@@ -6071,7 +5787,7 @@
TEST_F(SingleTouchInputMapperTest, Process_XYAxes_AffineCalibration) {
addConfigurationProperty("touch.deviceType", "touchScreen");
- prepareDisplay(DISPLAY_ORIENTATION_0);
+ prepareDisplay(ui::ROTATION_0);
prepareLocationCalibration();
prepareButtons();
prepareAxes(POSITION);
@@ -6094,7 +5810,7 @@
TEST_F(SingleTouchInputMapperTest, Process_ShouldHandleAllButtons) {
addConfigurationProperty("touch.deviceType", "touchScreen");
- prepareDisplay(DISPLAY_ORIENTATION_0);
+ prepareDisplay(ui::ROTATION_0);
prepareButtons();
prepareAxes(POSITION);
SingleTouchInputMapper& mapper = addMapperAndConfigure<SingleTouchInputMapper>();
@@ -6337,7 +6053,7 @@
TEST_F(SingleTouchInputMapperTest, Process_ShouldHandleAllToolTypes) {
addConfigurationProperty("touch.deviceType", "touchScreen");
- prepareDisplay(DISPLAY_ORIENTATION_0);
+ prepareDisplay(ui::ROTATION_0);
prepareButtons();
prepareAxes(POSITION);
SingleTouchInputMapper& mapper = addMapperAndConfigure<SingleTouchInputMapper>();
@@ -6472,7 +6188,7 @@
TEST_F(SingleTouchInputMapperTest, Process_WhenBtnTouchPresent_HoversIfItsValueIsZero) {
addConfigurationProperty("touch.deviceType", "touchScreen");
- prepareDisplay(DISPLAY_ORIENTATION_0);
+ prepareDisplay(ui::ROTATION_0);
prepareButtons();
prepareAxes(POSITION);
mFakeEventHub->addKey(EVENTHUB_ID, BTN_TOOL_FINGER, 0, AKEYCODE_UNKNOWN, 0);
@@ -6544,7 +6260,7 @@
TEST_F(SingleTouchInputMapperTest, Process_WhenAbsPressureIsPresent_HoversIfItsValueIsZero) {
addConfigurationProperty("touch.deviceType", "touchScreen");
- prepareDisplay(DISPLAY_ORIENTATION_0);
+ prepareDisplay(ui::ROTATION_0);
prepareButtons();
prepareAxes(POSITION | PRESSURE);
SingleTouchInputMapper& mapper = addMapperAndConfigure<SingleTouchInputMapper>();
@@ -6615,7 +6331,7 @@
TEST_F(SingleTouchInputMapperTest, Reset_CancelsOngoingGesture) {
addConfigurationProperty("touch.deviceType", "touchScreen");
- prepareDisplay(DISPLAY_ORIENTATION_0);
+ prepareDisplay(ui::ROTATION_0);
prepareButtons();
prepareAxes(POSITION | PRESSURE);
SingleTouchInputMapper& mapper = addMapperAndConfigure<SingleTouchInputMapper>();
@@ -6637,7 +6353,7 @@
TEST_F(SingleTouchInputMapperTest, Reset_RecreatesTouchState) {
addConfigurationProperty("touch.deviceType", "touchScreen");
- prepareDisplay(DISPLAY_ORIENTATION_0);
+ prepareDisplay(ui::ROTATION_0);
prepareButtons();
prepareAxes(POSITION | PRESSURE);
SingleTouchInputMapper& mapper = addMapperAndConfigure<SingleTouchInputMapper>();
@@ -6665,7 +6381,7 @@
TEST_F(SingleTouchInputMapperTest,
Process_WhenViewportDisplayIdChanged_TouchIsCanceledAndDeviceIsReset) {
addConfigurationProperty("touch.deviceType", "touchScreen");
- prepareDisplay(DISPLAY_ORIENTATION_0);
+ prepareDisplay(ui::ROTATION_0);
prepareButtons();
prepareAxes(POSITION);
SingleTouchInputMapper& mapper = addMapperAndConfigure<SingleTouchInputMapper>();
@@ -6693,7 +6409,7 @@
TEST_F(SingleTouchInputMapperTest,
Process_WhenViewportActiveStatusChanged_TouchIsCanceledAndDeviceIsReset) {
addConfigurationProperty("touch.deviceType", "touchScreen");
- prepareDisplay(DISPLAY_ORIENTATION_0);
+ prepareDisplay(ui::ROTATION_0);
prepareButtons();
prepareAxes(POSITION);
SingleTouchInputMapper& mapper = addMapperAndConfigure<SingleTouchInputMapper>();
@@ -6753,7 +6469,7 @@
TEST_F(SingleTouchInputMapperTest, ButtonIsReleasedOnTouchUp) {
addConfigurationProperty("touch.deviceType", "touchScreen");
- prepareDisplay(DISPLAY_ORIENTATION_0);
+ prepareDisplay(ui::ROTATION_0);
prepareButtons();
prepareAxes(POSITION);
SingleTouchInputMapper& mapper = addMapperAndConfigure<SingleTouchInputMapper>();
@@ -6796,27 +6512,25 @@
// The values inside DisplayViewport are expected to be pre-rotated. This updates the current
// DisplayViewport to pre-rotate the values. The viewport's physical display will be set to the
// rotated equivalent of the given un-rotated physical display bounds.
- void configurePhysicalDisplay(int32_t orientation, Rect naturalPhysicalDisplay) {
+ void configurePhysicalDisplay(ui::Rotation orientation, Rect naturalPhysicalDisplay) {
uint32_t inverseRotationFlags;
auto width = DISPLAY_WIDTH;
auto height = DISPLAY_HEIGHT;
switch (orientation) {
- case DISPLAY_ORIENTATION_90:
+ case ui::ROTATION_90:
inverseRotationFlags = ui::Transform::ROT_270;
std::swap(width, height);
break;
- case DISPLAY_ORIENTATION_180:
+ case ui::ROTATION_180:
inverseRotationFlags = ui::Transform::ROT_180;
break;
- case DISPLAY_ORIENTATION_270:
+ case ui::ROTATION_270:
inverseRotationFlags = ui::Transform::ROT_90;
std::swap(width, height);
break;
- case DISPLAY_ORIENTATION_0:
+ case ui::ROTATION_0:
inverseRotationFlags = ui::Transform::ROT_0;
break;
- default:
- FAIL() << "Invalid orientation: " << orientation;
}
const ui::Transform rotation(inverseRotationFlags, width, height);
@@ -6860,7 +6574,7 @@
TEST_F(TouchDisplayProjectionTest, IgnoresTouchesOutsidePhysicalDisplay) {
addConfigurationProperty("touch.deviceType", "touchScreen");
- prepareDisplay(DISPLAY_ORIENTATION_0);
+ prepareDisplay(ui::ROTATION_0);
prepareButtons();
prepareAxes(POSITION);
@@ -6875,8 +6589,7 @@
static const std::array<Point, 6> kPointsOutsidePhysicalDisplay{
{{-10, -10}, {0, 0}, {5, 100}, {50, 15}, {75, 100}, {50, 165}}};
- for (auto orientation : {DISPLAY_ORIENTATION_0, DISPLAY_ORIENTATION_90, DISPLAY_ORIENTATION_180,
- DISPLAY_ORIENTATION_270}) {
+ for (auto orientation : {ui::ROTATION_0, ui::ROTATION_90, ui::ROTATION_180, ui::ROTATION_270}) {
configurePhysicalDisplay(orientation, kPhysicalDisplay);
// Touches outside the physical display should be ignored, and should not generate any
@@ -6896,7 +6609,7 @@
TEST_F(TouchDisplayProjectionTest, EmitsTouchDownAfterEnteringPhysicalDisplay) {
addConfigurationProperty("touch.deviceType", "touchScreen");
- prepareDisplay(DISPLAY_ORIENTATION_0);
+ prepareDisplay(ui::ROTATION_0);
prepareButtons();
prepareAxes(POSITION);
@@ -6909,8 +6622,7 @@
// points (10, 20) and (70, 160) inside the display space, which is of the size 400 x 800.
static const Rect kPhysicalDisplay{10, 20, 70, 160};
- for (auto orientation : {DISPLAY_ORIENTATION_0, DISPLAY_ORIENTATION_90, DISPLAY_ORIENTATION_180,
- DISPLAY_ORIENTATION_270}) {
+ for (auto orientation : {ui::ROTATION_0, ui::ROTATION_90, ui::ROTATION_180, ui::ROTATION_270}) {
configurePhysicalDisplay(orientation, kPhysicalDisplay);
// Touches that start outside the physical display should be ignored until it enters the
@@ -6961,7 +6673,7 @@
public:
SingleTouchInputMapper& initializeInputMapperWithExternalStylus() {
addConfigurationProperty("touch.deviceType", "touchScreen");
- prepareDisplay(DISPLAY_ORIENTATION_0);
+ prepareDisplay(ui::ROTATION_0);
prepareButtons();
prepareAxes(POSITION);
auto& mapper = addMapperAndConfigure<SingleTouchInputMapper>();
@@ -7450,7 +7162,7 @@
TEST_F(MultiTouchInputMapperTest, Process_NormalMultiTouchGesture_WithoutTrackingIds) {
addConfigurationProperty("touch.deviceType", "touchScreen");
- prepareDisplay(DISPLAY_ORIENTATION_0);
+ prepareDisplay(ui::ROTATION_0);
prepareAxes(POSITION);
prepareVirtualKeys();
MultiTouchInputMapper& mapper = addMapperAndConfigure<MultiTouchInputMapper>();
@@ -7722,7 +7434,7 @@
TEST_F(MultiTouchInputMapperTest, AxisResolution_IsPopulated) {
addConfigurationProperty("touch.deviceType", "touchScreen");
- prepareDisplay(DISPLAY_ORIENTATION_0);
+ prepareDisplay(ui::ROTATION_0);
mFakeEventHub->addAbsoluteAxis(EVENTHUB_ID, ABS_MT_POSITION_X, RAW_X_MIN, RAW_X_MAX, /*flat*/ 0,
/*fuzz*/ 0, /*resolution*/ 10);
@@ -7752,7 +7464,7 @@
TEST_F(MultiTouchInputMapperTest, TouchMajorAndMinorAxes_DoNotAppearIfNotSupported) {
addConfigurationProperty("touch.deviceType", "touchScreen");
- prepareDisplay(DISPLAY_ORIENTATION_0);
+ prepareDisplay(ui::ROTATION_0);
mFakeEventHub->addAbsoluteAxis(EVENTHUB_ID, ABS_MT_POSITION_X, RAW_X_MIN, RAW_X_MAX, /*flat*/ 0,
/*fuzz*/ 0, /*resolution*/ 10);
@@ -7773,7 +7485,7 @@
TEST_F(MultiTouchInputMapperTest, Process_NormalMultiTouchGesture_WithTrackingIds) {
addConfigurationProperty("touch.deviceType", "touchScreen");
- prepareDisplay(DISPLAY_ORIENTATION_0);
+ prepareDisplay(ui::ROTATION_0);
prepareAxes(POSITION | ID);
prepareVirtualKeys();
MultiTouchInputMapper& mapper = addMapperAndConfigure<MultiTouchInputMapper>();
@@ -7944,7 +7656,7 @@
TEST_F(MultiTouchInputMapperTest, Process_NormalMultiTouchGesture_WithSlots) {
addConfigurationProperty("touch.deviceType", "touchScreen");
- prepareDisplay(DISPLAY_ORIENTATION_0);
+ prepareDisplay(ui::ROTATION_0);
prepareAxes(POSITION | ID | SLOT);
prepareVirtualKeys();
MultiTouchInputMapper& mapper = addMapperAndConfigure<MultiTouchInputMapper>();
@@ -8110,7 +7822,7 @@
TEST_F(MultiTouchInputMapperTest, Process_AllAxes_WithDefaultCalibration) {
addConfigurationProperty("touch.deviceType", "touchScreen");
- prepareDisplay(DISPLAY_ORIENTATION_0);
+ prepareDisplay(ui::ROTATION_0);
prepareAxes(POSITION | TOUCH | TOOL | PRESSURE | ORIENTATION | ID | MINOR | DISTANCE);
MultiTouchInputMapper& mapper = addMapperAndConfigure<MultiTouchInputMapper>();
@@ -8159,7 +7871,7 @@
TEST_F(MultiTouchInputMapperTest, Process_TouchAndToolAxes_GeometricCalibration) {
addConfigurationProperty("touch.deviceType", "touchScreen");
- prepareDisplay(DISPLAY_ORIENTATION_0);
+ prepareDisplay(ui::ROTATION_0);
prepareAxes(POSITION | TOUCH | TOOL | MINOR);
addConfigurationProperty("touch.size.calibration", "geometric");
MultiTouchInputMapper& mapper = addMapperAndConfigure<MultiTouchInputMapper>();
@@ -8196,7 +7908,7 @@
TEST_F(MultiTouchInputMapperTest, Process_TouchAndToolAxes_SummedLinearCalibration) {
addConfigurationProperty("touch.deviceType", "touchScreen");
- prepareDisplay(DISPLAY_ORIENTATION_0);
+ prepareDisplay(ui::ROTATION_0);
prepareAxes(POSITION | TOUCH | TOOL);
addConfigurationProperty("touch.size.calibration", "diameter");
addConfigurationProperty("touch.size.scale", "10");
@@ -8247,7 +7959,7 @@
TEST_F(MultiTouchInputMapperTest, Process_TouchAndToolAxes_AreaCalibration) {
addConfigurationProperty("touch.deviceType", "touchScreen");
- prepareDisplay(DISPLAY_ORIENTATION_0);
+ prepareDisplay(ui::ROTATION_0);
prepareAxes(POSITION | TOUCH | TOOL);
addConfigurationProperty("touch.size.calibration", "area");
addConfigurationProperty("touch.size.scale", "43");
@@ -8280,7 +7992,7 @@
TEST_F(MultiTouchInputMapperTest, Process_PressureAxis_AmplitudeCalibration) {
addConfigurationProperty("touch.deviceType", "touchScreen");
- prepareDisplay(DISPLAY_ORIENTATION_0);
+ prepareDisplay(ui::ROTATION_0);
prepareAxes(POSITION | PRESSURE);
addConfigurationProperty("touch.pressure.calibration", "amplitude");
addConfigurationProperty("touch.pressure.scale", "0.01");
@@ -8314,7 +8026,7 @@
TEST_F(MultiTouchInputMapperTest, Process_ShouldHandleAllButtons) {
addConfigurationProperty("touch.deviceType", "touchScreen");
- prepareDisplay(DISPLAY_ORIENTATION_0);
+ prepareDisplay(ui::ROTATION_0);
prepareAxes(POSITION | ID | SLOT);
MultiTouchInputMapper& mapper = addMapperAndConfigure<MultiTouchInputMapper>();
@@ -8557,7 +8269,7 @@
TEST_F(MultiTouchInputMapperTest, Process_ShouldHandleMappedStylusButtons) {
addConfigurationProperty("touch.deviceType", "touchScreen");
- prepareDisplay(DISPLAY_ORIENTATION_0);
+ prepareDisplay(ui::ROTATION_0);
prepareAxes(POSITION | ID | SLOT);
MultiTouchInputMapper& mapper = addMapperAndConfigure<MultiTouchInputMapper>();
@@ -8614,7 +8326,7 @@
TEST_F(MultiTouchInputMapperTest, Process_ShouldHandleAllToolTypes) {
addConfigurationProperty("touch.deviceType", "touchScreen");
- prepareDisplay(DISPLAY_ORIENTATION_0);
+ prepareDisplay(ui::ROTATION_0);
prepareAxes(POSITION | ID | SLOT | TOOL_TYPE);
MultiTouchInputMapper& mapper = addMapperAndConfigure<MultiTouchInputMapper>();
@@ -8764,7 +8476,7 @@
TEST_F(MultiTouchInputMapperTest, Process_WhenBtnTouchPresent_HoversIfItsValueIsZero) {
addConfigurationProperty("touch.deviceType", "touchScreen");
- prepareDisplay(DISPLAY_ORIENTATION_0);
+ prepareDisplay(ui::ROTATION_0);
prepareAxes(POSITION | ID | SLOT);
mFakeEventHub->addKey(EVENTHUB_ID, BTN_TOUCH, 0, AKEYCODE_UNKNOWN, 0);
MultiTouchInputMapper& mapper = addMapperAndConfigure<MultiTouchInputMapper>();
@@ -8835,7 +8547,7 @@
TEST_F(MultiTouchInputMapperTest, Process_WhenAbsMTPressureIsPresent_HoversIfItsValueIsZero) {
addConfigurationProperty("touch.deviceType", "touchScreen");
- prepareDisplay(DISPLAY_ORIENTATION_0);
+ prepareDisplay(ui::ROTATION_0);
prepareAxes(POSITION | ID | SLOT | PRESSURE);
MultiTouchInputMapper& mapper = addMapperAndConfigure<MultiTouchInputMapper>();
@@ -8935,7 +8647,7 @@
ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasNotCalled());
// Add viewport for display 1 on hdmi1
- prepareDisplay(DISPLAY_ORIENTATION_0, hdmi1);
+ prepareDisplay(ui::ROTATION_0, hdmi1);
// Send a touch event again
processPosition(mapper, 100, 100);
processSync(mapper);
@@ -8952,8 +8664,8 @@
mFakePolicy->addInputUniqueIdAssociation(DEVICE_LOCATION, VIRTUAL_DISPLAY_UNIQUE_ID);
- prepareDisplay(DISPLAY_ORIENTATION_0);
- prepareVirtualDisplay(DISPLAY_ORIENTATION_0);
+ prepareDisplay(ui::ROTATION_0);
+ prepareVirtualDisplay(ui::ROTATION_0);
// Send a touch event
processPosition(mapper, 100, 100);
@@ -8976,7 +8688,7 @@
mFakePolicy->setDefaultPointerDisplayId(SECONDARY_DISPLAY_ID);
prepareSecondaryDisplay(ViewportType::EXTERNAL);
- prepareDisplay(DISPLAY_ORIENTATION_0);
+ prepareDisplay(ui::ROTATION_0);
prepareAxes(POSITION);
MultiTouchInputMapper& mapper = addMapperAndConfigure<MultiTouchInputMapper>();
@@ -9000,7 +8712,7 @@
prepareAxes(POSITION);
MultiTouchInputMapper& mapper = addMapperAndConfigure<MultiTouchInputMapper>();
- prepareDisplay(DISPLAY_ORIENTATION_0);
+ prepareDisplay(ui::ROTATION_0);
process(mapper, 10, 11 /*readTime*/, EV_ABS, ABS_MT_TRACKING_ID, 1);
process(mapper, 15, 16 /*readTime*/, EV_ABS, ABS_MT_POSITION_X, 100);
process(mapper, 20, 21 /*readTime*/, EV_ABS, ABS_MT_POSITION_Y, 100);
@@ -9025,9 +8737,8 @@
TEST_F(MultiTouchInputMapperTest, WhenViewportIsNotActive_TouchesAreDropped) {
addConfigurationProperty("touch.deviceType", "touchScreen");
// Don't set touch.enableForInactiveViewport to verify the default behavior.
- mFakePolicy->addDisplayViewport(DISPLAY_ID, DISPLAY_WIDTH, DISPLAY_HEIGHT,
- DISPLAY_ORIENTATION_0, false /*isActive*/, UNIQUE_ID, NO_PORT,
- ViewportType::INTERNAL);
+ mFakePolicy->addDisplayViewport(DISPLAY_ID, DISPLAY_WIDTH, DISPLAY_HEIGHT, ui::ROTATION_0,
+ false /*isActive*/, UNIQUE_ID, NO_PORT, ViewportType::INTERNAL);
configureDevice(InputReaderConfiguration::CHANGE_DISPLAY_INFO);
prepareAxes(POSITION);
MultiTouchInputMapper& mapper = addMapperAndConfigure<MultiTouchInputMapper>();
@@ -9046,9 +8757,8 @@
TEST_F(MultiTouchInputMapperTest, WhenViewportIsNotActive_TouchesAreProcessed) {
addConfigurationProperty("touch.deviceType", "touchScreen");
addConfigurationProperty("touch.enableForInactiveViewport", "1");
- mFakePolicy->addDisplayViewport(DISPLAY_ID, DISPLAY_WIDTH, DISPLAY_HEIGHT,
- DISPLAY_ORIENTATION_0, false /*isActive*/, UNIQUE_ID, NO_PORT,
- ViewportType::INTERNAL);
+ mFakePolicy->addDisplayViewport(DISPLAY_ID, DISPLAY_WIDTH, DISPLAY_HEIGHT, ui::ROTATION_0,
+ false /*isActive*/, UNIQUE_ID, NO_PORT, ViewportType::INTERNAL);
configureDevice(InputReaderConfiguration::CHANGE_DISPLAY_INFO);
prepareAxes(POSITION);
MultiTouchInputMapper& mapper = addMapperAndConfigure<MultiTouchInputMapper>();
@@ -9064,9 +8774,8 @@
TEST_F(MultiTouchInputMapperTest, Process_DeactivateViewport_AbortTouches) {
addConfigurationProperty("touch.deviceType", "touchScreen");
addConfigurationProperty("touch.enableForInactiveViewport", "0");
- mFakePolicy->addDisplayViewport(DISPLAY_ID, DISPLAY_WIDTH, DISPLAY_HEIGHT,
- DISPLAY_ORIENTATION_0, true /*isActive*/, UNIQUE_ID, NO_PORT,
- ViewportType::INTERNAL);
+ mFakePolicy->addDisplayViewport(DISPLAY_ID, DISPLAY_WIDTH, DISPLAY_HEIGHT, ui::ROTATION_0,
+ true /*isActive*/, UNIQUE_ID, NO_PORT, ViewportType::INTERNAL);
std::optional<DisplayViewport> optionalDisplayViewport =
mFakePolicy->getDisplayViewportByUniqueId(UNIQUE_ID);
ASSERT_TRUE(optionalDisplayViewport.has_value());
@@ -9160,7 +8869,7 @@
mFakePolicy->setShowTouches(true);
// Create displays.
- prepareDisplay(DISPLAY_ORIENTATION_0, hdmi1);
+ prepareDisplay(ui::ROTATION_0, hdmi1);
prepareSecondaryDisplay(ViewportType::EXTERNAL, hdmi2);
// Default device will reconfigure above, need additional reconfiguration for another device.
@@ -9205,7 +8914,7 @@
TEST_F(MultiTouchInputMapperTest, VideoFrames_ReceivedByListener) {
prepareAxes(POSITION);
addConfigurationProperty("touch.deviceType", "touchScreen");
- prepareDisplay(DISPLAY_ORIENTATION_0);
+ prepareDisplay(ui::ROTATION_0);
MultiTouchInputMapper& mapper = addMapperAndConfigure<MultiTouchInputMapper>();
NotifyMotionArgs motionArgs;
@@ -9236,8 +8945,7 @@
NotifyMotionArgs motionArgs;
// Test all 4 orientations
- for (int32_t orientation : {DISPLAY_ORIENTATION_0, DISPLAY_ORIENTATION_90,
- DISPLAY_ORIENTATION_180, DISPLAY_ORIENTATION_270}) {
+ for (ui::Rotation orientation : ftl::enum_range<ui::Rotation>()) {
SCOPED_TRACE("Orientation " + StringPrintf("%i", orientation));
clearViewports();
prepareDisplay(orientation);
@@ -9262,8 +8970,7 @@
NotifyMotionArgs motionArgs;
// Test all 4 orientations
- for (int32_t orientation : {DISPLAY_ORIENTATION_0, DISPLAY_ORIENTATION_90,
- DISPLAY_ORIENTATION_180, DISPLAY_ORIENTATION_270}) {
+ for (ui::Rotation orientation : ftl::enum_range<ui::Rotation>()) {
SCOPED_TRACE("Orientation " + StringPrintf("%i", orientation));
clearViewports();
prepareDisplay(orientation);
@@ -9297,7 +9004,7 @@
std::vector<TouchVideoFrame> frames{frame1, frame2, frame3};
NotifyMotionArgs motionArgs;
- prepareDisplay(DISPLAY_ORIENTATION_90);
+ prepareDisplay(ui::ROTATION_90);
mFakeEventHub->setVideoFrames({{EVENTHUB_ID, frames}});
processPosition(mapper, 100, 200);
processSync(mapper);
@@ -9320,7 +9027,7 @@
std::vector<TouchVideoFrame> frames{frame1, frame2, frame3};
NotifyMotionArgs motionArgs;
- prepareDisplay(DISPLAY_ORIENTATION_90);
+ prepareDisplay(ui::ROTATION_90);
mFakeEventHub->setVideoFrames({{EVENTHUB_ID, frames}});
processPosition(mapper, 100, 200);
processSync(mapper);
@@ -9330,7 +9037,7 @@
// compared to the display. This is so that when the window transform (which contains the
// display rotation) is applied later by InputDispatcher, the coordinates end up in the
// window's coordinate space.
- frame.rotate(getInverseRotation(DISPLAY_ORIENTATION_90));
+ frame.rotate(getInverseRotation(ui::ROTATION_90));
});
ASSERT_EQ(frames, motionArgs.videoFrames);
}
@@ -9367,7 +9074,7 @@
TEST_F(MultiTouchInputMapperTest, Process_ShouldHandleSingleTouch) {
addConfigurationProperty("touch.deviceType", "touchScreen");
- prepareDisplay(DISPLAY_ORIENTATION_0);
+ prepareDisplay(ui::ROTATION_0);
prepareAxes(POSITION | ID | SLOT | TOOL_TYPE);
MultiTouchInputMapper& mapper = addMapperAndConfigure<MultiTouchInputMapper>();
@@ -9412,7 +9119,7 @@
*/
TEST_F(MultiTouchInputMapperTest, Process_ShouldHandlePalmToolType_SinglePointer) {
addConfigurationProperty("touch.deviceType", "touchScreen");
- prepareDisplay(DISPLAY_ORIENTATION_0);
+ prepareDisplay(ui::ROTATION_0);
prepareAxes(POSITION | ID | SLOT | TOOL_TYPE);
MultiTouchInputMapper& mapper = addMapperAndConfigure<MultiTouchInputMapper>();
@@ -9460,7 +9167,7 @@
*/
TEST_F(MultiTouchInputMapperTest, Process_ShouldHandlePalmToolType_TwoPointers) {
addConfigurationProperty("touch.deviceType", "touchScreen");
- prepareDisplay(DISPLAY_ORIENTATION_0);
+ prepareDisplay(ui::ROTATION_0);
prepareAxes(POSITION | ID | SLOT | TOOL_TYPE);
MultiTouchInputMapper& mapper = addMapperAndConfigure<MultiTouchInputMapper>();
@@ -9535,7 +9242,7 @@
*/
TEST_F(MultiTouchInputMapperTest, Process_ShouldHandlePalmToolType_ShouldCancelWhenAllTouchIsPalm) {
addConfigurationProperty("touch.deviceType", "touchScreen");
- prepareDisplay(DISPLAY_ORIENTATION_0);
+ prepareDisplay(ui::ROTATION_0);
prepareAxes(POSITION | ID | SLOT | TOOL_TYPE);
MultiTouchInputMapper& mapper = addMapperAndConfigure<MultiTouchInputMapper>();
@@ -9633,7 +9340,7 @@
*/
TEST_F(MultiTouchInputMapperTest, Process_ShouldHandlePalmToolType_KeepFirstPointer) {
addConfigurationProperty("touch.deviceType", "touchScreen");
- prepareDisplay(DISPLAY_ORIENTATION_0);
+ prepareDisplay(ui::ROTATION_0);
prepareAxes(POSITION | ID | SLOT | TOOL_TYPE);
MultiTouchInputMapper& mapper = addMapperAndConfigure<MultiTouchInputMapper>();
@@ -9705,7 +9412,7 @@
*/
TEST_F(MultiTouchInputMapperTest, Process_MultiTouch_WithInvalidTrackingId) {
addConfigurationProperty("touch.deviceType", "touchScreen");
- prepareDisplay(DISPLAY_ORIENTATION_0);
+ prepareDisplay(ui::ROTATION_0);
prepareAxes(POSITION | ID | SLOT | PRESSURE);
MultiTouchInputMapper& mapper = addMapperAndConfigure<MultiTouchInputMapper>();
@@ -9762,7 +9469,7 @@
TEST_F(MultiTouchInputMapperTest, Reset_PreservesLastTouchState) {
addConfigurationProperty("touch.deviceType", "touchScreen");
- prepareDisplay(DISPLAY_ORIENTATION_0);
+ prepareDisplay(ui::ROTATION_0);
prepareAxes(POSITION | ID | SLOT | PRESSURE);
MultiTouchInputMapper& mapper = addMapperAndConfigure<MultiTouchInputMapper>();
@@ -9803,7 +9510,7 @@
TEST_F(MultiTouchInputMapperTest, Reset_PreservesLastTouchState_NoPointersDown) {
addConfigurationProperty("touch.deviceType", "touchScreen");
- prepareDisplay(DISPLAY_ORIENTATION_0);
+ prepareDisplay(ui::ROTATION_0);
prepareAxes(POSITION | ID | SLOT | PRESSURE);
MultiTouchInputMapper& mapper = addMapperAndConfigure<MultiTouchInputMapper>();
@@ -9831,7 +9538,7 @@
TEST_F(MultiTouchInputMapperTest, StylusSourceIsAddedDynamicallyFromToolType) {
addConfigurationProperty("touch.deviceType", "touchScreen");
- prepareDisplay(DISPLAY_ORIENTATION_0);
+ prepareDisplay(ui::ROTATION_0);
prepareAxes(POSITION | ID | SLOT | PRESSURE | TOOL_TYPE);
MultiTouchInputMapper& mapper = addMapperAndConfigure<MultiTouchInputMapper>();
ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyDeviceResetWasCalled());
@@ -9890,7 +9597,7 @@
TEST_F(MultiTouchInputMapperTest_ExternalDevice, Viewports_Fallback) {
prepareAxes(POSITION);
addConfigurationProperty("touch.deviceType", "touchScreen");
- prepareDisplay(DISPLAY_ORIENTATION_0);
+ prepareDisplay(ui::ROTATION_0);
MultiTouchInputMapper& mapper = addMapperAndConfigure<MultiTouchInputMapper>();
ASSERT_EQ(AINPUT_SOURCE_TOUCHSCREEN, mapper.getSources());
@@ -9921,7 +9628,7 @@
fakePointerController->setButtonState(0);
// prepare device and capture
- prepareDisplay(DISPLAY_ORIENTATION_0);
+ prepareDisplay(ui::ROTATION_0);
prepareAxes(POSITION | ID | SLOT);
mFakeEventHub->addKey(EVENTHUB_ID, BTN_LEFT, 0, AKEYCODE_UNKNOWN, 0);
mFakeEventHub->addKey(EVENTHUB_ID, BTN_TOUCH, 0, AKEYCODE_UNKNOWN, 0);
@@ -10071,7 +9778,7 @@
fakePointerController->setButtonState(0);
// prepare device and capture
- prepareDisplay(DISPLAY_ORIENTATION_0);
+ prepareDisplay(ui::ROTATION_0);
prepareAxes(POSITION | ID | SLOT);
mFakeEventHub->addKey(EVENTHUB_ID, BTN_LEFT, 0, AKEYCODE_UNKNOWN, 0);
mFakeEventHub->addKey(EVENTHUB_ID, BTN_TOUCH, 0, AKEYCODE_UNKNOWN, 0);
@@ -10112,7 +9819,7 @@
std::shared_ptr<FakePointerController> fakePointerController =
std::make_shared<FakePointerController>();
- prepareDisplay(DISPLAY_ORIENTATION_0);
+ prepareDisplay(ui::ROTATION_0);
prepareAxes(POSITION | ID | SLOT);
mFakeEventHub->addKey(EVENTHUB_ID, BTN_LEFT, 0, AKEYCODE_UNKNOWN, 0);
mFakePolicy->setPointerController(fakePointerController);
@@ -10139,7 +9846,7 @@
TEST_F(BluetoothMultiTouchInputMapperTest, TimestampSmoothening) {
addConfigurationProperty("touch.deviceType", "touchScreen");
- prepareDisplay(DISPLAY_ORIENTATION_0);
+ prepareDisplay(ui::ROTATION_0);
prepareAxes(POSITION | ID | SLOT | PRESSURE);
MultiTouchInputMapper& mapper = addMapperAndConfigure<MultiTouchInputMapper>();
@@ -10189,7 +9896,7 @@
fakePointerController->setBounds(0, 0, DISPLAY_WIDTH - 1, DISPLAY_HEIGHT - 1);
fakePointerController->setPosition(0, 0);
fakePointerController->setButtonState(0);
- prepareDisplay(DISPLAY_ORIENTATION_0);
+ prepareDisplay(ui::ROTATION_0);
prepareAxes(POSITION);
prepareAbsoluteAxisResolution(xAxisResolution, yAxisResolution);
@@ -10549,7 +10256,7 @@
process(mapper, ARBITRARY_TIME, READ_TIME, EV_SYN, SYN_REPORT, 0);
}
- void prepareVirtualDisplay(int32_t orientation) {
+ void prepareVirtualDisplay(ui::Rotation orientation) {
setDisplayInfoAndReconfigure(VIRTUAL_DISPLAY_ID, VIRTUAL_DISPLAY_WIDTH,
VIRTUAL_DISPLAY_HEIGHT, orientation, VIRTUAL_DISPLAY_UNIQUE_ID,
NO_PORT, ViewportType::VIRTUAL);
@@ -10567,7 +10274,7 @@
mFakePolicy->addInputUniqueIdAssociation(DEVICE_LOCATION, VIRTUAL_DISPLAY_UNIQUE_ID);
- prepareVirtualDisplay(DISPLAY_ORIENTATION_0);
+ prepareVirtualDisplay(ui::ROTATION_0);
// Send an axis event
processAxis(mapper, ABS_X, 100);
diff --git a/services/inputflinger/tests/InstrumentedInputReader.cpp b/services/inputflinger/tests/InstrumentedInputReader.cpp
new file mode 100644
index 0000000..1f8cd12
--- /dev/null
+++ b/services/inputflinger/tests/InstrumentedInputReader.cpp
@@ -0,0 +1,50 @@
+/*
+ * 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 "InstrumentedInputReader.h"
+
+namespace android {
+
+InstrumentedInputReader::InstrumentedInputReader(std::shared_ptr<EventHubInterface> eventHub,
+ const sp<InputReaderPolicyInterface>& policy,
+ InputListenerInterface& listener)
+ : InputReader(eventHub, policy, listener), mFakeContext(this) {}
+
+void InstrumentedInputReader::pushNextDevice(std::shared_ptr<InputDevice> device) {
+ mNextDevices.push(device);
+}
+
+std::shared_ptr<InputDevice> InstrumentedInputReader::newDevice(int32_t deviceId,
+ const std::string& name,
+ const std::string& location) {
+ InputDeviceIdentifier identifier;
+ identifier.name = name;
+ identifier.location = location;
+ int32_t generation = deviceId + 1;
+ return std::make_shared<InputDevice>(&mFakeContext, deviceId, generation, identifier);
+}
+
+std::shared_ptr<InputDevice> InstrumentedInputReader::createDeviceLocked(
+ int32_t eventHubId, const InputDeviceIdentifier& identifier) REQUIRES(mLock) {
+ if (!mNextDevices.empty()) {
+ std::shared_ptr<InputDevice> device(std::move(mNextDevices.front()));
+ mNextDevices.pop();
+ return device;
+ }
+ return InputReader::createDeviceLocked(eventHubId, identifier);
+}
+
+} // namespace android
diff --git a/services/inputflinger/tests/InstrumentedInputReader.h b/services/inputflinger/tests/InstrumentedInputReader.h
new file mode 100644
index 0000000..7f8d556
--- /dev/null
+++ b/services/inputflinger/tests/InstrumentedInputReader.h
@@ -0,0 +1,123 @@
+/*
+ * 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 <memory>
+#include <queue>
+#include <string>
+
+#include <InputDevice.h>
+#include <InputReader.h>
+#include <gtest/gtest.h>
+#include <utils/StrongPointer.h>
+
+namespace android {
+
+class InstrumentedInputReader : public InputReader {
+public:
+ InstrumentedInputReader(std::shared_ptr<EventHubInterface> eventHub,
+ const sp<InputReaderPolicyInterface>& policy,
+ InputListenerInterface& listener);
+ virtual ~InstrumentedInputReader() {}
+
+ void pushNextDevice(std::shared_ptr<InputDevice> device);
+
+ std::shared_ptr<InputDevice> newDevice(int32_t deviceId, const std::string& name,
+ const std::string& location = "");
+
+ // Make the protected loopOnce method accessible to tests.
+ using InputReader::loopOnce;
+
+protected:
+ virtual std::shared_ptr<InputDevice> createDeviceLocked(
+ int32_t eventHubId, const InputDeviceIdentifier& identifier);
+
+ class FakeInputReaderContext : public ContextImpl {
+ public:
+ FakeInputReaderContext(InputReader* reader)
+ : ContextImpl(reader),
+ mGlobalMetaState(0),
+ mUpdateGlobalMetaStateWasCalled(false),
+ mGeneration(1) {}
+
+ virtual ~FakeInputReaderContext() {}
+
+ void assertUpdateGlobalMetaStateWasCalled() {
+ ASSERT_TRUE(mUpdateGlobalMetaStateWasCalled)
+ << "Expected updateGlobalMetaState() to have been called.";
+ mUpdateGlobalMetaStateWasCalled = false;
+ }
+
+ void setGlobalMetaState(int32_t state) { mGlobalMetaState = state; }
+
+ uint32_t getGeneration() { return mGeneration; }
+
+ void updateGlobalMetaState() override {
+ mUpdateGlobalMetaStateWasCalled = true;
+ ContextImpl::updateGlobalMetaState();
+ }
+
+ int32_t getGlobalMetaState() override {
+ return mGlobalMetaState | ContextImpl::getGlobalMetaState();
+ }
+
+ int32_t bumpGeneration() override {
+ mGeneration = ContextImpl::bumpGeneration();
+ return mGeneration;
+ }
+
+ void requestTimeoutAtTime(nsecs_t when) override { mRequestedTimeout = when; }
+
+ void assertTimeoutWasRequested(nsecs_t when) {
+ ASSERT_TRUE(mRequestedTimeout) << "Expected timeout at time " << when
+ << " but there was no timeout requested.";
+ ASSERT_EQ(when, *mRequestedTimeout);
+ mRequestedTimeout.reset();
+ }
+
+ void assertTimeoutWasNotRequested() {
+ ASSERT_FALSE(mRequestedTimeout) << "Expected no timeout to have been requested,"
+ " but one was requested at time "
+ << *mRequestedTimeout;
+ }
+
+ void getExternalStylusDevices(std::vector<InputDeviceInfo>& outDevices) override {
+ outDevices = mExternalStylusDevices;
+ }
+
+ void setExternalStylusDevices(std::vector<InputDeviceInfo>&& devices) {
+ mExternalStylusDevices = devices;
+ }
+
+ private:
+ int32_t mGlobalMetaState;
+ bool mUpdateGlobalMetaStateWasCalled;
+ int32_t mGeneration;
+ std::optional<nsecs_t> mRequestedTimeout;
+ std::vector<InputDeviceInfo> mExternalStylusDevices;
+ } mFakeContext;
+
+ friend class InputReaderTest;
+
+public:
+ FakeInputReaderContext* getContext() { return &mFakeContext; }
+
+private:
+ std::queue<std::shared_ptr<InputDevice>> mNextDevices;
+};
+
+} // namespace android
diff --git a/services/inputflinger/tests/TestConstants.h b/services/inputflinger/tests/TestConstants.h
index 8dc9d71..27881f6 100644
--- a/services/inputflinger/tests/TestConstants.h
+++ b/services/inputflinger/tests/TestConstants.h
@@ -27,4 +27,7 @@
static constexpr nsecs_t ARBITRARY_TIME = 1234;
static constexpr nsecs_t READ_TIME = 4321;
+// Error tolerance for floating point assertions.
+static const float EPSILON = 0.001f;
+
} // namespace android
diff --git a/services/inputflinger/tests/fuzzers/InputReaderFuzzer.cpp b/services/inputflinger/tests/fuzzers/InputReaderFuzzer.cpp
index 2eed997..057c15d 100644
--- a/services/inputflinger/tests/fuzzers/InputReaderFuzzer.cpp
+++ b/services/inputflinger/tests/fuzzers/InputReaderFuzzer.cpp
@@ -153,6 +153,10 @@
return reader->getLightPlayerId(deviceId, lightId);
}
+ void addKeyRemapping(int32_t deviceId, int32_t fromKeyCode, int32_t toKeyCode) const {
+ reader->addKeyRemapping(deviceId, fromKeyCode, toKeyCode);
+ }
+
int32_t getKeyCodeForKeyLocation(int32_t deviceId, int32_t locationKeyCode) const {
return reader->getKeyCodeForKeyLocation(deviceId, locationKeyCode);
}
diff --git a/services/inputflinger/tests/fuzzers/MapperHelpers.h b/services/inputflinger/tests/fuzzers/MapperHelpers.h
index 64316ba..cd852d6 100644
--- a/services/inputflinger/tests/fuzzers/MapperHelpers.h
+++ b/services/inputflinger/tests/fuzzers/MapperHelpers.h
@@ -211,6 +211,7 @@
int32_t getSwitchState(int32_t deviceId, int32_t sw) const override {
return mFdp->ConsumeIntegral<int32_t>();
}
+ void addKeyRemapping(int32_t deviceId, int32_t fromKeyCode, int32_t toKeyCode) const override {}
int32_t getKeyCodeForKeyLocation(int32_t deviceId, int32_t locationKeyCode) const override {
return mFdp->ConsumeIntegral<int32_t>();
}
@@ -311,7 +312,7 @@
return mFdp->ConsumeRandomLengthString(32);
}
TouchAffineTransformation getTouchAffineTransformation(const std::string& inputDeviceDescriptor,
- int32_t surfaceRotation) override {
+ ui::Rotation surfaceRotation) override {
return mTransform;
}
void setTouchAffineTransformation(const TouchAffineTransformation t) { mTransform = t; }
diff --git a/services/sensorservice/SensorDirectConnection.cpp b/services/sensorservice/SensorDirectConnection.cpp
index 291c770..4ac9651 100644
--- a/services/sensorservice/SensorDirectConnection.cpp
+++ b/services/sensorservice/SensorDirectConnection.cpp
@@ -151,7 +151,7 @@
return PERMISSION_DENIED;
}
- sp<SensorInterface> si = mService->getSensorInterfaceFromHandle(handle);
+ std::shared_ptr<SensorInterface> si = mService->getSensorInterfaceFromHandle(handle);
if (si == nullptr) {
return NAME_NOT_FOUND;
}
@@ -228,7 +228,7 @@
for (auto &i : existingConnections) {
int handle = i.first;
int rateLevel = i.second;
- sp<SensorInterface> si = mService->getSensorInterfaceFromHandle(handle);
+ std::shared_ptr<SensorInterface> si = mService->getSensorInterfaceFromHandle(handle);
if (si != nullptr) {
const Sensor& s = si->getSensor();
if (mService->isSensorInCappedSet(s.getType()) &&
diff --git a/services/sensorservice/SensorEventConnection.cpp b/services/sensorservice/SensorEventConnection.cpp
index f06f947..82d0295 100644
--- a/services/sensorservice/SensorEventConnection.cpp
+++ b/services/sensorservice/SensorEventConnection.cpp
@@ -160,7 +160,7 @@
bool SensorService::SensorEventConnection::addSensor(int32_t handle) {
Mutex::Autolock _l(mConnectionLock);
- sp<SensorInterface> si = mService->getSensorInterfaceFromHandle(handle);
+ std::shared_ptr<SensorInterface> si = mService->getSensorInterfaceFromHandle(handle);
if (si == nullptr ||
!mService->canAccessSensor(si->getSensor(), "Add to SensorEventConnection: ",
mOpPackageName) ||
@@ -202,7 +202,7 @@
Mutex::Autolock _l(mConnectionLock);
for (auto &it : mSensorInfo) {
const int handle = it.first;
- sp<SensorInterface> si = mService->getSensorInterfaceFromHandle(handle);
+ std::shared_ptr<SensorInterface> si = mService->getSensorInterfaceFromHandle(handle);
if (si != nullptr && si->getSensor().getReportingMode() == AREPORTING_MODE_ONE_SHOT) {
return true;
}
@@ -245,7 +245,7 @@
if (mDataInjectionMode) looper_flags |= ALOOPER_EVENT_INPUT;
for (auto& it : mSensorInfo) {
const int handle = it.first;
- sp<SensorInterface> si = mService->getSensorInterfaceFromHandle(handle);
+ std::shared_ptr<SensorInterface> si = mService->getSensorInterfaceFromHandle(handle);
if (si != nullptr && si->getSensor().isWakeUpSensor()) {
looper_flags |= ALOOPER_EVENT_INPUT;
}
@@ -555,7 +555,7 @@
// flush complete events to be sent.
for (auto& it : mSensorInfo) {
const int handle = it.first;
- sp<SensorInterface> si = mService->getSensorInterfaceFromHandle(handle);
+ std::shared_ptr<SensorInterface> si = mService->getSensorInterfaceFromHandle(handle);
if (si == nullptr) {
continue;
}
@@ -689,7 +689,7 @@
if (enabled) {
nsecs_t requestedSamplingPeriodNs = samplingPeriodNs;
bool isSensorCapped = false;
- sp<SensorInterface> si = mService->getSensorInterfaceFromHandle(handle);
+ std::shared_ptr<SensorInterface> si = mService->getSensorInterfaceFromHandle(handle);
if (si != nullptr) {
const Sensor& s = si->getSensor();
if (mService->isSensorInCappedSet(s.getType())) {
@@ -729,7 +729,7 @@
nsecs_t requestedSamplingPeriodNs = samplingPeriodNs;
bool isSensorCapped = false;
- sp<SensorInterface> si = mService->getSensorInterfaceFromHandle(handle);
+ std::shared_ptr<SensorInterface> si = mService->getSensorInterfaceFromHandle(handle);
if (si != nullptr) {
const Sensor& s = si->getSensor();
if (mService->isSensorInCappedSet(s.getType())) {
@@ -852,7 +852,7 @@
}
sensors_event_t sensor_event;
memcpy(&sensor_event, buf, sizeof(sensors_event_t));
- sp<SensorInterface> si =
+ std::shared_ptr<SensorInterface> si =
mService->getSensorInterfaceFromHandle(sensor_event.sensor);
if (si == nullptr) {
return 1;
@@ -903,7 +903,7 @@
size_t fifoWakeUpSensors = 0;
size_t fifoNonWakeUpSensors = 0;
for (auto& it : mSensorInfo) {
- sp<SensorInterface> si = mService->getSensorInterfaceFromHandle(it.first);
+ std::shared_ptr<SensorInterface> si = mService->getSensorInterfaceFromHandle(it.first);
if (si == nullptr) {
continue;
}
diff --git a/services/sensorservice/SensorList.cpp b/services/sensorservice/SensorList.cpp
index 6d36b47..daff4d0 100644
--- a/services/sensorservice/SensorList.cpp
+++ b/services/sensorservice/SensorList.cpp
@@ -28,13 +28,13 @@
const Sensor SensorList::mNonSensor = Sensor("unknown");
-bool SensorList::add(
- int handle, SensorInterface* si, bool isForDebug, bool isVirtual, int deviceId) {
+bool SensorList::add(int handle, std::shared_ptr<SensorInterface> si, bool isForDebug,
+ bool isVirtual, int deviceId) {
std::lock_guard<std::mutex> lk(mLock);
if (handle == si->getSensor().getHandle() &&
mUsedHandle.insert(handle).second) {
// will succeed as the mUsedHandle does not have this handle
- mHandleMap.emplace(handle, Entry(si, isForDebug, isVirtual, deviceId));
+ mHandleMap.emplace(handle, Entry(std::move(si), isForDebug, isVirtual, deviceId));
return true;
}
// handle exist already or handle mismatch
@@ -63,12 +63,12 @@
mNonSensor.getStringType());
}
-sp<SensorInterface> SensorList::getInterface(int handle) const {
- return getOne<sp<SensorInterface>>(
- handle, [] (const Entry& e) -> sp<SensorInterface> {return e.si;}, nullptr);
+std::shared_ptr<SensorInterface> SensorList::getInterface(int handle) const {
+ return getOne<std::shared_ptr<SensorInterface>>(
+ handle, [] (const Entry& e) -> std::shared_ptr<SensorInterface> {return e.si;},
+ nullptr);
}
-
bool SensorList::isNewHandle(int handle) const {
std::lock_guard<std::mutex> lk(mLock);
return mUsedHandle.find(handle) == mUsedHandle.end();
diff --git a/services/sensorservice/SensorList.h b/services/sensorservice/SensorList.h
index 79f6701..ad5b21f 100644
--- a/services/sensorservice/SensorList.h
+++ b/services/sensorservice/SensorList.h
@@ -37,22 +37,18 @@
class SensorList : public Dumpable {
public:
struct Entry {
- sp<SensorInterface> si;
+ std::shared_ptr<SensorInterface> si;
const bool isForDebug;
const bool isVirtual;
const int deviceId;
- Entry(SensorInterface* si_, bool debug_, bool virtual_, int deviceId_) :
- si(si_), isForDebug(debug_), isVirtual(virtual_), deviceId(deviceId_) {
+ Entry(std::shared_ptr<SensorInterface> si_, bool debug_, bool virtual_, int deviceId_) :
+ si(std::move(si_)), isForDebug(debug_), isVirtual(virtual_), deviceId(deviceId_) {
}
};
- // After SensorInterface * is added into SensorList, it can be assumed that SensorList own the
- // object it pointed to and the object should not be released elsewhere.
- bool add(int handle, SensorInterface* si, bool isForDebug = false, bool isVirtual = false,
- int deviceId = RuntimeSensor::DEFAULT_DEVICE_ID);
-
- // After a handle is removed, the object that SensorInterface * pointing to may get deleted if
- // no more sp<> of the same object exist.
+ // SensorList owns the SensorInterface pointer.
+ bool add(int handle, std::shared_ptr<SensorInterface> si, bool isForDebug = false,
+ bool isVirtual = false, int deviceId = RuntimeSensor::DEFAULT_DEVICE_ID);
bool remove(int handle);
inline bool hasAnySensor() const { return mHandleMap.size() > 0;}
@@ -67,7 +63,7 @@
String8 getName(int handle) const;
String8 getStringType(int handle) const;
- sp<SensorInterface> getInterface(int handle) const;
+ std::shared_ptr<SensorInterface> getInterface(int handle) const;
bool isNewHandle(int handle) const;
// Iterate through Sensor in sensor list and perform operation f on each Sensor object.
diff --git a/services/sensorservice/SensorService.cpp b/services/sensorservice/SensorService.cpp
index d341b71..6504b79 100644
--- a/services/sensorservice/SensorService.cpp
+++ b/services/sensorservice/SensorService.cpp
@@ -182,15 +182,14 @@
sensor_t runtimeSensor = sensor;
// force the handle to be consistent
runtimeSensor.handle = handle;
- SensorInterface *si = new RuntimeSensor(runtimeSensor, std::move(runtimeSensorCallback));
+ auto si = std::make_shared<RuntimeSensor>(runtimeSensor, std::move(runtimeSensorCallback));
Mutex::Autolock _l(mLock);
- const Sensor& s = registerSensor(si, /* isDebug= */ false, /* isVirtual= */ false, deviceId);
-
- if (s.getHandle() != handle) {
+ if (!registerSensor(std::move(si), /* isDebug= */ false, /* isVirtual= */ false, deviceId)) {
// The registration was unsuccessful.
- return s.getHandle();
+ return mSensors.getNonSensor().getHandle();
}
+
return handle;
}
@@ -318,11 +317,13 @@
}
if (useThisSensor) {
if (list[i].type == SENSOR_TYPE_PROXIMITY) {
- SensorInterface* s = new ProximitySensor(list[i], *this);
- registerSensor(s);
- mProxSensorHandles.push_back(s->getSensor().getHandle());
+ auto s = std::make_shared<ProximitySensor>(list[i], *this);
+ const int handle = s->getSensor().getHandle();
+ if (registerSensor(std::move(s))) {
+ mProxSensorHandles.push_back(handle);
+ }
} else {
- registerSensor(new HardwareSensor(list[i]));
+ registerSensor(std::make_shared<HardwareSensor>(list[i]));
}
}
}
@@ -337,56 +338,63 @@
// available in the HAL
bool needRotationVector =
(virtualSensorsNeeds & (1<<SENSOR_TYPE_ROTATION_VECTOR)) != 0;
-
- registerSensor(new RotationVectorSensor(), !needRotationVector, true);
- registerSensor(new OrientationSensor(), !needRotationVector, true);
+ registerVirtualSensor(std::make_shared<RotationVectorSensor>(),
+ /* isDebug= */ !needRotationVector);
+ registerVirtualSensor(std::make_shared<OrientationSensor>(),
+ /* isDebug= */ !needRotationVector);
// virtual debugging sensors are not for user
- registerSensor( new CorrectedGyroSensor(list, count), true, true);
- registerSensor( new GyroDriftSensor(), true, true);
+ registerVirtualSensor(std::make_shared<CorrectedGyroSensor>(list, count),
+ /* isDebug= */ true);
+ registerVirtualSensor(std::make_shared<GyroDriftSensor>(), /* isDebug= */ true);
}
if (hasAccel && (hasGyro || hasGyroUncalibrated)) {
bool needGravitySensor = (virtualSensorsNeeds & (1<<SENSOR_TYPE_GRAVITY)) != 0;
- registerSensor(new GravitySensor(list, count), !needGravitySensor, true);
+ registerVirtualSensor(std::make_shared<GravitySensor>(list, count),
+ /* isDebug= */ !needGravitySensor);
bool needLinearAcceleration =
(virtualSensorsNeeds & (1<<SENSOR_TYPE_LINEAR_ACCELERATION)) != 0;
- registerSensor(new LinearAccelerationSensor(list, count),
- !needLinearAcceleration, true);
+ registerVirtualSensor(std::make_shared<LinearAccelerationSensor>(list, count),
+ /* isDebug= */ !needLinearAcceleration);
bool needGameRotationVector =
(virtualSensorsNeeds & (1<<SENSOR_TYPE_GAME_ROTATION_VECTOR)) != 0;
- registerSensor(new GameRotationVectorSensor(), !needGameRotationVector, true);
+ registerVirtualSensor(std::make_shared<GameRotationVectorSensor>(),
+ /* isDebug= */ !needGameRotationVector);
}
if (hasAccel && hasMag) {
bool needGeoMagRotationVector =
(virtualSensorsNeeds & (1<<SENSOR_TYPE_GEOMAGNETIC_ROTATION_VECTOR)) != 0;
- registerSensor(new GeoMagRotationVectorSensor(), !needGeoMagRotationVector, true);
+ registerVirtualSensor(std::make_shared<GeoMagRotationVectorSensor>(),
+ /* isDebug= */ !needGeoMagRotationVector);
}
if (isAutomotive()) {
if (hasAccel) {
- registerSensor(new LimitedAxesImuSensor(list, count, SENSOR_TYPE_ACCELEROMETER),
- /*isDebug=*/false, /*isVirtual=*/true);
+ registerVirtualSensor(
+ std::make_shared<LimitedAxesImuSensor>(
+ list, count, SENSOR_TYPE_ACCELEROMETER));
}
if (hasGyro) {
- registerSensor(new LimitedAxesImuSensor(list, count, SENSOR_TYPE_GYROSCOPE),
- /*isDebug=*/false, /*isVirtual=*/true);
+ registerVirtualSensor(
+ std::make_shared<LimitedAxesImuSensor>(
+ list, count, SENSOR_TYPE_GYROSCOPE));
}
if (hasAccelUncalibrated) {
- registerSensor(new LimitedAxesImuSensor(list, count,
- SENSOR_TYPE_ACCELEROMETER_UNCALIBRATED),
- /*isDebug=*/false, /*isVirtual=*/true);
+ registerVirtualSensor(
+ std::make_shared<LimitedAxesImuSensor>(
+ list, count, SENSOR_TYPE_ACCELEROMETER_UNCALIBRATED));
}
if (hasGyroUncalibrated) {
- registerSensor(new LimitedAxesImuSensor(list, count,
- SENSOR_TYPE_GYROSCOPE_UNCALIBRATED),
- /*isDebug=*/false, /*isVirtual=*/true);
+ registerVirtualSensor(
+ std::make_shared<LimitedAxesImuSensor>(
+ list, count, SENSOR_TYPE_GYROSCOPE_UNCALIBRATED));
}
}
@@ -487,20 +495,21 @@
&& isUidActive(uid) && !isOperationRestrictedLocked(opPackageName);
}
-const Sensor& SensorService::registerSensor(SensorInterface* s, bool isDebug, bool isVirtual,
- int deviceId) {
- int handle = s->getSensor().getHandle();
- int type = s->getSensor().getType();
- if (mSensors.add(handle, s, isDebug, isVirtual, deviceId)) {
+bool SensorService::registerSensor(std::shared_ptr<SensorInterface> s, bool isDebug, bool isVirtual,
+ int deviceId) {
+ const int handle = s->getSensor().getHandle();
+ const int type = s->getSensor().getType();
+ if (mSensors.add(handle, std::move(s), isDebug, isVirtual, deviceId)) {
mRecentEvent.emplace(handle, new SensorServiceUtil::RecentEventLogger(type));
- return s->getSensor();
+ return true;
} else {
- return mSensors.getNonSensor();
+ LOG_FATAL("Failed to register sensor with handle %d", handle);
+ return false;
}
}
-const Sensor& SensorService::registerDynamicSensorLocked(SensorInterface* s, bool isDebug) {
- return registerSensor(s, isDebug);
+bool SensorService::registerDynamicSensorLocked(std::shared_ptr<SensorInterface> s, bool isDebug) {
+ return registerSensor(std::move(s), isDebug);
}
bool SensorService::unregisterDynamicSensorLocked(int handle) {
@@ -514,8 +523,8 @@
return ret;
}
-const Sensor& SensorService::registerVirtualSensor(SensorInterface* s, bool isDebug) {
- return registerSensor(s, isDebug, true);
+bool SensorService::registerVirtualSensor(std::shared_ptr<SensorInterface> s, bool isDebug) {
+ return registerSensor(std::move(s), isDebug, true);
}
SensorService::~SensorService() {
@@ -610,8 +619,8 @@
result.append("Recent Sensor events:\n");
for (auto&& i : mRecentEvent) {
- sp<SensorInterface> s = mSensors.getInterface(i.first);
- if (!i.second->isEmpty()) {
+ std::shared_ptr<SensorInterface> s = getSensorInterfaceFromHandle(i.first);
+ if (!i.second->isEmpty() && s != nullptr) {
if (privileged || s->getSensor().getRequiredPermission().isEmpty()) {
i.second->setFormat("normal");
} else {
@@ -728,8 +737,8 @@
// Write SensorEventsProto
token = proto.start(SENSOR_EVENTS);
for (auto&& i : mRecentEvent) {
- sp<SensorInterface> s = mSensors.getInterface(i.first);
- if (!i.second->isEmpty()) {
+ std::shared_ptr<SensorInterface> s = getSensorInterfaceFromHandle(i.first);
+ if (!i.second->isEmpty() && s != nullptr) {
i.second->setFormat(privileged || s->getSensor().getRequiredPermission().isEmpty() ?
"normal" : "mask_data");
const uint64_t mToken = proto.start(service::SensorEventsProto::RECENT_EVENTS_LOGS);
@@ -1019,7 +1028,7 @@
handle = buffer[i].meta_data.sensor;
}
if (connection->hasSensor(handle)) {
- sp<SensorInterface> si = getSensorInterfaceFromHandle(handle);
+ std::shared_ptr<SensorInterface> si = getSensorInterfaceFromHandle(handle);
// If this buffer has an event from a one_shot sensor and this connection is registered
// for this particular one_shot sensor, try cleaning up the connection.
if (si != nullptr &&
@@ -1104,7 +1113,7 @@
break;
}
sensors_event_t out;
- sp<SensorInterface> si = mSensors.getInterface(handle);
+ std::shared_ptr<SensorInterface> si = getSensorInterfaceFromHandle(handle);
if (si == nullptr) {
ALOGE("handle %d is not an valid virtual sensor", handle);
continue;
@@ -1199,12 +1208,12 @@
// force the handle to be consistent
s.handle = handle;
- SensorInterface *si = new HardwareSensor(s, uuid);
+ auto si = std::make_shared<HardwareSensor>(s, uuid);
// This will release hold on dynamic sensor meta, so it should be called
// after Sensor object is created.
device.handleDynamicSensorConnection(handle, true /*connected*/);
- registerDynamicSensorLocked(si);
+ registerDynamicSensorLocked(std::move(si));
} else {
ALOGE("Handle %d has been used, cannot use again before reboot.", handle);
}
@@ -1331,7 +1340,7 @@
}
bool SensorService::isVirtualSensor(int handle) const {
- sp<SensorInterface> sensor = getSensorInterfaceFromHandle(handle);
+ std::shared_ptr<SensorInterface> sensor = getSensorInterfaceFromHandle(handle);
return sensor != nullptr && sensor->isVirtual();
}
@@ -1340,7 +1349,7 @@
if (event.type == SENSOR_TYPE_META_DATA) {
handle = event.meta_data.sensor;
}
- sp<SensorInterface> sensor = getSensorInterfaceFromHandle(handle);
+ std::shared_ptr<SensorInterface> sensor = getSensorInterfaceFromHandle(handle);
return sensor != nullptr && sensor->getSensor().isWakeUpSensor();
}
@@ -1740,7 +1749,7 @@
int handle = mActiveSensors.keyAt(i);
if (c->hasSensor(handle)) {
ALOGD_IF(DEBUG_CONNECTIONS, "%zu: disabling handle=0x%08x", i, handle);
- sp<SensorInterface> sensor = getSensorInterfaceFromHandle(handle);
+ std::shared_ptr<SensorInterface> sensor = getSensorInterfaceFromHandle(handle);
if (sensor != nullptr) {
sensor->activate(c, false);
} else {
@@ -1854,7 +1863,7 @@
return NAME_NOT_FOUND;
}
-sp<SensorInterface> SensorService::getSensorInterfaceFromHandle(int handle) const {
+std::shared_ptr<SensorInterface> SensorService::getSensorInterfaceFromHandle(int handle) const {
return mSensors.getInterface(handle);
}
@@ -1864,7 +1873,7 @@
if (mInitCheck != NO_ERROR)
return mInitCheck;
- sp<SensorInterface> sensor = getSensorInterfaceFromHandle(handle);
+ std::shared_ptr<SensorInterface> sensor = getSensorInterfaceFromHandle(handle);
if (sensor == nullptr ||
!canAccessSensor(sensor->getSensor(), "Tried enabling", opPackageName)) {
return BAD_VALUE;
@@ -2010,7 +2019,7 @@
Mutex::Autolock _l(mLock);
status_t err = cleanupWithoutDisableLocked(connection, handle);
if (err == NO_ERROR) {
- sp<SensorInterface> sensor = getSensorInterfaceFromHandle(handle);
+ std::shared_ptr<SensorInterface> sensor = getSensorInterfaceFromHandle(handle);
err = sensor != nullptr ? sensor->activate(connection.get(), false) : status_t(BAD_VALUE);
}
@@ -2056,7 +2065,7 @@
if (mInitCheck != NO_ERROR)
return mInitCheck;
- sp<SensorInterface> sensor = getSensorInterfaceFromHandle(handle);
+ std::shared_ptr<SensorInterface> sensor = getSensorInterfaceFromHandle(handle);
if (sensor == nullptr ||
!canAccessSensor(sensor->getSensor(), "Tried configuring", opPackageName)) {
return BAD_VALUE;
@@ -2082,7 +2091,7 @@
Mutex::Autolock _l(mLock);
// Loop through all sensors for this connection and call flush on each of them.
for (int handle : connection->getActiveSensorHandles()) {
- sp<SensorInterface> sensor = getSensorInterfaceFromHandle(handle);
+ std::shared_ptr<SensorInterface> sensor = getSensorInterfaceFromHandle(handle);
if (sensor == nullptr) {
continue;
}
diff --git a/services/sensorservice/SensorService.h b/services/sensorservice/SensorService.h
index e490398..78df501 100644
--- a/services/sensorservice/SensorService.h
+++ b/services/sensorservice/SensorService.h
@@ -375,15 +375,14 @@
String8 getSensorName(int handle) const;
String8 getSensorStringType(int handle) const;
bool isVirtualSensor(int handle) const;
- sp<SensorInterface> getSensorInterfaceFromHandle(int handle) const;
+ std::shared_ptr<SensorInterface> getSensorInterfaceFromHandle(int handle) const;
bool isWakeUpSensor(int type) const;
void recordLastValueLocked(sensors_event_t const* buffer, size_t count);
static void sortEventBuffer(sensors_event_t* buffer, size_t count);
- const Sensor& registerSensor(SensorInterface* sensor, bool isDebug = false,
- bool isVirtual = false,
- int deviceId = RuntimeSensor::DEFAULT_DEVICE_ID);
- const Sensor& registerVirtualSensor(SensorInterface* sensor, bool isDebug = false);
- const Sensor& registerDynamicSensorLocked(SensorInterface* sensor, bool isDebug = false);
+ bool registerSensor(std::shared_ptr<SensorInterface> sensor, bool isDebug = false,
+ bool isVirtual = false, int deviceId = RuntimeSensor::DEFAULT_DEVICE_ID);
+ bool registerVirtualSensor(std::shared_ptr<SensorInterface> sensor, bool isDebug = false);
+ bool registerDynamicSensorLocked(std::shared_ptr<SensorInterface> sensor, bool isDebug = false);
bool unregisterDynamicSensorLocked(int handle);
status_t cleanupWithoutDisable(const sp<SensorEventConnection>& connection, int handle);
status_t cleanupWithoutDisableLocked(const sp<SensorEventConnection>& connection, int handle);
diff --git a/services/sensorservice/aidl/SensorManager.cpp b/services/sensorservice/aidl/SensorManager.cpp
index 6d8d574..9b03344 100644
--- a/services/sensorservice/aidl/SensorManager.cpp
+++ b/services/sensorservice/aidl/SensorManager.cpp
@@ -201,7 +201,7 @@
// if thread not initialized, start thread
mStopThread = false;
std::thread pollThread{[&stopThread = mStopThread, looper = mLooper, javaVm = mJavaVm] {
- struct sched_param p = {0};
+ struct sched_param p = {};
p.sched_priority = 10;
if (sched_setscheduler(0 /* current thread*/, SCHED_FIFO, &p) != 0) {
LOG(ERROR) << "Could not use SCHED_FIFO for looper thread: " << strerror(errno);
diff --git a/services/sensorservice/aidl/utils.cpp b/services/sensorservice/aidl/utils.cpp
index 26bcdc5..beb38b9 100644
--- a/services/sensorservice/aidl/utils.cpp
+++ b/services/sensorservice/aidl/utils.cpp
@@ -58,7 +58,7 @@
::aidl::android::hardware::sensors::Event convertEvent(const ::ASensorEvent& src) {
::aidl::android::hardware::sensors::Event dst;
::android::hardware::sensors::implementation::
- convertFromSensorEvent(reinterpret_cast<const sensors_event_t&>(src), &dst);
+ convertFromASensorEvent(src, &dst);
return dst;
}
diff --git a/services/sensorservice/hidl/utils.cpp b/services/sensorservice/hidl/utils.cpp
index 2f9e922..5fa594d 100644
--- a/services/sensorservice/hidl/utils.cpp
+++ b/services/sensorservice/hidl/utils.cpp
@@ -76,8 +76,8 @@
::android::hardware::sensors::V1_0::Event convertEvent(const ::ASensorEvent& src) {
::android::hardware::sensors::V1_0::Event dst;
- ::android::hardware::sensors::V1_0::implementation::convertFromSensorEvent(
- reinterpret_cast<const sensors_event_t&>(src), &dst);
+ ::android::hardware::sensors::V1_0::implementation::convertFromASensorEvent(
+ src, &dst);
return dst;
}
diff --git a/services/stats/StatsAidl.cpp b/services/stats/StatsAidl.cpp
index 9e13849..410a5af 100644
--- a/services/stats/StatsAidl.cpp
+++ b/services/stats/StatsAidl.cpp
@@ -30,14 +30,14 @@
StatsHal::StatsHal() {}
ndk::ScopedAStatus StatsHal::reportVendorAtom(const VendorAtom& vendorAtom) {
- std::string reverseDomainName = (std::string) vendorAtom.reverseDomainName;
if (vendorAtom.atomId < 100000 || vendorAtom.atomId >= 200000) {
ALOGE("Atom ID %ld is not a valid vendor atom ID", (long) vendorAtom.atomId);
return ndk::ScopedAStatus::fromServiceSpecificErrorWithMessage(
-1, "Not a valid vendor atom ID");
}
- if (reverseDomainName.length() > 50) {
- ALOGE("Vendor atom reverse domain name %s is too long.", reverseDomainName.c_str());
+ if (vendorAtom.reverseDomainName.length() > 50) {
+ ALOGE("Vendor atom reverse domain name %s is too long.",
+ vendorAtom.reverseDomainName.c_str());
return ndk::ScopedAStatus::fromServiceSpecificErrorWithMessage(
-1, "Vendor atom reverse domain name is too long");
}
diff --git a/services/surfaceflinger/Android.bp b/services/surfaceflinger/Android.bp
index b1bd705..0a192c5 100644
--- a/services/surfaceflinger/Android.bp
+++ b/services/surfaceflinger/Android.bp
@@ -157,6 +157,7 @@
"EventLog/EventLog.cpp",
"FrontEnd/LayerCreationArgs.cpp",
"FrontEnd/LayerHandle.cpp",
+ "FrontEnd/LayerHierarchy.cpp",
"FrontEnd/LayerLifecycleManager.cpp",
"FrontEnd/RequestedLayerState.cpp",
"FrontEnd/TransactionHandler.cpp",
@@ -165,7 +166,6 @@
"FrameTracer/FrameTracer.cpp",
"FrameTracker.cpp",
"HdrLayerInfoReporter.cpp",
- "HwcSlotGenerator.cpp",
"WindowInfosListenerInvoker.cpp",
"Layer.cpp",
"LayerFE.cpp",
diff --git a/services/surfaceflinger/ClientCache.cpp b/services/surfaceflinger/ClientCache.cpp
index 2bd8f32..09e41ff 100644
--- a/services/surfaceflinger/ClientCache.cpp
+++ b/services/surfaceflinger/ClientCache.cpp
@@ -118,7 +118,8 @@
Usage::READABLE));
}
-void ClientCache::erase(const client_cache_t& cacheId) {
+sp<GraphicBuffer> ClientCache::erase(const client_cache_t& cacheId) {
+ sp<GraphicBuffer> buffer;
auto& [processToken, id] = cacheId;
std::vector<sp<ErasedRecipient>> pendingErase;
{
@@ -126,9 +127,11 @@
ClientCacheBuffer* buf = nullptr;
if (!getBuffer(cacheId, &buf)) {
ALOGE("failed to erase buffer, could not retrieve buffer");
- return;
+ return nullptr;
}
+ buffer = buf->buffer->getBuffer();
+
for (auto& recipient : buf->recipients) {
sp<ErasedRecipient> erasedRecipient = recipient.promote();
if (erasedRecipient) {
@@ -142,6 +145,7 @@
for (auto& recipient : pendingErase) {
recipient->bufferErased(cacheId);
}
+ return buffer;
}
std::shared_ptr<renderengine::ExternalTexture> ClientCache::get(const client_cache_t& cacheId) {
diff --git a/services/surfaceflinger/ClientCache.h b/services/surfaceflinger/ClientCache.h
index cdeac2b..b56b252 100644
--- a/services/surfaceflinger/ClientCache.h
+++ b/services/surfaceflinger/ClientCache.h
@@ -33,6 +33,17 @@
namespace android {
+// This class manages a cache of buffer handles between SurfaceFlinger clients
+// and the SurfaceFlinger process which optimizes away some of the cost of
+// sending buffer handles across processes.
+//
+// Buffers are explicitly cached and uncached by the SurfaceFlinger client. When
+// a buffer is uncached, it is not only purged from this cache, but the buffer
+// ID is also passed down to CompositionEngine to purge it from a similar cache
+// used between SurfaceFlinger and Composer HAL. The buffer ID used to purge
+// both the SurfaceFlinger side of this other cache, as well as Composer HAL's
+// side of the cache.
+//
class ClientCache : public Singleton<ClientCache> {
public:
ClientCache();
@@ -41,7 +52,8 @@
base::expected<std::shared_ptr<renderengine::ExternalTexture>, AddError> add(
const client_cache_t& cacheId, const sp<GraphicBuffer>& buffer);
- void erase(const client_cache_t& cacheId);
+
+ sp<GraphicBuffer> erase(const client_cache_t& cacheId);
std::shared_ptr<renderengine::ExternalTexture> get(const client_cache_t& cacheId);
diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/CompositionRefreshArgs.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/CompositionRefreshArgs.h
index f861fc9..415a041 100644
--- a/services/surfaceflinger/CompositionEngine/include/compositionengine/CompositionRefreshArgs.h
+++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/CompositionRefreshArgs.h
@@ -52,6 +52,9 @@
// All the layers that have queued updates.
Layers layersWithQueuedFrames;
+ // All graphic buffers that will no longer be used and should be removed from caches.
+ std::vector<uint64_t> bufferIdsToUncache;
+
// Controls how the color mode is chosen for an output
OutputColorSetting outputColorSetting{OutputColorSetting::kEnhanced};
diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/LayerFECompositionState.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/LayerFECompositionState.h
index 974f7c6..ad98e93 100644
--- a/services/surfaceflinger/CompositionEngine/include/compositionengine/LayerFECompositionState.h
+++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/LayerFECompositionState.h
@@ -163,7 +163,6 @@
// The buffer and related state
sp<GraphicBuffer> buffer;
- int bufferSlot{BufferQueue::INVALID_BUFFER_SLOT};
sp<Fence> acquireFence = Fence::NO_FENCE;
Region surfaceDamage;
uint64_t frameNumber = 0;
diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/Output.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/Output.h
index 874b330..bd43c89 100644
--- a/services/surfaceflinger/CompositionEngine/include/compositionengine/Output.h
+++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/Output.h
@@ -23,6 +23,7 @@
#include <type_traits>
#include <unordered_map>
#include <utility>
+#include <vector>
#include <compositionengine/LayerFE.h>
#include <renderengine/LayerSettings.h>
@@ -272,6 +273,7 @@
virtual void setDisplayColorProfile(std::unique_ptr<DisplayColorProfile>) = 0;
virtual void setRenderSurface(std::unique_ptr<RenderSurface>) = 0;
+ virtual void uncacheBuffers(const std::vector<uint64_t>&) = 0;
virtual void rebuildLayerStacks(const CompositionRefreshArgs&, LayerFESet&) = 0;
virtual void collectVisibleLayers(const CompositionRefreshArgs&, CoverageState&) = 0;
virtual void ensureOutputLayerIfVisible(sp<LayerFE>&, CoverageState&) = 0;
diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/OutputLayer.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/OutputLayer.h
index 6d0c395..4dbf8d2 100644
--- a/services/surfaceflinger/CompositionEngine/include/compositionengine/OutputLayer.h
+++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/OutputLayer.h
@@ -19,6 +19,7 @@
#include <cstdint>
#include <optional>
#include <string>
+#include <vector>
#include <ui/Transform.h>
#include <utils/StrongPointer.h>
@@ -81,6 +82,10 @@
// TODO(lpique): Make this protected once it is only internally called.
virtual CompositionState& editState() = 0;
+ // Clear the cache entries for a set of buffers that SurfaceFlinger no
+ // longer cares about.
+ virtual void uncacheBuffers(const std::vector<uint64_t>& bufferIdsToUncache) = 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/HwcBufferCache.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/HwcBufferCache.h
index fd22aa3..6e9ea6f 100644
--- a/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/HwcBufferCache.h
+++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/HwcBufferCache.h
@@ -17,7 +17,8 @@
#pragma once
#include <cstdint>
-#include <vector>
+#include <stack>
+#include <unordered_map>
// TODO(b/129481165): remove the #pragma below and fix conversion issues
#pragma clang diagnostic push
@@ -37,35 +38,76 @@
namespace compositionengine::impl {
-// With HIDLized hwcomposer HAL, the HAL can maintain a buffer cache for each
-// HWC display and layer. When updating a display target or a layer buffer,
-// we have the option to send the buffer handle over or to request the HAL to
-// retrieve it from its cache. The latter is cheaper since it eliminates the
-// overhead to transfer the handle over the trasport layer, and the overhead
-// for the HAL to clone and retain the handle.
-//
-// To be able to find out whether a buffer is already in the HAL's cache, we
-// use HWComposerBufferCache to mirror the cache in SF.
-class HwcBufferCache {
-public:
- HwcBufferCache();
- // Given a buffer, return the HWC cache slot and
- // buffer to be sent to HWC.
- //
- // outBuffer is set to buffer when buffer is not in the HWC cache;
- // otherwise, outBuffer is set to nullptr.
- void getHwcBuffer(int slot, const sp<GraphicBuffer>& buffer, uint32_t* outSlot,
- sp<GraphicBuffer>* outBuffer);
+// The buffer cache returns both a slot and the buffer that should be sent to HWC. In cases
+// where the buffer is already cached, the buffer is a nullptr and will not be sent to HWC as
+// an optimization.
+struct HwcSlotAndBuffer {
+ uint32_t slot;
+ sp<GraphicBuffer> buffer;
+};
- // Special caching slot for the layer caching feature.
- static const constexpr size_t FLATTENER_CACHING_SLOT = BufferQueue::NUM_BUFFER_SLOTS;
+//
+// Manages the slot assignments for a buffers stored in Composer HAL's cache.
+//
+// Cache slots are an optimization when communicating buffer handles to Composer
+// HAL. When updating a layer's buffer, we can either send a new buffer handle
+// along with it's slot assignment or request the HAL to reuse a buffer handle
+// that we've already sent by using the slot assignment. The latter is cheaper
+// since it eliminates the overhead to transfer the buffer handle over IPC and
+// the overhead for the HAL to clone the handle.
+//
+class HwcBufferCache {
+private:
+ static const constexpr size_t kMaxLayerBufferCount = BufferQueue::NUM_BUFFER_SLOTS;
+
+public:
+ // public for testing
+ // Override buffers don't use the normal cache slots because we don't want them to evict client
+ // buffers from the cache. We add an extra slot at the end for the override buffers.
+ static const constexpr size_t kOverrideBufferSlot = kMaxLayerBufferCount;
+
+ HwcBufferCache();
+
+ //
+ // Given a buffer, return the HWC cache slot and buffer to send to HWC.
+ //
+ // If the buffer is already in the cache, the buffer is null to optimize away sending HWC the
+ // buffer handle.
+ //
+ HwcSlotAndBuffer getHwcSlotAndBuffer(const sp<GraphicBuffer>& buffer);
+ //
+ // Given a buffer, return the HWC cache slot and buffer to send to HWC.
+ //
+ // A special slot number is used for override buffers.
+ //
+ // If the buffer is already in the cache, the buffer is null to optimize away sending HWC the
+ // buffer handle.
+ //
+ HwcSlotAndBuffer getHwcSlotAndBufferForOverride(const sp<GraphicBuffer>& buffer);
+
+ //
+ // When a client process discards a buffer, it needs to be purged from the HWC cache.
+ //
+ // Returns the slot number of the buffer, or UINT32_MAX if it wasn't found in the cache.
+ //
+ uint32_t uncache(uint64_t graphicBufferId);
private:
- // an array where the index corresponds to a slot and the value corresponds to a (counter,
- // buffer) pair. "counter" is a unique value that indicates the last time this slot was updated
- // or used and allows us to keep track of the least-recently used buffer.
- static const constexpr size_t kMaxLayerBufferCount = BufferQueue::NUM_BUFFER_SLOTS + 1;
- wp<GraphicBuffer> mBuffers[kMaxLayerBufferCount];
+ uint32_t cache(const sp<GraphicBuffer>& buffer);
+ uint32_t getLeastRecentlyUsedSlot();
+
+ struct Cache {
+ sp<GraphicBuffer> buffer;
+ uint32_t slot;
+ // Cache entries are evicted according to least-recently-used when more than
+ // kMaxLayerBufferCount unique buffers have been sent to a layer.
+ uint64_t lruCounter;
+ };
+
+ std::unordered_map<uint64_t, Cache> mCacheByBufferId;
+ sp<GraphicBuffer> mLastOverrideBuffer;
+ std::stack<uint32_t> mFreeSlots;
+ uint64_t mLeastRecentlyUsedCounter;
};
} // namespace compositionengine::impl
diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/Output.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/Output.h
index 9ca5da9..1393e29 100644
--- a/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/Output.h
+++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/Output.h
@@ -82,6 +82,7 @@
void prepare(const CompositionRefreshArgs&, LayerFESet&) override;
void present(const CompositionRefreshArgs&) override;
+ void uncacheBuffers(const std::vector<uint64_t>& bufferIdsToUncache) override;
void rebuildLayerStacks(const CompositionRefreshArgs&, LayerFESet&) override;
void collectVisibleLayers(const CompositionRefreshArgs&,
compositionengine::Output::CoverageState&) override;
diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/OutputLayer.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/OutputLayer.h
index 6d4abf9..f383392 100644
--- a/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/OutputLayer.h
+++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/OutputLayer.h
@@ -20,6 +20,7 @@
#include <memory>
#include <optional>
#include <string>
+#include <vector>
#include <compositionengine/LayerFE.h>
#include <compositionengine/OutputLayer.h>
@@ -44,6 +45,8 @@
void setHwcLayer(std::shared_ptr<HWC2::Layer>) override;
+ void uncacheBuffers(const std::vector<uint64_t>& bufferIdsToUncache) override;
+
void updateCompositionState(bool includeGeometry, bool forceClientComposition,
ui::Transform::RotationFlags) override;
void writeStateToHWC(bool includeGeometry, bool skipLayer, uint32_t z, bool zIsOverridden,
diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/mock/Output.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/mock/Output.h
index 7592cac..18e6879 100644
--- a/services/surfaceflinger/CompositionEngine/include/compositionengine/mock/Output.h
+++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/mock/Output.h
@@ -82,6 +82,7 @@
MOCK_METHOD2(prepare, void(const compositionengine::CompositionRefreshArgs&, LayerFESet&));
MOCK_METHOD1(present, void(const compositionengine::CompositionRefreshArgs&));
+ MOCK_METHOD1(uncacheBuffers, void(const std::vector<uint64_t>&));
MOCK_METHOD2(rebuildLayerStacks,
void(const compositionengine::CompositionRefreshArgs&, LayerFESet&));
MOCK_METHOD2(collectVisibleLayers,
diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/mock/OutputLayer.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/mock/OutputLayer.h
index c22f1bf..5fef63a 100644
--- a/services/surfaceflinger/CompositionEngine/include/compositionengine/mock/OutputLayer.h
+++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/mock/OutputLayer.h
@@ -35,6 +35,8 @@
MOCK_METHOD1(setHwcLayer, void(std::shared_ptr<HWC2::Layer>));
+ MOCK_METHOD1(uncacheBuffers, void(const std::vector<uint64_t>&));
+
MOCK_CONST_METHOD0(getOutput, const compositionengine::Output&());
MOCK_CONST_METHOD0(getLayerFE, compositionengine::LayerFE&());
diff --git a/services/surfaceflinger/CompositionEngine/src/HwcBufferCache.cpp b/services/surfaceflinger/CompositionEngine/src/HwcBufferCache.cpp
index f95382d..d64fd57 100644
--- a/services/surfaceflinger/CompositionEngine/src/HwcBufferCache.cpp
+++ b/services/surfaceflinger/CompositionEngine/src/HwcBufferCache.cpp
@@ -16,43 +16,80 @@
#include <compositionengine/impl/HwcBufferCache.h>
-// TODO(b/129481165): remove the #pragma below and fix conversion issues
-#pragma clang diagnostic push
-#pragma clang diagnostic ignored "-Wconversion"
-
#include <gui/BufferQueue.h>
#include <ui/GraphicBuffer.h>
-// TODO(b/129481165): remove the #pragma below and fix conversion issues
-#pragma clang diagnostic pop // ignored "-Wconversion"
-
namespace android::compositionengine::impl {
HwcBufferCache::HwcBufferCache() {
- std::fill(std::begin(mBuffers), std::end(mBuffers), wp<GraphicBuffer>(nullptr));
+ for (uint32_t i = 0; i < kMaxLayerBufferCount; i++) {
+ mFreeSlots.push(i);
+ }
}
-void HwcBufferCache::getHwcBuffer(int slot, const sp<GraphicBuffer>& buffer, uint32_t* outSlot,
- sp<GraphicBuffer>* outBuffer) {
- // default is 0
- if (slot == BufferQueue::INVALID_BUFFER_SLOT || slot < 0 ||
- slot >= static_cast<int32_t>(kMaxLayerBufferCount)) {
- *outSlot = 0;
- } else {
- *outSlot = static_cast<uint32_t>(slot);
+HwcSlotAndBuffer HwcBufferCache::getHwcSlotAndBuffer(const sp<GraphicBuffer>& buffer) {
+ // TODO(b/261930578): This is for unit tests which don't mock GraphicBuffers but instead send
+ // in nullptrs.
+ if (buffer == nullptr) {
+ return {0, nullptr};
}
-
- auto& currentBuffer = mBuffers[*outSlot];
- wp<GraphicBuffer> weakCopy(buffer);
- if (currentBuffer == weakCopy) {
- // already cached in HWC, skip sending the buffer
- *outBuffer = nullptr;
- } else {
- *outBuffer = buffer;
-
- // update cache
- currentBuffer = buffer;
+ if (auto i = mCacheByBufferId.find(buffer->getId()); i != mCacheByBufferId.end()) {
+ Cache& cache = i->second;
+ // mark this cache slot as more recently used so it won't get evicted anytime soon
+ cache.lruCounter = mLeastRecentlyUsedCounter++;
+ return {cache.slot, nullptr};
}
+ return {cache(buffer), buffer};
+}
+
+HwcSlotAndBuffer HwcBufferCache::getHwcSlotAndBufferForOverride(const sp<GraphicBuffer>& buffer) {
+ if (buffer == mLastOverrideBuffer) {
+ return {kOverrideBufferSlot, nullptr};
+ }
+ mLastOverrideBuffer = buffer;
+ return {kOverrideBufferSlot, buffer};
+}
+
+uint32_t HwcBufferCache::uncache(uint64_t bufferId) {
+ if (auto i = mCacheByBufferId.find(bufferId); i != mCacheByBufferId.end()) {
+ uint32_t slot = i->second.slot;
+ mCacheByBufferId.erase(i);
+ mFreeSlots.push(slot);
+ return slot;
+ }
+ if (mLastOverrideBuffer && bufferId == mLastOverrideBuffer->getId()) {
+ mLastOverrideBuffer = nullptr;
+ return kOverrideBufferSlot;
+ }
+ return UINT32_MAX;
+}
+
+uint32_t HwcBufferCache::cache(const sp<GraphicBuffer>& buffer) {
+ Cache cache;
+ cache.slot = getLeastRecentlyUsedSlot();
+ cache.lruCounter = mLeastRecentlyUsedCounter++;
+ cache.buffer = buffer;
+ mCacheByBufferId.emplace(buffer->getId(), cache);
+ return cache.slot;
+}
+
+uint32_t HwcBufferCache::getLeastRecentlyUsedSlot() {
+ if (mFreeSlots.empty()) {
+ assert(!mCacheByBufferId.empty());
+ // evict the least recently used cache entry
+ auto cacheToErase = mCacheByBufferId.begin();
+ for (auto i = cacheToErase; i != mCacheByBufferId.end(); ++i) {
+ if (i->second.lruCounter < cacheToErase->second.lruCounter) {
+ cacheToErase = i;
+ }
+ }
+ uint32_t slot = cacheToErase->second.slot;
+ mCacheByBufferId.erase(cacheToErase);
+ mFreeSlots.push(slot);
+ }
+ uint32_t slot = mFreeSlots.top();
+ mFreeSlots.pop();
+ return slot;
}
} // namespace android::compositionengine::impl
diff --git a/services/surfaceflinger/CompositionEngine/src/LayerFECompositionState.cpp b/services/surfaceflinger/CompositionEngine/src/LayerFECompositionState.cpp
index 6631a27..a405c4d 100644
--- a/services/surfaceflinger/CompositionEngine/src/LayerFECompositionState.cpp
+++ b/services/surfaceflinger/CompositionEngine/src/LayerFECompositionState.cpp
@@ -106,7 +106,6 @@
dumpVal(out, "composition type", toString(compositionType), compositionType);
out.append("\n buffer: ");
- dumpVal(out, "slot", bufferSlot);
dumpVal(out, "buffer", buffer.get());
out.append("\n ");
diff --git a/services/surfaceflinger/CompositionEngine/src/Output.cpp b/services/surfaceflinger/CompositionEngine/src/Output.cpp
index 3ee8017..16ef812 100644
--- a/services/surfaceflinger/CompositionEngine/src/Output.cpp
+++ b/services/surfaceflinger/CompositionEngine/src/Output.cpp
@@ -428,6 +428,7 @@
ALOGV(__FUNCTION__);
rebuildLayerStacks(refreshArgs, geomSnapshots);
+ uncacheBuffers(refreshArgs.bufferIdsToUncache);
}
void Output::present(const compositionengine::CompositionRefreshArgs& refreshArgs) {
@@ -455,6 +456,15 @@
renderCachedSets(refreshArgs);
}
+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) {
ATRACE_CALL();
diff --git a/services/surfaceflinger/CompositionEngine/src/OutputLayer.cpp b/services/surfaceflinger/CompositionEngine/src/OutputLayer.cpp
index a39c527..a7c24b6 100644
--- a/services/surfaceflinger/CompositionEngine/src/OutputLayer.cpp
+++ b/services/surfaceflinger/CompositionEngine/src/OutputLayer.cpp
@@ -610,6 +610,19 @@
}
}
+void OutputLayer::uncacheBuffers(std::vector<uint64_t> const& bufferIdsToUncache) {
+ auto& state = editState();
+ // Skip doing this if there is no HWC interface
+ if (!state.hwc) {
+ return;
+ }
+
+ for (auto bufferId : bufferIdsToUncache) {
+ state.hwc->hwcBufferCache.uncache(bufferId);
+ // TODO(b/258196272): send uncache requests to Composer HAL
+ }
+}
+
void OutputLayer::writeBufferStateToHWC(HWC2::Layer* hwcLayer,
const LayerFECompositionState& outputIndependentState,
bool skipLayer) {
@@ -622,27 +635,24 @@
to_string(error).c_str(), static_cast<int32_t>(error));
}
- sp<GraphicBuffer> buffer = outputIndependentState.buffer;
- sp<Fence> acquireFence = outputIndependentState.acquireFence;
- int slot = outputIndependentState.bufferSlot;
+ HwcSlotAndBuffer hwcSlotAndBuffer;
+ sp<Fence> hwcFence;
+ // Override buffers use a special cache slot so that they don't evict client buffers.
if (getState().overrideInfo.buffer != nullptr && !skipLayer) {
- buffer = getState().overrideInfo.buffer->getBuffer();
- acquireFence = getState().overrideInfo.acquireFence;
- slot = HwcBufferCache::FLATTENER_CACHING_SLOT;
+ hwcSlotAndBuffer = editState().hwc->hwcBufferCache.getHwcSlotAndBufferForOverride(
+ getState().overrideInfo.buffer->getBuffer());
+ hwcFence = getState().overrideInfo.acquireFence;
+ } else {
+ hwcSlotAndBuffer =
+ editState().hwc->hwcBufferCache.getHwcSlotAndBuffer(outputIndependentState.buffer);
+ hwcFence = outputIndependentState.acquireFence;
}
- ALOGV("Writing buffer %p", buffer.get());
-
- uint32_t hwcSlot = 0;
- sp<GraphicBuffer> hwcBuffer;
- // We need access to the output-dependent state for the buffer cache there,
- // though otherwise the buffer is not output-dependent.
- editState().hwc->hwcBufferCache.getHwcBuffer(slot, buffer, &hwcSlot, &hwcBuffer);
-
- if (auto error = hwcLayer->setBuffer(hwcSlot, hwcBuffer, acquireFence);
+ if (auto error = hwcLayer->setBuffer(hwcSlotAndBuffer.slot, hwcSlotAndBuffer.buffer, hwcFence);
error != hal::Error::NONE) {
- ALOGE("[%s] Failed to set buffer %p: %s (%d)", getLayerFE().getDebugName(), buffer->handle,
- to_string(error).c_str(), static_cast<int32_t>(error));
+ ALOGE("[%s] Failed to set buffer %p: %s (%d)", getLayerFE().getDebugName(),
+ hwcSlotAndBuffer.buffer->handle, to_string(error).c_str(),
+ static_cast<int32_t>(error));
}
}
diff --git a/services/surfaceflinger/CompositionEngine/tests/HwcBufferCacheTest.cpp b/services/surfaceflinger/CompositionEngine/tests/HwcBufferCacheTest.cpp
index 7197780..cf72e8f 100644
--- a/services/surfaceflinger/CompositionEngine/tests/HwcBufferCacheTest.cpp
+++ b/services/surfaceflinger/CompositionEngine/tests/HwcBufferCacheTest.cpp
@@ -22,66 +22,172 @@
namespace android::compositionengine {
namespace {
-class TestableHwcBufferCache : public impl::HwcBufferCache {
-public:
- void getHwcBuffer(int slot, const sp<GraphicBuffer>& buffer, uint32_t* outSlot,
- sp<GraphicBuffer>* outBuffer) {
- HwcBufferCache::getHwcBuffer(slot, buffer, outSlot, outBuffer);
- }
-};
+using impl::HwcBufferCache;
+using impl::HwcSlotAndBuffer;
class HwcBufferCacheTest : public testing::Test {
public:
~HwcBufferCacheTest() override = default;
- void testSlot(const int inSlot, const uint32_t expectedSlot) {
- uint32_t outSlot;
- sp<GraphicBuffer> outBuffer;
-
- // The first time, the output is the same as the input
- mCache.getHwcBuffer(inSlot, mBuffer1, &outSlot, &outBuffer);
- EXPECT_EQ(expectedSlot, outSlot);
- EXPECT_EQ(mBuffer1, outBuffer);
-
- // The second time with the same buffer, the outBuffer is nullptr.
- mCache.getHwcBuffer(inSlot, mBuffer1, &outSlot, &outBuffer);
- EXPECT_EQ(expectedSlot, outSlot);
- EXPECT_EQ(nullptr, outBuffer.get());
-
- // With a new buffer, the outBuffer is the input.
- mCache.getHwcBuffer(inSlot, mBuffer2, &outSlot, &outBuffer);
- EXPECT_EQ(expectedSlot, outSlot);
- EXPECT_EQ(mBuffer2, outBuffer);
-
- // Again, the second request with the same buffer sets outBuffer to nullptr.
- mCache.getHwcBuffer(inSlot, mBuffer2, &outSlot, &outBuffer);
- EXPECT_EQ(expectedSlot, outSlot);
- EXPECT_EQ(nullptr, outBuffer.get());
-
- // Setting a slot to use nullptr lookslike works, but note that
- // the output values make it look like no new buffer is being set....
- mCache.getHwcBuffer(inSlot, sp<GraphicBuffer>(), &outSlot, &outBuffer);
- EXPECT_EQ(expectedSlot, outSlot);
- EXPECT_EQ(nullptr, outBuffer.get());
- }
-
- impl::HwcBufferCache mCache;
sp<GraphicBuffer> mBuffer1 =
sp<GraphicBuffer>::make(1u, 1u, HAL_PIXEL_FORMAT_RGBA_8888, 1u, 0u);
sp<GraphicBuffer> mBuffer2 =
sp<GraphicBuffer>::make(1u, 1u, HAL_PIXEL_FORMAT_RGBA_8888, 1u, 0u);
};
-TEST_F(HwcBufferCacheTest, cacheWorksForSlotZero) {
- testSlot(0, 0);
+TEST_F(HwcBufferCacheTest, getHwcSlotAndBuffer_returnsUniqueSlotNumberForEachBuffer) {
+ HwcBufferCache cache;
+ sp<GraphicBuffer> outBuffer;
+
+ HwcSlotAndBuffer slotAndBufferFor1 = cache.getHwcSlotAndBuffer(mBuffer1);
+ EXPECT_NE(slotAndBufferFor1.slot, UINT32_MAX);
+ EXPECT_EQ(slotAndBufferFor1.buffer, mBuffer1);
+
+ HwcSlotAndBuffer slotAndBufferFor2 = cache.getHwcSlotAndBuffer(mBuffer2);
+ EXPECT_NE(slotAndBufferFor2.slot, slotAndBufferFor1.slot);
+ EXPECT_NE(slotAndBufferFor2.slot, UINT32_MAX);
+ EXPECT_EQ(slotAndBufferFor2.buffer, mBuffer2);
}
-TEST_F(HwcBufferCacheTest, cacheWorksForMaxSlot) {
- testSlot(BufferQueue::NUM_BUFFER_SLOTS - 1, BufferQueue::NUM_BUFFER_SLOTS - 1);
+TEST_F(HwcBufferCacheTest, getHwcSlotAndBuffer_whenCached_returnsSameSlotNumberAndNullBuffer) {
+ HwcBufferCache cache;
+ sp<GraphicBuffer> outBuffer;
+
+ HwcSlotAndBuffer originalSlotAndBuffer = cache.getHwcSlotAndBuffer(mBuffer1);
+ EXPECT_NE(originalSlotAndBuffer.slot, UINT32_MAX);
+ EXPECT_EQ(originalSlotAndBuffer.buffer, mBuffer1);
+
+ HwcSlotAndBuffer finalSlotAndBuffer = cache.getHwcSlotAndBuffer(mBuffer1);
+ EXPECT_EQ(finalSlotAndBuffer.slot, originalSlotAndBuffer.slot);
+ EXPECT_EQ(finalSlotAndBuffer.buffer, nullptr);
}
-TEST_F(HwcBufferCacheTest, cacheMapsNegativeSlotToZero) {
- testSlot(-123, 0);
+TEST_F(HwcBufferCacheTest, getHwcSlotAndBuffer_whenSlotsFull_evictsOldestCachedBuffer) {
+ HwcBufferCache cache;
+ sp<GraphicBuffer> outBuffer;
+
+ sp<GraphicBuffer> graphicBuffers[100];
+ HwcSlotAndBuffer slotsAndBuffers[100];
+ int finalCachedBufferIndex = 0;
+ for (int i = 0; i < 100; ++i) {
+ graphicBuffers[i] = sp<GraphicBuffer>::make(1u, 1u, HAL_PIXEL_FORMAT_RGBA_8888, 1u, 0u);
+ slotsAndBuffers[i] = cache.getHwcSlotAndBuffer(graphicBuffers[i]);
+ // we fill up the cache when the slot number for the first buffer is reused
+ if (i > 0 && slotsAndBuffers[i].slot == slotsAndBuffers[0].slot) {
+ finalCachedBufferIndex = i;
+ break;
+ }
+ }
+ ASSERT_GT(finalCachedBufferIndex, 1);
+ // the final cached buffer has the same slot value as the oldest buffer
+ EXPECT_EQ(slotsAndBuffers[finalCachedBufferIndex].slot, slotsAndBuffers[0].slot);
+ // the oldest buffer is no longer in the cache because it was evicted
+ EXPECT_EQ(cache.uncache(graphicBuffers[0]->getId()), UINT32_MAX);
+}
+
+TEST_F(HwcBufferCacheTest, uncache_whenCached_returnsSlotNumber) {
+ HwcBufferCache cache;
+ sp<GraphicBuffer> outBuffer;
+
+ HwcSlotAndBuffer slotAndBufferFor1 = cache.getHwcSlotAndBuffer(mBuffer1);
+ ASSERT_NE(slotAndBufferFor1.slot, UINT32_MAX);
+
+ HwcSlotAndBuffer slotAndBufferFor2 = cache.getHwcSlotAndBuffer(mBuffer2);
+ ASSERT_NE(slotAndBufferFor2.slot, UINT32_MAX);
+
+ // the 1st buffer should be found in the cache with a slot number
+ EXPECT_EQ(cache.uncache(mBuffer1->getId()), slotAndBufferFor1.slot);
+ // since the 1st buffer has been previously uncached, we should no longer receive a slot number
+ EXPECT_EQ(cache.uncache(mBuffer1->getId()), UINT32_MAX);
+ // the 2nd buffer should be still found in the cache with a slot number
+ EXPECT_EQ(cache.uncache(mBuffer2->getId()), slotAndBufferFor2.slot);
+ // since the 2nd buffer has been previously uncached, we should no longer receive a slot number
+ EXPECT_EQ(cache.uncache(mBuffer2->getId()), UINT32_MAX);
+}
+
+TEST_F(HwcBufferCacheTest, uncache_whenUncached_returnsInvalidSlotNumber) {
+ HwcBufferCache cache;
+ sp<GraphicBuffer> outBuffer;
+
+ HwcSlotAndBuffer slotAndBufferFor1 = cache.getHwcSlotAndBuffer(mBuffer1);
+ ASSERT_NE(slotAndBufferFor1.slot, UINT32_MAX);
+
+ EXPECT_EQ(cache.uncache(mBuffer2->getId()), UINT32_MAX);
+}
+
+TEST_F(HwcBufferCacheTest, getHwcSlotAndBufferForOverride_whenCached_returnsSameSlotAndNullBuffer) {
+ HwcBufferCache cache;
+ sp<GraphicBuffer> outBuffer;
+
+ HwcSlotAndBuffer originalSlotAndBuffer = cache.getHwcSlotAndBufferForOverride(mBuffer1);
+ EXPECT_NE(originalSlotAndBuffer.slot, UINT32_MAX);
+ EXPECT_EQ(originalSlotAndBuffer.buffer, mBuffer1);
+
+ HwcSlotAndBuffer finalSlotAndBuffer = cache.getHwcSlotAndBufferForOverride(mBuffer1);
+ EXPECT_EQ(finalSlotAndBuffer.slot, originalSlotAndBuffer.slot);
+ EXPECT_EQ(finalSlotAndBuffer.buffer, nullptr);
+}
+
+TEST_F(HwcBufferCacheTest, getHwcSlotAndBufferForOverride_whenSlotsFull_returnsIndependentSlot) {
+ HwcBufferCache cache;
+ sp<GraphicBuffer> outBuffer;
+
+ sp<GraphicBuffer> graphicBuffers[100];
+ HwcSlotAndBuffer slotsAndBuffers[100];
+ int finalCachedBufferIndex = -1;
+ for (int i = 0; i < 100; ++i) {
+ graphicBuffers[i] = sp<GraphicBuffer>::make(1u, 1u, HAL_PIXEL_FORMAT_RGBA_8888, 1u, 0u);
+ slotsAndBuffers[i] = cache.getHwcSlotAndBuffer(graphicBuffers[i]);
+ // we fill up the cache when the slot number for the first buffer is reused
+ if (i > 0 && slotsAndBuffers[i].slot == slotsAndBuffers[0].slot) {
+ finalCachedBufferIndex = i;
+ break;
+ }
+ }
+ // expect to have cached at least a few buffers before evicting
+ ASSERT_GT(finalCachedBufferIndex, 1);
+
+ sp<GraphicBuffer> overrideBuffer =
+ sp<GraphicBuffer>::make(1u, 1u, HAL_PIXEL_FORMAT_RGBA_8888, 1u, 0u);
+ HwcSlotAndBuffer overrideSlotAndBuffer = cache.getHwcSlotAndBufferForOverride(overrideBuffer);
+ // expect us to have a slot number
+ EXPECT_NE(overrideSlotAndBuffer.slot, UINT32_MAX);
+ // expect this to be the first time we cached the buffer
+ EXPECT_NE(overrideSlotAndBuffer.buffer, nullptr);
+
+ // expect the slot number to not equal any other slot number, even after the slots have been
+ // exhausted, indicating that the override buffer slot is independent from the slots for
+ // non-override buffers
+ for (int i = 0; i < finalCachedBufferIndex; ++i) {
+ EXPECT_NE(overrideSlotAndBuffer.slot, slotsAndBuffers[i].slot);
+ }
+ // the override buffer is independently uncached from the oldest cached buffer
+ // expect to find the override buffer still in the override buffer slot
+ EXPECT_EQ(cache.uncache(overrideBuffer->getId()), overrideSlotAndBuffer.slot);
+ // expect that the first buffer was not evicted from the cache when the override buffer was
+ // cached
+ EXPECT_EQ(cache.uncache(graphicBuffers[1]->getId()), slotsAndBuffers[1].slot);
+}
+
+TEST_F(HwcBufferCacheTest, uncache_whenOverrideCached_returnsSlotNumber) {
+ HwcBufferCache cache;
+ sp<GraphicBuffer> outBuffer;
+
+ HwcSlotAndBuffer hwcSlotAndBuffer = cache.getHwcSlotAndBufferForOverride(mBuffer1);
+ ASSERT_NE(hwcSlotAndBuffer.slot, UINT32_MAX);
+
+ EXPECT_EQ(cache.uncache(mBuffer1->getId()), hwcSlotAndBuffer.slot);
+ EXPECT_EQ(cache.uncache(mBuffer1->getId()), UINT32_MAX);
+}
+
+TEST_F(HwcBufferCacheTest, uncache_whenOverrideUncached_returnsInvalidSlotNumber) {
+ HwcBufferCache cache;
+ sp<GraphicBuffer> outBuffer;
+
+ HwcSlotAndBuffer hwcSlotAndBuffer = cache.getHwcSlotAndBufferForOverride(mBuffer1);
+ ASSERT_NE(hwcSlotAndBuffer.slot, UINT32_MAX);
+
+ EXPECT_EQ(cache.uncache(mBuffer2->getId()), UINT32_MAX);
}
} // namespace
diff --git a/services/surfaceflinger/CompositionEngine/tests/OutputLayerTest.cpp b/services/surfaceflinger/CompositionEngine/tests/OutputLayerTest.cpp
index 0f7dce8..03cf292 100644
--- a/services/surfaceflinger/CompositionEngine/tests/OutputLayerTest.cpp
+++ b/services/surfaceflinger/CompositionEngine/tests/OutputLayerTest.cpp
@@ -774,7 +774,7 @@
static constexpr ui::Dataspace kOverrideDataspace = static_cast<ui::Dataspace>(72);
static constexpr int kSupportedPerFrameMetadata = 101;
static constexpr int kExpectedHwcSlot = 0;
- static constexpr int kOverrideHwcSlot = impl::HwcBufferCache::FLATTENER_CACHING_SLOT;
+ static constexpr int kOverrideHwcSlot = impl::HwcBufferCache::kOverrideBufferSlot;
static constexpr bool kLayerGenericMetadata1Mandatory = true;
static constexpr bool kLayerGenericMetadata2Mandatory = true;
static constexpr float kWhitePointNits = 200.f;
@@ -823,7 +823,6 @@
mLayerFEState.hdrMetadata = kHdrMetadata;
mLayerFEState.sidebandStream = NativeHandle::create(kSidebandStreamHandle, false);
mLayerFEState.buffer = kBuffer;
- mLayerFEState.bufferSlot = BufferQueue::INVALID_BUFFER_SLOT;
mLayerFEState.acquireFence = kFence;
mOutputState.displayBrightnessNits = kDisplayBrightnessNits;
diff --git a/services/surfaceflinger/CompositionEngine/tests/OutputTest.cpp b/services/surfaceflinger/CompositionEngine/tests/OutputTest.cpp
index 2109987..bfd863b 100644
--- a/services/surfaceflinger/CompositionEngine/tests/OutputTest.cpp
+++ b/services/surfaceflinger/CompositionEngine/tests/OutputTest.cpp
@@ -1192,14 +1192,49 @@
compositionengine::LayerFESet&));
};
+ OutputPrepareTest() {
+ EXPECT_CALL(mOutput, getOutputLayerCount()).WillRepeatedly(Return(2u));
+ EXPECT_CALL(mOutput, getOutputLayerOrderedByZByIndex(0))
+ .WillRepeatedly(Return(&mLayer1.outputLayer));
+ EXPECT_CALL(mOutput, getOutputLayerOrderedByZByIndex(1))
+ .WillRepeatedly(Return(&mLayer2.outputLayer));
+
+ mRefreshArgs.layers.push_back(mLayer1.layerFE);
+ mRefreshArgs.layers.push_back(mLayer2.layerFE);
+ }
+
+ struct Layer {
+ StrictMock<mock::OutputLayer> outputLayer;
+ sp<StrictMock<mock::LayerFE>> layerFE = sp<StrictMock<mock::LayerFE>>::make();
+ };
+
StrictMock<OutputPartialMock> mOutput;
CompositionRefreshArgs mRefreshArgs;
LayerFESet mGeomSnapshots;
+ Layer mLayer1;
+ Layer mLayer2;
};
-TEST_F(OutputPrepareTest, justInvokesRebuildLayerStacks) {
+TEST_F(OutputPrepareTest, callsUncacheBuffersOnEachOutputLayerAndThenRebuildsLayerStacks) {
InSequence seq;
+
+ mRefreshArgs.bufferIdsToUncache = {1, 3, 5};
+
EXPECT_CALL(mOutput, rebuildLayerStacks(Ref(mRefreshArgs), Ref(mGeomSnapshots)));
+ EXPECT_CALL(mLayer1.outputLayer, uncacheBuffers(Ref(mRefreshArgs.bufferIdsToUncache)));
+ EXPECT_CALL(mLayer2.outputLayer, uncacheBuffers(Ref(mRefreshArgs.bufferIdsToUncache)));
+
+ mOutput.prepare(mRefreshArgs, mGeomSnapshots);
+}
+
+TEST_F(OutputPrepareTest, skipsUncacheBuffersIfEmptyAndThenRebuildsLayerStacks) {
+ InSequence seq;
+
+ mRefreshArgs.bufferIdsToUncache = {};
+
+ EXPECT_CALL(mOutput, rebuildLayerStacks(Ref(mRefreshArgs), Ref(mGeomSnapshots)));
+ EXPECT_CALL(mLayer1.outputLayer, uncacheBuffers(_)).Times(0);
+ EXPECT_CALL(mLayer2.outputLayer, uncacheBuffers(_)).Times(0);
mOutput.prepare(mRefreshArgs, mGeomSnapshots);
}
diff --git a/services/surfaceflinger/DisplayDevice.cpp b/services/surfaceflinger/DisplayDevice.cpp
index 269a5ea..46b857b 100644
--- a/services/surfaceflinger/DisplayDevice.cpp
+++ b/services/surfaceflinger/DisplayDevice.cpp
@@ -203,7 +203,7 @@
mRefreshRateSelector->setActiveMode(modeId, renderFps);
if (mRefreshRateOverlay) {
- mRefreshRateOverlay->changeRefreshRate(displayFps);
+ mRefreshRateOverlay->changeRefreshRate(displayFps, renderFps);
}
}
@@ -410,26 +410,35 @@
capabilities.getDesiredMinLuminance());
}
-void DisplayDevice::enableRefreshRateOverlay(bool enable, bool showSpinnner) {
+void DisplayDevice::enableRefreshRateOverlay(bool enable, bool showSpinner, bool showRenderRate) {
if (!enable) {
mRefreshRateOverlay.reset();
return;
}
+ ftl::Flags<RefreshRateOverlay::Features> features;
+ if (showSpinner) {
+ features |= RefreshRateOverlay::Features::Spinner;
+ }
+
+ if (showRenderRate) {
+ features |= RefreshRateOverlay::Features::RenderRate;
+ }
+
const auto fpsRange = mRefreshRateSelector->getSupportedRefreshRateRange();
- mRefreshRateOverlay = std::make_unique<RefreshRateOverlay>(fpsRange, showSpinnner);
+ mRefreshRateOverlay = std::make_unique<RefreshRateOverlay>(fpsRange, features);
mRefreshRateOverlay->setLayerStack(getLayerStack());
mRefreshRateOverlay->setViewport(getSize());
- mRefreshRateOverlay->changeRefreshRate(getActiveMode().modePtr->getFps());
+ mRefreshRateOverlay->changeRefreshRate(getActiveMode().modePtr->getFps(), getActiveMode().fps);
}
bool DisplayDevice::onKernelTimerChanged(std::optional<DisplayModeId> desiredModeId,
bool timerExpired) {
if (mRefreshRateSelector && mRefreshRateOverlay) {
- const auto newRefreshRate =
+ const auto newMode =
mRefreshRateSelector->onKernelTimerChanged(desiredModeId, timerExpired);
- if (newRefreshRate) {
- mRefreshRateOverlay->changeRefreshRate(*newRefreshRate);
+ if (newMode) {
+ mRefreshRateOverlay->changeRefreshRate(newMode->modePtr->getFps(), newMode->fps);
return true;
}
}
diff --git a/services/surfaceflinger/DisplayDevice.h b/services/surfaceflinger/DisplayDevice.h
index 852a8a2..8f9b2a1 100644
--- a/services/surfaceflinger/DisplayDevice.h
+++ b/services/surfaceflinger/DisplayDevice.h
@@ -236,7 +236,8 @@
}
// Enables an overlay to be displayed with the current refresh rate
- void enableRefreshRateOverlay(bool enable, bool showSpinner) REQUIRES(kMainThreadContext);
+ void enableRefreshRateOverlay(bool enable, bool showSpinner, bool showRenderRate)
+ REQUIRES(kMainThreadContext);
bool isRefreshRateOverlayEnabled() const { return mRefreshRateOverlay != nullptr; }
bool onKernelTimerChanged(std::optional<DisplayModeId>, bool timerExpired);
void animateRefreshRateOverlay();
diff --git a/services/surfaceflinger/DisplayHardware/AidlComposerHal.cpp b/services/surfaceflinger/DisplayHardware/AidlComposerHal.cpp
index 3782c6c..800e36d 100644
--- a/services/surfaceflinger/DisplayHardware/AidlComposerHal.cpp
+++ b/services/surfaceflinger/DisplayHardware/AidlComposerHal.cpp
@@ -535,7 +535,7 @@
return static_cast<Error>(status.getServiceSpecificError());
}
- *outTypes = translate<Hdr>(capabilities.types);
+ *outTypes = capabilities.types;
*outMaxLuminance = capabilities.maxLuminance;
*outMaxAverageLuminance = capabilities.maxAverageLuminance;
*outMinLuminance = capabilities.minLuminance;
diff --git a/services/surfaceflinger/DisplayHardware/AidlComposerHal.h b/services/surfaceflinger/DisplayHardware/AidlComposerHal.h
index d84efe7..2a043fd 100644
--- a/services/surfaceflinger/DisplayHardware/AidlComposerHal.h
+++ b/services/surfaceflinger/DisplayHardware/AidlComposerHal.h
@@ -104,7 +104,7 @@
Error getDozeSupport(Display display, bool* outSupport) override;
Error hasDisplayIdleTimerCapability(Display display, bool* outSupport) override;
- Error getHdrCapabilities(Display display, std::vector<Hdr>* outTypes, float* outMaxLuminance,
+ Error getHdrCapabilities(Display display, std::vector<Hdr>* outHdrTypes, float* outMaxLuminance,
float* outMaxAverageLuminance, float* outMinLuminance) override;
Error getOverlaySupport(OverlayProperties* outProperties) override;
diff --git a/services/surfaceflinger/DisplayHardware/ComposerHal.h b/services/surfaceflinger/DisplayHardware/ComposerHal.h
index a9bf282..ec23935 100644
--- a/services/surfaceflinger/DisplayHardware/ComposerHal.h
+++ b/services/surfaceflinger/DisplayHardware/ComposerHal.h
@@ -66,7 +66,6 @@
using types::V1_1::RenderIntent;
using types::V1_2::ColorMode;
using types::V1_2::Dataspace;
-using types::V1_2::Hdr;
using types::V1_2::PixelFormat;
using V2_1::Config;
@@ -84,6 +83,7 @@
using PerFrameMetadataKey = IComposerClient::PerFrameMetadataKey;
using PerFrameMetadataBlob = IComposerClient::PerFrameMetadataBlob;
using AidlTransform = ::aidl::android::hardware::graphics::common::Transform;
+using aidl::android::hardware::graphics::common::Hdr;
class Composer {
public:
@@ -140,7 +140,7 @@
virtual Error getDozeSupport(Display display, bool* outSupport) = 0;
virtual Error hasDisplayIdleTimerCapability(Display display, bool* outSupport) = 0;
- virtual Error getHdrCapabilities(Display display, std::vector<Hdr>* outTypes,
+ virtual Error getHdrCapabilities(Display display, std::vector<Hdr>* outHdrTypes,
float* outMaxLuminance, float* outMaxAverageLuminance,
float* outMinLuminance) = 0;
diff --git a/services/surfaceflinger/DisplayHardware/FramebufferSurface.cpp b/services/surfaceflinger/DisplayHardware/FramebufferSurface.cpp
index eb14933..ce602a8 100644
--- a/services/surfaceflinger/DisplayHardware/FramebufferSurface.cpp
+++ b/services/surfaceflinger/DisplayHardware/FramebufferSurface.cpp
@@ -72,6 +72,10 @@
mConsumer->setDefaultBufferSize(limitedSize.width, limitedSize.height);
mConsumer->setMaxAcquiredBufferCount(
SurfaceFlinger::maxFrameBufferAcquiredBuffers - 1);
+
+ for (size_t i = 0; i < sizeof(mHwcBufferIds) / sizeof(mHwcBufferIds[0]); ++i) {
+ mHwcBufferIds[i] = UINT64_MAX;
+ }
}
void FramebufferSurface::resizeBuffers(const ui::Size& newSize) {
@@ -88,31 +92,16 @@
}
status_t FramebufferSurface::advanceFrame() {
- uint32_t slot = 0;
- sp<GraphicBuffer> buf;
- sp<Fence> acquireFence(Fence::NO_FENCE);
- Dataspace dataspace = Dataspace::UNKNOWN;
- status_t result = nextBuffer(slot, buf, acquireFence, dataspace);
- mDataSpace = dataspace;
- if (result != NO_ERROR) {
- ALOGE("error latching next FramebufferSurface buffer: %s (%d)",
- strerror(-result), result);
- }
- return result;
-}
-
-status_t FramebufferSurface::nextBuffer(uint32_t& outSlot,
- sp<GraphicBuffer>& outBuffer, sp<Fence>& outFence,
- Dataspace& outDataspace) {
Mutex::Autolock lock(mMutex);
BufferItem item;
status_t err = acquireBufferLocked(&item, 0);
if (err == BufferQueue::NO_BUFFER_AVAILABLE) {
- mHwcBufferCache.getHwcBuffer(mCurrentBufferSlot, mCurrentBuffer, &outSlot, &outBuffer);
+ mDataspace = Dataspace::UNKNOWN;
return NO_ERROR;
} else if (err != NO_ERROR) {
ALOGE("error acquiring buffer: %s (%d)", strerror(-err), err);
+ mDataspace = Dataspace::UNKNOWN;
return err;
}
@@ -133,13 +122,18 @@
mCurrentBufferSlot = item.mSlot;
mCurrentBuffer = mSlots[mCurrentBufferSlot].mGraphicBuffer;
mCurrentFence = item.mFence;
+ mDataspace = static_cast<Dataspace>(item.mDataSpace);
- outFence = item.mFence;
- mHwcBufferCache.getHwcBuffer(mCurrentBufferSlot, mCurrentBuffer, &outSlot, &outBuffer);
- outDataspace = static_cast<Dataspace>(item.mDataSpace);
- status_t result = mHwc.setClientTarget(mDisplayId, outSlot, outFence, outBuffer, outDataspace);
+ // assume HWC has previously seen the buffer in this slot
+ sp<GraphicBuffer> hwcBuffer = sp<GraphicBuffer>(nullptr);
+ if (mCurrentBuffer->getId() != mHwcBufferIds[mCurrentBufferSlot]) {
+ mHwcBufferIds[mCurrentBufferSlot] = mCurrentBuffer->getId();
+ hwcBuffer = mCurrentBuffer; // HWC hasn't previously seen this buffer in this slot
+ }
+ status_t result = mHwc.setClientTarget(mDisplayId, mCurrentBufferSlot, mCurrentFence, hwcBuffer,
+ mDataspace);
if (result != NO_ERROR) {
- ALOGE("error posting framebuffer: %d", result);
+ ALOGE("error posting framebuffer: %s (%d)", strerror(-result), result);
return result;
}
@@ -190,7 +184,7 @@
limitedSize.width = maxSize.height * aspectRatio;
wasLimited = true;
}
- ALOGI_IF(wasLimited, "framebuffer size has been limited to [%dx%d] from [%dx%d]",
+ ALOGI_IF(wasLimited, "Framebuffer size has been limited to [%dx%d] from [%dx%d]",
limitedSize.width, limitedSize.height, size.width, size.height);
return limitedSize;
}
@@ -198,9 +192,9 @@
void FramebufferSurface::dumpAsString(String8& result) const {
Mutex::Autolock lock(mMutex);
result.append(" FramebufferSurface\n");
- result.appendFormat(" mDataSpace=%s (%d)\n",
- dataspaceDetails(static_cast<android_dataspace>(mDataSpace)).c_str(),
- mDataSpace);
+ result.appendFormat(" mDataspace=%s (%d)\n",
+ dataspaceDetails(static_cast<android_dataspace>(mDataspace)).c_str(),
+ mDataspace);
ConsumerBase::dumpLocked(result, " ");
}
diff --git a/services/surfaceflinger/DisplayHardware/FramebufferSurface.h b/services/surfaceflinger/DisplayHardware/FramebufferSurface.h
index d41a856..0b863da 100644
--- a/services/surfaceflinger/DisplayHardware/FramebufferSurface.h
+++ b/services/surfaceflinger/DisplayHardware/FramebufferSurface.h
@@ -21,7 +21,7 @@
#include <sys/types.h>
#include <compositionengine/DisplaySurface.h>
-#include <compositionengine/impl/HwcBufferCache.h>
+#include <gui/BufferQueue.h>
#include <gui/ConsumerBase.h>
#include <ui/DisplayId.h>
#include <ui/Size.h>
@@ -69,12 +69,6 @@
virtual void dumpLocked(String8& result, const char* prefix) const;
- // nextBuffer waits for and then latches the next buffer from the
- // BufferQueue and releases the previously latched buffer to the
- // BufferQueue. The new buffer is returned in the 'buffer' argument.
- status_t nextBuffer(uint32_t& outSlot, sp<GraphicBuffer>& outBuffer,
- sp<Fence>& outFence, ui::Dataspace& outDataspace);
-
const PhysicalDisplayId mDisplayId;
// Framebuffer size has a dimension limitation in pixels based on the graphics capabilities of
@@ -91,7 +85,7 @@
// compositing. Otherwise it will display the dataspace of the buffer
// use for compositing which can change as wide-color content is
// on/off.
- ui::Dataspace mDataSpace;
+ ui::Dataspace mDataspace;
// mCurrentBuffer is the current buffer or nullptr to indicate that there is
// no current buffer.
@@ -103,7 +97,9 @@
// Hardware composer, owned by SurfaceFlinger.
HWComposer& mHwc;
- compositionengine::impl::HwcBufferCache mHwcBufferCache;
+ // Buffers that HWC has seen before, indexed by slot number.
+ // NOTE: The BufferQueue slot number is the same as the HWC slot number.
+ uint64_t mHwcBufferIds[BufferQueue::NUM_BUFFER_SLOTS];
// Previous buffer to release after getting an updated retire fence
bool mHasPendingRelease;
diff --git a/services/surfaceflinger/DisplayHardware/HWC2.cpp b/services/surfaceflinger/DisplayHardware/HWC2.cpp
index e264570..d0126d0 100644
--- a/services/surfaceflinger/DisplayHardware/HWC2.cpp
+++ b/services/surfaceflinger/DisplayHardware/HWC2.cpp
@@ -320,17 +320,17 @@
float maxLuminance = -1.0f;
float maxAverageLuminance = -1.0f;
float minLuminance = -1.0f;
- std::vector<Hwc2::Hdr> types;
- auto intError = mComposer.getHdrCapabilities(mId, &types,
- &maxLuminance, &maxAverageLuminance, &minLuminance);
+ std::vector<Hwc2::Hdr> hdrTypes;
+ auto intError = mComposer.getHdrCapabilities(mId, &hdrTypes, &maxLuminance,
+ &maxAverageLuminance, &minLuminance);
auto error = static_cast<HWC2::Error>(intError);
if (error != Error::NONE) {
return error;
}
- *outCapabilities = HdrCapabilities(std::move(types),
- maxLuminance, maxAverageLuminance, minLuminance);
+ *outCapabilities =
+ HdrCapabilities(std::move(hdrTypes), maxLuminance, maxAverageLuminance, minLuminance);
return Error::NONE;
}
diff --git a/services/surfaceflinger/DisplayHardware/Hal.h b/services/surfaceflinger/DisplayHardware/Hal.h
index 8238828..537d545 100644
--- a/services/surfaceflinger/DisplayHardware/Hal.h
+++ b/services/surfaceflinger/DisplayHardware/Hal.h
@@ -20,6 +20,7 @@
#include <android/hardware/graphics/composer/2.4/IComposer.h>
#include <android/hardware/graphics/composer/2.4/IComposerClient.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>
@@ -39,7 +40,6 @@
using types::V1_1::RenderIntent;
using types::V1_2::ColorMode;
using types::V1_2::Dataspace;
-using types::V1_2::Hdr;
using types::V1_2::PixelFormat;
using V2_1::Error;
@@ -69,6 +69,7 @@
using PowerMode = IComposerClient::PowerMode;
using Vsync = IComposerClient::Vsync;
using VsyncPeriodChangeConstraints = IComposerClient::VsyncPeriodChangeConstraints;
+using Hdr = aidl::android::hardware::graphics::common::Hdr;
} // namespace hardware::graphics::composer::hal
diff --git a/services/surfaceflinger/DisplayHardware/HidlComposerHal.cpp b/services/surfaceflinger/DisplayHardware/HidlComposerHal.cpp
index f8522e2..b607df0 100644
--- a/services/surfaceflinger/DisplayHardware/HidlComposerHal.cpp
+++ b/services/surfaceflinger/DisplayHardware/HidlComposerHal.cpp
@@ -496,13 +496,13 @@
"OptionalFeature::KernelIdleTimer is not supported on HIDL");
}
-Error HidlComposer::getHdrCapabilities(Display display, std::vector<Hdr>* outTypes,
+Error HidlComposer::getHdrCapabilities(Display display, std::vector<Hdr>* outHdrTypes,
float* outMaxLuminance, float* outMaxAverageLuminance,
float* outMinLuminance) {
Error error = kDefaultError;
if (mClient_2_3) {
mClient_2_3->getHdrCapabilities_2_3(display,
- [&](const auto& tmpError, const auto& tmpTypes,
+ [&](const auto& tmpError, const auto& tmpHdrTypes,
const auto& tmpMaxLuminance,
const auto& tmpMaxAverageLuminance,
const auto& tmpMinLuminance) {
@@ -510,15 +510,15 @@
if (error != Error::NONE) {
return;
}
+ *outHdrTypes = translate<ui::Hdr>(tmpHdrTypes);
- *outTypes = tmpTypes;
*outMaxLuminance = tmpMaxLuminance;
*outMaxAverageLuminance = tmpMaxAverageLuminance;
*outMinLuminance = tmpMinLuminance;
});
} else {
mClient->getHdrCapabilities(display,
- [&](const auto& tmpError, const auto& tmpTypes,
+ [&](const auto& tmpError, const auto& tmpHdrTypes,
const auto& tmpMaxLuminance,
const auto& tmpMaxAverageLuminance,
const auto& tmpMinLuminance) {
@@ -526,11 +526,7 @@
if (error != Error::NONE) {
return;
}
-
- outTypes->clear();
- for (auto type : tmpTypes) {
- outTypes->push_back(static_cast<Hdr>(type));
- }
+ *outHdrTypes = translate<ui::Hdr>(tmpHdrTypes);
*outMaxLuminance = tmpMaxLuminance;
*outMaxAverageLuminance = tmpMaxAverageLuminance;
diff --git a/services/surfaceflinger/DisplayHardware/HidlComposerHal.h b/services/surfaceflinger/DisplayHardware/HidlComposerHal.h
index 48b720c..3602bbb 100644
--- a/services/surfaceflinger/DisplayHardware/HidlComposerHal.h
+++ b/services/surfaceflinger/DisplayHardware/HidlComposerHal.h
@@ -56,7 +56,6 @@
using types::V1_1::RenderIntent;
using types::V1_2::ColorMode;
using types::V1_2::Dataspace;
-using types::V1_2::Hdr;
using types::V1_2::PixelFormat;
using V2_1::Config;
@@ -209,7 +208,7 @@
Error getDozeSupport(Display display, bool* outSupport) override;
Error hasDisplayIdleTimerCapability(Display display, bool* outSupport) override;
- Error getHdrCapabilities(Display display, std::vector<Hdr>* outTypes, float* outMaxLuminance,
+ Error getHdrCapabilities(Display display, std::vector<Hdr>* outHdrTypes, float* outMaxLuminance,
float* outMaxAverageLuminance, float* outMinLuminance) override;
Error getOverlaySupport(aidl::android::hardware::graphics::composer3::OverlayProperties*
outProperties) override;
diff --git a/services/surfaceflinger/DisplayHardware/VirtualDisplaySurface.cpp b/services/surfaceflinger/DisplayHardware/VirtualDisplaySurface.cpp
index 3803a78..d62075e 100644
--- a/services/surfaceflinger/DisplayHardware/VirtualDisplaySurface.cpp
+++ b/services/surfaceflinger/DisplayHardware/VirtualDisplaySurface.cpp
@@ -103,6 +103,10 @@
sink->setAsyncMode(true);
IGraphicBufferProducer::QueueBufferOutput output;
mSource[SOURCE_SCRATCH]->connect(nullptr, NATIVE_WINDOW_API_EGL, false, &output);
+
+ for (size_t i = 0; i < sizeof(mHwcBufferIds) / sizeof(mHwcBufferIds[0]); ++i) {
+ mHwcBufferIds[i] = UINT64_MAX;
+ }
}
VirtualDisplaySurface::~VirtualDisplaySurface() {
@@ -197,9 +201,9 @@
return NO_MEMORY;
}
- sp<GraphicBuffer> fbBuffer = mFbProducerSlot >= 0 ?
- mProducerBuffers[mFbProducerSlot] : sp<GraphicBuffer>(nullptr);
- sp<GraphicBuffer> outBuffer = mProducerBuffers[mOutputProducerSlot];
+ sp<GraphicBuffer> const& fbBuffer =
+ mFbProducerSlot >= 0 ? mProducerBuffers[mFbProducerSlot] : sp<GraphicBuffer>(nullptr);
+ sp<GraphicBuffer> const& outBuffer = mProducerBuffers[mOutputProducerSlot];
VDS_LOGV("%s: fb=%d(%p) out=%d(%p)", __func__, mFbProducerSlot, fbBuffer.get(),
mOutputProducerSlot, outBuffer.get());
@@ -211,12 +215,14 @@
status_t result = NO_ERROR;
if (fbBuffer != nullptr) {
- uint32_t hwcSlot = 0;
- sp<GraphicBuffer> hwcBuffer;
- mHwcBufferCache.getHwcBuffer(mFbProducerSlot, fbBuffer, &hwcSlot, &hwcBuffer);
-
+ // assume that HWC has previously seen the buffer in this slot
+ sp<GraphicBuffer> hwcBuffer = sp<GraphicBuffer>(nullptr);
+ if (fbBuffer->getId() != mHwcBufferIds[mFbProducerSlot]) {
+ mHwcBufferIds[mFbProducerSlot] = fbBuffer->getId();
+ hwcBuffer = fbBuffer; // HWC hasn't previously seen this buffer in this slot
+ }
// TODO: Correctly propagate the dataspace from GL composition
- result = mHwc.setClientTarget(*halDisplayId, hwcSlot, mFbFence, hwcBuffer,
+ result = mHwc.setClientTarget(*halDisplayId, mFbProducerSlot, mFbFence, hwcBuffer,
ui::Dataspace::UNKNOWN);
}
diff --git a/services/surfaceflinger/DisplayHardware/VirtualDisplaySurface.h b/services/surfaceflinger/DisplayHardware/VirtualDisplaySurface.h
index e21095a..be06e2b 100644
--- a/services/surfaceflinger/DisplayHardware/VirtualDisplaySurface.h
+++ b/services/surfaceflinger/DisplayHardware/VirtualDisplaySurface.h
@@ -20,7 +20,7 @@
#include <string>
#include <compositionengine/DisplaySurface.h>
-#include <compositionengine/impl/HwcBufferCache.h>
+#include <gui/BufferQueue.h>
#include <gui/ConsumerBase.h>
#include <gui/IGraphicBufferProducer.h>
#include <ui/DisplayId.h>
@@ -164,6 +164,10 @@
sp<IGraphicBufferProducer> mSource[2]; // indexed by SOURCE_*
uint32_t mDefaultOutputFormat;
+ // Buffers that HWC has seen before, indexed by HWC slot number.
+ // NOTE: The BufferQueue slot number is the same as the HWC slot number.
+ uint64_t mHwcBufferIds[BufferQueue::NUM_BUFFER_SLOTS];
+
//
// Inter-frame state
//
@@ -260,8 +264,6 @@
bool mMustRecompose = false;
- compositionengine::impl::HwcBufferCache mHwcBufferCache;
-
bool mForceHwcCopy;
};
diff --git a/services/surfaceflinger/FrontEnd/LayerHierarchy.cpp b/services/surfaceflinger/FrontEnd/LayerHierarchy.cpp
new file mode 100644
index 0000000..db4e8af
--- /dev/null
+++ b/services/surfaceflinger/FrontEnd/LayerHierarchy.cpp
@@ -0,0 +1,472 @@
+/*
+ * 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.
+ */
+
+#define ATRACE_TAG ATRACE_TAG_GRAPHICS
+#undef LOG_TAG
+#define LOG_TAG "LayerHierarchy"
+
+#include "LayerHierarchy.h"
+#include "SwapErase.h"
+
+namespace android::surfaceflinger::frontend {
+
+namespace {
+auto layerZCompare = [](const std::pair<LayerHierarchy*, LayerHierarchy::Variant>& lhs,
+ const std::pair<LayerHierarchy*, LayerHierarchy::Variant>& rhs) {
+ auto lhsLayer = lhs.first->getLayer();
+ auto rhsLayer = rhs.first->getLayer();
+ if (lhsLayer->layerStack != rhsLayer->layerStack) {
+ return lhsLayer->layerStack.id < rhsLayer->layerStack.id;
+ }
+ if (lhsLayer->z != rhsLayer->z) {
+ return lhsLayer->z < rhsLayer->z;
+ }
+ return lhsLayer->id < rhsLayer->id;
+};
+
+void insertSorted(std::vector<std::pair<LayerHierarchy*, LayerHierarchy::Variant>>& vec,
+ std::pair<LayerHierarchy*, LayerHierarchy::Variant> value) {
+ auto it = std::upper_bound(vec.begin(), vec.end(), value, layerZCompare);
+ vec.insert(it, std::move(value));
+}
+} // namespace
+
+LayerHierarchy::LayerHierarchy(RequestedLayerState* layer) : mLayer(layer) {}
+
+LayerHierarchy::LayerHierarchy(const LayerHierarchy& hierarchy, bool childrenOnly) {
+ mLayer = (childrenOnly) ? nullptr : hierarchy.mLayer;
+ mChildren = hierarchy.mChildren;
+}
+
+void LayerHierarchy::traverse(const Visitor& visitor,
+ LayerHierarchy::TraversalPath& traversalPath) const {
+ if (mLayer) {
+ bool breakTraversal = !visitor(*this, traversalPath);
+ if (breakTraversal) {
+ return;
+ }
+ }
+ if (traversalPath.hasRelZLoop()) {
+ LOG_ALWAYS_FATAL("Found relative z loop layerId:%d", traversalPath.invalidRelativeRootId);
+ }
+ for (auto& [child, childVariant] : mChildren) {
+ ScopedAddToTraversalPath addChildToTraversalPath(traversalPath, child->mLayer->id,
+ childVariant);
+ child->traverse(visitor, traversalPath);
+ }
+}
+
+void LayerHierarchy::traverseInZOrder(const Visitor& visitor,
+ LayerHierarchy::TraversalPath& traversalPath) const {
+ bool traverseThisLayer = (mLayer != nullptr);
+ for (auto it = mChildren.begin(); it < mChildren.end(); it++) {
+ auto& [child, childVariant] = *it;
+ if (traverseThisLayer && child->getLayer()->z >= 0) {
+ bool breakTraversal = !visitor(*this, traversalPath);
+ if (breakTraversal) {
+ return;
+ }
+ traverseThisLayer = false;
+ }
+ if (childVariant == LayerHierarchy::Variant::Detached) {
+ continue;
+ }
+ ScopedAddToTraversalPath addChildToTraversalPath(traversalPath, child->mLayer->id,
+ childVariant);
+ child->traverseInZOrder(visitor, traversalPath);
+ }
+
+ if (traverseThisLayer) {
+ visitor(*this, traversalPath);
+ }
+}
+
+void LayerHierarchy::addChild(LayerHierarchy* child, LayerHierarchy::Variant variant) {
+ insertSorted(mChildren, {child, variant});
+}
+
+void LayerHierarchy::removeChild(LayerHierarchy* child) {
+ auto it = std::find_if(mChildren.begin(), mChildren.end(),
+ [child](const std::pair<LayerHierarchy*, Variant>& x) {
+ return x.first == child;
+ });
+ if (it == mChildren.end()) {
+ LOG_ALWAYS_FATAL("Could not find child!");
+ }
+ mChildren.erase(it);
+}
+
+void LayerHierarchy::sortChildrenByZOrder() {
+ std::sort(mChildren.begin(), mChildren.end(), layerZCompare);
+}
+
+void LayerHierarchy::updateChild(LayerHierarchy* hierarchy, LayerHierarchy::Variant variant) {
+ auto it = std::find_if(mChildren.begin(), mChildren.end(),
+ [hierarchy](std::pair<LayerHierarchy*, Variant>& child) {
+ return child.first == hierarchy;
+ });
+ if (it == mChildren.end()) {
+ LOG_ALWAYS_FATAL("Could not find child!");
+ } else {
+ it->second = variant;
+ }
+}
+
+const RequestedLayerState* LayerHierarchy::getLayer() const {
+ return mLayer;
+}
+
+std::string LayerHierarchy::getDebugStringShort() const {
+ std::string debug = "LayerHierarchy{";
+ debug += ((mLayer) ? mLayer->getDebugStringShort() : "root") + " ";
+ if (mChildren.empty()) {
+ debug += "no children";
+ } else {
+ debug += std::to_string(mChildren.size()) + " children";
+ }
+ return debug + "}";
+}
+
+std::string LayerHierarchy::getDebugString(const char* prefix) const {
+ std::string debug = prefix + getDebugStringShort();
+ for (auto& [child, childVariant] : mChildren) {
+ std::string childPrefix = " " + std::string(prefix) + " " + std::to_string(childVariant);
+ debug += "\n" + child->getDebugString(childPrefix.c_str());
+ }
+ return debug;
+}
+
+bool LayerHierarchy::hasRelZLoop(uint32_t& outInvalidRelativeRoot) const {
+ outInvalidRelativeRoot = UNASSIGNED_LAYER_ID;
+ traverse([&outInvalidRelativeRoot](const LayerHierarchy&,
+ const LayerHierarchy::TraversalPath& traversalPath) -> bool {
+ if (traversalPath.hasRelZLoop()) {
+ outInvalidRelativeRoot = traversalPath.invalidRelativeRootId;
+ return false;
+ }
+ return true;
+ });
+ return outInvalidRelativeRoot != UNASSIGNED_LAYER_ID;
+}
+
+LayerHierarchyBuilder::LayerHierarchyBuilder(
+ const std::vector<std::unique_ptr<RequestedLayerState>>& layers) {
+ mHierarchies.reserve(layers.size());
+ mLayerIdToHierarchy.reserve(layers.size());
+ for (auto& layer : layers) {
+ mHierarchies.emplace_back(std::make_unique<LayerHierarchy>(layer.get()));
+ mLayerIdToHierarchy[layer->id] = mHierarchies.back().get();
+ }
+ for (const auto& layer : layers) {
+ onLayerAdded(layer.get());
+ }
+ detachHierarchyFromRelativeParent(&mOffscreenRoot);
+}
+
+void LayerHierarchyBuilder::attachToParent(LayerHierarchy* hierarchy) {
+ auto layer = hierarchy->mLayer;
+ LayerHierarchy::Variant type = layer->hasValidRelativeParent()
+ ? LayerHierarchy::Variant::Detached
+ : LayerHierarchy::Variant::Attached;
+
+ LayerHierarchy* parent;
+
+ if (layer->parentId != UNASSIGNED_LAYER_ID) {
+ parent = getHierarchyFromId(layer->parentId);
+ } else if (layer->canBeRoot) {
+ parent = &mRoot;
+ } else {
+ parent = &mOffscreenRoot;
+ }
+ parent->addChild(hierarchy, type);
+ hierarchy->mParent = parent;
+}
+
+void LayerHierarchyBuilder::detachFromParent(LayerHierarchy* hierarchy) {
+ hierarchy->mParent->removeChild(hierarchy);
+ hierarchy->mParent = nullptr;
+}
+
+void LayerHierarchyBuilder::attachToRelativeParent(LayerHierarchy* hierarchy) {
+ auto layer = hierarchy->mLayer;
+ if (!layer->hasValidRelativeParent() || hierarchy->mRelativeParent) {
+ return;
+ }
+
+ if (layer->relativeParentId != UNASSIGNED_LAYER_ID) {
+ hierarchy->mRelativeParent = getHierarchyFromId(layer->relativeParentId);
+ } else {
+ hierarchy->mRelativeParent = &mOffscreenRoot;
+ }
+ hierarchy->mRelativeParent->addChild(hierarchy, LayerHierarchy::Variant::Relative);
+ hierarchy->mParent->updateChild(hierarchy, LayerHierarchy::Variant::Detached);
+}
+
+void LayerHierarchyBuilder::detachFromRelativeParent(LayerHierarchy* hierarchy) {
+ if (hierarchy->mRelativeParent) {
+ hierarchy->mRelativeParent->removeChild(hierarchy);
+ }
+ hierarchy->mRelativeParent = nullptr;
+ hierarchy->mParent->updateChild(hierarchy, LayerHierarchy::Variant::Attached);
+}
+
+void LayerHierarchyBuilder::attachHierarchyToRelativeParent(LayerHierarchy* root) {
+ if (root->mLayer) {
+ attachToRelativeParent(root);
+ }
+ for (auto& [child, childVariant] : root->mChildren) {
+ if (childVariant == LayerHierarchy::Variant::Detached ||
+ childVariant == LayerHierarchy::Variant::Attached) {
+ attachHierarchyToRelativeParent(child);
+ }
+ }
+}
+
+void LayerHierarchyBuilder::detachHierarchyFromRelativeParent(LayerHierarchy* root) {
+ if (root->mLayer) {
+ detachFromRelativeParent(root);
+ }
+ for (auto& [child, childVariant] : root->mChildren) {
+ if (childVariant == LayerHierarchy::Variant::Detached ||
+ childVariant == LayerHierarchy::Variant::Attached) {
+ detachHierarchyFromRelativeParent(child);
+ }
+ }
+}
+
+void LayerHierarchyBuilder::onLayerAdded(RequestedLayerState* layer) {
+ LayerHierarchy* hierarchy = getHierarchyFromId(layer->id);
+ attachToParent(hierarchy);
+ attachToRelativeParent(hierarchy);
+
+ if (layer->mirrorId != UNASSIGNED_LAYER_ID) {
+ LayerHierarchy* mirror = getHierarchyFromId(layer->mirrorId);
+ hierarchy->addChild(mirror, LayerHierarchy::Variant::Mirror);
+ }
+}
+
+void LayerHierarchyBuilder::onLayerDestroyed(RequestedLayerState* layer) {
+ LayerHierarchy* hierarchy = getHierarchyFromId(layer->id, /*crashOnFailure=*/false);
+ if (!hierarchy) {
+ // Layer was never part of the hierarchy if it was created and destroyed in the same
+ // transaction.
+ return;
+ }
+ // detach from parent
+ detachFromRelativeParent(hierarchy);
+ detachFromParent(hierarchy);
+
+ // detach children
+ for (auto& [child, variant] : hierarchy->mChildren) {
+ if (variant == LayerHierarchy::Variant::Attached ||
+ variant == LayerHierarchy::Variant::Detached) {
+ mOffscreenRoot.addChild(child, LayerHierarchy::Variant::Attached);
+ child->mParent = &mOffscreenRoot;
+ } else if (variant == LayerHierarchy::Variant::Relative) {
+ mOffscreenRoot.addChild(child, LayerHierarchy::Variant::Attached);
+ child->mRelativeParent = &mOffscreenRoot;
+ }
+ }
+
+ swapErase(mHierarchies, [hierarchy](std::unique_ptr<LayerHierarchy>& layerHierarchy) {
+ return layerHierarchy.get() == hierarchy;
+ });
+ mLayerIdToHierarchy.erase(layer->id);
+}
+
+void LayerHierarchyBuilder::updateMirrorLayer(RequestedLayerState* layer) {
+ LayerHierarchy* hierarchy = getHierarchyFromId(layer->id);
+ auto it = hierarchy->mChildren.begin();
+ while (it != hierarchy->mChildren.end()) {
+ if (it->second == LayerHierarchy::Variant::Mirror) {
+ hierarchy->mChildren.erase(it);
+ break;
+ }
+ it++;
+ }
+
+ if (layer->mirrorId != UNASSIGNED_LAYER_ID) {
+ hierarchy->addChild(getHierarchyFromId(layer->mirrorId), LayerHierarchy::Variant::Mirror);
+ }
+}
+
+void LayerHierarchyBuilder::update(
+ const std::vector<std::unique_ptr<RequestedLayerState>>& layers,
+ const std::vector<std::unique_ptr<RequestedLayerState>>& destroyedLayers) {
+ // rebuild map
+ for (auto& layer : layers) {
+ if (layer->changes.test(RequestedLayerState::Changes::Created)) {
+ mHierarchies.emplace_back(std::make_unique<LayerHierarchy>(layer.get()));
+ mLayerIdToHierarchy[layer->id] = mHierarchies.back().get();
+ }
+ }
+
+ for (auto& layer : layers) {
+ if (layer->changes.get() == 0) {
+ continue;
+ }
+ if (layer->changes.test(RequestedLayerState::Changes::Created)) {
+ onLayerAdded(layer.get());
+ continue;
+ }
+ LayerHierarchy* hierarchy = getHierarchyFromId(layer->id);
+ if (layer->changes.test(RequestedLayerState::Changes::Parent)) {
+ detachFromParent(hierarchy);
+ attachToParent(hierarchy);
+ }
+ if (layer->changes.test(RequestedLayerState::Changes::RelativeParent)) {
+ detachFromRelativeParent(hierarchy);
+ attachToRelativeParent(hierarchy);
+ }
+ if (layer->changes.test(RequestedLayerState::Changes::Z)) {
+ hierarchy->mParent->sortChildrenByZOrder();
+ if (hierarchy->mRelativeParent) {
+ hierarchy->mRelativeParent->sortChildrenByZOrder();
+ }
+ }
+ if (layer->changes.test(RequestedLayerState::Changes::Mirror)) {
+ updateMirrorLayer(layer.get());
+ }
+ }
+
+ for (auto& layer : destroyedLayers) {
+ onLayerDestroyed(layer.get());
+ }
+ // When moving from onscreen to offscreen and vice versa, we need to attach and detach
+ // from our relative parents. This walks down both trees to do so. We can optimize this
+ // further by tracking onscreen, offscreen state in LayerHierarchy.
+ detachHierarchyFromRelativeParent(&mOffscreenRoot);
+ attachHierarchyToRelativeParent(&mRoot);
+}
+
+const LayerHierarchy& LayerHierarchyBuilder::getHierarchy() const {
+ return mRoot;
+}
+
+const LayerHierarchy& LayerHierarchyBuilder::getOffscreenHierarchy() const {
+ return mOffscreenRoot;
+}
+
+std::string LayerHierarchyBuilder::getDebugString(uint32_t layerId, uint32_t depth) const {
+ if (depth > 10) return "too deep, loop?";
+ if (layerId == UNASSIGNED_LAYER_ID) return "";
+ auto it = mLayerIdToHierarchy.find(layerId);
+ if (it == mLayerIdToHierarchy.end()) return "not found";
+
+ LayerHierarchy* hierarchy = it->second;
+ if (!hierarchy->mLayer) return "none";
+
+ std::string debug =
+ "[" + std::to_string(hierarchy->mLayer->id) + "] " + hierarchy->mLayer->name;
+ if (hierarchy->mRelativeParent) {
+ debug += " Relative:" + hierarchy->mRelativeParent->getDebugStringShort();
+ }
+ if (hierarchy->mParent) {
+ debug += " Parent:" + hierarchy->mParent->getDebugStringShort();
+ }
+ return debug;
+}
+
+LayerHierarchy LayerHierarchyBuilder::getPartialHierarchy(uint32_t layerId,
+ bool childrenOnly) const {
+ auto it = mLayerIdToHierarchy.find(layerId);
+ if (it == mLayerIdToHierarchy.end()) return {nullptr};
+
+ LayerHierarchy hierarchy(*it->second, childrenOnly);
+ return hierarchy;
+}
+
+LayerHierarchy* LayerHierarchyBuilder::getHierarchyFromId(uint32_t layerId, bool crashOnFailure) {
+ auto it = mLayerIdToHierarchy.find(layerId);
+ if (it == mLayerIdToHierarchy.end()) {
+ if (crashOnFailure) {
+ LOG_ALWAYS_FATAL("Could not find hierarchy for layer id %d", layerId);
+ }
+ return nullptr;
+ };
+
+ return it->second;
+}
+
+LayerHierarchy::TraversalPath LayerHierarchy::TraversalPath::ROOT_TRAVERSAL_ID =
+ {.id = UNASSIGNED_LAYER_ID, .variant = LayerHierarchy::Attached};
+
+std::string LayerHierarchy::TraversalPath::toString() const {
+ std::string debugString = "TraversalPath{.id = " + std::to_string(id);
+
+ if (!mirrorRootIds.empty()) {
+ debugString += ", .mirrorRootIds=";
+ for (auto rootId : mirrorRootIds) {
+ debugString += std::to_string(rootId) + ",";
+ }
+ }
+
+ if (!relativeRootIds.empty()) {
+ debugString += ", .relativeRootIds=";
+ for (auto rootId : relativeRootIds) {
+ debugString += std::to_string(rootId) + ",";
+ }
+ }
+
+ if (hasRelZLoop()) {
+ debugString += ", hasRelZLoop=true invalidRelativeRootId=";
+ debugString += std::to_string(invalidRelativeRootId) + ",";
+ }
+
+ debugString += "}";
+ return debugString;
+}
+
+// Helper class to update a passed in TraversalPath when visiting a child. When the object goes out
+// of scope the TraversalPath is reset to its original state.
+LayerHierarchy::ScopedAddToTraversalPath::ScopedAddToTraversalPath(TraversalPath& traversalPath,
+ uint32_t layerId,
+ LayerHierarchy::Variant variant)
+ : mTraversalPath(traversalPath),
+ mParentId(traversalPath.id),
+ mParentVariant(traversalPath.variant) {
+ // Update the traversal id with the child layer id and variant. Parent id and variant are
+ // stored to reset the id upon destruction.
+ traversalPath.id = layerId;
+ traversalPath.variant = variant;
+ if (variant == LayerHierarchy::Variant::Mirror) {
+ traversalPath.mirrorRootIds.emplace_back(layerId);
+ }
+ if (variant == LayerHierarchy::Variant::Relative) {
+ if (std::find(traversalPath.relativeRootIds.begin(), traversalPath.relativeRootIds.end(),
+ layerId) != traversalPath.relativeRootIds.end()) {
+ traversalPath.invalidRelativeRootId = layerId;
+ }
+ traversalPath.relativeRootIds.emplace_back(layerId);
+ }
+}
+LayerHierarchy::ScopedAddToTraversalPath::~ScopedAddToTraversalPath() {
+ // Reset the traversal id to its original parent state using the state that was saved in
+ // the constructor.
+ if (mTraversalPath.variant == LayerHierarchy::Variant::Mirror) {
+ mTraversalPath.mirrorRootIds.pop_back();
+ }
+ if (mTraversalPath.variant == LayerHierarchy::Variant::Relative) {
+ mTraversalPath.relativeRootIds.pop_back();
+ }
+ if (mTraversalPath.invalidRelativeRootId == mTraversalPath.id) {
+ mTraversalPath.invalidRelativeRootId = UNASSIGNED_LAYER_ID;
+ }
+ mTraversalPath.id = mParentId;
+ mTraversalPath.variant = mParentVariant;
+}
+
+} // namespace android::surfaceflinger::frontend
diff --git a/services/surfaceflinger/FrontEnd/LayerHierarchy.h b/services/surfaceflinger/FrontEnd/LayerHierarchy.h
new file mode 100644
index 0000000..f83a859
--- /dev/null
+++ b/services/surfaceflinger/FrontEnd/LayerHierarchy.h
@@ -0,0 +1,163 @@
+/*
+ * 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 "FrontEnd/LayerCreationArgs.h"
+#include "RequestedLayerState.h"
+#include "ftl/small_vector.h"
+
+namespace android::surfaceflinger::frontend {
+class LayerHierarchyBuilder;
+
+// LayerHierarchy allows us to navigate the layer hierarchy in z-order, or depth first traversal.
+// The hierarchy is created from a set of RequestedLayerStates. The hierarchy itself does not
+// contain additional states. Instead, it is a representation of RequestedLayerStates as a graph.
+//
+// Each node in the hierarchy can be visited by multiple parents (making this a graph). While
+// traversing the hierarchy, a new concept called Variant can be used to understand the
+// relationship of the layer to its parent. The following variants are possible:
+// Attached - child of the parent
+// Detached - child of the parent but currently relative parented to another layer
+// Relative - relative child of the parent
+// Mirror - mirrored from another layer
+//
+// By representing the hierarchy as a graph, we can represent mirrored layer hierarchies without
+// cloning the layer requested state. The mirrored hierarchy and its corresponding
+// RequestedLayerStates are kept in sync because the mirrored hierarchy does not clone any
+// states.
+class LayerHierarchy {
+public:
+ enum Variant {
+ Attached,
+ Detached,
+ Relative,
+ Mirror,
+ };
+ // Represents a unique path to a node.
+ struct TraversalPath {
+ uint32_t id;
+ LayerHierarchy::Variant variant;
+ // Mirrored layers can have a different geometry than their parents so we need to track
+ // the mirror roots in the traversal.
+ ftl::SmallVector<uint32_t, 5> mirrorRootIds;
+ // Relative layers can be visited twice, once by their parent and then once again by
+ // their relative parent. We keep track of the roots here to detect any loops in the
+ // hierarchy. If a relative root already exists in the list while building the
+ // TraversalPath, it means that somewhere in the hierarchy two layers are relatively
+ // parented to each other.
+ ftl::SmallVector<uint32_t, 5> relativeRootIds;
+ // First duplicate relative root id found. If this is a valid layer id that means we are
+ // in a loop.
+ uint32_t invalidRelativeRootId = UNASSIGNED_LAYER_ID;
+ bool hasRelZLoop() const { return invalidRelativeRootId != UNASSIGNED_LAYER_ID; }
+ bool isRelative() { return !relativeRootIds.empty(); }
+
+ bool operator==(const TraversalPath& other) const {
+ return id == other.id && mirrorRootIds == other.mirrorRootIds;
+ }
+ std::string toString() const;
+
+ static TraversalPath ROOT_TRAVERSAL_ID;
+ };
+
+ // Helper class to add nodes to an existing traversal id and removes the
+ // node when it goes out of scope.
+ class ScopedAddToTraversalPath {
+ public:
+ ScopedAddToTraversalPath(TraversalPath& traversalPath, uint32_t layerId,
+ LayerHierarchy::Variant variantArg);
+ ~ScopedAddToTraversalPath();
+
+ private:
+ TraversalPath& mTraversalPath;
+ uint32_t mParentId;
+ LayerHierarchy::Variant mParentVariant;
+ };
+ LayerHierarchy(RequestedLayerState* layer);
+
+ // Visitor function that provides the hierarchy node and a traversal id which uniquely
+ // identifies how was visited. The hierarchy contains a pointer to the RequestedLayerState.
+ // Return false to stop traversing down the hierarchy.
+ typedef std::function<bool(const LayerHierarchy& hierarchy,
+ const LayerHierarchy::TraversalPath& traversalPath)>
+ Visitor;
+
+ // Traverse the hierarchy and visit all child variants.
+ void traverse(const Visitor& visitor) const {
+ traverse(visitor, TraversalPath::ROOT_TRAVERSAL_ID);
+ }
+
+ // Traverse the hierarchy in z-order, skipping children that have relative parents.
+ void traverseInZOrder(const Visitor& visitor) const {
+ traverseInZOrder(visitor, TraversalPath::ROOT_TRAVERSAL_ID);
+ }
+
+ const RequestedLayerState* getLayer() const;
+ std::string getDebugString(const char* prefix = "") const;
+ std::string getDebugStringShort() const;
+ // Traverse the hierarchy and return true if loops are found. The outInvalidRelativeRoot
+ // will contain the first relative root that was visited twice in a traversal.
+ bool hasRelZLoop(uint32_t& outInvalidRelativeRoot) const;
+ std::vector<std::pair<LayerHierarchy*, Variant>> mChildren;
+
+private:
+ friend LayerHierarchyBuilder;
+ LayerHierarchy(const LayerHierarchy& hierarchy, bool childrenOnly);
+ void addChild(LayerHierarchy*, LayerHierarchy::Variant);
+ void removeChild(LayerHierarchy*);
+ void sortChildrenByZOrder();
+ void updateChild(LayerHierarchy*, LayerHierarchy::Variant);
+ void traverseInZOrder(const Visitor& visitor, LayerHierarchy::TraversalPath& parent) const;
+ void traverse(const Visitor& visitor, LayerHierarchy::TraversalPath& parent) const;
+
+ const RequestedLayerState* mLayer;
+ LayerHierarchy* mParent = nullptr;
+ LayerHierarchy* mRelativeParent = nullptr;
+};
+
+// Given a list of RequestedLayerState, this class will build a root hierarchy and an
+// offscreen hierarchy. The builder also has an update method which can update an existing
+// hierarchy from a list of RequestedLayerState and associated change flags.
+class LayerHierarchyBuilder {
+public:
+ LayerHierarchyBuilder(const std::vector<std::unique_ptr<RequestedLayerState>>&);
+ void update(const std::vector<std::unique_ptr<RequestedLayerState>>& layers,
+ const std::vector<std::unique_ptr<RequestedLayerState>>& destroyedLayers);
+ LayerHierarchy getPartialHierarchy(uint32_t, bool childrenOnly) const;
+ const LayerHierarchy& getHierarchy() const;
+ const LayerHierarchy& getOffscreenHierarchy() const;
+ std::string getDebugString(uint32_t layerId, uint32_t depth = 0) const;
+
+private:
+ void onLayerAdded(RequestedLayerState* layer);
+ void attachToParent(LayerHierarchy*);
+ void detachFromParent(LayerHierarchy*);
+ void attachToRelativeParent(LayerHierarchy*);
+ void detachFromRelativeParent(LayerHierarchy*);
+ void attachHierarchyToRelativeParent(LayerHierarchy*);
+ void detachHierarchyFromRelativeParent(LayerHierarchy*);
+
+ void onLayerDestroyed(RequestedLayerState* layer);
+ void updateMirrorLayer(RequestedLayerState* layer);
+ LayerHierarchy* getHierarchyFromId(uint32_t layerId, bool crashOnFailure = true);
+ std::unordered_map<uint32_t, LayerHierarchy*> mLayerIdToHierarchy;
+ std::vector<std::unique_ptr<LayerHierarchy>> mHierarchies;
+ LayerHierarchy mRoot{nullptr};
+ LayerHierarchy mOffscreenRoot{nullptr};
+};
+
+} // namespace android::surfaceflinger::frontend
diff --git a/services/surfaceflinger/FrontEnd/LayerLifecycleManager.cpp b/services/surfaceflinger/FrontEnd/LayerLifecycleManager.cpp
index 7afa144..fdf60b3 100644
--- a/services/surfaceflinger/FrontEnd/LayerLifecycleManager.cpp
+++ b/services/surfaceflinger/FrontEnd/LayerLifecycleManager.cpp
@@ -42,10 +42,10 @@
it->second.owner.getDebugString().c_str());
}
- linkLayer(layer.parentId, layer.id);
- linkLayer(layer.relativeParentId, layer.id);
- linkLayer(layer.mirrorId, layer.id);
- linkLayer(layer.touchCropId, layer.id);
+ layer.parentId = linkLayer(layer.parentId, layer.id);
+ layer.relativeParentId = linkLayer(layer.relativeParentId, layer.id);
+ layer.mirrorId = linkLayer(layer.mirrorId, layer.id);
+ layer.touchCropId = linkLayer(layer.touchCropId, layer.id);
mLayers.emplace_back(std::move(newLayer));
}
@@ -83,10 +83,10 @@
RequestedLayerState& layer = it->second.owner;
- unlinkLayer(layer.parentId, layer.id);
- unlinkLayer(layer.relativeParentId, layer.id);
- unlinkLayer(layer.mirrorId, layer.id);
- unlinkLayer(layer.touchCropId, layer.id);
+ layer.parentId = unlinkLayer(layer.parentId, layer.id);
+ layer.relativeParentId = unlinkLayer(layer.relativeParentId, layer.id);
+ layer.mirrorId = unlinkLayer(layer.mirrorId, layer.id);
+ layer.touchCropId = unlinkLayer(layer.touchCropId, layer.id);
auto& references = it->second.references;
for (uint32_t linkedLayerId : references) {
@@ -199,15 +199,15 @@
if (oldParentId != layer->parentId) {
unlinkLayer(oldParentId, layer->id);
- linkLayer(layer->parentId, layer->id);
+ layer->parentId = linkLayer(layer->parentId, layer->id);
}
if (oldRelativeParentId != layer->relativeParentId) {
unlinkLayer(oldRelativeParentId, layer->id);
- linkLayer(layer->relativeParentId, layer->id);
+ layer->relativeParentId = linkLayer(layer->relativeParentId, layer->id);
}
if (oldTouchCropId != layer->touchCropId) {
unlinkLayer(oldTouchCropId, layer->id);
- linkLayer(layer->touchCropId, layer->id);
+ layer->touchCropId = linkLayer(layer->touchCropId, layer->id);
}
mGlobalChanges |= layer->changes &
@@ -287,26 +287,28 @@
return &it->second.references;
}
-void LayerLifecycleManager::linkLayer(uint32_t layerId, uint32_t layerToLink) {
- if (layerToLink && layerId != UNASSIGNED_LAYER_ID) {
- std::vector<uint32_t>* linkedLayers = getLinkedLayersFromId(layerId);
- if (!linkedLayers) {
- LOG_ALWAYS_FATAL("Could not find layer id %d to link %d", layerId, layerToLink);
- return;
- }
- linkedLayers->emplace_back(layerToLink);
+uint32_t LayerLifecycleManager::linkLayer(uint32_t layerId, uint32_t layerToLink) {
+ if (layerId == UNASSIGNED_LAYER_ID) {
+ return UNASSIGNED_LAYER_ID;
}
-}
-
-void LayerLifecycleManager::unlinkLayer(uint32_t& inOutLayerId, uint32_t linkedLayer) {
- uint32_t layerId = inOutLayerId;
- inOutLayerId = UNASSIGNED_LAYER_ID;
std::vector<uint32_t>* linkedLayers = getLinkedLayersFromId(layerId);
if (!linkedLayers) {
- return;
+ ALOGV("Could not find layer id %d to link %d. Parent is probably destroyed", layerId,
+ layerToLink);
+ return UNASSIGNED_LAYER_ID;
+ }
+ linkedLayers->emplace_back(layerToLink);
+ return layerId;
+}
+
+uint32_t LayerLifecycleManager::unlinkLayer(uint32_t layerId, uint32_t linkedLayer) {
+ std::vector<uint32_t>* linkedLayers = getLinkedLayersFromId(layerId);
+ if (!linkedLayers) {
+ return UNASSIGNED_LAYER_ID;
}
swapErase(*linkedLayers, linkedLayer);
+ return UNASSIGNED_LAYER_ID;
}
std::string LayerLifecycleManager::References::getDebugString() const {
@@ -318,4 +320,16 @@
return debugInfo;
}
+void LayerLifecycleManager::fixRelativeZLoop(uint32_t relativeRootId) {
+ auto it = mIdToLayer.find(relativeRootId);
+ if (it == mIdToLayer.end()) {
+ return;
+ }
+ RequestedLayerState& layer = it->second.owner;
+ layer.relativeParentId = unlinkLayer(layer.relativeParentId, layer.id);
+ layer.changes |=
+ RequestedLayerState::Changes::Hierarchy | RequestedLayerState::Changes::RelativeParent;
+ mGlobalChanges |= RequestedLayerState::Changes::Hierarchy;
+}
+
} // namespace android::surfaceflinger::frontend
diff --git a/services/surfaceflinger/FrontEnd/LayerLifecycleManager.h b/services/surfaceflinger/FrontEnd/LayerLifecycleManager.h
index ad70d3f..63a7afc 100644
--- a/services/surfaceflinger/FrontEnd/LayerLifecycleManager.h
+++ b/services/surfaceflinger/FrontEnd/LayerLifecycleManager.h
@@ -42,6 +42,12 @@
void applyTransactions(const std::vector<TransactionState>&);
void onHandlesDestroyed(const std::vector<uint32_t>&);
+ // Detaches the layer from its relative parent to prevent a loop in the
+ // layer hierarchy. This overrides the RequestedLayerState and leaves
+ // the system in an invalid state. This is always a client error that
+ // needs to be fixed but overriding the state allows us to fail gracefully.
+ void fixRelativeZLoop(uint32_t relativeRootId);
+
// Destroys RequestedLayerStates that are marked to be destroyed. Invokes all
// ILifecycleListener callbacks and clears any change flags from previous state
// updates. This function should be called outside the hot path since it's not
@@ -72,8 +78,8 @@
RequestedLayerState* getLayerFromId(uint32_t);
std::vector<uint32_t>* getLinkedLayersFromId(uint32_t);
- void linkLayer(uint32_t layerId, uint32_t layerToLink);
- void unlinkLayer(uint32_t& inOutLayerId, uint32_t linkedLayer);
+ uint32_t linkLayer(uint32_t layerId, uint32_t layerToLink);
+ uint32_t unlinkLayer(uint32_t layerId, uint32_t linkedLayer);
struct References {
// Lifetime tied to mLayers
diff --git a/services/surfaceflinger/FrontEnd/RequestedLayerState.cpp b/services/surfaceflinger/FrontEnd/RequestedLayerState.cpp
index 45058d9..054382c 100644
--- a/services/surfaceflinger/FrontEnd/RequestedLayerState.cpp
+++ b/services/surfaceflinger/FrontEnd/RequestedLayerState.cpp
@@ -40,7 +40,7 @@
}
std::string layerIdToString(uint32_t layerId) {
- return layerId == UNASSIGNED_LAYER_ID ? std::to_string(layerId) : "none";
+ return layerId == UNASSIGNED_LAYER_ID ? "none" : std::to_string(layerId);
}
} // namespace
@@ -147,13 +147,17 @@
static const mat4 identityMatrix = mat4();
hasColorTransform = colorTransform != identityMatrix;
}
- if (clientState.what & layer_state_t::eLayerChanged) {
+ if (clientState.what & (layer_state_t::eLayerChanged | layer_state_t::eRelativeLayerChanged)) {
changes |= RequestedLayerState::Changes::Z;
}
if (clientState.what & layer_state_t::eReparent) {
changes |= RequestedLayerState::Changes::Parent;
parentId = getLayerIdFromSurfaceControl(clientState.parentSurfaceControlForChild);
parentSurfaceControlForChild = nullptr;
+ // Once a layer has be reparented, it cannot be placed at the root. It sounds odd
+ // but thats the existing logic and until we make this behavior more explicit, we need
+ // to maintain this logic.
+ canBeRoot = false;
}
if (clientState.what & layer_state_t::eRelativeLayerChanged) {
changes |= RequestedLayerState::Changes::RelativeParent;
@@ -190,7 +194,6 @@
if (clientState.what & layer_state_t::eBufferChanged) {
externalTexture = resolvedComposerState.externalTexture;
- hwcBufferSlot = resolvedComposerState.hwcBufferSlot;
}
if (clientState.what & layer_state_t::ePositionChanged) {
@@ -254,7 +257,7 @@
",relativeParent=" + layerIdToString(relativeParentId) +
",isRelativeOf=" + std::to_string(isRelativeOf) +
",mirrorId=" + layerIdToString(mirrorId) +
- ",handleAlive=" + std::to_string(handleAlive);
+ ",handleAlive=" + std::to_string(handleAlive) + ",z=" + std::to_string(z);
}
std::string RequestedLayerState::getDebugStringShort() const {
@@ -355,4 +358,13 @@
return Region(win).subtract(exclude).getBounds();
}
+// Returns true if the layer has a relative parent that is not its own parent. This is an input
+// error from the client, and this check allows us to handle it gracefully. If both parentId and
+// relativeParentId is unassigned then the layer does not have a valid relative parent.
+// If the relative parentid is unassigned, the layer will be considered relative but won't be
+// reachable.
+bool RequestedLayerState::hasValidRelativeParent() const {
+ return isRelativeOf && parentId != relativeParentId;
+}
+
} // namespace android::surfaceflinger::frontend
diff --git a/services/surfaceflinger/FrontEnd/RequestedLayerState.h b/services/surfaceflinger/FrontEnd/RequestedLayerState.h
index 0ddf5e2..7849165 100644
--- a/services/surfaceflinger/FrontEnd/RequestedLayerState.h
+++ b/services/surfaceflinger/FrontEnd/RequestedLayerState.h
@@ -62,13 +62,14 @@
std::string getDebugString() const;
std::string getDebugStringShort() const;
aidl::android::hardware::graphics::composer3::Composition getCompositionType() const;
+ bool hasValidRelativeParent() const;
// Layer serial number. This gives layers an explicit ordering, so we
// have a stable sort order when their layer stack and Z-order are
// the same.
const uint32_t id;
const std::string name;
- const bool canBeRoot = false;
+ bool canBeRoot = false;
const uint32_t layerCreationFlags;
const uint32_t textureName;
// The owner of the layer. If created from a non system process, it will be the calling uid.
@@ -86,7 +87,6 @@
ui::Transform requestedTransform;
std::shared_ptr<FenceTime> acquireFenceTime;
std::shared_ptr<renderengine::ExternalTexture> externalTexture;
- int hwcBufferSlot = 0;
// book keeping states
bool handleAlive = true;
diff --git a/services/surfaceflinger/FrontEnd/TransactionHandler.cpp b/services/surfaceflinger/FrontEnd/TransactionHandler.cpp
index 8629671..c2109b3 100644
--- a/services/surfaceflinger/FrontEnd/TransactionHandler.cpp
+++ b/services/surfaceflinger/FrontEnd/TransactionHandler.cpp
@@ -177,7 +177,7 @@
}
mStalledTransactions.push_back(transactionId);
- listener->onTransactionQueueStalled(String8(reason.c_str()));
+ listener->onTransactionQueueStalled(reason);
}
void TransactionHandler::removeFromStalledTransactions(uint64_t id) {
diff --git a/services/surfaceflinger/FrontEnd/TransactionHandler.h b/services/surfaceflinger/FrontEnd/TransactionHandler.h
index a06b870..475ff1b 100644
--- a/services/surfaceflinger/FrontEnd/TransactionHandler.h
+++ b/services/surfaceflinger/FrontEnd/TransactionHandler.h
@@ -29,6 +29,7 @@
namespace android {
class TestableSurfaceFlinger;
+using gui::IListenerHash;
namespace surfaceflinger::frontend {
class TransactionHandler {
diff --git a/services/surfaceflinger/HwcSlotGenerator.cpp b/services/surfaceflinger/HwcSlotGenerator.cpp
deleted file mode 100644
index 939c35b..0000000
--- a/services/surfaceflinger/HwcSlotGenerator.cpp
+++ /dev/null
@@ -1,106 +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.
- */
-
-#undef LOG_TAG
-#define LOG_TAG "HwcSlotGenerator"
-#define ATRACE_TAG ATRACE_TAG_GRAPHICS
-
-#include <gui/BufferQueue.h>
-
-#include "HwcSlotGenerator.h"
-
-namespace android {
-
-HwcSlotGenerator::HwcSlotGenerator() {
- for (int i = 0; i < BufferQueue::NUM_BUFFER_SLOTS; i++) {
- mFreeHwcCacheSlots.push(i);
- }
-}
-
-void HwcSlotGenerator::bufferErased(const client_cache_t& clientCacheId) {
- std::lock_guard lock(mMutex);
- if (!clientCacheId.isValid()) {
- ALOGE("invalid process, failed to erase buffer");
- return;
- }
- eraseBufferLocked(clientCacheId);
-}
-
-int HwcSlotGenerator::getHwcCacheSlot(const client_cache_t& clientCacheId) {
- std::lock_guard<std::mutex> lock(mMutex);
- auto itr = mCachedBuffers.find(clientCacheId);
- if (itr == mCachedBuffers.end()) {
- return addCachedBuffer(clientCacheId);
- }
- auto& [hwcCacheSlot, counter] = itr->second;
- counter = mCounter++;
- return hwcCacheSlot;
-}
-
-int HwcSlotGenerator::addCachedBuffer(const client_cache_t& clientCacheId) REQUIRES(mMutex) {
- if (!clientCacheId.isValid()) {
- ALOGE("invalid process, returning invalid slot");
- return BufferQueue::INVALID_BUFFER_SLOT;
- }
-
- ClientCache::getInstance().registerErasedRecipient(clientCacheId,
- wp<ErasedRecipient>::fromExisting(this));
-
- int hwcCacheSlot = getFreeHwcCacheSlot();
- mCachedBuffers[clientCacheId] = {hwcCacheSlot, mCounter++};
- return hwcCacheSlot;
-}
-
-int HwcSlotGenerator::getFreeHwcCacheSlot() REQUIRES(mMutex) {
- if (mFreeHwcCacheSlots.empty()) {
- evictLeastRecentlyUsed();
- }
-
- int hwcCacheSlot = mFreeHwcCacheSlots.top();
- mFreeHwcCacheSlots.pop();
- return hwcCacheSlot;
-}
-
-void HwcSlotGenerator::evictLeastRecentlyUsed() REQUIRES(mMutex) {
- uint64_t minCounter = UINT_MAX;
- client_cache_t minClientCacheId = {};
- for (const auto& [clientCacheId, slotCounter] : mCachedBuffers) {
- const auto& [hwcCacheSlot, counter] = slotCounter;
- if (counter < minCounter) {
- minCounter = counter;
- minClientCacheId = clientCacheId;
- }
- }
- eraseBufferLocked(minClientCacheId);
-
- ClientCache::getInstance().unregisterErasedRecipient(minClientCacheId,
- wp<ErasedRecipient>::fromExisting(this));
-}
-
-void HwcSlotGenerator::eraseBufferLocked(const client_cache_t& clientCacheId) REQUIRES(mMutex) {
- auto itr = mCachedBuffers.find(clientCacheId);
- if (itr == mCachedBuffers.end()) {
- return;
- }
- auto& [hwcCacheSlot, counter] = itr->second;
-
- // TODO send to hwc cache and resources
-
- mFreeHwcCacheSlots.push(hwcCacheSlot);
- mCachedBuffers.erase(clientCacheId);
-}
-
-} // namespace android
diff --git a/services/surfaceflinger/HwcSlotGenerator.h b/services/surfaceflinger/HwcSlotGenerator.h
deleted file mode 100644
index 5a1b6d7..0000000
--- a/services/surfaceflinger/HwcSlotGenerator.h
+++ /dev/null
@@ -1,58 +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 <functional>
-#include <mutex>
-#include <stack>
-#include <unordered_map>
-
-#include "ClientCache.h"
-
-namespace android {
-
-class HwcSlotGenerator : public ClientCache::ErasedRecipient {
-public:
- HwcSlotGenerator();
- void bufferErased(const client_cache_t& clientCacheId);
- int getHwcCacheSlot(const client_cache_t& clientCacheId);
-
-private:
- friend class SlotGenerationTest;
- int addCachedBuffer(const client_cache_t& clientCacheId) REQUIRES(mMutex);
- int getFreeHwcCacheSlot() REQUIRES(mMutex);
- void evictLeastRecentlyUsed() REQUIRES(mMutex);
- void eraseBufferLocked(const client_cache_t& clientCacheId) REQUIRES(mMutex);
-
- struct CachedBufferHash {
- std::size_t operator()(const client_cache_t& clientCacheId) const {
- return std::hash<uint64_t>{}(clientCacheId.id);
- }
- };
-
- std::mutex mMutex;
-
- std::unordered_map<client_cache_t, std::pair<int /*HwcCacheSlot*/, uint64_t /*counter*/>,
- CachedBufferHash>
- mCachedBuffers GUARDED_BY(mMutex);
- std::stack<int /*HwcCacheSlot*/> mFreeHwcCacheSlots GUARDED_BY(mMutex);
-
- // The cache increments this counter value when a slot is updated or used.
- // Used to track the least recently-used buffer
- uint64_t mCounter = 0;
-};
-} // namespace android
diff --git a/services/surfaceflinger/Layer.cpp b/services/surfaceflinger/Layer.cpp
index 0017af0..1f159ae 100644
--- a/services/surfaceflinger/Layer.cpp
+++ b/services/surfaceflinger/Layer.cpp
@@ -144,7 +144,6 @@
mLayerCreationFlags(args.flags),
mBorderEnabled(false),
mTextureName(args.textureName),
- mHwcSlotGenerator(sp<HwcSlotGenerator>::make()),
mLayerFE(args.flinger->getFactory().createLayerFE(mName)) {
ALOGV("Creating Layer %s", getDebugName());
@@ -217,6 +216,9 @@
}
Layer::~Layer() {
+ LOG_ALWAYS_FATAL_IF(std::this_thread::get_id() != mFlinger->mMainThreadId,
+ "Layer destructor called off the main thread.");
+
// The original layer and the clone layer share the same texture and buffer. Therefore, only
// one of the layers, in this case the original layer, needs to handle the deletion. The
// original layer and the clone should be removed at the same time so there shouldn't be any
@@ -573,9 +575,6 @@
}
snapshot->buffer = getBuffer();
- snapshot->bufferSlot = (mBufferInfo.mBufferSlot == BufferQueue::INVALID_BUFFER_SLOT)
- ? 0
- : mBufferInfo.mBufferSlot;
snapshot->acquireFence = mBufferInfo.mFence;
snapshot->frameNumber = mBufferInfo.mFrameNumber;
snapshot->sidebandStreamHasFrame = false;
@@ -1472,8 +1471,9 @@
mFrameTracker.getStats(outStats);
}
-void Layer::dumpCallingUidPid(std::string& result) const {
- StringAppendF(&result, "Layer %s (%s) ownerPid:%d ownerUid:%d\n", getName().c_str(), getType(),
+void Layer::dumpOffscreenDebugInfo(std::string& result) const {
+ std::string hasBuffer = hasBufferOrSidebandStream() ? " (contains buffer)" : "";
+ StringAppendF(&result, "Layer %s%s pid:%d uid:%d\n", getName().c_str(), hasBuffer.c_str(),
mOwnerPid, mOwnerUid);
}
@@ -2604,9 +2604,12 @@
return;
}
ATRACE_FORMAT_INSTANT("callReleaseBufferCallback %s - %" PRIu64, getDebugName(), framenumber);
- listener->onReleaseBuffer({buffer->getId(), framenumber},
- releaseFence ? releaseFence : Fence::NO_FENCE,
- currentMaxAcquiredBufferCount);
+ std::optional<os::ParcelFileDescriptor> fenceFd;
+ if (releaseFence) {
+ fenceFd = os::ParcelFileDescriptor(base::unique_fd(::dup(releaseFence->get())));
+ }
+ listener->onReleaseBuffer({buffer->getId(), framenumber}, fenceFd,
+ static_cast<int32_t>(currentMaxAcquiredBufferCount));
}
void Layer::onLayerDisplayed(ftl::SharedFuture<FenceResult> futureFenceResult) {
@@ -2840,7 +2843,7 @@
bool Layer::setBuffer(std::shared_ptr<renderengine::ExternalTexture>& buffer,
const BufferData& bufferData, nsecs_t postTime, nsecs_t desiredPresentTime,
bool isAutoTimestamp, std::optional<nsecs_t> dequeueTime,
- const FrameTimelineInfo& info, int hwcBufferSlot) {
+ const FrameTimelineInfo& info) {
ATRACE_FORMAT("setBuffer %s - hasBuffer=%s", getDebugName(), (buffer ? "true" : "false"));
if (!buffer) {
return false;
@@ -2886,7 +2889,6 @@
mDrawingState.releaseBufferListener = bufferData.releaseBufferListener;
mDrawingState.buffer = std::move(buffer);
mDrawingState.clientCacheId = bufferData.cachedBuffer;
- mDrawingState.hwcBufferSlot = hwcBufferSlot;
mDrawingState.acquireFence = bufferData.flags.test(BufferData::BufferDataChange::fenceChanged)
? bufferData.acquireFence
: Fence::NO_FENCE;
@@ -3185,7 +3187,6 @@
mBufferInfo.mHdrMetadata = mDrawingState.hdrMetadata;
mBufferInfo.mApi = mDrawingState.api;
mBufferInfo.mTransformToDisplayInverse = mDrawingState.transformToDisplayInverse;
- mBufferInfo.mBufferSlot = mDrawingState.hwcBufferSlot;
}
Rect Layer::computeBufferCrop(const State& s) {
@@ -3968,10 +3969,6 @@
}
}
-int Layer::getHwcCacheSlot(const client_cache_t& clientCacheId) {
- return mHwcSlotGenerator->getHwcCacheSlot(clientCacheId);
-}
-
LayerSnapshotGuard::LayerSnapshotGuard(Layer* layer) : mLayer(layer) {
if (mLayer) {
mLayer->mLayerFE->mSnapshot = std::move(mLayer->mSnapshot);
diff --git a/services/surfaceflinger/Layer.h b/services/surfaceflinger/Layer.h
index f743896..08a13a3 100644
--- a/services/surfaceflinger/Layer.h
+++ b/services/surfaceflinger/Layer.h
@@ -51,7 +51,6 @@
#include "Client.h"
#include "DisplayHardware/HWComposer.h"
#include "FrameTracker.h"
-#include "HwcSlotGenerator.h"
#include "LayerFE.h"
#include "LayerVector.h"
#include "Scheduler/LayerInfo.h"
@@ -146,7 +145,6 @@
bool transformToDisplayInverse;
Region transparentRegionHint;
std::shared_ptr<renderengine::ExternalTexture> buffer;
- int hwcBufferSlot;
client_cache_t clientCacheId;
sp<Fence> acquireFence;
std::shared_ptr<FenceTime> acquireFenceTime;
@@ -298,8 +296,7 @@
bool setBuffer(std::shared_ptr<renderengine::ExternalTexture>& /* buffer */,
const BufferData& /* bufferData */, nsecs_t /* postTime */,
nsecs_t /*desiredPresentTime*/, bool /*isAutoTimestamp*/,
- std::optional<nsecs_t> /* dequeueTime */, const FrameTimelineInfo& /*info*/,
- int /* hwcBufferSlot */);
+ std::optional<nsecs_t> /* dequeueTime */, const FrameTimelineInfo& /*info*/);
bool setDataspace(ui::Dataspace /*dataspace*/);
bool setHdrMetadata(const HdrMetadata& /*hdrMetadata*/);
bool setSurfaceDamageRegion(const Region& /*surfaceDamage*/);
@@ -500,7 +497,6 @@
std::shared_ptr<renderengine::ExternalTexture> mBuffer;
uint64_t mFrameNumber;
- int mBufferSlot{BufferQueue::INVALID_BUFFER_SLOT};
bool mFrameLatencyNeeded{false};
};
@@ -634,7 +630,7 @@
void miniDump(std::string& result, const DisplayDevice&) const;
void dumpFrameStats(std::string& result) const;
- void dumpCallingUidPid(std::string& result) const;
+ void dumpOffscreenDebugInfo(std::string& result) const;
void clearFrameStats();
void logFrameStats();
void getFrameStats(FrameStats* outStats) const;
@@ -813,7 +809,6 @@
void updateMetadataSnapshot(const LayerMetadata& parentMetadata);
void updateRelativeMetadataSnapshot(const LayerMetadata& relativeLayerMetadata,
std::unordered_set<Layer*>& visited);
- int getHwcCacheSlot(const client_cache_t& clientCacheId);
protected:
// For unit tests
@@ -1124,8 +1119,6 @@
// not specify a destination frame.
ui::Transform mRequestedTransform;
- sp<HwcSlotGenerator> mHwcSlotGenerator;
-
sp<LayerFE> mLayerFE;
std::unique_ptr<LayerSnapshot> mSnapshot = std::make_unique<LayerSnapshot>();
diff --git a/services/surfaceflinger/RefreshRateOverlay.cpp b/services/surfaceflinger/RefreshRateOverlay.cpp
index 9b19afb..7aa7e17 100644
--- a/services/surfaceflinger/RefreshRateOverlay.cpp
+++ b/services/surfaceflinger/RefreshRateOverlay.cpp
@@ -42,8 +42,8 @@
constexpr int kDigitHeight = 100;
constexpr int kDigitSpace = 16;
-// Layout is digit, space, digit, space, digit, space, spinner.
-constexpr int kBufferWidth = 4 * kDigitWidth + 3 * kDigitSpace;
+constexpr int kMaxDigits = /*displayFps*/ 3 + /*renderFps*/ 3 + /*spinner*/ 1;
+constexpr int kBufferWidth = kMaxDigits * kDigitWidth + (kMaxDigits - 1) * kDigitSpace;
constexpr int kBufferHeight = kDigitHeight;
SurfaceComposerClient::Transaction createTransaction(const sp<SurfaceControl>& surface) {
@@ -121,16 +121,10 @@
drawSegment(Segment::Bottom, left, color, canvas);
}
-auto RefreshRateOverlay::SevenSegmentDrawer::draw(int number, SkColor color,
+auto RefreshRateOverlay::SevenSegmentDrawer::draw(int displayFps, int renderFps, SkColor color,
ui::Transform::RotationFlags rotation,
- bool showSpinner) -> Buffers {
- if (number < 0 || number > 1000) return {};
-
- const auto hundreds = number / 100;
- const auto tens = (number / 10) % 10;
- const auto ones = number % 10;
-
- const size_t loopCount = showSpinner ? 6 : 1;
+ ftl::Flags<Features> features) -> Buffers {
+ const size_t loopCount = features.test(Features::Spinner) ? 6 : 1;
Buffers buffers;
buffers.reserve(loopCount);
@@ -169,20 +163,9 @@
canvas->setMatrix(canvasTransform);
int left = 0;
- if (hundreds != 0) {
- drawDigit(hundreds, left, color, *canvas);
- }
- left += kDigitWidth + kDigitSpace;
-
- if (tens != 0) {
- drawDigit(tens, left, color, *canvas);
- }
- left += kDigitWidth + kDigitSpace;
-
- drawDigit(ones, left, color, *canvas);
- left += kDigitWidth + kDigitSpace;
-
- if (showSpinner) {
+ drawNumber(displayFps, left, color, *canvas);
+ left += 3 * (kDigitWidth + kDigitSpace);
+ if (features.test(Features::Spinner)) {
switch (i) {
case 0:
drawSegment(Segment::Upper, left, color, *canvas);
@@ -205,6 +188,13 @@
}
}
+ left += kDigitWidth + kDigitSpace;
+
+ if (features.test(Features::RenderRate)) {
+ drawNumber(renderFps, left, color, *canvas);
+ }
+ left += 3 * (kDigitWidth + kDigitSpace);
+
void* pixels = nullptr;
buffer->lock(GRALLOC_USAGE_SW_WRITE_RARELY, reinterpret_cast<void**>(&pixels));
@@ -219,6 +209,23 @@
return buffers;
}
+void RefreshRateOverlay::SevenSegmentDrawer::drawNumber(int number, int left, SkColor color,
+ SkCanvas& canvas) {
+ if (number < 0 || number >= 1000) return;
+
+ if (number >= 100) {
+ drawDigit(number / 100, left, color, canvas);
+ }
+ left += kDigitWidth + kDigitSpace;
+
+ if (number >= 10) {
+ drawDigit((number / 10) % 10, left, color, canvas);
+ }
+ left += kDigitWidth + kDigitSpace;
+
+ drawDigit(number % 10, left, color, canvas);
+}
+
std::unique_ptr<SurfaceControlHolder> createSurfaceControlHolder() {
sp<SurfaceControl> surfaceControl =
SurfaceComposerClient::getDefault()
@@ -228,10 +235,8 @@
return std::make_unique<SurfaceControlHolder>(std::move(surfaceControl));
}
-RefreshRateOverlay::RefreshRateOverlay(FpsRange fpsRange, bool showSpinner)
- : mFpsRange(fpsRange),
- mShowSpinner(showSpinner),
- mSurfaceControl(createSurfaceControlHolder()) {
+RefreshRateOverlay::RefreshRateOverlay(FpsRange fpsRange, ftl::Flags<Features> features)
+ : mFpsRange(fpsRange), mFeatures(features), mSurfaceControl(createSurfaceControlHolder()) {
if (!mSurfaceControl) {
ALOGE("%s: Failed to create buffer state layer", __func__);
return;
@@ -243,10 +248,15 @@
.apply();
}
-auto RefreshRateOverlay::getOrCreateBuffers(Fps fps) -> const Buffers& {
+auto RefreshRateOverlay::getOrCreateBuffers(Fps displayFps, Fps renderFps) -> const Buffers& {
static const Buffers kNoBuffers;
if (!mSurfaceControl) return kNoBuffers;
+ // avoid caching different render rates if RenderRate is anyway not visible
+ if (!mFeatures.test(Features::RenderRate)) {
+ renderFps = 0_Hz;
+ }
+
const auto transformHint =
static_cast<ui::Transform::RotationFlags>(mSurfaceControl->get()->getTransformHint());
@@ -266,17 +276,20 @@
.setTransform(mSurfaceControl->get(), transform)
.apply();
- BufferCache::const_iterator it = mBufferCache.find({fps.getIntValue(), transformHint});
+ BufferCache::const_iterator it =
+ mBufferCache.find({displayFps.getIntValue(), renderFps.getIntValue(), transformHint});
if (it == mBufferCache.end()) {
const int minFps = mFpsRange.min.getIntValue();
const int maxFps = mFpsRange.max.getIntValue();
- // Clamp to the range. The current fps may be outside of this range if the display has
- // changed its set of supported refresh rates.
- const int intFps = std::clamp(fps.getIntValue(), minFps, maxFps);
+ // Clamp to the range. The current displayFps may be outside of this range if the display
+ // has changed its set of supported refresh rates.
+ const int displayIntFps = std::clamp(displayFps.getIntValue(), minFps, maxFps);
+ const int renderIntFps = renderFps.getIntValue();
// Ensure non-zero range to avoid division by zero.
- const float fpsScale = static_cast<float>(intFps - minFps) / std::max(1, maxFps - minFps);
+ const float fpsScale =
+ static_cast<float>(displayIntFps - minFps) / std::max(1, maxFps - minFps);
constexpr SkColor kMinFpsColor = SK_ColorRED;
constexpr SkColor kMaxFpsColor = SK_ColorGREEN;
@@ -292,8 +305,11 @@
const SkColor color = colorBase.toSkColor();
- auto buffers = SevenSegmentDrawer::draw(intFps, color, transformHint, mShowSpinner);
- it = mBufferCache.try_emplace({intFps, transformHint}, std::move(buffers)).first;
+ auto buffers = SevenSegmentDrawer::draw(displayIntFps, renderIntFps, color, transformHint,
+ mFeatures);
+ it = mBufferCache
+ .try_emplace({displayIntFps, renderIntFps, transformHint}, std::move(buffers))
+ .first;
}
return it->second;
@@ -303,7 +319,7 @@
constexpr int32_t kMaxWidth = 1000;
const auto width = std::min({kMaxWidth, viewport.width, viewport.height});
const auto height = 2 * width;
- Rect frame((3 * width) >> 4, height >> 5);
+ Rect frame((5 * width) >> 4, height >> 5);
frame.offsetBy(width >> 5, height >> 4);
createTransaction(mSurfaceControl->get())
@@ -317,16 +333,17 @@
createTransaction(mSurfaceControl->get()).setLayerStack(mSurfaceControl->get(), stack).apply();
}
-void RefreshRateOverlay::changeRefreshRate(Fps fps) {
- mCurrentFps = fps;
- const auto buffer = getOrCreateBuffers(fps)[mFrame];
+void RefreshRateOverlay::changeRefreshRate(Fps displayFps, Fps renderFps) {
+ mDisplayFps = displayFps;
+ mRenderFps = renderFps;
+ const auto buffer = getOrCreateBuffers(displayFps, renderFps)[mFrame];
createTransaction(mSurfaceControl->get()).setBuffer(mSurfaceControl->get(), buffer).apply();
}
void RefreshRateOverlay::animate() {
- if (!mShowSpinner || !mCurrentFps) return;
+ if (!mFeatures.test(Features::Spinner) || !mDisplayFps) return;
- const auto& buffers = getOrCreateBuffers(*mCurrentFps);
+ const auto& buffers = getOrCreateBuffers(*mDisplayFps, *mRenderFps);
mFrame = (mFrame + 1) % buffers.size();
const auto buffer = buffers[mFrame];
createTransaction(mSurfaceControl->get()).setBuffer(mSurfaceControl->get(), buffer).apply();
diff --git a/services/surfaceflinger/RefreshRateOverlay.h b/services/surfaceflinger/RefreshRateOverlay.h
index a2966e6..d6f828f 100644
--- a/services/surfaceflinger/RefreshRateOverlay.h
+++ b/services/surfaceflinger/RefreshRateOverlay.h
@@ -19,6 +19,7 @@
#include <SkColor.h>
#include <vector>
+#include <ftl/flags.h>
#include <ftl/small_map.h>
#include <ui/LayerStack.h>
#include <ui/Size.h>
@@ -50,11 +51,16 @@
class RefreshRateOverlay {
public:
- RefreshRateOverlay(FpsRange, bool showSpinner);
+ enum class Features {
+ Spinner = 1 << 0,
+ RenderRate = 1 << 1,
+ };
+
+ RefreshRateOverlay(FpsRange, ftl::Flags<Features>);
void setLayerStack(ui::LayerStack);
void setViewport(ui::Size);
- void changeRefreshRate(Fps);
+ void changeRefreshRate(Fps, Fps);
void animate();
private:
@@ -62,32 +68,39 @@
class SevenSegmentDrawer {
public:
- static Buffers draw(int number, SkColor, ui::Transform::RotationFlags, bool showSpinner);
+ static Buffers draw(int displayFps, int renderFps, SkColor, ui::Transform::RotationFlags,
+ ftl::Flags<Features>);
private:
enum class Segment { Upper, UpperLeft, UpperRight, Middle, LowerLeft, LowerRight, Bottom };
static void drawSegment(Segment, int left, SkColor, SkCanvas&);
static void drawDigit(int digit, int left, SkColor, SkCanvas&);
+ static void drawNumber(int number, int left, SkColor, SkCanvas&);
};
- const Buffers& getOrCreateBuffers(Fps);
+ const Buffers& getOrCreateBuffers(Fps, Fps);
struct Key {
- int fps;
+ int displayFps;
+ int renderFps;
ui::Transform::RotationFlags flags;
- bool operator==(Key other) const { return fps == other.fps && flags == other.flags; }
+ bool operator==(Key other) const {
+ return displayFps == other.displayFps && renderFps == other.renderFps &&
+ flags == other.flags;
+ }
};
using BufferCache = ftl::SmallMap<Key, Buffers, 9>;
BufferCache mBufferCache;
- std::optional<Fps> mCurrentFps;
+ std::optional<Fps> mDisplayFps;
+ std::optional<Fps> mRenderFps;
size_t mFrame = 0;
const FpsRange mFpsRange; // For color interpolation.
- const bool mShowSpinner;
+ const ftl::Flags<Features> mFeatures;
const std::unique_ptr<SurfaceControlHolder> mSurfaceControl;
};
diff --git a/services/surfaceflinger/Scheduler/EventThread.cpp b/services/surfaceflinger/Scheduler/EventThread.cpp
index 1acf15a..008d8c4 100644
--- a/services/surfaceflinger/Scheduler/EventThread.cpp
+++ b/services/surfaceflinger/Scheduler/EventThread.cpp
@@ -124,12 +124,12 @@
return event;
}
-DisplayEventReceiver::Event makeModeChanged(DisplayModePtr mode) {
+DisplayEventReceiver::Event makeModeChanged(const scheduler::FrameRateMode& mode) {
DisplayEventReceiver::Event event;
- event.header = {DisplayEventReceiver::DISPLAY_EVENT_MODE_CHANGE, mode->getPhysicalDisplayId(),
- systemTime()};
- event.modeChange.modeId = mode->getId().value();
- event.modeChange.vsyncPeriod = mode->getVsyncPeriod();
+ event.header = {DisplayEventReceiver::DISPLAY_EVENT_MODE_CHANGE,
+ mode.modePtr->getPhysicalDisplayId(), systemTime()};
+ event.modeChange.modeId = mode.modePtr->getId().value();
+ event.modeChange.vsyncPeriod = mode.fps.getPeriodNsecs();
return event;
}
@@ -405,7 +405,7 @@
mCondition.notify_all();
}
-void EventThread::onModeChanged(DisplayModePtr mode) {
+void EventThread::onModeChanged(const scheduler::FrameRateMode& mode) {
std::lock_guard<std::mutex> lock(mMutex);
mPendingEvents.push_back(makeModeChanged(mode));
diff --git a/services/surfaceflinger/Scheduler/EventThread.h b/services/surfaceflinger/Scheduler/EventThread.h
index 7a5a348..43c3598 100644
--- a/services/surfaceflinger/Scheduler/EventThread.h
+++ b/services/surfaceflinger/Scheduler/EventThread.h
@@ -23,6 +23,7 @@
#include <sys/types.h>
#include <utils/Errors.h>
+#include <scheduler/FrameRateMode.h>
#include <condition_variable>
#include <cstdint>
#include <deque>
@@ -134,7 +135,7 @@
virtual void onHotplugReceived(PhysicalDisplayId displayId, bool connected) = 0;
// called when SF changes the active mode and apps needs to be notified about the change
- virtual void onModeChanged(DisplayModePtr) = 0;
+ virtual void onModeChanged(const scheduler::FrameRateMode&) = 0;
// called when SF updates the Frame Rate Override list
virtual void onFrameRateOverridesChanged(PhysicalDisplayId displayId,
@@ -185,7 +186,7 @@
void onHotplugReceived(PhysicalDisplayId displayId, bool connected) override;
- void onModeChanged(DisplayModePtr) override;
+ void onModeChanged(const scheduler::FrameRateMode&) override;
void onFrameRateOverridesChanged(PhysicalDisplayId displayId,
std::vector<FrameRateOverride> overrides) override;
diff --git a/services/surfaceflinger/Scheduler/RefreshRateSelector.cpp b/services/surfaceflinger/Scheduler/RefreshRateSelector.cpp
index 4be1ac7..a05d3df 100644
--- a/services/surfaceflinger/Scheduler/RefreshRateSelector.cpp
+++ b/services/surfaceflinger/Scheduler/RefreshRateSelector.cpp
@@ -107,7 +107,8 @@
}
using fps_approx_ops::operator/;
- const auto start = std::max(1u, fps / range.max - 1);
+ // use signed type as `fps / range.max` might be 0
+ const auto start = std::max(1, static_cast<int>(fps / range.max) - 1);
const auto end = fps /
std::max(range.min, RefreshRateSelector::kMinSupportedFrameRate,
fps_approx_ops::operator<);
@@ -836,21 +837,25 @@
return frameRateOverrides;
}
-std::optional<Fps> RefreshRateSelector::onKernelTimerChanged(
+ftl::Optional<FrameRateMode> RefreshRateSelector::onKernelTimerChanged(
std::optional<DisplayModeId> desiredActiveModeId, bool timerExpired) const {
std::lock_guard lock(mLock);
- const DisplayModePtr& current = desiredActiveModeId
- ? mDisplayModes.get(*desiredActiveModeId)->get()
- : getActiveModeLocked().modePtr.get();
+ const auto current = [&]() REQUIRES(mLock) -> FrameRateMode {
+ if (desiredActiveModeId) {
+ const auto& modePtr = mDisplayModes.get(*desiredActiveModeId)->get();
+ return FrameRateMode{modePtr->getFps(), ftl::as_non_null(modePtr)};
+ }
+
+ return getActiveModeLocked();
+ }();
const DisplayModePtr& min = mMinRefreshRateModeIt->second;
- if (current == min) {
+ if (current.modePtr->getId() == min->getId()) {
return {};
}
- const auto& mode = timerExpired ? min : current;
- return mode->getFps();
+ return timerExpired ? FrameRateMode{min->getFps(), ftl::as_non_null(min)} : current;
}
const DisplayModePtr& RefreshRateSelector::getMinRefreshRateByPolicyLocked() const {
@@ -1053,8 +1058,12 @@
const auto& primaryRanges = policy.primaryRanges;
const auto& appRequestRanges = policy.appRequestRanges;
ALOGE_IF(!appRequestRanges.physical.includes(primaryRanges.physical),
- "Physical range is invalid");
- ALOGE_IF(!appRequestRanges.render.includes(primaryRanges.render), "Render range is invalid");
+ "Physical range is invalid: primary: %s appRequest: %s",
+ to_string(primaryRanges.physical).c_str(),
+ to_string(appRequestRanges.physical).c_str());
+ ALOGE_IF(!appRequestRanges.render.includes(primaryRanges.render),
+ "Render range is invalid: primary: %s appRequest: %s",
+ to_string(primaryRanges.render).c_str(), to_string(appRequestRanges.render).c_str());
return primaryRanges.valid() && appRequestRanges.valid();
}
@@ -1156,8 +1165,8 @@
const auto frameRateModes = createFrameRateModes(filterModes, ranges.render);
LOG_ALWAYS_FATAL_IF(frameRateModes.empty(),
- "No matching frame rate modes for %s physicalRange %s", rangeName,
- to_string(ranges.physical).c_str());
+ "No matching frame rate modes for %s range. policy: %s", rangeName,
+ policy->toString().c_str());
const auto stringifyModes = [&] {
std::string str;
diff --git a/services/surfaceflinger/Scheduler/RefreshRateSelector.h b/services/surfaceflinger/Scheduler/RefreshRateSelector.h
index 1ed16c6..4f5842a 100644
--- a/services/surfaceflinger/Scheduler/RefreshRateSelector.h
+++ b/services/surfaceflinger/Scheduler/RefreshRateSelector.h
@@ -240,8 +240,9 @@
return {mMinRefreshRateModeIt->second->getFps(), mMaxRefreshRateModeIt->second->getFps()};
}
- std::optional<Fps> onKernelTimerChanged(std::optional<DisplayModeId> desiredActiveModeId,
- bool timerExpired) const EXCLUDES(mLock);
+ ftl::Optional<FrameRateMode> onKernelTimerChanged(
+ std::optional<DisplayModeId> desiredActiveModeId, bool timerExpired) const
+ EXCLUDES(mLock);
void setActiveMode(DisplayModeId, Fps renderFrameRate) EXCLUDES(mLock);
diff --git a/services/surfaceflinger/Scheduler/Scheduler.cpp b/services/surfaceflinger/Scheduler/Scheduler.cpp
index 7f8f600..34f8df2 100644
--- a/services/surfaceflinger/Scheduler/Scheduler.cpp
+++ b/services/surfaceflinger/Scheduler/Scheduler.cpp
@@ -330,7 +330,7 @@
RETURN_IF_INVALID_HANDLE(handle);
thread = mConnections[handle].thread.get();
}
- thread->onModeChanged(mode.modePtr.get());
+ thread->onModeChanged(mode);
}
size_t Scheduler::getEventThreadConnectionCount(ConnectionHandle handle) {
diff --git a/services/surfaceflinger/SurfaceFlinger.cpp b/services/surfaceflinger/SurfaceFlinger.cpp
index 365ffb7..cdd6044 100644
--- a/services/surfaceflinger/SurfaceFlinger.cpp
+++ b/services/surfaceflinger/SurfaceFlinger.cpp
@@ -112,6 +112,7 @@
#include <ui/DisplayIdentification.h>
#include "BackgroundExecutor.h"
#include "Client.h"
+#include "ClientCache.h"
#include "Colorizer.h"
#include "Display/DisplayMap.h"
#include "DisplayDevice.h"
@@ -198,6 +199,9 @@
namespace {
+static constexpr int FOUR_K_WIDTH = 3840;
+static constexpr int FOUR_K_HEIGHT = 2160;
+
// TODO(b/141333600): Consolidate with DisplayMode::Builder::getDefaultDensity.
constexpr float FALLBACK_DENSITY = ACONFIGURATION_DENSITY_TV;
@@ -245,6 +249,44 @@
return displaySupportKernelIdleTimer || sysprop::support_kernel_idle_timer(false);
}
+bool isAbove4k30(const ui::DisplayMode& outMode) {
+ using fps_approx_ops::operator>;
+ Fps refreshRate = Fps::fromValue(outMode.refreshRate);
+ return outMode.resolution.getWidth() >= FOUR_K_WIDTH &&
+ outMode.resolution.getHeight() >= FOUR_K_HEIGHT && refreshRate > 30_Hz;
+}
+
+void excludeDolbyVisionIf4k30Present(const std::vector<ui::Hdr>& displayHdrTypes,
+ ui::DisplayMode& outMode) {
+ if (isAbove4k30(outMode) &&
+ std::any_of(displayHdrTypes.begin(), displayHdrTypes.end(),
+ [](ui::Hdr type) { return type == ui::Hdr::DOLBY_VISION_4K30; })) {
+ for (ui::Hdr type : displayHdrTypes) {
+ if (type != ui::Hdr::DOLBY_VISION_4K30 && type != ui::Hdr::DOLBY_VISION) {
+ outMode.supportedHdrTypes.push_back(type);
+ }
+ }
+ } else {
+ for (ui::Hdr type : displayHdrTypes) {
+ if (type != ui::Hdr::DOLBY_VISION_4K30) {
+ outMode.supportedHdrTypes.push_back(type);
+ }
+ }
+ }
+}
+
+HdrCapabilities filterOut4k30(const HdrCapabilities& displayHdrCapabilities) {
+ std::vector<ui::Hdr> hdrTypes;
+ for (ui::Hdr type : displayHdrCapabilities.getSupportedHdrTypes()) {
+ if (type != ui::Hdr::DOLBY_VISION_4K30) {
+ hdrTypes.push_back(type);
+ }
+ }
+ return {hdrTypes, displayHdrCapabilities.getDesiredMaxLuminance(),
+ displayHdrCapabilities.getDesiredMaxAverageLuminance(),
+ displayHdrCapabilities.getDesiredMinLuminance()};
+}
+
} // namespace anonymous
// ---------------------------------------------------------------------------
@@ -422,7 +464,9 @@
android::hardware::details::setTrebleTestingOverride(true);
}
- mRefreshRateOverlaySpinner = property_get_bool("sf.debug.show_refresh_rate_overlay_spinner", 0);
+ mRefreshRateOverlaySpinner = property_get_bool("debug.sf.show_refresh_rate_overlay_spinner", 0);
+ mRefreshRateOverlayRenderRate =
+ property_get_bool("debug.sf.show_refresh_rate_overlay_render_rate", 0);
if (!mIsUserBuild && base::GetBoolProperty("debug.sf.enable_transaction_tracing"s, true)) {
mTransactionTracing.emplace();
@@ -931,17 +975,14 @@
return NO_ERROR;
}
-status_t SurfaceFlinger::getStaticDisplayInfo(const sp<IBinder>& displayToken,
- ui::StaticDisplayInfo* info) {
- if (!displayToken || !info) {
+status_t SurfaceFlinger::getStaticDisplayInfo(int64_t displayId, ui::StaticDisplayInfo* info) {
+ if (!info) {
return BAD_VALUE;
}
Mutex::Autolock lock(mStateLock);
-
- const auto displayOpt = ftl::find_if(mPhysicalDisplays, PhysicalDisplay::hasToken(displayToken))
- .transform(&ftl::to_mapped_ref<PhysicalDisplays>)
- .and_then(getDisplayDeviceAndSnapshot());
+ const auto id = DisplayId::fromValue<PhysicalDisplayId>(static_cast<uint64_t>(displayId));
+ const auto displayOpt = mPhysicalDisplays.get(*id).and_then(getDisplayDeviceAndSnapshot());
if (!displayOpt) {
return NAME_NOT_FOUND;
@@ -968,26 +1009,10 @@
return NO_ERROR;
}
-status_t SurfaceFlinger::getDynamicDisplayInfo(const sp<IBinder>& displayToken,
- ui::DynamicDisplayInfo* info) {
- if (!displayToken || !info) {
- return BAD_VALUE;
- }
-
- Mutex::Autolock lock(mStateLock);
-
- const auto displayOpt = ftl::find_if(mPhysicalDisplays, PhysicalDisplay::hasToken(displayToken))
- .transform(&ftl::to_mapped_ref<PhysicalDisplays>)
- .and_then(getDisplayDeviceAndSnapshot());
- if (!displayOpt) {
- return NAME_NOT_FOUND;
- }
-
- const auto& [display, snapshotRef] = *displayOpt;
- const auto& snapshot = snapshotRef.get();
-
+void SurfaceFlinger::getDynamicDisplayInfoInternal(ui::DynamicDisplayInfo*& info,
+ const sp<DisplayDevice>& display,
+ const display::DisplaySnapshot& snapshot) {
const auto& displayModes = snapshot.displayModes();
-
info->supportedDisplayModes.clear();
info->supportedDisplayModes.reserve(displayModes.size());
@@ -1031,7 +1056,8 @@
// We add an additional 1ms to allow for processing time and
// differences between the ideal and actual refresh rate.
outMode.presentationDeadline = period - outMode.sfVsyncOffset + 1000000;
-
+ excludeDolbyVisionIf4k30Present(display->getHdrCapabilities().getSupportedHdrTypes(),
+ outMode);
info->supportedDisplayModes.push_back(outMode);
}
@@ -1039,10 +1065,11 @@
const PhysicalDisplayId displayId = snapshot.displayId();
- info->activeDisplayModeId =
- display->refreshRateSelector().getActiveMode().modePtr->getId().value();
+ const auto mode = display->refreshRateSelector().getActiveMode();
+ info->activeDisplayModeId = mode.modePtr->getId().value();
+ info->renderFrameRate = mode.fps.getValue();
info->activeColorMode = display->getCompositionDisplay()->getState().colorMode;
- info->hdrCapabilities = display->getHdrCapabilities();
+ info->hdrCapabilities = filterOut4k30(display->getHdrCapabilities());
info->autoLowLatencyModeSupported =
getHwComposer().hasDisplayCapability(displayId,
@@ -1059,7 +1086,47 @@
}
}
}
+}
+status_t SurfaceFlinger::getDynamicDisplayInfoFromId(int64_t physicalDisplayId,
+ ui::DynamicDisplayInfo* info) {
+ if (!info) {
+ return BAD_VALUE;
+ }
+
+ Mutex::Autolock lock(mStateLock);
+
+ const auto id_ =
+ DisplayId::fromValue<PhysicalDisplayId>(static_cast<uint64_t>(physicalDisplayId));
+ const auto displayOpt = mPhysicalDisplays.get(*id_).and_then(getDisplayDeviceAndSnapshot());
+
+ if (!displayOpt) {
+ return NAME_NOT_FOUND;
+ }
+
+ const auto& [display, snapshotRef] = *displayOpt;
+ getDynamicDisplayInfoInternal(info, display, snapshotRef.get());
+ return NO_ERROR;
+}
+
+status_t SurfaceFlinger::getDynamicDisplayInfoFromToken(const sp<IBinder>& displayToken,
+ ui::DynamicDisplayInfo* info) {
+ if (!displayToken || !info) {
+ return BAD_VALUE;
+ }
+
+ Mutex::Autolock lock(mStateLock);
+
+ const auto displayOpt = ftl::find_if(mPhysicalDisplays, PhysicalDisplay::hasToken(displayToken))
+ .transform(&ftl::to_mapped_ref<PhysicalDisplays>)
+ .and_then(getDisplayDeviceAndSnapshot());
+
+ if (!displayOpt) {
+ return NAME_NOT_FOUND;
+ }
+
+ const auto& [display, snapshotRef] = *displayOpt;
+ getDynamicDisplayInfoInternal(info, display, snapshotRef.get());
return NO_ERROR;
}
@@ -1083,8 +1150,8 @@
return;
}
- const Fps renderFps = request.mode.fps;
- const Fps displayFps = request.mode.modePtr->getFps();
+ const auto mode = request.mode;
+ const bool emitEvent = request.emitEvent;
switch (display->setDesiredActiveMode(DisplayDevice::ActiveModeInfo(std::move(request)))) {
case DisplayDevice::DesiredActiveModeAction::InitiateDisplayModeSwitch:
@@ -1092,21 +1159,22 @@
// Start receiving vsync samples now, so that we can detect a period
// switch.
- mScheduler->resyncToHardwareVsync(true, displayFps);
+ mScheduler->resyncToHardwareVsync(true, mode.modePtr->getFps());
// As we called to set period, we will call to onRefreshRateChangeCompleted once
// VsyncController model is locked.
modulateVsync(&VsyncModulator::onRefreshRateChangeInitiated);
- updatePhaseConfiguration(renderFps);
+ updatePhaseConfiguration(mode.fps);
mScheduler->setModeChangePending(true);
break;
case DisplayDevice::DesiredActiveModeAction::InitiateRenderRateSwitch:
- mScheduler->setRenderRate(renderFps);
- updatePhaseConfiguration(renderFps);
- mRefreshRateStats->setRefreshRate(renderFps);
+ mScheduler->setRenderRate(mode.fps);
+ updatePhaseConfiguration(mode.fps);
+ mRefreshRateStats->setRefreshRate(mode.fps);
+ if (display->getPhysicalId() == mActiveDisplayId && emitEvent) {
+ mScheduler->onPrimaryDisplayModeChanged(mAppConnectionHandle, mode);
+ }
- // TODO(b/259740021): send event to display manager about
- // the render rate change
break;
case DisplayDevice::DesiredActiveModeAction::None:
break;
@@ -1512,7 +1580,8 @@
return NO_ERROR;
}
-status_t SurfaceFlinger::onPullAtom(const int32_t atomId, std::string* pulledData, bool* success) {
+status_t SurfaceFlinger::onPullAtom(const int32_t atomId, std::vector<uint8_t>* pulledData,
+ bool* success) {
*success = mTimeStats->onPullAtom(atomId, pulledData);
return NO_ERROR;
}
@@ -2211,6 +2280,8 @@
});
}
+ refreshArgs.bufferIdsToUncache = std::move(mBufferIdsToUncache);
+
refreshArgs.layersWithQueuedFrames.reserve(mLayersWithQueuedFrames.size());
for (auto layer : mLayersWithQueuedFrames) {
if (auto layerFE = layer->getCompositionEngineLayerFE())
@@ -2830,7 +2901,7 @@
return Config::FrameRateOverride::AppOverrideNativeRefreshRates;
}
- if (!base::GetBoolProperty("debug.sf.frame_rate_override_global"s, false)) {
+ if (!sysprop::frame_rate_override_global(false)) {
return Config::FrameRateOverride::AppOverride;
}
@@ -3955,10 +4026,6 @@
getExternalTextureFromBufferData(*resolvedState.state.bufferData,
layerName.c_str(), transactionId);
mBufferCountTracker.increment(resolvedState.state.surface->localBinder());
- if (layer) {
- resolvedState.hwcBufferSlot =
- layer->getHwcCacheSlot(resolvedState.state.bufferData->cachedBuffer);
- }
}
}
@@ -3990,7 +4057,7 @@
bool SurfaceFlinger::applyTransactionState(const FrameTimelineInfo& frameTimelineInfo,
std::vector<ResolvedComposerState>& states,
- const Vector<DisplayState>& displays, uint32_t flags,
+ Vector<DisplayState>& displays, uint32_t flags,
const InputWindowCommands& inputWindowCommands,
const int64_t desiredPresentTime, bool isAutoTimestamp,
const client_cache_t& uncacheBuffer,
@@ -3999,7 +4066,8 @@
const std::vector<ListenerCallbacks>& listenerCallbacks,
int originPid, int originUid, uint64_t transactionId) {
uint32_t transactionFlags = 0;
- for (const DisplayState& display : displays) {
+ for (DisplayState& display : displays) {
+ display.sanitize(permissions);
transactionFlags |= setDisplayStateLocked(display);
}
@@ -4034,7 +4102,10 @@
}
if (uncacheBuffer.isValid()) {
- ClientCache::getInstance().erase(uncacheBuffer);
+ sp<GraphicBuffer> buffer = ClientCache::getInstance().erase(uncacheBuffer);
+ if (buffer != nullptr) {
+ mBufferIdsToUncache.push_back(buffer->getId());
+ }
}
// If a synchronous transaction is explicitly requested without any changes, force a transaction
@@ -4425,7 +4496,7 @@
if (what & layer_state_t::eBufferChanged) {
if (layer->setBuffer(composerState.externalTexture, *s.bufferData, postTime,
desiredPresentTime, isAutoTimestamp, dequeueBufferTimestamp,
- frameTimelineInfo, composerState.hwcBufferSlot)) {
+ frameTimelineInfo)) {
flags |= eTraversalNeeded;
}
} else if (frameTimelineInfo.vsyncId != FrameTimelineInfo::INVALID_VSYNC_ID) {
@@ -4685,19 +4756,34 @@
.value_or(false);
const auto activeDisplay = getDisplayDeviceLocked(mActiveDisplayId);
- if (isInternalDisplay && activeDisplay != display && activeDisplay &&
- activeDisplay->isPoweredOn()) {
- ALOGW("Trying to change power mode on non active display while the active display is ON");
- }
+
+ ALOGW_IF(display != activeDisplay && isInternalDisplay && activeDisplay &&
+ activeDisplay->isPoweredOn(),
+ "Trying to change power mode on inactive display without powering off active display");
display->setPowerMode(mode);
const auto refreshRate = display->refreshRateSelector().getActiveMode().modePtr->getFps();
if (!currentModeOpt || *currentModeOpt == hal::PowerMode::OFF) {
// Turn on the display
- if (isInternalDisplay && (!activeDisplay || !activeDisplay->isPoweredOn())) {
- onActiveDisplayChangedLocked(display);
+
+ // Activate the display (which involves a modeset to the active mode):
+ // 1) When the first (a.k.a. primary) display is powered on during boot.
+ // 2) When the inner or outer display of a foldable is powered on. This condition relies
+ // on the above DisplayDevice::setPowerMode. If `display` and `activeDisplay` are the
+ // same display, then the `activeDisplay->isPoweredOn()` below is true, such that the
+ // display is not activated every time it is powered on.
+ //
+ // TODO(b/255635821): Remove the concept of active display.
+ const bool activeDisplayChanged =
+ isInternalDisplay && (!activeDisplay || !activeDisplay->isPoweredOn());
+
+ static bool sPrimaryDisplay = true;
+ if (sPrimaryDisplay || activeDisplayChanged) {
+ onActiveDisplayChangedLocked(activeDisplay, display);
+ sPrimaryDisplay = false;
}
+
// Keep uclamp in a separate syscall and set it before changing to RT due to b/190237315.
// We can merge the syscall later.
if (SurfaceFlinger::setSchedAttr(true) != NO_ERROR) {
@@ -5150,7 +5236,7 @@
std::string result;
for (Layer* offscreenLayer : mOffscreenLayers) {
offscreenLayer->traverse(LayerVector::StateSet::Drawing,
- [&](Layer* layer) { layer->dumpCallingUidPid(result); });
+ [&](Layer* layer) { layer->dumpOffscreenDebugInfo(result); });
}
return result;
});
@@ -6882,7 +6968,8 @@
for (const auto& [id, display] : mPhysicalDisplays) {
if (display.snapshot().connectionType() == ui::DisplayConnectionType::Internal) {
if (const auto device = getDisplayDeviceLocked(id)) {
- device->enableRefreshRateOverlay(enable, mRefreshRateOverlaySpinner);
+ device->enableRefreshRateOverlay(enable, mRefreshRateOverlaySpinner,
+ mRefreshRateOverlayRenderRate);
}
}
}
@@ -6995,17 +7082,14 @@
getRenderEngine().onActiveDisplaySizeChanged(activeDisplay->getSize());
}
-void SurfaceFlinger::onActiveDisplayChangedLocked(const sp<DisplayDevice>& activeDisplay) {
+void SurfaceFlinger::onActiveDisplayChangedLocked(const sp<DisplayDevice>& inactiveDisplay,
+ const sp<DisplayDevice>& activeDisplay) {
ATRACE_CALL();
- if (const auto display = getDisplayDeviceLocked(mActiveDisplayId)) {
- display->getCompositionDisplay()->setLayerCachingTexturePoolEnabled(false);
+ if (inactiveDisplay) {
+ inactiveDisplay->getCompositionDisplay()->setLayerCachingTexturePoolEnabled(false);
}
- if (!activeDisplay) {
- ALOGE("%s: activeDisplay is null", __func__);
- return;
- }
mActiveDisplayId = activeDisplay->getPhysicalId();
activeDisplay->getCompositionDisplay()->setLayerCachingTexturePoolEnabled(true);
@@ -7046,8 +7130,7 @@
layerName, static_cast<uint32_t>(mMaxRenderTargetSize));
ALOGD("%s", errorMessage.c_str());
if (bufferData.releaseBufferListener) {
- bufferData.releaseBufferListener->onTransactionQueueStalled(
- String8(errorMessage.c_str()));
+ bufferData.releaseBufferListener->onTransactionQueueStalled(errorMessage);
}
return nullptr;
}
@@ -7065,7 +7148,7 @@
if (bufferData.releaseBufferListener) {
bufferData.releaseBufferListener->onTransactionQueueStalled(
- String8("Buffer processing hung due to full buffer cache"));
+ "Buffer processing hung due to full buffer cache");
}
}
@@ -7239,6 +7322,10 @@
binder::Status SurfaceComposerAIDL::getPhysicalDisplayToken(int64_t displayId,
sp<IBinder>* outDisplay) {
+ status_t status = checkAccessPermission();
+ if (status != OK) {
+ return binderStatusFromStatusT(status);
+ }
const auto id = DisplayId::fromValue<PhysicalDisplayId>(static_cast<uint64_t>(displayId));
*outDisplay = mFlinger->getPhysicalDisplayToken(*id);
return binder::Status::ok();
@@ -7289,11 +7376,12 @@
return binderStatusFromStatusT(status);
}
-binder::Status SurfaceComposerAIDL::getStaticDisplayInfo(const sp<IBinder>& display,
+binder::Status SurfaceComposerAIDL::getStaticDisplayInfo(int64_t displayId,
gui::StaticDisplayInfo* outInfo) {
using Tag = gui::DeviceProductInfo::ManufactureOrModelDate::Tag;
ui::StaticDisplayInfo info;
- status_t status = mFlinger->getStaticDisplayInfo(display, &info);
+
+ status_t status = mFlinger->getStaticDisplayInfo(displayId, &info);
if (status == NO_ERROR) {
// convert ui::StaticDisplayInfo to gui::StaticDisplayInfo
outInfo->connectionType = static_cast<gui::DisplayConnectionType>(info.connectionType);
@@ -7332,53 +7420,71 @@
return binderStatusFromStatusT(status);
}
-binder::Status SurfaceComposerAIDL::getDynamicDisplayInfo(const sp<IBinder>& display,
- gui::DynamicDisplayInfo* outInfo) {
+void SurfaceComposerAIDL::getDynamicDisplayInfoInternal(ui::DynamicDisplayInfo& info,
+ gui::DynamicDisplayInfo*& outInfo) {
+ // convert ui::DynamicDisplayInfo to gui::DynamicDisplayInfo
+ outInfo->supportedDisplayModes.clear();
+ outInfo->supportedDisplayModes.reserve(info.supportedDisplayModes.size());
+ for (const auto& mode : info.supportedDisplayModes) {
+ gui::DisplayMode outMode;
+ outMode.id = mode.id;
+ outMode.resolution.width = mode.resolution.width;
+ outMode.resolution.height = mode.resolution.height;
+ outMode.xDpi = mode.xDpi;
+ outMode.yDpi = mode.yDpi;
+ outMode.refreshRate = mode.refreshRate;
+ outMode.appVsyncOffset = mode.appVsyncOffset;
+ outMode.sfVsyncOffset = mode.sfVsyncOffset;
+ outMode.presentationDeadline = mode.presentationDeadline;
+ outMode.group = mode.group;
+ std::transform(mode.supportedHdrTypes.begin(), mode.supportedHdrTypes.end(),
+ std::back_inserter(outMode.supportedHdrTypes),
+ [](const ui::Hdr& value) { return static_cast<int32_t>(value); });
+ outInfo->supportedDisplayModes.push_back(outMode);
+ }
+
+ outInfo->activeDisplayModeId = info.activeDisplayModeId;
+ outInfo->renderFrameRate = info.renderFrameRate;
+
+ outInfo->supportedColorModes.clear();
+ outInfo->supportedColorModes.reserve(info.supportedColorModes.size());
+ for (const auto& cmode : info.supportedColorModes) {
+ outInfo->supportedColorModes.push_back(static_cast<int32_t>(cmode));
+ }
+
+ outInfo->activeColorMode = static_cast<int32_t>(info.activeColorMode);
+
+ gui::HdrCapabilities& hdrCapabilities = outInfo->hdrCapabilities;
+ hdrCapabilities.supportedHdrTypes.clear();
+ hdrCapabilities.supportedHdrTypes.reserve(info.hdrCapabilities.getSupportedHdrTypes().size());
+ for (const auto& hdr : info.hdrCapabilities.getSupportedHdrTypes()) {
+ hdrCapabilities.supportedHdrTypes.push_back(static_cast<int32_t>(hdr));
+ }
+ hdrCapabilities.maxLuminance = info.hdrCapabilities.getDesiredMaxLuminance();
+ hdrCapabilities.maxAverageLuminance = info.hdrCapabilities.getDesiredMaxAverageLuminance();
+ hdrCapabilities.minLuminance = info.hdrCapabilities.getDesiredMinLuminance();
+
+ outInfo->autoLowLatencyModeSupported = info.autoLowLatencyModeSupported;
+ outInfo->gameContentTypeSupported = info.gameContentTypeSupported;
+ outInfo->preferredBootDisplayMode = info.preferredBootDisplayMode;
+}
+
+binder::Status SurfaceComposerAIDL::getDynamicDisplayInfoFromToken(
+ const sp<IBinder>& display, gui::DynamicDisplayInfo* outInfo) {
ui::DynamicDisplayInfo info;
- status_t status = mFlinger->getDynamicDisplayInfo(display, &info);
+ status_t status = mFlinger->getDynamicDisplayInfoFromToken(display, &info);
if (status == NO_ERROR) {
- // convert ui::DynamicDisplayInfo to gui::DynamicDisplayInfo
- outInfo->supportedDisplayModes.clear();
- outInfo->supportedDisplayModes.reserve(info.supportedDisplayModes.size());
- for (const auto& mode : info.supportedDisplayModes) {
- gui::DisplayMode outMode;
- outMode.id = mode.id;
- outMode.resolution.width = mode.resolution.width;
- outMode.resolution.height = mode.resolution.height;
- outMode.xDpi = mode.xDpi;
- outMode.yDpi = mode.yDpi;
- outMode.refreshRate = mode.refreshRate;
- outMode.appVsyncOffset = mode.appVsyncOffset;
- outMode.sfVsyncOffset = mode.sfVsyncOffset;
- outMode.presentationDeadline = mode.presentationDeadline;
- outMode.group = mode.group;
- outInfo->supportedDisplayModes.push_back(outMode);
- }
+ getDynamicDisplayInfoInternal(info, outInfo);
+ }
+ return binderStatusFromStatusT(status);
+}
- outInfo->activeDisplayModeId = info.activeDisplayModeId;
-
- outInfo->supportedColorModes.clear();
- outInfo->supportedColorModes.reserve(info.supportedColorModes.size());
- for (const auto& cmode : info.supportedColorModes) {
- outInfo->supportedColorModes.push_back(static_cast<int32_t>(cmode));
- }
-
- outInfo->activeColorMode = static_cast<int32_t>(info.activeColorMode);
-
- gui::HdrCapabilities& hdrCapabilities = outInfo->hdrCapabilities;
- hdrCapabilities.supportedHdrTypes.clear();
- hdrCapabilities.supportedHdrTypes.reserve(
- info.hdrCapabilities.getSupportedHdrTypes().size());
- for (const auto& hdr : info.hdrCapabilities.getSupportedHdrTypes()) {
- hdrCapabilities.supportedHdrTypes.push_back(static_cast<int32_t>(hdr));
- }
- hdrCapabilities.maxLuminance = info.hdrCapabilities.getDesiredMaxLuminance();
- hdrCapabilities.maxAverageLuminance = info.hdrCapabilities.getDesiredMaxAverageLuminance();
- hdrCapabilities.minLuminance = info.hdrCapabilities.getDesiredMinLuminance();
-
- outInfo->autoLowLatencyModeSupported = info.autoLowLatencyModeSupported;
- outInfo->gameContentTypeSupported = info.gameContentTypeSupported;
- outInfo->preferredBootDisplayMode = info.preferredBootDisplayMode;
+binder::Status SurfaceComposerAIDL::getDynamicDisplayInfoFromId(int64_t displayId,
+ gui::DynamicDisplayInfo* outInfo) {
+ ui::DynamicDisplayInfo info;
+ status_t status = mFlinger->getDynamicDisplayInfoFromId(displayId, &info);
+ if (status == NO_ERROR) {
+ getDynamicDisplayInfoInternal(info, outInfo);
}
return binderStatusFromStatusT(status);
}
diff --git a/services/surfaceflinger/SurfaceFlinger.h b/services/surfaceflinger/SurfaceFlinger.h
index e3217a3..c957b67 100644
--- a/services/surfaceflinger/SurfaceFlinger.h
+++ b/services/surfaceflinger/SurfaceFlinger.h
@@ -26,6 +26,7 @@
#include <android/gui/DisplayStatInfo.h>
#include <android/gui/DisplayState.h>
#include <android/gui/ISurfaceComposerClient.h>
+#include <android/gui/ITransactionCompletedListener.h>
#include <cutils/atomic.h>
#include <cutils/compiler.h>
#include <ftl/future.h>
@@ -34,8 +35,8 @@
#include <gui/CompositorTiming.h>
#include <gui/FrameTimestamps.h>
#include <gui/ISurfaceComposer.h>
-#include <gui/ITransactionCompletedListener.h>
#include <gui/LayerDebugInfo.h>
+
#include <gui/LayerState.h>
#include <layerproto/LayerProtoHeader.h>
#include <math/mat4.h>
@@ -96,6 +97,7 @@
#include <unordered_map>
#include <unordered_set>
#include <utility>
+#include <vector>
#include <aidl/android/hardware/graphics/common/DisplayDecorationSupport.h>
#include "Client.h"
@@ -125,7 +127,9 @@
using gui::CaptureArgs;
using gui::DisplayCaptureArgs;
using gui::IRegionSamplingListener;
+using gui::ITransactionCompletedListener;
using gui::LayerCaptureArgs;
+
using gui::ScreenCaptureResults;
namespace frametimeline {
@@ -512,10 +516,13 @@
status_t getDisplayStats(const sp<IBinder>& displayToken, DisplayStatInfo* stats);
status_t getDisplayState(const sp<IBinder>& displayToken, ui::DisplayState*)
EXCLUDES(mStateLock);
- status_t getStaticDisplayInfo(const sp<IBinder>& displayToken, ui::StaticDisplayInfo*)
+ status_t getStaticDisplayInfo(int64_t displayId, ui::StaticDisplayInfo*) EXCLUDES(mStateLock);
+ status_t getDynamicDisplayInfoFromId(int64_t displayId, ui::DynamicDisplayInfo*)
EXCLUDES(mStateLock);
- status_t getDynamicDisplayInfo(const sp<IBinder>& displayToken, ui::DynamicDisplayInfo*)
- EXCLUDES(mStateLock);
+ status_t getDynamicDisplayInfoFromToken(const sp<IBinder>& displayToken,
+ ui::DynamicDisplayInfo*) EXCLUDES(mStateLock);
+ void getDynamicDisplayInfoInternal(ui::DynamicDisplayInfo*&, const sp<DisplayDevice>&,
+ const display::DisplaySnapshot&);
status_t getDisplayNativePrimaries(const sp<IBinder>& displayToken, ui::DisplayPrimaries&);
status_t setActiveColorMode(const sp<IBinder>& displayToken, ui::ColorMode colorMode);
status_t getBootDisplayModeSupport(bool* outSupport) const;
@@ -527,7 +534,7 @@
void setPowerMode(const sp<IBinder>& displayToken, int mode);
status_t overrideHdrTypes(const sp<IBinder>& displayToken,
const std::vector<ui::Hdr>& hdrTypes);
- status_t onPullAtom(const int32_t atomId, std::string* pulledData, bool* success);
+ status_t onPullAtom(const int32_t atomId, std::vector<uint8_t>* pulledData, bool* success);
status_t getLayerDebugInfo(std::vector<gui::LayerDebugInfo>* outLayers);
status_t getColorManagement(bool* outGetColorManagement) const;
status_t getCompositionPreference(ui::Dataspace* outDataspace, ui::PixelFormat* outPixelFormat,
@@ -639,6 +646,8 @@
bool mKernelIdleTimerEnabled = false;
// Show spinner with refresh rate overlay
bool mRefreshRateOverlaySpinner = false;
+ // Show render rate with refresh rate overlay
+ bool mRefreshRateOverlayRenderRate = false;
void setDesiredActiveMode(display::DisplayModeRequest&&) REQUIRES(mStateLock);
@@ -700,7 +709,7 @@
*/
bool applyTransactionState(const FrameTimelineInfo& info,
std::vector<ResolvedComposerState>& state,
- const Vector<DisplayState>& displays, uint32_t flags,
+ Vector<DisplayState>& displays, uint32_t flags,
const InputWindowCommands& inputWindowCommands,
const int64_t desiredPresentTime, bool isAutoTimestamp,
const client_cache_t& uncacheBuffer, const int64_t postTime,
@@ -1000,7 +1009,10 @@
VirtualDisplayId acquireVirtualDisplay(ui::Size, ui::PixelFormat) REQUIRES(mStateLock);
void releaseVirtualDisplay(VirtualDisplayId);
- void onActiveDisplayChangedLocked(const sp<DisplayDevice>& activeDisplay)
+ // TODO(b/255635821): Replace pointers with references. `inactiveDisplay` is only ever `nullptr`
+ // in tests, and `activeDisplay` must not be `nullptr` as a precondition.
+ void onActiveDisplayChangedLocked(const sp<DisplayDevice>& inactiveDisplay,
+ const sp<DisplayDevice>& activeDisplay)
REQUIRES(mStateLock, kMainThreadContext);
void onActiveDisplaySizeChanged(const sp<const DisplayDevice>&);
@@ -1089,6 +1101,10 @@
std::atomic<uint32_t> mUniqueTransactionId = 1;
SortedVector<sp<Layer>> mLayersPendingRemoval;
+ // Buffers that have been discarded by clients and need to be evicted from per-layer caches so
+ // the graphics memory can be immediately freed.
+ std::vector<uint64_t> mBufferIdsToUncache;
+
// global color transform states
Daltonizer mDaltonizer;
float mGlobalSaturationFactor = 1.0f;
@@ -1396,10 +1412,12 @@
gui::DisplayStatInfo* outStatInfo) override;
binder::Status getDisplayState(const sp<IBinder>& display,
gui::DisplayState* outState) override;
- binder::Status getStaticDisplayInfo(const sp<IBinder>& display,
+ binder::Status getStaticDisplayInfo(int64_t displayId,
gui::StaticDisplayInfo* outInfo) override;
- binder::Status getDynamicDisplayInfo(const sp<IBinder>& display,
- gui::DynamicDisplayInfo* outInfo) override;
+ binder::Status getDynamicDisplayInfoFromId(int64_t displayId,
+ gui::DynamicDisplayInfo* outInfo) override;
+ binder::Status getDynamicDisplayInfoFromToken(const sp<IBinder>& display,
+ gui::DynamicDisplayInfo* outInfo) override;
binder::Status getDisplayNativePrimaries(const sp<IBinder>& display,
gui::DisplayPrimaries* outPrimaries) override;
binder::Status setActiveColorMode(const sp<IBinder>& display, int colorMode) override;
@@ -1484,6 +1502,8 @@
status_t checkAccessPermission(bool usePermissionCache = kUsePermissionCache);
status_t checkControlDisplayBrightnessPermission();
status_t checkReadFrameBufferPermission();
+ static void getDynamicDisplayInfoInternal(ui::DynamicDisplayInfo& info,
+ gui::DynamicDisplayInfo*& outInfo);
private:
sp<SurfaceFlinger> mFlinger;
diff --git a/services/surfaceflinger/SurfaceFlingerProperties.cpp b/services/surfaceflinger/SurfaceFlingerProperties.cpp
index c8c71df..5b73030 100644
--- a/services/surfaceflinger/SurfaceFlingerProperties.cpp
+++ b/services/surfaceflinger/SurfaceFlingerProperties.cpp
@@ -371,6 +371,10 @@
return SurfaceFlingerProperties::frame_rate_override_for_native_rates().value_or(defaultValue);
}
+bool frame_rate_override_global(bool defaultValue) {
+ return SurfaceFlingerProperties::frame_rate_override_global().value_or(defaultValue);
+}
+
bool enable_layer_caching(bool defaultValue) {
return SurfaceFlingerProperties::enable_layer_caching().value_or(defaultValue);
}
diff --git a/services/surfaceflinger/SurfaceFlingerProperties.h b/services/surfaceflinger/SurfaceFlingerProperties.h
index 5e316cf..09629cf 100644
--- a/services/surfaceflinger/SurfaceFlingerProperties.h
+++ b/services/surfaceflinger/SurfaceFlingerProperties.h
@@ -98,6 +98,8 @@
bool frame_rate_override_for_native_rates(bool defaultValue);
+bool frame_rate_override_global(bool defaultValue);
+
bool enable_layer_caching(bool defaultValue);
bool enable_sdr_dimming(bool defaultValue);
diff --git a/services/surfaceflinger/TimeStats/TimeStats.cpp b/services/surfaceflinger/TimeStats/TimeStats.cpp
index e860d88..630cef1 100644
--- a/services/surfaceflinger/TimeStats/TimeStats.cpp
+++ b/services/surfaceflinger/TimeStats/TimeStats.cpp
@@ -27,6 +27,7 @@
#include <algorithm>
#include <chrono>
+#include <cmath>
#include <unordered_map>
#include "TimeStats.h"
@@ -90,7 +91,7 @@
}
} // namespace
-bool TimeStats::populateGlobalAtom(std::string* pulledData) {
+bool TimeStats::populateGlobalAtom(std::vector<uint8_t>* pulledData) {
std::lock_guard<std::mutex> lock(mMutex);
if (mTimeStats.statsStartLegacy == 0) {
@@ -138,10 +139,11 @@
// Always clear data.
clearGlobalLocked();
- return atomList.SerializeToString(pulledData);
+ pulledData->resize(atomList.ByteSizeLong());
+ return atomList.SerializeToArray(pulledData->data(), atomList.ByteSizeLong());
}
-bool TimeStats::populateLayerAtom(std::string* pulledData) {
+bool TimeStats::populateLayerAtom(std::vector<uint8_t>* pulledData) {
std::lock_guard<std::mutex> lock(mMutex);
std::vector<TimeStatsHelper::TimeStatsLayer*> dumpStats;
@@ -179,6 +181,12 @@
*atom->mutable_present_to_present() =
histogramToProto(present2PresentHist->second.hist, mMaxPulledHistogramBuckets);
}
+ const auto& present2PresentDeltaHist = layer->deltas.find("present2presentDelta");
+ if (present2PresentDeltaHist != layer->deltas.cend()) {
+ *atom->mutable_present_to_present_delta() =
+ histogramToProto(present2PresentDeltaHist->second.hist,
+ mMaxPulledHistogramBuckets);
+ }
const auto& post2presentHist = layer->deltas.find("post2present");
if (post2presentHist != layer->deltas.cend()) {
*atom->mutable_post_to_present() =
@@ -229,7 +237,8 @@
// Always clear data.
clearLayersLocked();
- return atomList.SerializeToString(pulledData);
+ pulledData->resize(atomList.ByteSizeLong());
+ return atomList.SerializeToArray(pulledData->data(), atomList.ByteSizeLong());
}
TimeStats::TimeStats() : TimeStats(std::nullopt, std::nullopt) {}
@@ -245,7 +254,7 @@
}
}
-bool TimeStats::onPullAtom(const int atomId, std::string* pulledData) {
+bool TimeStats::onPullAtom(const int atomId, std::vector<uint8_t>* pulledData) {
bool success = false;
if (atomId == 10062) { // SURFACEFLINGER_STATS_GLOBAL_INFO
success = populateGlobalAtom(pulledData);
@@ -450,6 +459,7 @@
LayerRecord& layerRecord = mTimeStatsTracker[layerId];
TimeRecord& prevTimeRecord = layerRecord.prevTimeRecord;
+ std::optional<int32_t>& prevPresentToPresentMs = layerRecord.prevPresentToPresentMs;
std::deque<TimeRecord>& timeRecords = layerRecord.timeRecords;
const int32_t refreshRateBucket =
clampToNearestBucket(displayRefreshRate, REFRESH_RATE_BUCKET_WIDTH);
@@ -527,6 +537,12 @@
ALOGV("[%d]-[%" PRIu64 "]-present2present[%d]", layerId,
timeRecords[0].frameTime.frameNumber, presentToPresentMs);
timeStatsLayer.deltas["present2present"].insert(presentToPresentMs);
+ if (prevPresentToPresentMs) {
+ const int32_t presentToPresentDeltaMs =
+ std::abs(presentToPresentMs - *prevPresentToPresentMs);
+ timeStatsLayer.deltas["present2presentDelta"].insert(presentToPresentDeltaMs);
+ }
+ prevPresentToPresentMs = presentToPresentMs;
}
prevTimeRecord = timeRecords[0];
timeRecords.pop_front();
diff --git a/services/surfaceflinger/TimeStats/TimeStats.h b/services/surfaceflinger/TimeStats/TimeStats.h
index 61d7c22..5f58657 100644
--- a/services/surfaceflinger/TimeStats/TimeStats.h
+++ b/services/surfaceflinger/TimeStats/TimeStats.h
@@ -47,7 +47,7 @@
virtual ~TimeStats() = default;
// Process a pull request from statsd.
- virtual bool onPullAtom(const int atomId, std::string* pulledData) = 0;
+ virtual bool onPullAtom(const int atomId, std::vector<uint8_t>* pulledData) = 0;
virtual void parseArgs(bool asProto, const Vector<String16>& args, std::string& result) = 0;
virtual bool isEnabled() = 0;
@@ -219,6 +219,7 @@
uint32_t lateAcquireFrames = 0;
uint32_t badDesiredPresentFrames = 0;
TimeRecord prevTimeRecord;
+ std::optional<int32_t> prevPresentToPresentMs;
std::deque<TimeRecord> timeRecords;
};
@@ -244,7 +245,7 @@
TimeStats(std::optional<size_t> maxPulledLayers,
std::optional<size_t> maxPulledHistogramBuckets);
- bool onPullAtom(const int atomId, std::string* pulledData) override;
+ bool onPullAtom(const int atomId, std::vector<uint8_t>* pulledData) override;
void parseArgs(bool asProto, const Vector<String16>& args, std::string& result) override;
bool isEnabled() override;
std::string miniDump() override;
@@ -292,8 +293,8 @@
static const size_t MAX_NUM_TIME_RECORDS = 64;
private:
- bool populateGlobalAtom(std::string* pulledData);
- bool populateLayerAtom(std::string* pulledData);
+ bool populateGlobalAtom(std::vector<uint8_t>* pulledData);
+ bool populateLayerAtom(std::vector<uint8_t>* pulledData);
bool recordReadyLocked(int32_t layerId, TimeRecord* timeRecord);
void flushAvailableRecordsToStatsLocked(int32_t layerId, Fps displayRefreshRate,
std::optional<Fps> renderRate, SetFrameRateVote,
diff --git a/services/surfaceflinger/TimeStats/timestatsatomsproto/timestats_atoms.proto b/services/surfaceflinger/TimeStats/timestatsatomsproto/timestats_atoms.proto
index d4d444e..8615947 100644
--- a/services/surfaceflinger/TimeStats/timestatsatomsproto/timestats_atoms.proto
+++ b/services/surfaceflinger/TimeStats/timestatsatomsproto/timestats_atoms.proto
@@ -289,7 +289,11 @@
// Introduced in Android 12.
optional FrameTimingHistogram app_deadline_misses = 25;
- // Next ID: 27
+ // Variability histogram of present_to_present timings.
+ // Introduced in Android 14.
+ optional FrameTimingHistogram present_to_present_delta = 27;
+
+ // Next ID: 28
}
/**
diff --git a/services/surfaceflinger/TransactionCallbackInvoker.h b/services/surfaceflinger/TransactionCallbackInvoker.h
index 61ff9bc..c09bcce 100644
--- a/services/surfaceflinger/TransactionCallbackInvoker.h
+++ b/services/surfaceflinger/TransactionCallbackInvoker.h
@@ -26,14 +26,27 @@
#include <unordered_set>
#include <android-base/thread_annotations.h>
+#include <android/gui/ITransactionCompletedListener.h>
+
#include <binder/IBinder.h>
-#include <ftl/future.h>
-#include <gui/ITransactionCompletedListener.h>
+#include <gui/ListenerStats.h>
+#include <gui/ReleaseCallbackId.h>
+#include <renderengine/RenderEngine.h>
#include <ui/Fence.h>
#include <ui/FenceResult.h>
namespace android {
+using gui::CallbackId;
+using gui::FrameEventHistoryStats;
+using gui::IListenerHash;
+using gui::ITransactionCompletedListener;
+using gui::JankData;
+using gui::ListenerCallbacks;
+using gui::ListenerStats;
+using gui::ReleaseCallbackId;
+using gui::TransactionStats;
+
class CallbackHandle : public RefBase {
public:
CallbackHandle(const sp<IBinder>& transactionListener, const std::vector<CallbackId>& ids,
diff --git a/services/surfaceflinger/TransactionState.h b/services/surfaceflinger/TransactionState.h
index 7bde2c1..380301f 100644
--- a/services/surfaceflinger/TransactionState.h
+++ b/services/surfaceflinger/TransactionState.h
@@ -27,13 +27,12 @@
namespace android {
-// Extends the client side composer state by resolving buffer cache ids.
+// Extends the client side composer state by resolving buffer.
class ResolvedComposerState : public ComposerState {
public:
ResolvedComposerState() = default;
ResolvedComposerState(ComposerState&& source) { state = std::move(source.state); }
std::shared_ptr<renderengine::ExternalTexture> externalTexture;
- int hwcBufferSlot = 0;
};
struct TransactionState {
diff --git a/services/surfaceflinger/fuzzer/surfaceflinger_fuzzers_utils.h b/services/surfaceflinger/fuzzer/surfaceflinger_fuzzers_utils.h
index dbfce78..81ca659 100644
--- a/services/surfaceflinger/fuzzer/surfaceflinger_fuzzers_utils.h
+++ b/services/surfaceflinger/fuzzer/surfaceflinger_fuzzers_utils.h
@@ -81,7 +81,6 @@
using types::V1_1::RenderIntent;
using types::V1_2::ColorMode;
using types::V1_2::Dataspace;
-using types::V1_2::Hdr;
using types::V1_2::PixelFormat;
using V2_1::Config;
@@ -420,7 +419,7 @@
void onPullAtom(FuzzedDataProvider *fdp) {
const int32_t atomId = fdp->ConsumeIntegral<uint8_t>();
- std::string pulledData = fdp->ConsumeRandomLengthString().c_str();
+ std::vector<uint8_t> pulledData = fdp->ConsumeRemainingBytes<uint8_t>();
bool success = fdp->ConsumeBool();
mFlinger->onPullAtom(atomId, &pulledData, &success);
}
@@ -492,14 +491,14 @@
mFlinger->getDisplayState(display, &displayState);
}
- void getStaticDisplayInfo(sp<IBinder> &display) {
+ void getStaticDisplayInfo(int64_t displayId) {
ui::StaticDisplayInfo staticDisplayInfo;
- mFlinger->getStaticDisplayInfo(display, &staticDisplayInfo);
+ mFlinger->getStaticDisplayInfo(displayId, &staticDisplayInfo);
}
- void getDynamicDisplayInfo(sp<IBinder> &display) {
+ void getDynamicDisplayInfo(int64_t displayId) {
android::ui::DynamicDisplayInfo dynamicDisplayInfo;
- mFlinger->getDynamicDisplayInfo(display, &dynamicDisplayInfo);
+ mFlinger->getDynamicDisplayInfoFromId(displayId, &dynamicDisplayInfo);
}
void getDisplayNativePrimaries(sp<IBinder> &display) {
android::ui::DisplayPrimaries displayPrimaries;
@@ -523,7 +522,7 @@
return ids.front();
}
- sp<IBinder> fuzzBoot(FuzzedDataProvider *fdp) {
+ std::pair<sp<IBinder>, int64_t> fuzzBoot(FuzzedDataProvider *fdp) {
mFlinger->callingThreadHasUnscopedSurfaceFlingerAccess(fdp->ConsumeBool());
const sp<Client> client = sp<Client>::make(mFlinger);
@@ -550,13 +549,13 @@
mFlinger->bootFinished();
- return display;
+ return {display, physicalDisplayId.value};
}
void fuzzSurfaceFlinger(const uint8_t *data, size_t size) {
FuzzedDataProvider mFdp(data, size);
- sp<IBinder> display = fuzzBoot(&mFdp);
+ auto [display, displayId] = fuzzBoot(&mFdp);
sp<IGraphicBufferProducer> bufferProducer = sp<mock::GraphicBufferProducer>::make();
@@ -564,8 +563,8 @@
getDisplayStats(display);
getDisplayState(display);
- getStaticDisplayInfo(display);
- getDynamicDisplayInfo(display);
+ getStaticDisplayInfo(displayId);
+ getDynamicDisplayInfo(displayId);
getDisplayNativePrimaries(display);
mFlinger->setAutoLowLatencyMode(display, mFdp.ConsumeBool());
diff --git a/services/surfaceflinger/fuzzer/surfaceflinger_layer_fuzzer.cpp b/services/surfaceflinger/fuzzer/surfaceflinger_layer_fuzzer.cpp
index c5b3fa6..acfc1d4 100644
--- a/services/surfaceflinger/fuzzer/surfaceflinger_layer_fuzzer.cpp
+++ b/services/surfaceflinger/fuzzer/surfaceflinger_layer_fuzzer.cpp
@@ -160,7 +160,7 @@
layer->setBuffer(texture, {} /*bufferData*/, mFdp.ConsumeIntegral<nsecs_t>() /*postTime*/,
mFdp.ConsumeIntegral<nsecs_t>() /*desiredTime*/,
mFdp.ConsumeBool() /*isAutoTimestamp*/,
- {mFdp.ConsumeIntegral<nsecs_t>()} /*dequeue*/, {} /*info*/, 0 /* hwcslot */);
+ {mFdp.ConsumeIntegral<nsecs_t>()} /*dequeue*/, {} /*info*/);
LayerRenderArea layerArea(*(flinger.flinger()), layer, getFuzzedRect(),
{mFdp.ConsumeIntegral<int32_t>(),
diff --git a/services/surfaceflinger/sysprop/SurfaceFlingerProperties.sysprop b/services/surfaceflinger/sysprop/SurfaceFlingerProperties.sysprop
index 28da81f..8540c3d 100644
--- a/services/surfaceflinger/sysprop/SurfaceFlingerProperties.sysprop
+++ b/services/surfaceflinger/sysprop/SurfaceFlingerProperties.sysprop
@@ -447,8 +447,8 @@
# Limits the frame rate override feature (enable_frame_rate_override) to override the refresh rate
# to native display refresh rates only. Before introducing this flag, native display refresh rates
-# was the default behvaiour. With this flag we can control which behaviour we want explicitly.
-# This flag is intoruduced as a fail-safe machanism and planned to be defaulted to false.
+# was the default behaviour. With this flag we can control which behaviour we want explicitly.
+# This flag is introduced as a fail-safe mechanism and planned to be defaulted to false.
prop {
api_name: "frame_rate_override_for_native_rates"
type: Boolean
@@ -457,6 +457,16 @@
prop_name: "ro.surface_flinger.frame_rate_override_for_native_rates"
}
+# Enables the frame rate override feature (enable_frame_rate_override) to
+# override the frame rate globally instead of only for individual apps.
+prop {
+ api_name: "frame_rate_override_global"
+ type: Boolean
+ scope: Public
+ access: Readonly
+ prop_name: "ro.surface_flinger.frame_rate_override_global"
+}
+
# Enables Layer Caching
prop {
api_name: "enable_layer_caching"
diff --git a/services/surfaceflinger/sysprop/api/SurfaceFlingerProperties-current.txt b/services/surfaceflinger/sysprop/api/SurfaceFlingerProperties-current.txt
index 0dfb80e..9338133 100644
--- a/services/surfaceflinger/sysprop/api/SurfaceFlingerProperties-current.txt
+++ b/services/surfaceflinger/sysprop/api/SurfaceFlingerProperties-current.txt
@@ -65,6 +65,10 @@
prop_name: "ro.surface_flinger.frame_rate_override_for_native_rates"
}
prop {
+ api_name: "frame_rate_override_global"
+ prop_name: "ro.surface_flinger.frame_rate_override_global"
+ }
+ prop {
api_name: "has_HDR_display"
prop_name: "ro.surface_flinger.has_HDR_display"
}
diff --git a/services/surfaceflinger/tests/Credentials_test.cpp b/services/surfaceflinger/tests/Credentials_test.cpp
index 1676844..4a45eb5 100644
--- a/services/surfaceflinger/tests/Credentials_test.cpp
+++ b/services/surfaceflinger/tests/Credentials_test.cpp
@@ -83,6 +83,15 @@
return SurfaceComposerClient::getPhysicalDisplayToken(ids.front());
}
+ static std::optional<uint64_t> getFirstDisplayId() {
+ const auto ids = SurfaceComposerClient::getPhysicalDisplayIds();
+ if (ids.empty()) {
+ return std::nullopt;
+ }
+
+ return ids.front().value;
+ }
+
void setupBackgroundSurface() {
mDisplay = getFirstDisplayToken();
ASSERT_FALSE(mDisplay == nullptr);
@@ -169,29 +178,25 @@
TEST_F(CredentialsTest, GetBuiltInDisplayAccessTest) {
std::function<bool()> condition = [] { return getFirstDisplayToken() != nullptr; };
// Anyone can access display information.
- ASSERT_NO_FATAL_FAILURE(checkWithPrivileges(condition, true, true));
+ ASSERT_NO_FATAL_FAILURE(checkWithPrivileges(condition, true, false));
}
TEST_F(CredentialsTest, AllowedGetterMethodsTest) {
// The following methods are tested with a UID that is not root, graphics,
// or system, to show that anyone can access them.
UIDFaker f(AID_BIN);
- const auto display = getFirstDisplayToken();
- ASSERT_TRUE(display != nullptr);
-
- ui::DisplayMode mode;
- ASSERT_EQ(NO_ERROR, SurfaceComposerClient::getActiveDisplayMode(display, &mode));
-
- Vector<ui::DisplayMode> modes;
+ const auto id = getFirstDisplayId();
+ ASSERT_TRUE(id);
ui::DynamicDisplayInfo info;
- ASSERT_EQ(NO_ERROR, SurfaceComposerClient::getDynamicDisplayInfo(display, &info));
+ ASSERT_EQ(NO_ERROR, SurfaceComposerClient::getDynamicDisplayInfoFromId(*id, &info));
}
TEST_F(CredentialsTest, GetDynamicDisplayInfoTest) {
- const auto display = getFirstDisplayToken();
+ const auto id = getFirstDisplayId();
+ ASSERT_TRUE(id);
std::function<status_t()> condition = [=]() {
ui::DynamicDisplayInfo info;
- return SurfaceComposerClient::getDynamicDisplayInfo(display, &info);
+ return SurfaceComposerClient::getDynamicDisplayInfoFromId(*id, &info);
};
ASSERT_NO_FATAL_FAILURE(checkWithPrivileges<status_t>(condition, NO_ERROR, NO_ERROR));
}
@@ -335,8 +340,10 @@
status_t error = SurfaceComposerClient::isWideColorDisplay(display, &result);
ASSERT_EQ(NO_ERROR, error);
bool hasWideColorMode = false;
+ const auto id = getFirstDisplayId();
+ ASSERT_TRUE(id);
ui::DynamicDisplayInfo info;
- SurfaceComposerClient::getDynamicDisplayInfo(display, &info);
+ SurfaceComposerClient::getDynamicDisplayInfoFromId(*id, &info);
const auto& colorModes = info.supportedColorModes;
for (ColorMode colorMode : colorModes) {
switch (colorMode) {
@@ -363,10 +370,10 @@
}
TEST_F(CredentialsTest, GetActiveColorModeBasicCorrectness) {
- const auto display = getFirstDisplayToken();
- ASSERT_FALSE(display == nullptr);
+ const auto id = getFirstDisplayId();
+ ASSERT_TRUE(id);
ui::DynamicDisplayInfo info;
- SurfaceComposerClient::getDynamicDisplayInfo(display, &info);
+ SurfaceComposerClient::getDynamicDisplayInfoFromId(*id, &info);
ColorMode colorMode = info.activeColorMode;
ASSERT_NE(static_cast<ColorMode>(BAD_VALUE), colorMode);
}
diff --git a/services/surfaceflinger/tests/DisplayConfigs_test.cpp b/services/surfaceflinger/tests/DisplayConfigs_test.cpp
index 10dae46..4be961b 100644
--- a/services/surfaceflinger/tests/DisplayConfigs_test.cpp
+++ b/services/surfaceflinger/tests/DisplayConfigs_test.cpp
@@ -45,6 +45,7 @@
void SetUp() override {
const auto ids = SurfaceComposerClient::getPhysicalDisplayIds();
ASSERT_FALSE(ids.empty());
+ mDisplayId = ids.front().value;
mDisplayToken = SurfaceComposerClient::getPhysicalDisplayToken(ids.front());
status_t res = SurfaceComposerClient::getDesiredDisplayModeSpecs(mDisplayToken, &mSpecs);
ASSERT_EQ(res, NO_ERROR);
@@ -58,11 +59,14 @@
void testSetAllowGroupSwitching(bool allowGroupSwitching);
sp<IBinder> mDisplayToken;
+ uint64_t mDisplayId;
};
TEST_F(RefreshRateRangeTest, setAllConfigs) {
ui::DynamicDisplayInfo info;
- status_t res = SurfaceComposerClient::getDynamicDisplayInfo(mDisplayToken, &info);
+ status_t res =
+ SurfaceComposerClient::getDynamicDisplayInfoFromId(static_cast<int64_t>(mDisplayId),
+ &info);
const auto& modes = info.supportedDisplayModes;
ASSERT_EQ(res, NO_ERROR);
ASSERT_GT(modes.size(), 0);
diff --git a/services/surfaceflinger/tests/unittests/Android.bp b/services/surfaceflinger/tests/unittests/Android.bp
index 0114577..bf22521 100644
--- a/services/surfaceflinger/tests/unittests/Android.bp
+++ b/services/surfaceflinger/tests/unittests/Android.bp
@@ -70,7 +70,6 @@
":libsurfaceflinger_sources",
"libsurfaceflinger_unittest_main.cpp",
"AidlPowerHalWrapperTest.cpp",
- "CachingTest.cpp",
"CompositionTest.cpp",
"DispSyncSourceTest.cpp",
"DisplayIdGeneratorTest.cpp",
@@ -93,6 +92,7 @@
"LayerHistoryTest.cpp",
"LayerInfoTest.cpp",
"LayerMetadataTest.cpp",
+ "LayerHierarchyTest.cpp",
"LayerLifecycleManagerTest.cpp",
"LayerTest.cpp",
"LayerTestUtils.cpp",
@@ -111,6 +111,7 @@
"SurfaceFlinger_SetPowerModeInternalTest.cpp",
"SurfaceFlinger_SetupNewDisplayDeviceInternalTest.cpp",
"SurfaceFlinger_UpdateLayerMetadataSnapshotTest.cpp",
+ "SurfaceFlinger_ExcludeDolbyVisionTest.cpp",
"SchedulerTest.cpp",
"SetFrameRateTest.cpp",
"RefreshRateSelectorTest.cpp",
diff --git a/services/surfaceflinger/tests/unittests/CachingTest.cpp b/services/surfaceflinger/tests/unittests/CachingTest.cpp
deleted file mode 100644
index c1cbbfb..0000000
--- a/services/surfaceflinger/tests/unittests/CachingTest.cpp
+++ /dev/null
@@ -1,97 +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.
- */
-
-#undef LOG_TAG
-#define LOG_TAG "CachingTest"
-
-#include <gmock/gmock.h>
-#include <gtest/gtest.h>
-#include <gui/BufferQueue.h>
-
-#include "HwcSlotGenerator.h"
-
-namespace android {
-
-class SlotGenerationTest : public testing::Test {
-protected:
- sp<HwcSlotGenerator> mHwcSlotGenerator = sp<HwcSlotGenerator>::make();
- sp<GraphicBuffer> mBuffer1 =
- sp<GraphicBuffer>::make(1u, 1u, HAL_PIXEL_FORMAT_RGBA_8888, 1u, 0u);
- sp<GraphicBuffer> mBuffer2 =
- sp<GraphicBuffer>::make(1u, 1u, HAL_PIXEL_FORMAT_RGBA_8888, 1u, 0u);
- sp<GraphicBuffer> mBuffer3 =
- sp<GraphicBuffer>::make(10u, 10u, HAL_PIXEL_FORMAT_RGBA_8888, 1u, 0u);
-};
-
-TEST_F(SlotGenerationTest, getHwcCacheSlot_Invalid) {
- sp<IBinder> binder = sp<BBinder>::make();
- // test getting invalid client_cache_id
- client_cache_t id;
- int slot = mHwcSlotGenerator->getHwcCacheSlot(id);
- EXPECT_EQ(BufferQueue::INVALID_BUFFER_SLOT, slot);
-}
-
-TEST_F(SlotGenerationTest, getHwcCacheSlot_Basic) {
- sp<IBinder> binder = sp<BBinder>::make();
- client_cache_t id;
- id.token = binder;
- id.id = 0;
- int slot = mHwcSlotGenerator->getHwcCacheSlot(id);
- EXPECT_EQ(BufferQueue::NUM_BUFFER_SLOTS - 1, slot);
-
- client_cache_t idB;
- idB.token = binder;
- idB.id = 1;
- slot = mHwcSlotGenerator->getHwcCacheSlot(idB);
- EXPECT_EQ(BufferQueue::NUM_BUFFER_SLOTS - 2, slot);
-
- slot = mHwcSlotGenerator->getHwcCacheSlot(idB);
- EXPECT_EQ(BufferQueue::NUM_BUFFER_SLOTS - 2, slot);
-
- slot = mHwcSlotGenerator->getHwcCacheSlot(id);
- EXPECT_EQ(BufferQueue::NUM_BUFFER_SLOTS - 1, slot);
-}
-
-TEST_F(SlotGenerationTest, getHwcCacheSlot_Reuse) {
- sp<IBinder> binder = sp<BBinder>::make();
- std::vector<client_cache_t> ids;
- uint32_t cacheId = 0;
- // fill up cache
- for (int i = 0; i < BufferQueue::NUM_BUFFER_SLOTS; i++) {
- client_cache_t id;
- id.token = binder;
- id.id = cacheId;
- ids.push_back(id);
-
- int slot = mHwcSlotGenerator->getHwcCacheSlot(id);
- EXPECT_EQ(BufferQueue::NUM_BUFFER_SLOTS - (i + 1), slot);
- cacheId++;
- }
- for (int i = 0; i < BufferQueue::NUM_BUFFER_SLOTS; i++) {
- int slot = mHwcSlotGenerator->getHwcCacheSlot(ids[static_cast<uint32_t>(i)]);
- EXPECT_EQ(BufferQueue::NUM_BUFFER_SLOTS - (i + 1), slot);
- }
-
- for (int i = 0; i < BufferQueue::NUM_BUFFER_SLOTS; i++) {
- client_cache_t id;
- id.token = binder;
- id.id = cacheId;
- int slot = mHwcSlotGenerator->getHwcCacheSlot(id);
- EXPECT_EQ(BufferQueue::NUM_BUFFER_SLOTS - (i + 1), slot);
- cacheId++;
- }
-}
-} // namespace android
diff --git a/services/surfaceflinger/tests/unittests/DisplayTransactionTestHelpers.h b/services/surfaceflinger/tests/unittests/DisplayTransactionTestHelpers.h
index 19c7d5c..d58e644 100644
--- a/services/surfaceflinger/tests/unittests/DisplayTransactionTestHelpers.h
+++ b/services/surfaceflinger/tests/unittests/DisplayTransactionTestHelpers.h
@@ -359,6 +359,7 @@
}
// Called by tests to inject a HWC display setup
+ template <bool kInitPowerMode = true>
static void injectHwcDisplayWithNoDefaultCapabilities(DisplayTransactionTest* test) {
const auto displayId = DisplayVariant::DISPLAY_ID::get();
ASSERT_FALSE(GpuVirtualDisplayId::tryCast(displayId));
@@ -367,18 +368,21 @@
.setHwcDisplayId(HWC_DISPLAY_ID)
.setResolution(DisplayVariant::RESOLUTION)
.setActiveConfig(HWC_ACTIVE_CONFIG_ID)
- .setPowerMode(INIT_POWER_MODE)
+ .setPowerMode(kInitPowerMode ? std::make_optional(INIT_POWER_MODE) : std::nullopt)
.inject(&test->mFlinger, test->mComposer);
}
// Called by tests to inject a HWC display setup
+ template <bool kInitPowerMode = true>
static void injectHwcDisplay(DisplayTransactionTest* test) {
EXPECT_CALL(*test->mComposer, getDisplayCapabilities(HWC_DISPLAY_ID, _))
.WillOnce(DoAll(SetArgPointee<1>(std::vector<DisplayCapability>({})),
Return(Error::NONE)));
- EXPECT_CALL(*test->mComposer, setPowerMode(HWC_DISPLAY_ID, INIT_POWER_MODE))
- .WillOnce(Return(Error::NONE));
- injectHwcDisplayWithNoDefaultCapabilities(test);
+ if constexpr (kInitPowerMode) {
+ EXPECT_CALL(*test->mComposer, setPowerMode(HWC_DISPLAY_ID, INIT_POWER_MODE))
+ .WillOnce(Return(Error::NONE));
+ }
+ injectHwcDisplayWithNoDefaultCapabilities<kInitPowerMode>(test);
}
static std::shared_ptr<compositionengine::Display> injectCompositionDisplay(
diff --git a/services/surfaceflinger/tests/unittests/EventThreadTest.cpp b/services/surfaceflinger/tests/unittests/EventThreadTest.cpp
index a5beaba..dd87f9d 100644
--- a/services/surfaceflinger/tests/unittests/EventThreadTest.cpp
+++ b/services/surfaceflinger/tests/unittests/EventThreadTest.cpp
@@ -633,9 +633,10 @@
.setId(DisplayModeId(7))
.setVsyncPeriod(16666666)
.build();
+ const Fps fps = mode->getFps() / 2;
- mThread->onModeChanged(mode);
- expectConfigChangedEventReceivedByConnection(INTERNAL_DISPLAY_ID, 7, 16666666);
+ mThread->onModeChanged({fps, ftl::as_non_null(mode)});
+ expectConfigChangedEventReceivedByConnection(INTERNAL_DISPLAY_ID, 7, fps.getPeriodNsecs());
}
TEST_F(EventThreadTest, postConfigChangedExternal) {
@@ -644,9 +645,10 @@
.setId(DisplayModeId(5))
.setVsyncPeriod(16666666)
.build();
+ const Fps fps = mode->getFps() / 2;
- mThread->onModeChanged(mode);
- expectConfigChangedEventReceivedByConnection(EXTERNAL_DISPLAY_ID, 5, 16666666);
+ mThread->onModeChanged({fps, ftl::as_non_null(mode)});
+ expectConfigChangedEventReceivedByConnection(EXTERNAL_DISPLAY_ID, 5, fps.getPeriodNsecs());
}
TEST_F(EventThreadTest, postConfigChangedPrimary64bit) {
@@ -655,8 +657,9 @@
.setId(DisplayModeId(7))
.setVsyncPeriod(16666666)
.build();
- mThread->onModeChanged(mode);
- expectConfigChangedEventReceivedByConnection(DISPLAY_ID_64BIT, 7, 16666666);
+ const Fps fps = mode->getFps() / 2;
+ mThread->onModeChanged({fps, ftl::as_non_null(mode)});
+ expectConfigChangedEventReceivedByConnection(DISPLAY_ID_64BIT, 7, fps.getPeriodNsecs());
}
TEST_F(EventThreadTest, suppressConfigChanged) {
@@ -669,9 +672,10 @@
.setId(DisplayModeId(9))
.setVsyncPeriod(16666666)
.build();
+ const Fps fps = mode->getFps() / 2;
- mThread->onModeChanged(mode);
- expectConfigChangedEventReceivedByConnection(INTERNAL_DISPLAY_ID, 9, 16666666);
+ mThread->onModeChanged({fps, ftl::as_non_null(mode)});
+ expectConfigChangedEventReceivedByConnection(INTERNAL_DISPLAY_ID, 9, fps.getPeriodNsecs());
auto args = suppressConnectionEventRecorder.waitForCall();
ASSERT_FALSE(args.has_value());
diff --git a/services/surfaceflinger/tests/unittests/LayerHierarchyTest.cpp b/services/surfaceflinger/tests/unittests/LayerHierarchyTest.cpp
new file mode 100644
index 0000000..8560902
--- /dev/null
+++ b/services/surfaceflinger/tests/unittests/LayerHierarchyTest.cpp
@@ -0,0 +1,728 @@
+/*
+ * 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 <gmock/gmock.h>
+#include <gtest/gtest.h>
+
+#include "FrontEnd/LayerHandle.h"
+#include "FrontEnd/LayerHierarchy.h"
+#include "FrontEnd/LayerLifecycleManager.h"
+#include "Layer.h"
+#include "gui/SurfaceComposerClient.h"
+
+#define UPDATE_AND_VERIFY(HIERARCHY) \
+ ({ \
+ SCOPED_TRACE(""); \
+ updateAndVerify((HIERARCHY)); \
+ })
+
+namespace android::surfaceflinger::frontend {
+
+namespace {
+LayerCreationArgs createArgs(uint32_t id, bool canBeRoot, wp<IBinder> parent, wp<IBinder> mirror) {
+ LayerCreationArgs args(nullptr, nullptr, "testlayer", 0, {}, std::make_optional(id));
+ args.addToRoot = canBeRoot;
+ args.parentHandle = parent;
+ args.mirrorLayerHandle = mirror;
+ return args;
+}
+} // namespace
+
+// To run test:
+/**
+ mp :libsurfaceflinger_unittest && adb sync; adb shell \
+ /data/nativetest/libsurfaceflinger_unittest/libsurfaceflinger_unittest \
+ --gtest_filter="LayerHierarchyTest.*" --gtest_repeat=100 \
+ --gtest_shuffle \
+ --gtest_brief=1
+*/
+
+class LayerHierarchyTest : public testing::Test {
+protected:
+ LayerHierarchyTest() {
+ // tree with 3 levels of children
+ // ROOT
+ // ├── 1
+ // │ ├── 11
+ // │ │ └── 111
+ // │ ├── 12
+ // │ │ ├── 121
+ // │ │ └── 122
+ // │ │ └── 1221
+ // │ └── 13
+ // └── 2
+
+ createRootLayer(1);
+ createRootLayer(2);
+ createLayer(11, 1);
+ createLayer(12, 1);
+ createLayer(13, 1);
+ createLayer(111, 11);
+ createLayer(121, 12);
+ createLayer(122, 12);
+ createLayer(1221, 122);
+ mLifecycleManager.commitChanges();
+ }
+ std::vector<uint32_t> getTraversalPath(const LayerHierarchy& hierarchy) const {
+ std::vector<uint32_t> layerIds;
+ hierarchy.traverse([&layerIds = layerIds](const LayerHierarchy& hierarchy,
+ const LayerHierarchy::TraversalPath&) -> bool {
+ layerIds.emplace_back(hierarchy.getLayer()->id);
+ return true;
+ });
+ return layerIds;
+ }
+
+ std::vector<uint32_t> getTraversalPathInZOrder(const LayerHierarchy& hierarchy) const {
+ std::vector<uint32_t> layerIds;
+ hierarchy.traverseInZOrder(
+ [&layerIds = layerIds](const LayerHierarchy& hierarchy,
+ const LayerHierarchy::TraversalPath&) -> bool {
+ layerIds.emplace_back(hierarchy.getLayer()->id);
+ return true;
+ });
+ return layerIds;
+ }
+
+ void createRootLayer(uint32_t id) {
+ sp<LayerHandle> handle = sp<LayerHandle>::make(id);
+ mHandles[id] = handle;
+ std::vector<std::unique_ptr<RequestedLayerState>> layers;
+ layers.emplace_back(std::make_unique<RequestedLayerState>(
+ createArgs(/*id=*/id, /*canBeRoot=*/true, /*parent=*/nullptr, /*mirror=*/nullptr)));
+ mLifecycleManager.addLayers(std::move(layers));
+ }
+
+ void createLayer(uint32_t id, uint32_t parentId) {
+ sp<LayerHandle> handle = sp<LayerHandle>::make(id);
+ mHandles[id] = handle;
+ std::vector<std::unique_ptr<RequestedLayerState>> layers;
+ layers.emplace_back(std::make_unique<RequestedLayerState>(
+ createArgs(/*id=*/id, /*canBeRoot=*/false, /*parent=*/mHandles[parentId],
+ /*mirror=*/nullptr)));
+ mLifecycleManager.addLayers(std::move(layers));
+ }
+
+ void reparentLayer(uint32_t id, uint32_t newParentId) {
+ std::vector<TransactionState> transactions;
+ transactions.emplace_back();
+ transactions.back().states.push_back({});
+
+ if (newParentId == UNASSIGNED_LAYER_ID) {
+ transactions.back().states.front().state.parentSurfaceControlForChild = nullptr;
+ } else {
+ auto parentHandle = mHandles[newParentId];
+ transactions.back().states.front().state.parentSurfaceControlForChild =
+ sp<SurfaceControl>::make(SurfaceComposerClient::getDefault(), parentHandle,
+ static_cast<int32_t>(newParentId), "Test");
+ }
+ transactions.back().states.front().state.what = layer_state_t::eReparent;
+ transactions.back().states.front().state.surface = mHandles[id];
+ mLifecycleManager.applyTransactions(transactions);
+ }
+
+ void reparentRelativeLayer(uint32_t id, uint32_t relativeParentId) {
+ std::vector<TransactionState> transactions;
+ transactions.emplace_back();
+ transactions.back().states.push_back({});
+
+ if (relativeParentId == UNASSIGNED_LAYER_ID) {
+ transactions.back().states.front().state.what = layer_state_t::eLayerChanged;
+ } else {
+ auto parentHandle = mHandles[relativeParentId];
+ transactions.back().states.front().state.relativeLayerSurfaceControl =
+ sp<SurfaceControl>::make(SurfaceComposerClient::getDefault(), parentHandle,
+ static_cast<int32_t>(relativeParentId), "test");
+ transactions.back().states.front().state.what = layer_state_t::eRelativeLayerChanged;
+ }
+ transactions.back().states.front().state.surface = mHandles[id];
+ mLifecycleManager.applyTransactions(transactions);
+ }
+
+ void mirrorLayer(uint32_t id, uint32_t parent, uint32_t layerToMirror) {
+ auto parentHandle = (parent == UNASSIGNED_LAYER_ID) ? nullptr : mHandles[parent];
+ auto mirrorHandle =
+ (layerToMirror == UNASSIGNED_LAYER_ID) ? nullptr : mHandles[layerToMirror];
+
+ sp<LayerHandle> handle = sp<LayerHandle>::make(id);
+ mHandles[id] = handle;
+ std::vector<std::unique_ptr<RequestedLayerState>> layers;
+ layers.emplace_back(std::make_unique<RequestedLayerState>(
+ createArgs(/*id=*/id, /*canBeRoot=*/false, /*parent=*/parentHandle,
+ /*mirror=*/mHandles[layerToMirror])));
+ mLifecycleManager.addLayers(std::move(layers));
+ }
+
+ void updateBackgroundColor(uint32_t id, half alpha) {
+ std::vector<TransactionState> transactions;
+ transactions.emplace_back();
+ transactions.back().states.push_back({});
+ transactions.back().states.front().state.what = layer_state_t::eBackgroundColorChanged;
+ transactions.back().states.front().state.bgColorAlpha = alpha;
+ transactions.back().states.front().state.surface = mHandles[id];
+ mLifecycleManager.applyTransactions(transactions);
+ }
+
+ void destroyLayerHandle(uint32_t id) { mLifecycleManager.onHandlesDestroyed({id}); }
+
+ void updateAndVerify(LayerHierarchyBuilder& hierarchyBuilder) {
+ if (mLifecycleManager.getGlobalChanges().test(RequestedLayerState::Changes::Hierarchy)) {
+ hierarchyBuilder.update(mLifecycleManager.getLayers(),
+ mLifecycleManager.getDestroyedLayers());
+ }
+ mLifecycleManager.commitChanges();
+
+ // rebuild layer hierarchy from scratch and verify that it matches the updated state.
+ LayerHierarchyBuilder newBuilder(mLifecycleManager.getLayers());
+ EXPECT_EQ(getTraversalPath(hierarchyBuilder.getHierarchy()),
+ getTraversalPath(newBuilder.getHierarchy()));
+ EXPECT_EQ(getTraversalPathInZOrder(hierarchyBuilder.getHierarchy()),
+ getTraversalPathInZOrder(newBuilder.getHierarchy()));
+ EXPECT_FALSE(
+ mLifecycleManager.getGlobalChanges().test(RequestedLayerState::Changes::Hierarchy));
+ }
+
+ void setZ(uint32_t id, int32_t z) {
+ std::vector<TransactionState> transactions;
+ transactions.emplace_back();
+ transactions.back().states.push_back({});
+
+ transactions.back().states.front().state.what = layer_state_t::eLayerChanged;
+ transactions.back().states.front().state.layerId = static_cast<int32_t>(id);
+ transactions.back().states.front().state.z = z;
+ mLifecycleManager.applyTransactions(transactions);
+ }
+ LayerLifecycleManager mLifecycleManager;
+ std::unordered_map<uint32_t, sp<LayerHandle>> mHandles;
+};
+
+// reparenting tests
+TEST_F(LayerHierarchyTest, addLayer) {
+ LayerHierarchyBuilder hierarchyBuilder(mLifecycleManager.getLayers());
+ std::vector<uint32_t> expectedTraversalPath = {1, 11, 111, 12, 121, 122, 1221, 13, 2};
+ EXPECT_EQ(getTraversalPath(hierarchyBuilder.getHierarchy()), expectedTraversalPath);
+ EXPECT_EQ(getTraversalPathInZOrder(hierarchyBuilder.getHierarchy()), expectedTraversalPath);
+ expectedTraversalPath = {};
+ EXPECT_EQ(getTraversalPath(hierarchyBuilder.getOffscreenHierarchy()), expectedTraversalPath);
+
+ createRootLayer(3);
+ createLayer(112, 11);
+ createLayer(12211, 1221);
+ UPDATE_AND_VERIFY(hierarchyBuilder);
+ expectedTraversalPath = {1, 11, 111, 112, 12, 121, 122, 1221, 12211, 13, 2, 3};
+ EXPECT_EQ(getTraversalPath(hierarchyBuilder.getHierarchy()), expectedTraversalPath);
+ EXPECT_EQ(getTraversalPathInZOrder(hierarchyBuilder.getHierarchy()), expectedTraversalPath);
+ expectedTraversalPath = {};
+ EXPECT_EQ(getTraversalPath(hierarchyBuilder.getOffscreenHierarchy()), expectedTraversalPath);
+}
+
+TEST_F(LayerHierarchyTest, reparentLayer) {
+ LayerHierarchyBuilder hierarchyBuilder(mLifecycleManager.getLayers());
+ reparentLayer(2, 11);
+ reparentLayer(111, 12);
+ reparentLayer(1221, 1);
+ reparentLayer(1221, 13);
+ UPDATE_AND_VERIFY(hierarchyBuilder);
+
+ std::vector<uint32_t> expectedTraversalPath = {1, 11, 2, 12, 111, 121, 122, 13, 1221};
+ EXPECT_EQ(getTraversalPath(hierarchyBuilder.getHierarchy()), expectedTraversalPath);
+ EXPECT_EQ(getTraversalPathInZOrder(hierarchyBuilder.getHierarchy()), expectedTraversalPath);
+ expectedTraversalPath = {};
+ EXPECT_EQ(getTraversalPath(hierarchyBuilder.getOffscreenHierarchy()), expectedTraversalPath);
+}
+
+TEST_F(LayerHierarchyTest, reparentLayerToNull) {
+ LayerHierarchyBuilder hierarchyBuilder(mLifecycleManager.getLayers());
+
+ reparentLayer(2, UNASSIGNED_LAYER_ID);
+ reparentLayer(11, UNASSIGNED_LAYER_ID);
+ reparentLayer(1221, 13);
+ reparentLayer(1221, UNASSIGNED_LAYER_ID);
+
+ UPDATE_AND_VERIFY(hierarchyBuilder);
+
+ std::vector<uint32_t> expectedTraversalPath = {1, 12, 121, 122, 13};
+ EXPECT_EQ(getTraversalPath(hierarchyBuilder.getHierarchy()), expectedTraversalPath);
+ EXPECT_EQ(getTraversalPathInZOrder(hierarchyBuilder.getHierarchy()), expectedTraversalPath);
+ expectedTraversalPath = {2, 11, 111, 1221};
+ EXPECT_EQ(getTraversalPath(hierarchyBuilder.getOffscreenHierarchy()), expectedTraversalPath);
+}
+
+TEST_F(LayerHierarchyTest, reparentLayerToNullAndDestroyHandles) {
+ LayerHierarchyBuilder hierarchyBuilder(mLifecycleManager.getLayers());
+ reparentLayer(2, UNASSIGNED_LAYER_ID);
+ reparentLayer(11, UNASSIGNED_LAYER_ID);
+ reparentLayer(1221, UNASSIGNED_LAYER_ID);
+
+ destroyLayerHandle(2);
+ destroyLayerHandle(11);
+ destroyLayerHandle(1221);
+
+ UPDATE_AND_VERIFY(hierarchyBuilder);
+
+ std::vector<uint32_t> expectedTraversalPath = {1, 12, 121, 122, 13};
+ EXPECT_EQ(getTraversalPath(hierarchyBuilder.getHierarchy()), expectedTraversalPath);
+ EXPECT_EQ(getTraversalPathInZOrder(hierarchyBuilder.getHierarchy()), expectedTraversalPath);
+ expectedTraversalPath = {111};
+ EXPECT_EQ(getTraversalPath(hierarchyBuilder.getOffscreenHierarchy()), expectedTraversalPath);
+}
+
+TEST_F(LayerHierarchyTest, destroyHandleThenDestroyParentLayer) {
+ LayerHierarchyBuilder hierarchyBuilder(mLifecycleManager.getLayers());
+ destroyLayerHandle(111);
+ UPDATE_AND_VERIFY(hierarchyBuilder);
+
+ // handle is destroyed but layer is kept alive and reachable by parent
+ std::vector<uint32_t> expectedTraversalPath = {1, 11, 111, 12, 121, 122, 1221, 13, 2};
+ EXPECT_EQ(getTraversalPath(hierarchyBuilder.getHierarchy()), expectedTraversalPath);
+ EXPECT_EQ(getTraversalPathInZOrder(hierarchyBuilder.getHierarchy()), expectedTraversalPath);
+ expectedTraversalPath = {};
+ EXPECT_EQ(getTraversalPath(hierarchyBuilder.getOffscreenHierarchy()), expectedTraversalPath);
+
+ // destroy parent layer and the child gets destroyed
+ reparentLayer(11, UNASSIGNED_LAYER_ID);
+ destroyLayerHandle(11);
+ UPDATE_AND_VERIFY(hierarchyBuilder);
+
+ expectedTraversalPath = {1, 12, 121, 122, 1221, 13, 2};
+ EXPECT_EQ(getTraversalPath(hierarchyBuilder.getHierarchy()), expectedTraversalPath);
+ EXPECT_EQ(getTraversalPathInZOrder(hierarchyBuilder.getHierarchy()), expectedTraversalPath);
+ expectedTraversalPath = {};
+ EXPECT_EQ(getTraversalPath(hierarchyBuilder.getOffscreenHierarchy()), expectedTraversalPath);
+}
+
+TEST_F(LayerHierarchyTest, layerSurvivesTemporaryReparentToNull) {
+ LayerHierarchyBuilder hierarchyBuilder(mLifecycleManager.getLayers());
+ reparentLayer(11, UNASSIGNED_LAYER_ID);
+ reparentLayer(11, 1);
+
+ UPDATE_AND_VERIFY(hierarchyBuilder);
+
+ std::vector<uint32_t> expectedTraversalPath = {1, 11, 111, 12, 121, 122, 1221, 13, 2};
+ EXPECT_EQ(getTraversalPath(hierarchyBuilder.getHierarchy()), expectedTraversalPath);
+ EXPECT_EQ(getTraversalPathInZOrder(hierarchyBuilder.getHierarchy()), expectedTraversalPath);
+ expectedTraversalPath = {};
+ EXPECT_EQ(getTraversalPath(hierarchyBuilder.getOffscreenHierarchy()), expectedTraversalPath);
+}
+
+// offscreen tests
+TEST_F(LayerHierarchyTest, layerMovesOnscreen) {
+ LayerHierarchyBuilder hierarchyBuilder(mLifecycleManager.getLayers());
+
+ reparentLayer(11, UNASSIGNED_LAYER_ID);
+ UPDATE_AND_VERIFY(hierarchyBuilder);
+
+ reparentLayer(11, 1);
+ UPDATE_AND_VERIFY(hierarchyBuilder);
+
+ std::vector<uint32_t> expectedTraversalPath = {1, 11, 111, 12, 121, 122, 1221, 13, 2};
+ EXPECT_EQ(getTraversalPath(hierarchyBuilder.getHierarchy()), expectedTraversalPath);
+ EXPECT_EQ(getTraversalPathInZOrder(hierarchyBuilder.getHierarchy()), expectedTraversalPath);
+ expectedTraversalPath = {};
+ EXPECT_EQ(getTraversalPath(hierarchyBuilder.getOffscreenHierarchy()), expectedTraversalPath);
+}
+
+TEST_F(LayerHierarchyTest, addLayerToOffscreenParent) {
+ LayerHierarchyBuilder hierarchyBuilder(mLifecycleManager.getLayers());
+
+ reparentLayer(11, UNASSIGNED_LAYER_ID);
+ UPDATE_AND_VERIFY(hierarchyBuilder);
+
+ createLayer(112, 11);
+ UPDATE_AND_VERIFY(hierarchyBuilder);
+
+ std::vector<uint32_t> expectedTraversalPath = {1, 12, 121, 122, 1221, 13, 2};
+ EXPECT_EQ(getTraversalPath(hierarchyBuilder.getHierarchy()), expectedTraversalPath);
+ EXPECT_EQ(getTraversalPathInZOrder(hierarchyBuilder.getHierarchy()), expectedTraversalPath);
+ expectedTraversalPath = {11, 111, 112};
+ EXPECT_EQ(getTraversalPath(hierarchyBuilder.getOffscreenHierarchy()), expectedTraversalPath);
+}
+
+// rel-z tests
+TEST_F(LayerHierarchyTest, setRelativeParent) {
+ LayerHierarchyBuilder hierarchyBuilder(mLifecycleManager.getLayers());
+ reparentRelativeLayer(11, 2);
+ UPDATE_AND_VERIFY(hierarchyBuilder);
+
+ std::vector<uint32_t> expectedTraversalPath = {1, 11, 111, 12, 121, 122, 1221, 13, 2, 11, 111};
+ EXPECT_EQ(getTraversalPath(hierarchyBuilder.getHierarchy()), expectedTraversalPath);
+ expectedTraversalPath = {1, 12, 121, 122, 1221, 13, 2, 11, 111};
+ EXPECT_EQ(getTraversalPathInZOrder(hierarchyBuilder.getHierarchy()), expectedTraversalPath);
+ expectedTraversalPath = {};
+ EXPECT_EQ(getTraversalPath(hierarchyBuilder.getOffscreenHierarchy()), expectedTraversalPath);
+}
+
+TEST_F(LayerHierarchyTest, reparentFromRelativeParentWithSetLayer) {
+ LayerHierarchyBuilder hierarchyBuilder(mLifecycleManager.getLayers());
+ reparentRelativeLayer(11, 2);
+ UPDATE_AND_VERIFY(hierarchyBuilder);
+
+ reparentRelativeLayer(11, UNASSIGNED_LAYER_ID);
+ UPDATE_AND_VERIFY(hierarchyBuilder);
+
+ std::vector<uint32_t> expectedTraversalPath = {1, 11, 111, 12, 121, 122, 1221, 13, 2};
+ EXPECT_EQ(getTraversalPath(hierarchyBuilder.getHierarchy()), expectedTraversalPath);
+ EXPECT_EQ(getTraversalPathInZOrder(hierarchyBuilder.getHierarchy()), expectedTraversalPath);
+ expectedTraversalPath = {};
+ EXPECT_EQ(getTraversalPath(hierarchyBuilder.getOffscreenHierarchy()), expectedTraversalPath);
+}
+
+TEST_F(LayerHierarchyTest, reparentToRelativeParent) {
+ LayerHierarchyBuilder hierarchyBuilder(mLifecycleManager.getLayers());
+ reparentRelativeLayer(11, 2);
+ UPDATE_AND_VERIFY(hierarchyBuilder);
+
+ reparentLayer(11, 2);
+ UPDATE_AND_VERIFY(hierarchyBuilder);
+
+ std::vector<uint32_t> expectedTraversalPath = {1, 12, 121, 122, 1221, 13, 2, 11, 111};
+ EXPECT_EQ(getTraversalPath(hierarchyBuilder.getHierarchy()), expectedTraversalPath);
+ EXPECT_EQ(getTraversalPathInZOrder(hierarchyBuilder.getHierarchy()), expectedTraversalPath);
+ expectedTraversalPath = {};
+ EXPECT_EQ(getTraversalPath(hierarchyBuilder.getOffscreenHierarchy()), expectedTraversalPath);
+}
+
+TEST_F(LayerHierarchyTest, setParentAsRelativeParent) {
+ LayerHierarchyBuilder hierarchyBuilder(mLifecycleManager.getLayers());
+ reparentLayer(11, 2);
+ UPDATE_AND_VERIFY(hierarchyBuilder);
+
+ reparentRelativeLayer(11, 2);
+ UPDATE_AND_VERIFY(hierarchyBuilder);
+
+ std::vector<uint32_t> expectedTraversalPath = {1, 12, 121, 122, 1221, 13, 2, 11, 111};
+ EXPECT_EQ(getTraversalPath(hierarchyBuilder.getHierarchy()), expectedTraversalPath);
+ EXPECT_EQ(getTraversalPathInZOrder(hierarchyBuilder.getHierarchy()), expectedTraversalPath);
+ expectedTraversalPath = {};
+ EXPECT_EQ(getTraversalPath(hierarchyBuilder.getOffscreenHierarchy()), expectedTraversalPath);
+}
+
+TEST_F(LayerHierarchyTest, relativeChildMovesOffscreenIsNotTraversable) {
+ LayerHierarchyBuilder hierarchyBuilder(mLifecycleManager.getLayers());
+ reparentRelativeLayer(11, 2);
+ UPDATE_AND_VERIFY(hierarchyBuilder);
+
+ reparentLayer(2, UNASSIGNED_LAYER_ID);
+ UPDATE_AND_VERIFY(hierarchyBuilder);
+
+ std::vector<uint32_t> expectedTraversalPath = {1, 11, 111, 12, 121, 122, 1221, 13};
+ EXPECT_EQ(getTraversalPath(hierarchyBuilder.getHierarchy()), expectedTraversalPath);
+ expectedTraversalPath = {1, 12, 121, 122, 1221, 13};
+ EXPECT_EQ(getTraversalPathInZOrder(hierarchyBuilder.getHierarchy()), expectedTraversalPath);
+ expectedTraversalPath = {2, 11, 111};
+ EXPECT_EQ(getTraversalPath(hierarchyBuilder.getOffscreenHierarchy()), expectedTraversalPath);
+}
+
+// mirror tests
+TEST_F(LayerHierarchyTest, canTraverseMirrorLayer) {
+ LayerHierarchyBuilder hierarchyBuilder(mLifecycleManager.getLayers());
+
+ mirrorLayer(/*layer*/ 14, /*parent*/ 1, /*layerToMirror*/ 11);
+ UPDATE_AND_VERIFY(hierarchyBuilder);
+
+ std::vector<uint32_t> expectedTraversalPath = {1, 11, 111, 12, 121, 122,
+ 1221, 13, 14, 11, 111, 2};
+ EXPECT_EQ(getTraversalPath(hierarchyBuilder.getHierarchy()), expectedTraversalPath);
+ EXPECT_EQ(getTraversalPathInZOrder(hierarchyBuilder.getHierarchy()), expectedTraversalPath);
+ expectedTraversalPath = {};
+ EXPECT_EQ(getTraversalPath(hierarchyBuilder.getOffscreenHierarchy()), expectedTraversalPath);
+}
+
+TEST_F(LayerHierarchyTest, canMirrorOffscreenLayer) {
+ LayerHierarchyBuilder hierarchyBuilder(mLifecycleManager.getLayers());
+
+ reparentLayer(11, UNASSIGNED_LAYER_ID);
+ mirrorLayer(/*layer*/ 14, /*parent*/ 1, /*layerToMirror*/ 11);
+ UPDATE_AND_VERIFY(hierarchyBuilder);
+
+ std::vector<uint32_t> expectedTraversalPath = {1, 12, 121, 122, 1221, 13, 14, 11, 111, 2};
+ EXPECT_EQ(getTraversalPath(hierarchyBuilder.getHierarchy()), expectedTraversalPath);
+ EXPECT_EQ(getTraversalPathInZOrder(hierarchyBuilder.getHierarchy()), expectedTraversalPath);
+ expectedTraversalPath = {11, 111};
+ EXPECT_EQ(getTraversalPath(hierarchyBuilder.getOffscreenHierarchy()), expectedTraversalPath);
+}
+
+TEST_F(LayerHierarchyTest, newChildLayerIsUpdatedInMirrorHierarchy) {
+ mirrorLayer(/*layer*/ 14, /*parent*/ 1, /*layerToMirror*/ 11);
+ mLifecycleManager.commitChanges();
+ LayerHierarchyBuilder hierarchyBuilder(mLifecycleManager.getLayers());
+
+ createLayer(1111, 111);
+ createLayer(112, 11);
+ UPDATE_AND_VERIFY(hierarchyBuilder);
+
+ std::vector<uint32_t> expectedTraversalPath = {1, 11, 111, 1111, 112, 12, 121, 122,
+ 1221, 13, 14, 11, 111, 1111, 112, 2};
+ EXPECT_EQ(getTraversalPath(hierarchyBuilder.getHierarchy()), expectedTraversalPath);
+ EXPECT_EQ(getTraversalPathInZOrder(hierarchyBuilder.getHierarchy()), expectedTraversalPath);
+ expectedTraversalPath = {};
+ EXPECT_EQ(getTraversalPath(hierarchyBuilder.getOffscreenHierarchy()), expectedTraversalPath);
+}
+
+// mirror & relatives tests
+TEST_F(LayerHierarchyTest, mirrorWithRelativeOutsideMirrorHierarchy) {
+ LayerHierarchyBuilder hierarchyBuilder(mLifecycleManager.getLayers());
+ reparentRelativeLayer(111, 12);
+ mirrorLayer(/*layer*/ 14, /*parent*/ 1, /*layerToMirror*/ 11);
+
+ // ROOT
+ // ├── 1
+ // │ ├── 11
+ // │ │ └── 111
+ // │ ├── 12
+ // │ │ ├── 121
+ // │ │ ├── 122
+ // │ │ │ └── 1221
+ // │ │ └ - 111 (relative)
+ // │ ├── 13
+ // │ └── 14
+ // │ └ * 11 (mirroring)
+ // └── 2
+
+ UPDATE_AND_VERIFY(hierarchyBuilder);
+
+ std::vector<uint32_t> expectedTraversalPath = {1, 11, 111, 12, 111, 121, 122,
+ 1221, 13, 14, 11, 111, 2};
+ EXPECT_EQ(getTraversalPath(hierarchyBuilder.getHierarchy()), expectedTraversalPath);
+ // 111 is not reachable in the mirror
+ expectedTraversalPath = {1, 11, 12, 111, 121, 122, 1221, 13, 14, 11, 2};
+ EXPECT_EQ(getTraversalPathInZOrder(hierarchyBuilder.getHierarchy()), expectedTraversalPath);
+ expectedTraversalPath = {};
+ EXPECT_EQ(getTraversalPath(hierarchyBuilder.getOffscreenHierarchy()), expectedTraversalPath);
+}
+
+TEST_F(LayerHierarchyTest, mirrorWithRelativeInsideMirrorHierarchy) {
+ LayerHierarchyBuilder hierarchyBuilder(mLifecycleManager.getLayers());
+ reparentRelativeLayer(1221, 12);
+ mirrorLayer(/*layer*/ 14, /*parent*/ 1, /*layerToMirror*/ 12);
+
+ // ROOT
+ // ├── 1
+ // │ ├── 11
+ // │ │ └── 111
+ // │ ├── 12
+ // │ │ ├── 121
+ // │ │ ├── 122
+ // │ │ │ └── 1221
+ // │ │ └ - 1221 (relative)
+ // │ ├── 13
+ // │ └── 14
+ // │ └ * 12 (mirroring)
+ // └── 2
+
+ UPDATE_AND_VERIFY(hierarchyBuilder);
+ std::vector<uint32_t> expectedTraversalPath = {1, 11, 111, 12, 121, 122, 1221, 1221,
+ 13, 14, 12, 121, 122, 1221, 1221, 2};
+ EXPECT_EQ(getTraversalPath(hierarchyBuilder.getHierarchy()), expectedTraversalPath);
+ // relative layer 1221 is traversable in the mirrored hierarchy as well
+ expectedTraversalPath = {1, 11, 111, 12, 121, 122, 1221, 13, 14, 12, 121, 122, 1221, 2};
+ EXPECT_EQ(getTraversalPathInZOrder(hierarchyBuilder.getHierarchy()), expectedTraversalPath);
+ expectedTraversalPath = {};
+ EXPECT_EQ(getTraversalPath(hierarchyBuilder.getOffscreenHierarchy()), expectedTraversalPath);
+}
+
+TEST_F(LayerHierarchyTest, childMovesOffscreenWhenRelativeParentDies) {
+ LayerHierarchyBuilder hierarchyBuilder(mLifecycleManager.getLayers());
+
+ reparentRelativeLayer(11, 2);
+ reparentLayer(2, UNASSIGNED_LAYER_ID);
+ destroyLayerHandle(2);
+
+ UPDATE_AND_VERIFY(hierarchyBuilder);
+ std::vector<uint32_t> expectedTraversalPath = {1, 11, 111, 12, 121, 122, 1221, 13};
+ EXPECT_EQ(getTraversalPath(hierarchyBuilder.getHierarchy()), expectedTraversalPath);
+ expectedTraversalPath = {1, 12, 121, 122, 1221, 13};
+ EXPECT_EQ(getTraversalPathInZOrder(hierarchyBuilder.getHierarchy()), expectedTraversalPath);
+ expectedTraversalPath = {11, 111};
+ EXPECT_EQ(getTraversalPath(hierarchyBuilder.getOffscreenHierarchy()), expectedTraversalPath);
+
+ // remove relative parent so layer becomes onscreen again
+ reparentRelativeLayer(11, UNASSIGNED_LAYER_ID);
+ UPDATE_AND_VERIFY(hierarchyBuilder);
+
+ expectedTraversalPath = {1, 11, 111, 12, 121, 122, 1221, 13};
+ EXPECT_EQ(getTraversalPath(hierarchyBuilder.getHierarchy()), expectedTraversalPath);
+ EXPECT_EQ(getTraversalPathInZOrder(hierarchyBuilder.getHierarchy()), expectedTraversalPath);
+ expectedTraversalPath = {};
+ EXPECT_EQ(getTraversalPath(hierarchyBuilder.getOffscreenHierarchy()), expectedTraversalPath);
+}
+
+TEST_F(LayerHierarchyTest, offscreenLayerCannotBeRelativeToOnscreenLayer) {
+ LayerHierarchyBuilder hierarchyBuilder(mLifecycleManager.getLayers());
+ reparentRelativeLayer(1221, 2);
+ UPDATE_AND_VERIFY(hierarchyBuilder);
+
+ // verify relz path
+ std::vector<uint32_t> expectedTraversalPath = {1, 11, 111, 12, 121, 122, 1221, 13, 2, 1221};
+ EXPECT_EQ(getTraversalPath(hierarchyBuilder.getHierarchy()), expectedTraversalPath);
+ expectedTraversalPath = {1, 11, 111, 12, 121, 122, 13, 2, 1221};
+ EXPECT_EQ(getTraversalPathInZOrder(hierarchyBuilder.getHierarchy()), expectedTraversalPath);
+ expectedTraversalPath = {};
+ EXPECT_EQ(getTraversalPath(hierarchyBuilder.getOffscreenHierarchy()), expectedTraversalPath);
+
+ // offscreen layer cannot be reached as a relative child
+ reparentLayer(12, UNASSIGNED_LAYER_ID);
+ UPDATE_AND_VERIFY(hierarchyBuilder);
+
+ expectedTraversalPath = {1, 11, 111, 13, 2};
+ EXPECT_EQ(getTraversalPath(hierarchyBuilder.getHierarchy()), expectedTraversalPath);
+ EXPECT_EQ(getTraversalPathInZOrder(hierarchyBuilder.getHierarchy()), expectedTraversalPath);
+ expectedTraversalPath = {12, 121, 122, 1221};
+ EXPECT_EQ(getTraversalPath(hierarchyBuilder.getOffscreenHierarchy()), expectedTraversalPath);
+
+ // layer when onscreen can be reached as a relative child again
+ reparentLayer(12, 1);
+ UPDATE_AND_VERIFY(hierarchyBuilder);
+
+ expectedTraversalPath = {1, 11, 111, 12, 121, 122, 1221, 13, 2, 1221};
+ EXPECT_EQ(getTraversalPath(hierarchyBuilder.getHierarchy()), expectedTraversalPath);
+ expectedTraversalPath = {1, 11, 111, 12, 121, 122, 13, 2, 1221};
+ EXPECT_EQ(getTraversalPathInZOrder(hierarchyBuilder.getHierarchy()), expectedTraversalPath);
+ expectedTraversalPath = {};
+ EXPECT_EQ(getTraversalPath(hierarchyBuilder.getOffscreenHierarchy()), expectedTraversalPath);
+}
+
+TEST_F(LayerHierarchyTest, backgroundLayersAreBehindParentLayer) {
+ LayerHierarchyBuilder hierarchyBuilder(mLifecycleManager.getLayers());
+
+ updateBackgroundColor(1, 0.5);
+ UPDATE_AND_VERIFY(hierarchyBuilder);
+
+ std::vector<uint32_t> expectedTraversalPath = {1, 1222, 11, 111, 12, 121, 122, 1221, 13, 2};
+ EXPECT_EQ(getTraversalPath(hierarchyBuilder.getHierarchy()), expectedTraversalPath);
+ expectedTraversalPath = {1222, 1, 11, 111, 12, 121, 122, 1221, 13, 2};
+ EXPECT_EQ(getTraversalPathInZOrder(hierarchyBuilder.getHierarchy()), expectedTraversalPath);
+ expectedTraversalPath = {};
+ EXPECT_EQ(getTraversalPath(hierarchyBuilder.getOffscreenHierarchy()), expectedTraversalPath);
+}
+
+// cycle tests
+TEST_F(LayerHierarchyTest, ParentBecomesTheChild) {
+ // remove default hierarchy
+ mLifecycleManager = LayerLifecycleManager();
+ createRootLayer(1);
+ createLayer(11, 1);
+ reparentLayer(1, 11);
+ mLifecycleManager.commitChanges();
+ LayerHierarchyBuilder hierarchyBuilder(mLifecycleManager.getLayers());
+
+ std::vector<uint32_t> expectedTraversalPath = {};
+ EXPECT_EQ(getTraversalPath(hierarchyBuilder.getHierarchy()), expectedTraversalPath);
+ EXPECT_EQ(getTraversalPathInZOrder(hierarchyBuilder.getHierarchy()), expectedTraversalPath);
+ expectedTraversalPath = {};
+ EXPECT_EQ(getTraversalPath(hierarchyBuilder.getOffscreenHierarchy()), expectedTraversalPath);
+}
+
+TEST_F(LayerHierarchyTest, RelativeLoops) {
+ // remove default hierarchy
+ mLifecycleManager = LayerLifecycleManager();
+ createRootLayer(1);
+ createRootLayer(2);
+ createLayer(11, 1);
+ reparentRelativeLayer(11, 2);
+ reparentRelativeLayer(2, 11);
+ mLifecycleManager.commitChanges();
+ LayerHierarchyBuilder hierarchyBuilder(mLifecycleManager.getLayers());
+
+ // fix loop
+ uint32_t invalidRelativeRoot;
+ bool hasRelZLoop = hierarchyBuilder.getHierarchy().hasRelZLoop(invalidRelativeRoot);
+ EXPECT_TRUE(hasRelZLoop);
+ mLifecycleManager.fixRelativeZLoop(invalidRelativeRoot);
+ hierarchyBuilder.update(mLifecycleManager.getLayers(), mLifecycleManager.getDestroyedLayers());
+ EXPECT_EQ(invalidRelativeRoot, 11u);
+ EXPECT_FALSE(hierarchyBuilder.getHierarchy().hasRelZLoop(invalidRelativeRoot));
+
+ std::vector<uint32_t> expectedTraversalPath = {1, 11, 2, 2};
+ EXPECT_EQ(getTraversalPath(hierarchyBuilder.getHierarchy()), expectedTraversalPath);
+ expectedTraversalPath = {1};
+ EXPECT_EQ(getTraversalPathInZOrder(hierarchyBuilder.getHierarchy()), expectedTraversalPath);
+ expectedTraversalPath = {11, 2};
+ EXPECT_EQ(getTraversalPath(hierarchyBuilder.getOffscreenHierarchy()), expectedTraversalPath);
+}
+
+TEST_F(LayerHierarchyTest, IndirectRelativeLoops) {
+ // remove default hierarchy
+ mLifecycleManager = LayerLifecycleManager();
+ createRootLayer(1);
+ createRootLayer(2);
+ createLayer(11, 1);
+ createLayer(111, 11);
+ createLayer(21, 2);
+ createLayer(22, 2);
+ createLayer(221, 22);
+ reparentRelativeLayer(22, 111);
+ reparentRelativeLayer(11, 221);
+ mLifecycleManager.commitChanges();
+ LayerHierarchyBuilder hierarchyBuilder(mLifecycleManager.getLayers());
+
+ // fix loop
+ uint32_t invalidRelativeRoot;
+ bool hasRelZLoop = hierarchyBuilder.getHierarchy().hasRelZLoop(invalidRelativeRoot);
+ EXPECT_TRUE(hasRelZLoop);
+ mLifecycleManager.fixRelativeZLoop(invalidRelativeRoot);
+ hierarchyBuilder.update(mLifecycleManager.getLayers(), mLifecycleManager.getDestroyedLayers());
+ EXPECT_FALSE(hierarchyBuilder.getHierarchy().hasRelZLoop(invalidRelativeRoot));
+
+ std::vector<uint32_t> expectedTraversalPath = {1, 11, 111, 22, 221, 2, 21, 22, 221};
+ EXPECT_EQ(getTraversalPath(hierarchyBuilder.getHierarchy()), expectedTraversalPath);
+ expectedTraversalPath = {1, 2, 21};
+ EXPECT_EQ(getTraversalPathInZOrder(hierarchyBuilder.getHierarchy()), expectedTraversalPath);
+ expectedTraversalPath = {11, 111, 22, 221};
+ EXPECT_EQ(getTraversalPath(hierarchyBuilder.getOffscreenHierarchy()), expectedTraversalPath);
+}
+
+TEST_F(LayerHierarchyTest, ReparentRootLayerToNull) {
+ LayerHierarchyBuilder hierarchyBuilder(mLifecycleManager.getLayers());
+ reparentLayer(1, UNASSIGNED_LAYER_ID);
+ UPDATE_AND_VERIFY(hierarchyBuilder);
+
+ std::vector<uint32_t> expectedTraversalPath = {2};
+ EXPECT_EQ(getTraversalPath(hierarchyBuilder.getHierarchy()), expectedTraversalPath);
+ EXPECT_EQ(getTraversalPathInZOrder(hierarchyBuilder.getHierarchy()), expectedTraversalPath);
+ expectedTraversalPath = {1, 11, 111, 12, 121, 122, 1221, 13};
+ EXPECT_EQ(getTraversalPath(hierarchyBuilder.getOffscreenHierarchy()), expectedTraversalPath);
+}
+
+TEST_F(LayerHierarchyTest, AddRemoveLayerInSameTransaction) {
+ // remove default hierarchy
+ mLifecycleManager = LayerLifecycleManager();
+ LayerHierarchyBuilder hierarchyBuilder(mLifecycleManager.getLayers());
+ createRootLayer(1);
+ destroyLayerHandle(1);
+ UPDATE_AND_VERIFY(hierarchyBuilder);
+
+ std::vector<uint32_t> expectedTraversalPath = {};
+ EXPECT_EQ(getTraversalPath(hierarchyBuilder.getHierarchy()), expectedTraversalPath);
+ EXPECT_EQ(getTraversalPathInZOrder(hierarchyBuilder.getHierarchy()), expectedTraversalPath);
+ EXPECT_EQ(getTraversalPath(hierarchyBuilder.getOffscreenHierarchy()), expectedTraversalPath);
+}
+
+// traversal path test
+TEST_F(LayerHierarchyTest, traversalPathId) {
+ setZ(122, -1);
+ LayerHierarchyBuilder hierarchyBuilder(mLifecycleManager.getLayers());
+ auto checkTraversalPathIdVisitor =
+ [](const LayerHierarchy& hierarchy,
+ const LayerHierarchy::TraversalPath& traversalPath) -> bool {
+ EXPECT_EQ(hierarchy.getLayer()->id, traversalPath.id);
+ return true;
+ };
+ hierarchyBuilder.getHierarchy().traverse(checkTraversalPathIdVisitor);
+ hierarchyBuilder.getHierarchy().traverseInZOrder(checkTraversalPathIdVisitor);
+}
+
+} // namespace android::surfaceflinger::frontend
diff --git a/services/surfaceflinger/tests/unittests/RefreshRateSelectorTest.cpp b/services/surfaceflinger/tests/unittests/RefreshRateSelectorTest.cpp
index fdf2ffe..a3b3c4c 100644
--- a/services/surfaceflinger/tests/unittests/RefreshRateSelectorTest.cpp
+++ b/services/surfaceflinger/tests/unittests/RefreshRateSelectorTest.cpp
@@ -2943,5 +2943,18 @@
EXPECT_EQ(kModeId35, selector.getBestFrameRateMode({}, {.idle = true})->getId());
}
+TEST_P(RefreshRateSelectorTest, policyCanBeInfinity) {
+ auto selector = createSelector(kModes_60_120, kModeId120);
+
+ constexpr Fps inf = Fps::fromValue(std::numeric_limits<float>::infinity());
+
+ using namespace fps_approx_ops;
+ selector.setDisplayManagerPolicy({kModeId60, {0_Hz, inf}});
+
+ // With no layers, idle should still be lower priority than touch boost.
+ EXPECT_EQ(kMode120, selector.getMaxRefreshRateByPolicy());
+ EXPECT_EQ(kMode60, selector.getMinRefreshRateByPolicy());
+}
+
} // namespace
} // namespace android::scheduler
diff --git a/services/surfaceflinger/tests/unittests/SurfaceFlinger_DisplayModeSwitching.cpp b/services/surfaceflinger/tests/unittests/SurfaceFlinger_DisplayModeSwitching.cpp
index b81693a..074bf8c 100644
--- a/services/surfaceflinger/tests/unittests/SurfaceFlinger_DisplayModeSwitching.cpp
+++ b/services/surfaceflinger/tests/unittests/SurfaceFlinger_DisplayModeSwitching.cpp
@@ -144,7 +144,8 @@
// Verify that the next commit will complete the mode change and send
// a onModeChanged event to the framework.
- EXPECT_CALL(*mAppEventThread, onModeChanged(kMode90));
+ EXPECT_CALL(*mAppEventThread,
+ onModeChanged(scheduler::FrameRateMode{90_Hz, ftl::as_non_null(kMode90)}));
mFlinger.commit();
Mock::VerifyAndClearExpectations(mAppEventThread);
@@ -175,7 +176,8 @@
hal::HWConfigId(kModeId90.value()), _, _))
.WillOnce(DoAll(SetArgPointee<3>(timeline), Return(Error::NONE)));
- EXPECT_CALL(*mAppEventThread, onModeChanged(kMode90));
+ EXPECT_CALL(*mAppEventThread,
+ onModeChanged(scheduler::FrameRateMode{90_Hz, ftl::as_non_null(kMode90)}));
mFlinger.commit();
diff --git a/services/surfaceflinger/tests/unittests/SurfaceFlinger_ExcludeDolbyVisionTest.cpp b/services/surfaceflinger/tests/unittests/SurfaceFlinger_ExcludeDolbyVisionTest.cpp
new file mode 100644
index 0000000..0e149d2
--- /dev/null
+++ b/services/surfaceflinger/tests/unittests/SurfaceFlinger_ExcludeDolbyVisionTest.cpp
@@ -0,0 +1,109 @@
+/*
+ * Copyright 2020 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.
+ */
+
+#undef LOG_TAG
+#define LOG_TAG "LibSurfaceFlingerUnittests"
+
+#include "DisplayTransactionTestHelpers.h"
+
+namespace android {
+namespace {
+
+using FakeDisplayDeviceInjector = TestableSurfaceFlinger::FakeDisplayDeviceInjector;
+
+class ExcludeDolbyVisionTest : public DisplayTransactionTest {
+public:
+ void injectDisplayModes(std::vector<DisplayModePtr> displayModePtrs) {
+ DisplayModes modes;
+ for (DisplayModePtr displayMode : displayModePtrs) {
+ modes.try_emplace(displayMode->getId(), displayMode);
+ }
+
+ mDisplay = PrimaryDisplayVariant::makeFakeExistingDisplayInjector(this)
+ .setDisplayModes(std::move(modes), displayModePtrs[0]->getId())
+ .inject();
+ mDisplay->overrideHdrTypes(types);
+ }
+
+protected:
+ sp<DisplayDevice> mDisplay;
+
+ static constexpr DisplayModeId modeId1080p60{0};
+ static constexpr DisplayModeId modeId4k30{1};
+ static constexpr DisplayModeId modeId4k60{2};
+
+ static inline const DisplayModePtr mode1080p60 =
+ createDisplayMode(modeId1080p60, 60_Hz, 0, ui::Size(1920, 1080));
+ static inline const DisplayModePtr mode4k30 =
+ createDisplayMode(modeId4k30, 30_Hz, 1, ui::Size(3840, 2160));
+ static inline const DisplayModePtr mode4k30NonStandard =
+ createDisplayMode(modeId4k30, 30.1_Hz, 1, ui::Size(3840, 2160));
+ static inline const DisplayModePtr mode4k60 =
+ createDisplayMode(modeId4k60, 60_Hz, 2, ui::Size(3840, 2160));
+
+ const std::vector<ui::Hdr> types = {ui::Hdr::DOLBY_VISION, ui::Hdr::DOLBY_VISION_4K30,
+ ui::Hdr::HDR10_PLUS};
+};
+
+TEST_F(ExcludeDolbyVisionTest, excludesDolbyVisionOnModesHigherThan4k30) {
+ injectDisplayModes({mode4k60});
+ ui::DynamicDisplayInfo info;
+ mFlinger.getDynamicDisplayInfoFromToken(mDisplay->getDisplayToken().promote(), &info);
+
+ std::vector<ui::DisplayMode> displayModes = info.supportedDisplayModes;
+
+ ASSERT_EQ(1, displayModes.size());
+ ASSERT_TRUE(std::any_of(displayModes[0].supportedHdrTypes.begin(),
+ displayModes[0].supportedHdrTypes.end(),
+ [](ui::Hdr type) { return type == ui::Hdr::HDR10_PLUS; }));
+ ASSERT_TRUE(displayModes[0].supportedHdrTypes.size() == 1);
+}
+
+TEST_F(ExcludeDolbyVisionTest, includesDolbyVisionOnModesLowerThanOrEqualTo4k30) {
+ injectDisplayModes({mode1080p60, mode4k30, mode4k30NonStandard});
+ ui::DynamicDisplayInfo info;
+ mFlinger.getDynamicDisplayInfoFromToken(mDisplay->getDisplayToken().promote(), &info);
+
+ std::vector<ui::DisplayMode> displayModes = info.supportedDisplayModes;
+
+ ASSERT_EQ(2, displayModes.size());
+ for (size_t i = 0; i < displayModes.size(); i++) {
+ ASSERT_TRUE(std::any_of(displayModes[i].supportedHdrTypes.begin(),
+ displayModes[i].supportedHdrTypes.end(),
+ [](ui::Hdr type) { return type == ui::Hdr::HDR10_PLUS; }));
+ ASSERT_TRUE(std::any_of(displayModes[i].supportedHdrTypes.begin(),
+ displayModes[i].supportedHdrTypes.end(),
+ [](ui::Hdr type) { return type == ui::Hdr::DOLBY_VISION; }));
+ ASSERT_TRUE(displayModes[i].supportedHdrTypes.size() == 2);
+ }
+}
+
+TEST_F(ExcludeDolbyVisionTest, 4k30IsNotReportedAsAValidHdrType) {
+ injectDisplayModes({mode4k60});
+ ui::DynamicDisplayInfo info;
+ mFlinger.getDynamicDisplayInfoFromToken(mDisplay->getDisplayToken().promote(), &info);
+
+ std::vector<ui::Hdr> displayHdrTypes = info.hdrCapabilities.getSupportedHdrTypes();
+
+ ASSERT_EQ(2, displayHdrTypes.size());
+ ASSERT_TRUE(std::any_of(displayHdrTypes.begin(), displayHdrTypes.end(),
+ [](ui::Hdr type) { return type == ui::Hdr::HDR10_PLUS; }));
+ ASSERT_TRUE(std::any_of(displayHdrTypes.begin(), displayHdrTypes.end(),
+ [](ui::Hdr type) { return type == ui::Hdr::DOLBY_VISION; }));
+}
+
+} // namespace
+} // namespace android
diff --git a/services/surfaceflinger/tests/unittests/SurfaceFlinger_PowerHintTest.cpp b/services/surfaceflinger/tests/unittests/SurfaceFlinger_PowerHintTest.cpp
index bc66961..622717f 100644
--- a/services/surfaceflinger/tests/unittests/SurfaceFlinger_PowerHintTest.cpp
+++ b/services/surfaceflinger/tests/unittests/SurfaceFlinger_PowerHintTest.cpp
@@ -97,7 +97,6 @@
.setNativeWindow(mNativeWindow)
.setPowerMode(hal::PowerMode::ON)
.inject();
- mFlinger.mutableActiveDisplayId() = mDisplay->getPhysicalId();
}
void SurfaceFlingerPowerHintTest::setupScheduler() {
diff --git a/services/surfaceflinger/tests/unittests/SurfaceFlinger_SetPowerModeInternalTest.cpp b/services/surfaceflinger/tests/unittests/SurfaceFlinger_SetPowerModeInternalTest.cpp
index 25857ec..0fb8e2b 100644
--- a/services/surfaceflinger/tests/unittests/SurfaceFlinger_SetPowerModeInternalTest.cpp
+++ b/services/surfaceflinger/tests/unittests/SurfaceFlinger_SetPowerModeInternalTest.cpp
@@ -259,12 +259,6 @@
auto injector = Display::makeFakeExistingDisplayInjector(test);
const auto display = injector.inject();
display->setPowerMode(mode);
- if (injector.physicalDisplay()
- .transform(&display::PhysicalDisplay::isInternal)
- .value_or(false)) {
- test->mFlinger.mutableActiveDisplayId() = display->getPhysicalId();
- }
-
return display;
}
@@ -490,5 +484,37 @@
transitionDisplayCommon<ExternalDisplayPowerCase<TransitionOnToUnknownVariant>>();
}
+TEST_F(SetPowerModeInternalTest, designatesLeaderDisplay) {
+ using Case = SimplePrimaryDisplayCase;
+
+ // --------------------------------------------------------------------
+ // Preconditions
+
+ // Inject a primary display with uninitialized power mode.
+ constexpr bool kInitPowerMode = false;
+ Case::Display::injectHwcDisplay<kInitPowerMode>(this);
+ auto injector = Case::Display::makeFakeExistingDisplayInjector(this);
+ injector.setPowerMode(std::nullopt);
+ const auto display = injector.inject();
+
+ // --------------------------------------------------------------------
+ // Invocation
+
+ // FakeDisplayDeviceInjector registers the display with Scheduler, so it has already been
+ // designated as the leader. Set an arbitrary leader to verify that `setPowerModeInternal`
+ // designates a leader regardless of any preceding `Scheduler::registerDisplay` call(s).
+ constexpr PhysicalDisplayId kPlaceholderId = PhysicalDisplayId::fromPort(42);
+ ASSERT_NE(display->getPhysicalId(), kPlaceholderId);
+ mFlinger.scheduler()->setLeaderDisplay(kPlaceholderId);
+
+ mFlinger.setPowerModeInternal(display, PowerMode::ON);
+
+ // --------------------------------------------------------------------
+ // Postconditions
+
+ // The primary display should be designated as the leader.
+ EXPECT_EQ(mFlinger.scheduler()->leaderDisplayId(), display->getPhysicalId());
+}
+
} // namespace
} // namespace android
diff --git a/services/surfaceflinger/tests/unittests/TestableScheduler.h b/services/surfaceflinger/tests/unittests/TestableScheduler.h
index 54c10c5..b8a6063 100644
--- a/services/surfaceflinger/tests/unittests/TestableScheduler.h
+++ b/services/surfaceflinger/tests/unittests/TestableScheduler.h
@@ -87,6 +87,10 @@
Scheduler::unregisterDisplay(displayId);
}
+ std::optional<PhysicalDisplayId> leaderDisplayId() const NO_THREAD_SAFETY_ANALYSIS {
+ return mLeaderDisplayId;
+ }
+
void setLeaderDisplay(PhysicalDisplayId displayId) {
ftl::FakeGuard guard(kMainThreadContext);
Scheduler::setLeaderDisplay(displayId);
diff --git a/services/surfaceflinger/tests/unittests/TestableSurfaceFlinger.h b/services/surfaceflinger/tests/unittests/TestableSurfaceFlinger.h
index e29dd67..2117084 100644
--- a/services/surfaceflinger/tests/unittests/TestableSurfaceFlinger.h
+++ b/services/surfaceflinger/tests/unittests/TestableSurfaceFlinger.h
@@ -30,6 +30,7 @@
#include <ftl/fake_guard.h>
#include <gui/ScreenCaptureResults.h>
+#include <ui/DynamicDisplayInfo.h>
#include "DisplayDevice.h"
#include "FakeVsyncConfiguration.h"
#include "FrameTracer/FrameTracer.h"
@@ -470,7 +471,7 @@
void onActiveDisplayChanged(const sp<DisplayDevice>& activeDisplay) {
Mutex::Autolock lock(mFlinger->mStateLock);
ftl::FakeGuard guard(kMainThreadContext);
- mFlinger->onActiveDisplayChangedLocked(activeDisplay);
+ mFlinger->onActiveDisplayChangedLocked(nullptr, activeDisplay);
}
auto createLayer(LayerCreationArgs& args, const sp<IBinder>& parentHandle,
@@ -486,6 +487,11 @@
void updateLayerMetadataSnapshot() { mFlinger->updateLayerMetadataSnapshot(); }
+ void getDynamicDisplayInfoFromToken(const sp<IBinder>& displayToken,
+ ui::DynamicDisplayInfo* dynamicDisplayInfo) {
+ mFlinger->getDynamicDisplayInfoFromToken(displayToken, dynamicDisplayInfo);
+ }
+
/* ------------------------------------------------------------------------
* Read-only access to private data to assert post-conditions.
*/
@@ -621,7 +627,7 @@
return *this;
}
- auto& setPowerMode(hal::PowerMode mode) {
+ auto& setPowerMode(std::optional<hal::PowerMode> mode) {
mPowerMode = mode;
return *this;
}
@@ -643,16 +649,18 @@
// is much longer lived.
auto display = std::make_unique<HWC2Display>(*composer, *mCapabilities, mHwcDisplayId,
mHwcDisplayType);
-
display->mutableIsConnected() = true;
- display->setPowerMode(mPowerMode);
+
+ if (mPowerMode) {
+ display->setPowerMode(*mPowerMode);
+ }
+
flinger->mutableHwcDisplayData()[mDisplayId].hwcDisplay = std::move(display);
EXPECT_CALL(*composer, getDisplayConfigs(mHwcDisplayId, _))
.WillRepeatedly(
DoAll(SetArgPointee<1>(std::vector<hal::HWConfigId>{mActiveConfig}),
Return(hal::Error::NONE)));
-
EXPECT_CALL(*composer,
getDisplayAttribute(mHwcDisplayId, mActiveConfig, hal::Attribute::WIDTH, _))
.WillRepeatedly(DoAll(SetArgPointee<3>(mResolution.getWidth()),
@@ -711,7 +719,7 @@
int32_t mDpiY = DEFAULT_DPI;
int32_t mConfigGroup = DEFAULT_CONFIG_GROUP;
hal::HWConfigId mActiveConfig = DEFAULT_ACTIVE_CONFIG;
- hal::PowerMode mPowerMode = DEFAULT_POWER_MODE;
+ std::optional<hal::PowerMode> mPowerMode = DEFAULT_POWER_MODE;
const std::unordered_set<aidl::android::hardware::graphics::composer3::Capability>*
mCapabilities = nullptr;
};
@@ -788,7 +796,7 @@
return *this;
}
- auto& setPowerMode(hal::PowerMode mode) {
+ auto& setPowerMode(std::optional<hal::PowerMode> mode) {
mCreationArgs.initialPowerMode = mode;
return *this;
}
@@ -853,6 +861,10 @@
LOG_ALWAYS_FATAL_IF(!physicalIdOpt);
const auto physicalId = *physicalIdOpt;
+ if (mCreationArgs.isPrimary) {
+ mFlinger.mutableActiveDisplayId() = physicalId;
+ }
+
LOG_ALWAYS_FATAL_IF(!mHwcDisplayId);
const auto activeMode = modes.get(activeModeId);
diff --git a/services/surfaceflinger/tests/unittests/TimeStatsTest.cpp b/services/surfaceflinger/tests/unittests/TimeStatsTest.cpp
index 1dd4f25..a9ae1d3 100644
--- a/services/surfaceflinger/tests/unittests/TimeStatsTest.cpp
+++ b/services/surfaceflinger/tests/unittests/TimeStatsTest.cpp
@@ -44,11 +44,14 @@
namespace {
using testing::_;
+using testing::AllOf;
using testing::AnyNumber;
using testing::Contains;
+using testing::ElementsAre;
using testing::HasSubstr;
using testing::InSequence;
using testing::Not;
+using testing::Property;
using testing::SizeIs;
using testing::StrEq;
using testing::UnorderedElementsAre;
@@ -645,7 +648,7 @@
ASSERT_TRUE(globalProto.ParseFromString(inputCommand(InputCommand::DUMP_ALL, FMT_PROTO)));
ASSERT_EQ(1, globalProto.stats_size());
- const SFTimeStatsLayerProto& layerProto = globalProto.stats().Get(0);
+ const SFTimeStatsLayerProto& layerProto = globalProto.stats(0);
ASSERT_TRUE(layerProto.has_layer_name());
EXPECT_EQ(genLayerName(LAYER_ID_0), layerProto.layer_name());
ASSERT_TRUE(layerProto.has_total_frames());
@@ -653,7 +656,7 @@
ASSERT_EQ(6, layerProto.deltas_size());
for (const SFTimeStatsDeltaProto& deltaProto : layerProto.deltas()) {
ASSERT_EQ(1, deltaProto.histograms_size());
- const SFTimeStatsHistogramBucketProto& histogramProto = deltaProto.histograms().Get(0);
+ const SFTimeStatsHistogramBucketProto& histogramProto = deltaProto.histograms(0);
EXPECT_EQ(1, histogramProto.frame_count());
if ("post2acquire" == deltaProto.delta_name()) {
EXPECT_EQ(1, histogramProto.time_millis());
@@ -673,6 +676,46 @@
}
}
+using LayerProto = SFTimeStatsLayerProto;
+using DeltaProto = SFTimeStatsDeltaProto;
+using BucketProto = SFTimeStatsHistogramBucketProto;
+
+TEST_F(TimeStatsTest, canComputeLayerStabilityHistogram) {
+ EXPECT_TRUE(inputCommand(InputCommand::ENABLE, FMT_STRING).empty());
+
+ insertTimeRecord(NORMAL_SEQUENCE, LAYER_ID_0, 1, 1000000);
+ insertTimeRecord(NORMAL_SEQUENCE, LAYER_ID_0, 2, 2000000);
+ insertTimeRecord(NORMAL_SEQUENCE, LAYER_ID_0, 3, 3000000); // 0ms delta
+ // Slightly unstable frames
+ insertTimeRecord(NORMAL_SEQUENCE, LAYER_ID_0, 4, 5000000); // 1ms delta
+ insertTimeRecord(NORMAL_SEQUENCE, LAYER_ID_0, 5, 6000000); // 1ms delta
+
+ SFTimeStatsGlobalProto globalProto;
+ ASSERT_TRUE(globalProto.ParseFromString(inputCommand(InputCommand::DUMP_ALL, FMT_PROTO)));
+
+ EXPECT_THAT(globalProto.stats(),
+ ElementsAre(AllOf(
+ Property(&LayerProto::layer_name, genLayerName(LAYER_ID_0)),
+ Property(&LayerProto::total_frames, 4),
+ Property(&LayerProto::deltas,
+ Contains(AllOf(Property(&DeltaProto::delta_name,
+ "present2presentDelta"),
+ Property(&DeltaProto::histograms,
+ UnorderedElementsAre(
+ AllOf(Property(&BucketProto::
+ time_millis,
+ 0),
+ Property(&BucketProto::
+ frame_count,
+ 1)),
+ AllOf(Property(&BucketProto::
+ time_millis,
+ 1),
+ Property(&BucketProto::
+ frame_count,
+ 2))))))))));
+}
+
TEST_F(TimeStatsTest, canNotInsertInvalidLayerNameTimeStats) {
EXPECT_TRUE(inputCommand(InputCommand::ENABLE, FMT_STRING).empty());
@@ -1099,8 +1142,10 @@
kGameMode, JankType::None, DISPLAY_DEADLINE_DELTA,
DISPLAY_PRESENT_JITTER, APP_DEADLINE_DELTA});
+ std::vector<uint8_t> pulledBytes;
+ EXPECT_TRUE(mTimeStats->onPullAtom(10062 /*SURFACEFLINGER_STATS_GLOBAL_INFO*/, &pulledBytes));
std::string pulledData;
- EXPECT_TRUE(mTimeStats->onPullAtom(10062 /*SURFACEFLINGER_STATS_GLOBAL_INFO*/, &pulledData));
+ pulledData.assign(pulledBytes.begin(), pulledBytes.end());
android::surfaceflinger::SurfaceflingerStatsGlobalInfoWrapper atomList;
ASSERT_TRUE(atomList.ParseFromString(pulledData));
@@ -1234,8 +1279,10 @@
GameMode::Standard, JankType::None, DISPLAY_DEADLINE_DELTA,
DISPLAY_PRESENT_JITTER, APP_DEADLINE_DELTA_3MS});
+ std::vector<uint8_t> pulledBytes;
+ EXPECT_TRUE(mTimeStats->onPullAtom(10063 /*SURFACEFLINGER_STATS_LAYER_INFO*/, &pulledBytes));
std::string pulledData;
- EXPECT_TRUE(mTimeStats->onPullAtom(10063 /*SURFACEFLINGER_STATS_LAYER_INFO*/, &pulledData));
+ pulledData.assign(pulledBytes.begin(), pulledBytes.end());
SurfaceflingerStatsLayerInfoWrapper atomList;
ASSERT_TRUE(atomList.ParseFromString(pulledData));
@@ -1322,8 +1369,10 @@
insertTimeRecord(NORMAL_SEQUENCE, LAYER_ID_0, 5, 4000000, {}, GameMode::Battery);
insertTimeRecord(NORMAL_SEQUENCE, LAYER_ID_0, 6, 5000000, {}, GameMode::Custom);
+ std::vector<uint8_t> pulledBytes;
+ EXPECT_TRUE(mTimeStats->onPullAtom(10063 /*SURFACEFLINGER_STATS_LAYER_INFO*/, &pulledBytes));
std::string pulledData;
- EXPECT_TRUE(mTimeStats->onPullAtom(10063 /*SURFACEFLINGER_STATS_LAYER_INFO*/, &pulledData));
+ pulledData.assign(pulledBytes.begin(), pulledBytes.end());
SurfaceflingerStatsLayerInfoWrapper atomList;
ASSERT_TRUE(atomList.ParseFromString(pulledData));
@@ -1412,8 +1461,10 @@
insertTimeRecord(NORMAL_SEQUENCE, LAYER_ID_1, 1, 2000000);
insertTimeRecord(NORMAL_SEQUENCE, LAYER_ID_1, 2, 3000000);
+ std::vector<uint8_t> pulledBytes;
+ EXPECT_TRUE(mTimeStats->onPullAtom(10063 /*SURFACEFLINGER_STATS_LAYER_INFO*/, &pulledBytes));
std::string pulledData;
- EXPECT_TRUE(mTimeStats->onPullAtom(10063 /*SURFACEFLINGER_STATS_LAYER_INFO*/, &pulledData));
+ pulledData.assign(pulledBytes.begin(), pulledBytes.end());
SurfaceflingerStatsLayerInfoWrapper atomList;
ASSERT_TRUE(atomList.ParseFromString(pulledData));
@@ -1437,8 +1488,10 @@
mTimeStats->setPresentFenceGlobal(std::make_shared<FenceTime>(3000000));
mTimeStats->setPresentFenceGlobal(std::make_shared<FenceTime>(5000000));
+ std::vector<uint8_t> pulledBytes;
+ EXPECT_TRUE(mTimeStats->onPullAtom(10063 /*SURFACEFLINGER_STATS_LAYER_INFO*/, &pulledBytes));
std::string pulledData;
- EXPECT_TRUE(mTimeStats->onPullAtom(10063 /*SURFACEFLINGER_STATS_LAYER_INFO*/, &pulledData));
+ pulledData.assign(pulledBytes.begin(), pulledBytes.end());
SurfaceflingerStatsLayerInfoWrapper atomList;
ASSERT_TRUE(atomList.ParseFromString(pulledData));
@@ -1456,8 +1509,10 @@
insertTimeRecord(NORMAL_SEQUENCE, LAYER_ID_0, 3, 4000000);
insertTimeRecord(NORMAL_SEQUENCE, LAYER_ID_0, 4, 5000000);
+ std::vector<uint8_t> pulledBytes;
+ EXPECT_TRUE(mTimeStats->onPullAtom(10063 /*SURFACEFLINGER_STATS_LAYER_INFO*/, &pulledBytes));
std::string pulledData;
- EXPECT_TRUE(mTimeStats->onPullAtom(10063 /*SURFACEFLINGER_STATS_LAYER_INFO*/, &pulledData));
+ pulledData.assign(pulledBytes.begin(), pulledBytes.end());
SurfaceflingerStatsLayerInfoWrapper atomList;
ASSERT_TRUE(atomList.ParseFromString(pulledData));
@@ -1476,8 +1531,10 @@
insertTimeRecord(NORMAL_SEQUENCE, LAYER_ID_1, 2, 3000000);
insertTimeRecord(NORMAL_SEQUENCE, LAYER_ID_1, 4, 5000000);
+ std::vector<uint8_t> pulledBytes;
+ EXPECT_TRUE(mTimeStats->onPullAtom(10063 /*SURFACEFLINGER_STATS_LAYER_INFO*/, &pulledBytes));
std::string pulledData;
- EXPECT_TRUE(mTimeStats->onPullAtom(10063 /*SURFACEFLINGER_STATS_LAYER_INFO*/, &pulledData));
+ pulledData.assign(pulledBytes.begin(), pulledBytes.end());
SurfaceflingerStatsLayerInfoWrapper atomList;
ASSERT_TRUE(atomList.ParseFromString(pulledData));
diff --git a/services/surfaceflinger/tests/unittests/TransactionFrameTracerTest.cpp b/services/surfaceflinger/tests/unittests/TransactionFrameTracerTest.cpp
index 09d002f..1173d1c 100644
--- a/services/surfaceflinger/tests/unittests/TransactionFrameTracerTest.cpp
+++ b/services/surfaceflinger/tests/unittests/TransactionFrameTracerTest.cpp
@@ -126,7 +126,7 @@
HAL_PIXEL_FORMAT_RGBA_8888,
0ULL /*usage*/);
layer->setBuffer(externalTexture, bufferData, postTime, /*desiredPresentTime*/ 30, false,
- dequeueTime, FrameTimelineInfo{}, 0);
+ dequeueTime, FrameTimelineInfo{});
commitTransaction(layer.get());
nsecs_t latchTime = 25;
diff --git a/services/surfaceflinger/tests/unittests/TransactionSurfaceFrameTest.cpp b/services/surfaceflinger/tests/unittests/TransactionSurfaceFrameTest.cpp
index 7dfbcc0..ae03db4 100644
--- a/services/surfaceflinger/tests/unittests/TransactionSurfaceFrameTest.cpp
+++ b/services/surfaceflinger/tests/unittests/TransactionSurfaceFrameTest.cpp
@@ -131,7 +131,7 @@
FrameTimelineInfo ftInfo;
ftInfo.vsyncId = 1;
ftInfo.inputEventId = 0;
- layer->setBuffer(externalTexture, bufferData, 10, 20, false, std::nullopt, ftInfo, 0);
+ layer->setBuffer(externalTexture, bufferData, 10, 20, false, std::nullopt, ftInfo);
acquireFence->signalForTest(12);
commitTransaction(layer.get());
@@ -166,7 +166,7 @@
FrameTimelineInfo ftInfo;
ftInfo.vsyncId = 1;
ftInfo.inputEventId = 0;
- layer->setBuffer(externalTexture1, bufferData, 10, 20, false, std::nullopt, ftInfo, 0);
+ layer->setBuffer(externalTexture1, bufferData, 10, 20, false, std::nullopt, ftInfo);
EXPECT_EQ(0u, layer->mDrawingState.bufferlessSurfaceFramesTX.size());
ASSERT_NE(nullptr, layer->mDrawingState.bufferSurfaceFrameTX);
const auto droppedSurfaceFrame = layer->mDrawingState.bufferSurfaceFrameTX;
@@ -183,7 +183,7 @@
2ULL /* bufferId */,
HAL_PIXEL_FORMAT_RGBA_8888,
0ULL /*usage*/);
- layer->setBuffer(externalTexture2, bufferData, 10, 20, false, std::nullopt, ftInfo, 0);
+ layer->setBuffer(externalTexture2, bufferData, 10, 20, false, std::nullopt, ftInfo);
nsecs_t end = systemTime();
acquireFence2->signalForTest(12);
@@ -229,7 +229,7 @@
1ULL /* bufferId */,
HAL_PIXEL_FORMAT_RGBA_8888,
0ULL /*usage*/);
- layer->setBuffer(externalTexture, bufferData, 10, 20, false, std::nullopt, ftInfo, 0);
+ layer->setBuffer(externalTexture, bufferData, 10, 20, false, std::nullopt, ftInfo);
acquireFence->signalForTest(12);
EXPECT_EQ(0u, layer->mDrawingState.bufferlessSurfaceFramesTX.size());
@@ -264,7 +264,7 @@
FrameTimelineInfo ftInfo;
ftInfo.vsyncId = 1;
ftInfo.inputEventId = 0;
- layer->setBuffer(externalTexture, bufferData, 10, 20, false, std::nullopt, ftInfo, 0);
+ layer->setBuffer(externalTexture, bufferData, 10, 20, false, std::nullopt, ftInfo);
EXPECT_EQ(0u, layer->mDrawingState.bufferlessSurfaceFramesTX.size());
ASSERT_NE(nullptr, layer->mDrawingState.bufferSurfaceFrameTX);
@@ -307,7 +307,7 @@
FrameTimelineInfo ftInfo3;
ftInfo3.vsyncId = 3;
ftInfo3.inputEventId = 0;
- layer->setBuffer(externalTexture, bufferData, 10, 20, false, std::nullopt, ftInfo3, 0);
+ layer->setBuffer(externalTexture, bufferData, 10, 20, false, std::nullopt, ftInfo3);
EXPECT_EQ(2u, layer->mDrawingState.bufferlessSurfaceFramesTX.size());
ASSERT_NE(nullptr, layer->mDrawingState.bufferSurfaceFrameTX);
const auto bufferSurfaceFrameTX = layer->mDrawingState.bufferSurfaceFrameTX;
@@ -352,7 +352,7 @@
FrameTimelineInfo ftInfo;
ftInfo.vsyncId = 1;
ftInfo.inputEventId = 0;
- layer->setBuffer(externalTexture1, bufferData, 10, 20, false, std::nullopt, ftInfo, 0);
+ layer->setBuffer(externalTexture1, bufferData, 10, 20, false, std::nullopt, ftInfo);
ASSERT_NE(nullptr, layer->mDrawingState.bufferSurfaceFrameTX);
const auto droppedSurfaceFrame = layer->mDrawingState.bufferSurfaceFrameTX;
@@ -367,7 +367,7 @@
1ULL /* bufferId */,
HAL_PIXEL_FORMAT_RGBA_8888,
0ULL /*usage*/);
- layer->setBuffer(externalTexture2, bufferData, 10, 20, false, std::nullopt, ftInfo, 0);
+ layer->setBuffer(externalTexture2, bufferData, 10, 20, false, std::nullopt, ftInfo);
acquireFence2->signalForTest(12);
ASSERT_NE(nullptr, layer->mDrawingState.bufferSurfaceFrameTX);
@@ -404,7 +404,7 @@
FrameTimelineInfo ftInfo;
ftInfo.vsyncId = 1;
ftInfo.inputEventId = 0;
- layer->setBuffer(externalTexture1, bufferData, 10, 20, false, std::nullopt, ftInfo, 0);
+ layer->setBuffer(externalTexture1, bufferData, 10, 20, false, std::nullopt, ftInfo);
EXPECT_EQ(0u, layer->mDrawingState.bufferlessSurfaceFramesTX.size());
ASSERT_NE(nullptr, layer->mDrawingState.bufferSurfaceFrameTX);
const auto droppedSurfaceFrame1 = layer->mDrawingState.bufferSurfaceFrameTX;
@@ -424,7 +424,7 @@
FrameTimelineInfo ftInfoInv;
ftInfoInv.vsyncId = FrameTimelineInfo::INVALID_VSYNC_ID;
ftInfoInv.inputEventId = 0;
- layer->setBuffer(externalTexture2, bufferData, 10, 20, false, std::nullopt, ftInfoInv, 0);
+ layer->setBuffer(externalTexture2, bufferData, 10, 20, false, std::nullopt, ftInfoInv);
auto dropEndTime1 = systemTime();
EXPECT_EQ(0u, layer->mDrawingState.bufferlessSurfaceFramesTX.size());
ASSERT_NE(nullptr, layer->mDrawingState.bufferSurfaceFrameTX);
@@ -445,7 +445,7 @@
FrameTimelineInfo ftInfo2;
ftInfo2.vsyncId = 2;
ftInfo2.inputEventId = 0;
- layer->setBuffer(externalTexture3, bufferData, 10, 20, false, std::nullopt, ftInfo2, 0);
+ layer->setBuffer(externalTexture3, bufferData, 10, 20, false, std::nullopt, ftInfo2);
auto dropEndTime2 = systemTime();
acquireFence3->signalForTest(12);
@@ -494,7 +494,7 @@
FrameTimelineInfo ftInfo;
ftInfo.vsyncId = 1;
ftInfo.inputEventId = 0;
- layer->setBuffer(externalTexture, bufferData, 10, 20, false, std::nullopt, ftInfo, 0);
+ layer->setBuffer(externalTexture, bufferData, 10, 20, false, std::nullopt, ftInfo);
FrameTimelineInfo ftInfo2;
ftInfo2.vsyncId = 2;
ftInfo2.inputEventId = 0;
diff --git a/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockComposer.h b/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockComposer.h
index 5ee38ec..836e3a4 100644
--- a/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockComposer.h
+++ b/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockComposer.h
@@ -32,7 +32,6 @@
using android::hardware::graphics::common::V1_1::RenderIntent;
using android::hardware::graphics::common::V1_2::ColorMode;
using android::hardware::graphics::common::V1_2::Dataspace;
-using android::hardware::graphics::common::V1_2::Hdr;
using android::hardware::graphics::common::V1_2::PixelFormat;
using android::hardware::graphics::composer::V2_1::Config;
diff --git a/services/surfaceflinger/tests/unittests/mock/MockEventThread.h b/services/surfaceflinger/tests/unittests/mock/MockEventThread.h
index ded6806..f8567bd 100644
--- a/services/surfaceflinger/tests/unittests/mock/MockEventThread.h
+++ b/services/surfaceflinger/tests/unittests/mock/MockEventThread.h
@@ -34,7 +34,7 @@
MOCK_METHOD0(onScreenReleased, void());
MOCK_METHOD0(onScreenAcquired, void());
MOCK_METHOD2(onHotplugReceived, void(PhysicalDisplayId, bool));
- MOCK_METHOD1(onModeChanged, void(DisplayModePtr));
+ MOCK_METHOD1(onModeChanged, void(const scheduler::FrameRateMode &));
MOCK_METHOD2(onFrameRateOverridesChanged,
void(PhysicalDisplayId, std::vector<FrameRateOverride>));
MOCK_CONST_METHOD1(dump, void(std::string&));
diff --git a/services/surfaceflinger/tests/unittests/mock/MockTimeStats.h b/services/surfaceflinger/tests/unittests/mock/MockTimeStats.h
index 0dee800..86fbadc 100644
--- a/services/surfaceflinger/tests/unittests/mock/MockTimeStats.h
+++ b/services/surfaceflinger/tests/unittests/mock/MockTimeStats.h
@@ -27,7 +27,7 @@
TimeStats();
~TimeStats() override;
- MOCK_METHOD2(onPullAtom, bool(const int, std::string*));
+ MOCK_METHOD2(onPullAtom, bool(const int, std::vector<uint8_t>*));
MOCK_METHOD3(parseArgs, void(bool, const Vector<String16>&, std::string&));
MOCK_METHOD0(isEnabled, bool());
MOCK_METHOD0(miniDump, std::string());
diff --git a/vulkan/libvulkan/api_gen.cpp b/vulkan/libvulkan/api_gen.cpp
index df70bf4..a9706bc 100644
--- a/vulkan/libvulkan/api_gen.cpp
+++ b/vulkan/libvulkan/api_gen.cpp
@@ -682,6 +682,7 @@
"vkGetPhysicalDeviceMemoryProperties2",
"vkGetPhysicalDeviceMemoryProperties2KHR",
"vkGetPhysicalDeviceMultisamplePropertiesEXT",
+ "vkGetPhysicalDeviceOpticalFlowImageFormatsNV",
"vkGetPhysicalDevicePresentRectanglesKHR",
"vkGetPhysicalDeviceProperties",
"vkGetPhysicalDeviceProperties2",
diff --git a/vulkan/libvulkan/driver.cpp b/vulkan/libvulkan/driver.cpp
index 4927150..a99355f 100644
--- a/vulkan/libvulkan/driver.cpp
+++ b/vulkan/libvulkan/driver.cpp
@@ -1027,8 +1027,11 @@
}
}
-bool GetAndroidNativeBufferSpecVersion9Support(
- VkPhysicalDevice physicalDevice) {
+VkResult GetAndroidNativeBufferSpecVersion9Support(
+ VkPhysicalDevice physicalDevice,
+ bool& support) {
+ support = false;
+
const InstanceData& data = GetData(physicalDevice);
// Call to get propertyCount
@@ -1038,6 +1041,10 @@
physicalDevice, nullptr, &propertyCount, nullptr);
ATRACE_END();
+ if (result != VK_SUCCESS && result != VK_INCOMPLETE) {
+ return result;
+ }
+
// Call to enumerate properties
std::vector<VkExtensionProperties> properties(propertyCount);
ATRACE_BEGIN("driver.EnumerateDeviceExtensionProperties");
@@ -1045,6 +1052,10 @@
physicalDevice, nullptr, &propertyCount, properties.data());
ATRACE_END();
+ if (result != VK_SUCCESS && result != VK_INCOMPLETE) {
+ return result;
+ }
+
for (uint32_t i = 0; i < propertyCount; i++) {
auto& prop = properties[i];
@@ -1053,11 +1064,12 @@
continue;
if (prop.specVersion >= 9) {
- return true;
+ support = true;
+ return result;
}
}
- return false;
+ return result;
}
VkResult EnumerateDeviceExtensionProperties(
@@ -1101,18 +1113,30 @@
swapchainCompFeats.sType =
VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_IMAGE_COMPRESSION_CONTROL_SWAPCHAIN_FEATURES_EXT;
swapchainCompFeats.pNext = nullptr;
+ swapchainCompFeats.imageCompressionControlSwapchain = false;
VkPhysicalDeviceImageCompressionControlFeaturesEXT imageCompFeats = {};
imageCompFeats.sType =
VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_IMAGE_COMPRESSION_CONTROL_FEATURES_EXT;
imageCompFeats.pNext = &swapchainCompFeats;
+ imageCompFeats.imageCompressionControl = false;
VkPhysicalDeviceFeatures2 feats2 = {};
feats2.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_FEATURES_2;
feats2.pNext = &imageCompFeats;
- GetPhysicalDeviceFeatures2(physicalDevice, &feats2);
+ const auto& driver = GetData(physicalDevice).driver;
+ if (driver.GetPhysicalDeviceFeatures2 ||
+ driver.GetPhysicalDeviceFeatures2KHR) {
+ GetPhysicalDeviceFeatures2(physicalDevice, &feats2);
+ }
- bool anb9 = GetAndroidNativeBufferSpecVersion9Support(physicalDevice);
+ bool anb9 = false;
+ VkResult result =
+ GetAndroidNativeBufferSpecVersion9Support(physicalDevice, anb9);
+
+ if (result != VK_SUCCESS && result != VK_INCOMPLETE) {
+ return result;
+ }
if (anb9 && imageCompFeats.imageCompressionControl) {
loader_extensions.push_back(
@@ -1142,7 +1166,7 @@
}
ATRACE_BEGIN("driver.EnumerateDeviceExtensionProperties");
- VkResult result = data.driver.EnumerateDeviceExtensionProperties(
+ result = data.driver.EnumerateDeviceExtensionProperties(
physicalDevice, pLayerName, pPropertyCount, pProperties);
ATRACE_END();
@@ -1532,6 +1556,11 @@
} break;
case VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_IMAGE_COMPRESSION_CONTROL_SWAPCHAIN_FEATURES_EXT: {
+ VkPhysicalDeviceImageCompressionControlSwapchainFeaturesEXT*
+ compressionFeat = reinterpret_cast<
+ VkPhysicalDeviceImageCompressionControlSwapchainFeaturesEXT*>(
+ pFeats);
+ compressionFeat->imageCompressionControlSwapchain = false;
imageCompressionControlSwapchainInChain = true;
} break;
@@ -1551,6 +1580,7 @@
imageCompFeats.sType =
VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_IMAGE_COMPRESSION_CONTROL_FEATURES_EXT;
imageCompFeats.pNext = nullptr;
+ imageCompFeats.imageCompressionControl = false;
VkPhysicalDeviceFeatures2 feats2 = {};
feats2.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_FEATURES_2;
diff --git a/vulkan/libvulkan/swapchain.cpp b/vulkan/libvulkan/swapchain.cpp
index 475bc40..c7284ce 100644
--- a/vulkan/libvulkan/swapchain.cpp
+++ b/vulkan/libvulkan/swapchain.cpp
@@ -243,6 +243,11 @@
// syncronous requests to Surface Flinger):
enum { MIN_NUM_FRAMES_AGO = 5 };
+bool IsSharedPresentMode(VkPresentModeKHR mode) {
+ return mode == VK_PRESENT_MODE_SHARED_DEMAND_REFRESH_KHR ||
+ mode == VK_PRESENT_MODE_SHARED_CONTINUOUS_REFRESH_KHR;
+}
+
struct Swapchain {
Swapchain(Surface& surface_,
uint32_t num_images_,
@@ -254,9 +259,7 @@
pre_transform(pre_transform_),
frame_timestamps_enabled(false),
acquire_next_image_timeout(-1),
- shared(present_mode == VK_PRESENT_MODE_SHARED_DEMAND_REFRESH_KHR ||
- present_mode ==
- VK_PRESENT_MODE_SHARED_CONTINUOUS_REFRESH_KHR) {
+ shared(IsSharedPresentMode(present_mode)) {
ANativeWindow* window = surface.window.get();
native_window_get_refresh_cycle_duration(
window,
@@ -930,27 +933,36 @@
return GetPhysicalDeviceSurfaceFormatsKHR(physicalDevice,
pSurfaceInfo->surface,
pSurfaceFormatCount, nullptr);
- } else {
- // temp vector for forwarding; we'll marshal it into the pSurfaceFormats
- // after the call.
- std::vector<VkSurfaceFormatKHR> surface_formats(*pSurfaceFormatCount);
- VkResult result = GetPhysicalDeviceSurfaceFormatsKHR(
- physicalDevice, pSurfaceInfo->surface, pSurfaceFormatCount,
- surface_formats.data());
+ }
- if (result == VK_SUCCESS || result == VK_INCOMPLETE) {
- const auto& driver = GetData(physicalDevice).driver;
+ // temp vector for forwarding; we'll marshal it into the pSurfaceFormats
+ // after the call.
+ std::vector<VkSurfaceFormatKHR> surface_formats(*pSurfaceFormatCount);
+ VkResult result = GetPhysicalDeviceSurfaceFormatsKHR(
+ physicalDevice, pSurfaceInfo->surface, pSurfaceFormatCount,
+ surface_formats.data());
- // marshal results individually due to stride difference.
- uint32_t formats_to_marshal = *pSurfaceFormatCount;
- for (uint32_t i = 0u; i < formats_to_marshal; i++) {
- pSurfaceFormats[i].surfaceFormat = surface_formats[i];
+ if (result != VK_SUCCESS && result != VK_INCOMPLETE) {
+ return result;
+ }
- // Query the compression properties for the surface format
- if (pSurfaceFormats[i].pNext) {
+ const auto& driver = GetData(physicalDevice).driver;
+
+ // marshal results individually due to stride difference.
+ uint32_t formats_to_marshal = *pSurfaceFormatCount;
+ for (uint32_t i = 0u; i < formats_to_marshal; i++) {
+ pSurfaceFormats[i].surfaceFormat = surface_formats[i];
+
+ // Query the compression properties for the surface format
+ VkSurfaceFormat2KHR* pSurfaceFormat = &pSurfaceFormats[i];
+ while (pSurfaceFormat->pNext) {
+ pSurfaceFormat =
+ reinterpret_cast<VkSurfaceFormat2KHR*>(pSurfaceFormat->pNext);
+ switch (pSurfaceFormat->sType) {
+ case VK_STRUCTURE_TYPE_IMAGE_COMPRESSION_PROPERTIES_EXT: {
VkImageCompressionPropertiesEXT* surfaceCompressionProps =
reinterpret_cast<VkImageCompressionPropertiesEXT*>(
- pSurfaceFormats[i].pNext);
+ pSurfaceFormat);
if (surfaceCompressionProps &&
driver.GetPhysicalDeviceImageFormatProperties2KHR) {
@@ -992,12 +1004,16 @@
return compressionRes;
}
}
- }
+ } break;
+
+ default:
+ // Ignore all other extension structs
+ break;
}
}
-
- return result;
}
+
+ return result;
}
VKAPI_ATTR
@@ -1783,6 +1799,173 @@
return a != VK_SUCCESS ? a : b;
}
+// KHR_incremental_present aspect of QueuePresentKHR
+static void SetSwapchainSurfaceDamage(ANativeWindow *window, const VkPresentRegionKHR *pRegion) {
+ std::vector<android_native_rect_t> rects(pRegion->rectangleCount);
+ for (auto i = 0u; i < pRegion->rectangleCount; i++) {
+ auto const& rect = pRegion->pRectangles[i];
+ if (rect.layer > 0) {
+ ALOGV("vkQueuePresentKHR ignoring invalid layer (%u); using layer 0 instead",
+ rect.layer);
+ }
+
+ rects[i].left = rect.offset.x;
+ rects[i].bottom = rect.offset.y;
+ rects[i].right = rect.offset.x + rect.extent.width;
+ rects[i].top = rect.offset.y + rect.extent.height;
+ }
+ native_window_set_surface_damage(window, rects.data(), rects.size());
+}
+
+// GOOGLE_display_timing aspect of QueuePresentKHR
+static void SetSwapchainFrameTimestamp(Swapchain &swapchain, const VkPresentTimeGOOGLE *pTime) {
+ ANativeWindow *window = swapchain.surface.window.get();
+
+ // We don't know whether the app will actually use GOOGLE_display_timing
+ // with a particular swapchain until QueuePresent; enable it on the BQ
+ // now if needed
+ if (!swapchain.frame_timestamps_enabled) {
+ ALOGV("Calling native_window_enable_frame_timestamps(true)");
+ native_window_enable_frame_timestamps(window, true);
+ swapchain.frame_timestamps_enabled = true;
+ }
+
+ // Record the nativeFrameId so it can be later correlated to
+ // this present.
+ uint64_t nativeFrameId = 0;
+ int err = native_window_get_next_frame_id(
+ window, &nativeFrameId);
+ if (err != android::OK) {
+ ALOGE("Failed to get next native frame ID.");
+ }
+
+ // Add a new timing record with the user's presentID and
+ // the nativeFrameId.
+ swapchain.timing.emplace_back(pTime, nativeFrameId);
+ if (swapchain.timing.size() > MAX_TIMING_INFOS) {
+ swapchain.timing.erase(
+ swapchain.timing.begin(),
+ swapchain.timing.begin() + swapchain.timing.size() - MAX_TIMING_INFOS);
+ }
+ if (pTime->desiredPresentTime) {
+ ALOGV(
+ "Calling native_window_set_buffers_timestamp(%" PRId64 ")",
+ pTime->desiredPresentTime);
+ native_window_set_buffers_timestamp(
+ window,
+ static_cast<int64_t>(pTime->desiredPresentTime));
+ }
+}
+
+static VkResult PresentOneSwapchain(
+ VkQueue queue,
+ Swapchain& swapchain,
+ uint32_t imageIndex,
+ const VkPresentRegionKHR *pRegion,
+ const VkPresentTimeGOOGLE *pTime,
+ uint32_t waitSemaphoreCount,
+ const VkSemaphore *pWaitSemaphores) {
+
+ VkDevice device = GetData(queue).driver_device;
+ const auto& dispatch = GetData(queue).driver;
+
+ Swapchain::Image& img = swapchain.images[imageIndex];
+ VkResult swapchain_result = VK_SUCCESS;
+ VkResult result;
+ int err;
+
+ // XXX: long standing issue: QueueSignalReleaseImageANDROID consumes the
+ // wait semaphores, so this doesn't actually work for the multiple swapchain
+ // case.
+ int fence = -1;
+ result = dispatch.QueueSignalReleaseImageANDROID(
+ queue, waitSemaphoreCount,
+ pWaitSemaphores, img.image, &fence);
+ if (result != VK_SUCCESS) {
+ ALOGE("QueueSignalReleaseImageANDROID failed: %d", result);
+ swapchain_result = result;
+ }
+ if (img.release_fence >= 0)
+ close(img.release_fence);
+ img.release_fence = fence < 0 ? -1 : dup(fence);
+
+ if (swapchain.surface.swapchain_handle == HandleFromSwapchain(&swapchain)) {
+ ANativeWindow* window = swapchain.surface.window.get();
+ if (swapchain_result == VK_SUCCESS) {
+
+ if (pRegion) {
+ SetSwapchainSurfaceDamage(window, pRegion);
+ }
+ if (pTime) {
+ SetSwapchainFrameTimestamp(swapchain, pTime);
+ }
+
+ err = window->queueBuffer(window, img.buffer.get(), fence);
+ // queueBuffer always closes fence, even on error
+ if (err != android::OK) {
+ ALOGE("queueBuffer failed: %s (%d)", strerror(-err), err);
+ swapchain_result = WorstPresentResult(
+ swapchain_result, VK_ERROR_SURFACE_LOST_KHR);
+ } else {
+ if (img.dequeue_fence >= 0) {
+ close(img.dequeue_fence);
+ img.dequeue_fence = -1;
+ }
+ img.dequeued = false;
+ }
+
+ // If the swapchain is in shared mode, immediately dequeue the
+ // buffer so it can be presented again without an intervening
+ // call to AcquireNextImageKHR. We expect to get the same buffer
+ // back from every call to dequeueBuffer in this mode.
+ if (swapchain.shared && swapchain_result == VK_SUCCESS) {
+ ANativeWindowBuffer* buffer;
+ int fence_fd;
+ err = window->dequeueBuffer(window, &buffer, &fence_fd);
+ if (err != android::OK) {
+ ALOGE("dequeueBuffer failed: %s (%d)", strerror(-err), err);
+ swapchain_result = WorstPresentResult(swapchain_result,
+ VK_ERROR_SURFACE_LOST_KHR);
+ } else if (img.buffer != buffer) {
+ ALOGE("got wrong image back for shared swapchain");
+ swapchain_result = WorstPresentResult(swapchain_result,
+ VK_ERROR_SURFACE_LOST_KHR);
+ } else {
+ img.dequeue_fence = fence_fd;
+ img.dequeued = true;
+ }
+ }
+ }
+ if (swapchain_result != VK_SUCCESS) {
+ OrphanSwapchain(device, &swapchain);
+ }
+ // Android will only return VK_SUBOPTIMAL_KHR for vkQueuePresentKHR,
+ // and only when the window's transform/rotation changes. Extent
+ // changes will not cause VK_SUBOPTIMAL_KHR because of the
+ // application issues that were caused when the following transform
+ // change was added.
+ int window_transform_hint;
+ err = window->query(window, NATIVE_WINDOW_TRANSFORM_HINT,
+ &window_transform_hint);
+ if (err != android::OK) {
+ ALOGE("NATIVE_WINDOW_TRANSFORM_HINT query failed: %s (%d)",
+ strerror(-err), err);
+ swapchain_result = WorstPresentResult(
+ swapchain_result, VK_ERROR_SURFACE_LOST_KHR);
+ }
+ if (swapchain.pre_transform != window_transform_hint) {
+ swapchain_result =
+ WorstPresentResult(swapchain_result, VK_SUBOPTIMAL_KHR);
+ }
+ } else {
+ ReleaseSwapchainImage(device, swapchain.shared, nullptr, fence,
+ img, true);
+ swapchain_result = VK_ERROR_OUT_OF_DATE_KHR;
+ }
+
+ return swapchain_result;
+}
+
VKAPI_ATTR
VkResult QueuePresentKHR(VkQueue queue, const VkPresentInfoKHR* present_info) {
ATRACE_CALL();
@@ -1791,8 +1974,6 @@
"vkQueuePresentKHR: invalid VkPresentInfoKHR structure type %d",
present_info->sType);
- VkDevice device = GetData(queue).driver_device;
- const auto& dispatch = GetData(queue).driver;
VkResult final_result = VK_SUCCESS;
// Look at the pNext chain for supported extension structs:
@@ -1828,175 +2009,19 @@
(present_regions) ? present_regions->pRegions : nullptr;
const VkPresentTimeGOOGLE* times =
(present_times) ? present_times->pTimes : nullptr;
- const VkAllocationCallbacks* allocator = &GetData(device).allocator;
- android_native_rect_t* rects = nullptr;
- uint32_t nrects = 0;
for (uint32_t sc = 0; sc < present_info->swapchainCount; sc++) {
Swapchain& swapchain =
*SwapchainFromHandle(present_info->pSwapchains[sc]);
- uint32_t image_idx = present_info->pImageIndices[sc];
- Swapchain::Image& img = swapchain.images[image_idx];
- const VkPresentRegionKHR* region =
- (regions && !swapchain.mailbox_mode) ? ®ions[sc] : nullptr;
- const VkPresentTimeGOOGLE* time = (times) ? ×[sc] : nullptr;
- VkResult swapchain_result = VK_SUCCESS;
- VkResult result;
- int err;
- int fence = -1;
- result = dispatch.QueueSignalReleaseImageANDROID(
- queue, present_info->waitSemaphoreCount,
- present_info->pWaitSemaphores, img.image, &fence);
- if (result != VK_SUCCESS) {
- ALOGE("QueueSignalReleaseImageANDROID failed: %d", result);
- swapchain_result = result;
- }
- if (img.release_fence >= 0)
- close(img.release_fence);
- img.release_fence = fence < 0 ? -1 : dup(fence);
-
- if (swapchain.surface.swapchain_handle ==
- present_info->pSwapchains[sc]) {
- ANativeWindow* window = swapchain.surface.window.get();
- if (swapchain_result == VK_SUCCESS) {
- if (region) {
- // Process the incremental-present hint for this swapchain:
- uint32_t rcount = region->rectangleCount;
- if (rcount > nrects) {
- android_native_rect_t* new_rects =
- static_cast<android_native_rect_t*>(
- allocator->pfnReallocation(
- allocator->pUserData, rects,
- sizeof(android_native_rect_t) * rcount,
- alignof(android_native_rect_t),
- VK_SYSTEM_ALLOCATION_SCOPE_COMMAND));
- if (new_rects) {
- rects = new_rects;
- nrects = rcount;
- } else {
- rcount = 0; // Ignore the hint for this swapchain
- }
- }
- for (uint32_t r = 0; r < rcount; ++r) {
- if (region->pRectangles[r].layer > 0) {
- ALOGV(
- "vkQueuePresentKHR ignoring invalid layer "
- "(%u); using layer 0 instead",
- region->pRectangles[r].layer);
- }
- int x = region->pRectangles[r].offset.x;
- int y = region->pRectangles[r].offset.y;
- int width = static_cast<int>(
- region->pRectangles[r].extent.width);
- int height = static_cast<int>(
- region->pRectangles[r].extent.height);
- android_native_rect_t* cur_rect = &rects[r];
- cur_rect->left = x;
- cur_rect->top = y + height;
- cur_rect->right = x + width;
- cur_rect->bottom = y;
- }
- native_window_set_surface_damage(window, rects, rcount);
- }
- if (time) {
- if (!swapchain.frame_timestamps_enabled) {
- ALOGV(
- "Calling "
- "native_window_enable_frame_timestamps(true)");
- native_window_enable_frame_timestamps(window, true);
- swapchain.frame_timestamps_enabled = true;
- }
-
- // Record the nativeFrameId so it can be later correlated to
- // this present.
- uint64_t nativeFrameId = 0;
- err = native_window_get_next_frame_id(
- window, &nativeFrameId);
- if (err != android::OK) {
- ALOGE("Failed to get next native frame ID.");
- }
-
- // Add a new timing record with the user's presentID and
- // the nativeFrameId.
- swapchain.timing.emplace_back(time, nativeFrameId);
- while (swapchain.timing.size() > MAX_TIMING_INFOS) {
- swapchain.timing.erase(swapchain.timing.begin());
- }
- if (time->desiredPresentTime) {
- // Set the desiredPresentTime:
- ALOGV(
- "Calling "
- "native_window_set_buffers_timestamp(%" PRId64 ")",
- time->desiredPresentTime);
- native_window_set_buffers_timestamp(
- window,
- static_cast<int64_t>(time->desiredPresentTime));
- }
- }
-
- err = window->queueBuffer(window, img.buffer.get(), fence);
- // queueBuffer always closes fence, even on error
- if (err != android::OK) {
- ALOGE("queueBuffer failed: %s (%d)", strerror(-err), err);
- swapchain_result = WorstPresentResult(
- swapchain_result, VK_ERROR_SURFACE_LOST_KHR);
- } else {
- if (img.dequeue_fence >= 0) {
- close(img.dequeue_fence);
- img.dequeue_fence = -1;
- }
- img.dequeued = false;
- }
-
- // If the swapchain is in shared mode, immediately dequeue the
- // buffer so it can be presented again without an intervening
- // call to AcquireNextImageKHR. We expect to get the same buffer
- // back from every call to dequeueBuffer in this mode.
- if (swapchain.shared && swapchain_result == VK_SUCCESS) {
- ANativeWindowBuffer* buffer;
- int fence_fd;
- err = window->dequeueBuffer(window, &buffer, &fence_fd);
- if (err != android::OK) {
- ALOGE("dequeueBuffer failed: %s (%d)", strerror(-err), err);
- swapchain_result = WorstPresentResult(swapchain_result,
- VK_ERROR_SURFACE_LOST_KHR);
- } else if (img.buffer != buffer) {
- ALOGE("got wrong image back for shared swapchain");
- swapchain_result = WorstPresentResult(swapchain_result,
- VK_ERROR_SURFACE_LOST_KHR);
- } else {
- img.dequeue_fence = fence_fd;
- img.dequeued = true;
- }
- }
- }
- if (swapchain_result != VK_SUCCESS) {
- OrphanSwapchain(device, &swapchain);
- }
- // Android will only return VK_SUBOPTIMAL_KHR for vkQueuePresentKHR,
- // and only when the window's transform/rotation changes. Extent
- // changes will not cause VK_SUBOPTIMAL_KHR because of the
- // application issues that were caused when the following transform
- // change was added.
- int window_transform_hint;
- err = window->query(window, NATIVE_WINDOW_TRANSFORM_HINT,
- &window_transform_hint);
- if (err != android::OK) {
- ALOGE("NATIVE_WINDOW_TRANSFORM_HINT query failed: %s (%d)",
- strerror(-err), err);
- swapchain_result = WorstPresentResult(
- swapchain_result, VK_ERROR_SURFACE_LOST_KHR);
- }
- if (swapchain.pre_transform != window_transform_hint) {
- swapchain_result =
- WorstPresentResult(swapchain_result, VK_SUBOPTIMAL_KHR);
- }
- } else {
- ReleaseSwapchainImage(device, swapchain.shared, nullptr, fence,
- img, true);
- swapchain_result = VK_ERROR_OUT_OF_DATE_KHR;
- }
+ VkResult swapchain_result = PresentOneSwapchain(
+ queue,
+ swapchain,
+ present_info->pImageIndices[sc],
+ (regions && !swapchain.mailbox_mode) ? ®ions[sc] : nullptr,
+ times ? ×[sc] : nullptr,
+ present_info->waitSemaphoreCount,
+ present_info->pWaitSemaphores);
if (present_info->pResults)
present_info->pResults[sc] = swapchain_result;
@@ -2004,9 +2029,6 @@
if (swapchain_result != final_result)
final_result = WorstPresentResult(final_result, swapchain_result);
}
- if (rects) {
- allocator->pfnFree(allocator->pUserData, rects);
- }
return final_result;
}