Merge "Fix failing test in CompositionTest."
diff --git a/cmds/cmd/cmd.cpp b/cmds/cmd/cmd.cpp
index 7b4aeb2..8dad475 100644
--- a/cmds/cmd/cmd.cpp
+++ b/cmds/cmd/cmd.cpp
@@ -223,7 +223,8 @@
sp<MyResultReceiver> result = new MyResultReceiver();
#if DEBUG
- ALOGD("cmd: Invoking %s in=%d, out=%d, err=%d", cmd, in, out, err);
+ ALOGD("cmd: Invoking %.*s in=%d, out=%d, err=%d",
+ static_cast<int>(cmd.size()), cmd.data(), in, out, err);
#endif
// TODO: block until a result is returned to MyResultReceiver.
diff --git a/cmds/dumpstate/DumpstateInternal.h b/cmds/dumpstate/DumpstateInternal.h
index 10db5d6..c1ec55e 100644
--- a/cmds/dumpstate/DumpstateInternal.h
+++ b/cmds/dumpstate/DumpstateInternal.h
@@ -49,6 +49,7 @@
// TODO: use functions from <chrono> instead
const uint64_t NANOS_PER_SEC = 1000000000;
+const uint64_t NANOS_PER_MILLI = 1000000;
uint64_t Nanotime();
// Switches to non-root user and group.
diff --git a/cmds/dumpstate/dumpstate.cpp b/cmds/dumpstate/dumpstate.cpp
index fc94f23..a20db24 100644
--- a/cmds/dumpstate/dumpstate.cpp
+++ b/cmds/dumpstate/dumpstate.cpp
@@ -649,7 +649,7 @@
}
uint64_t Dumpstate::ConsentCallback::getElapsedTimeMs() const {
- return Nanotime() - start_time_;
+ return (Nanotime() - start_time_) / NANOS_PER_MILLI;
}
void Dumpstate::PrintHeader() const {
diff --git a/cmds/installd/InstalldNativeService.cpp b/cmds/installd/InstalldNativeService.cpp
index e7b0d5d..cfd6a3e 100644
--- a/cmds/installd/InstalldNativeService.cpp
+++ b/cmds/installd/InstalldNativeService.cpp
@@ -2250,26 +2250,6 @@
return *_aidl_return ? ok() : error("viewcompiler failed");
}
-binder::Status InstalldNativeService::markBootComplete(const std::string& instructionSet) {
- ENFORCE_UID(AID_SYSTEM);
- std::lock_guard<std::recursive_mutex> lock(mLock);
-
- const char* instruction_set = instructionSet.c_str();
-
- char boot_marker_path[PKG_PATH_MAX];
- sprintf(boot_marker_path,
- "%s/%s/%s/.booting",
- android_data_dir.c_str(),
- DALVIK_CACHE,
- instruction_set);
-
- ALOGV("mark_boot_complete : %s", boot_marker_path);
- if (unlink(boot_marker_path) != 0) {
- return error(StringPrintf("Failed to unlink %s", boot_marker_path));
- }
- return ok();
-}
-
binder::Status InstalldNativeService::linkNativeLibraryDirectory(
const std::unique_ptr<std::string>& uuid, const std::string& packageName,
const std::string& nativeLibPath32, int32_t userId) {
diff --git a/cmds/installd/InstalldNativeService.h b/cmds/installd/InstalldNativeService.h
index bf11002..dd56de6 100644
--- a/cmds/installd/InstalldNativeService.h
+++ b/cmds/installd/InstalldNativeService.h
@@ -130,7 +130,6 @@
const std::string& profileName);
binder::Status rmPackageDir(const std::string& packageDir);
- binder::Status markBootComplete(const std::string& instructionSet);
binder::Status freeCache(const std::unique_ptr<std::string>& uuid, int64_t targetFreeBytes,
int64_t cacheReservedBytes, int32_t flags);
binder::Status linkNativeLibraryDirectory(const std::unique_ptr<std::string>& uuid,
diff --git a/cmds/installd/binder/android/os/IInstalld.aidl b/cmds/installd/binder/android/os/IInstalld.aidl
index 891b26d..07ced0d 100644
--- a/cmds/installd/binder/android/os/IInstalld.aidl
+++ b/cmds/installd/binder/android/os/IInstalld.aidl
@@ -81,7 +81,6 @@
void destroyProfileSnapshot(@utf8InCpp String packageName, @utf8InCpp String profileName);
void rmPackageDir(@utf8InCpp String packageDir);
- void markBootComplete(@utf8InCpp String instructionSet);
void freeCache(@nullable @utf8InCpp String uuid, long targetFreeBytes,
long cacheReservedBytes, int flags);
void linkNativeLibraryDirectory(@nullable @utf8InCpp String uuid,
diff --git a/cmds/installd/tests/installd_utils_test.cpp b/cmds/installd/tests/installd_utils_test.cpp
index e61eb6e..d236f76 100644
--- a/cmds/installd/tests/installd_utils_test.cpp
+++ b/cmds/installd/tests/installd_utils_test.cpp
@@ -104,12 +104,12 @@
EXPECT_EQ(-1, validate_apk_path(badint2))
<< badint2 << " should be rejected as a invalid path";
- // Only one subdir should be allowed.
- const char *bad_path3 = TEST_APP_DIR "example.com/subdir/pkg.apk";
+ // Should not have more than two sub directories
+ const char *bad_path3 = TEST_APP_DIR "random/example.com/subdir/pkg.apk";
EXPECT_EQ(-1, validate_apk_path(bad_path3))
<< bad_path3 << " should be rejected as a invalid path";
- const char *bad_path4 = TEST_APP_DIR "example.com/subdir/../pkg.apk";
+ const char *bad_path4 = TEST_APP_DIR "random/example.com/subdir/pkg.apk";
EXPECT_EQ(-1, validate_apk_path(bad_path4))
<< bad_path4 << " should be rejected as a invalid path";
@@ -120,6 +120,7 @@
TEST_F(UtilsTest, IsValidApkPath_TopDir) {
EXPECT_EQ(0, validate_apk_path(TEST_DATA_DIR "app/com.example"));
+ EXPECT_EQ(0, validate_apk_path(TEST_DATA_DIR "app/random/com.example"));
EXPECT_EQ(0, validate_apk_path(TEST_EXPAND_DIR "app/com.example"));
EXPECT_EQ(-1, validate_apk_path(TEST_DATA_DIR "data/com.example"));
EXPECT_EQ(-1, validate_apk_path(TEST_EXPAND_DIR "data/com.example"));
@@ -127,6 +128,7 @@
TEST_F(UtilsTest, IsValidApkPath_TopFile) {
EXPECT_EQ(0, validate_apk_path(TEST_DATA_DIR "app/com.example/base.apk"));
+ EXPECT_EQ(0, validate_apk_path(TEST_DATA_DIR "app/random/com.example/base.apk"));
EXPECT_EQ(0, validate_apk_path(TEST_EXPAND_DIR "app/com.example/base.apk"));
EXPECT_EQ(-1, validate_apk_path(TEST_DATA_DIR "data/com.example/base.apk"));
EXPECT_EQ(-1, validate_apk_path(TEST_EXPAND_DIR "data/com.example/base.apk"));
@@ -134,6 +136,7 @@
TEST_F(UtilsTest, IsValidApkPath_OatDir) {
EXPECT_EQ(0, validate_apk_path_subdirs(TEST_DATA_DIR "app/com.example/oat"));
+ EXPECT_EQ(0, validate_apk_path_subdirs(TEST_DATA_DIR "app/random/com.example/oat"));
EXPECT_EQ(0, validate_apk_path_subdirs(TEST_EXPAND_DIR "app/com.example/oat"));
EXPECT_EQ(-1, validate_apk_path_subdirs(TEST_DATA_DIR "data/com.example/oat"));
EXPECT_EQ(-1, validate_apk_path_subdirs(TEST_EXPAND_DIR "data/com.example/oat"));
@@ -141,6 +144,7 @@
TEST_F(UtilsTest, IsValidApkPath_OatDirDir) {
EXPECT_EQ(0, validate_apk_path_subdirs(TEST_DATA_DIR "app/com.example/oat/arm64"));
+ EXPECT_EQ(0, validate_apk_path_subdirs(TEST_DATA_DIR "app/random/com.example/oat/arm64"));
EXPECT_EQ(0, validate_apk_path_subdirs(TEST_EXPAND_DIR "app/com.example/oat/arm64"));
EXPECT_EQ(-1, validate_apk_path_subdirs(TEST_DATA_DIR "data/com.example/oat/arm64"));
EXPECT_EQ(-1, validate_apk_path_subdirs(TEST_EXPAND_DIR "data/com.example/oat/arm64"));
@@ -148,6 +152,7 @@
TEST_F(UtilsTest, IsValidApkPath_OatDirDirFile) {
EXPECT_EQ(0, validate_apk_path_subdirs(TEST_DATA_DIR "app/com.example/oat/arm64/base.odex"));
+ EXPECT_EQ(0, validate_apk_path_subdirs(TEST_DATA_DIR "app/random/com.example/oat/arm64/base.odex"));
EXPECT_EQ(0, validate_apk_path_subdirs(TEST_EXPAND_DIR "app/com.example/oat/arm64/base.odex"));
EXPECT_EQ(-1, validate_apk_path_subdirs(TEST_DATA_DIR "data/com.example/oat/arm64/base.odex"));
EXPECT_EQ(-1, validate_apk_path_subdirs(TEST_EXPAND_DIR "data/com.example/oat/arm64/base.odex"));
@@ -164,6 +169,10 @@
EXPECT_EQ(0, validate_apk_path(path2))
<< path2 << " should be allowed as a valid path";
+ const char *path3 = TEST_APP_DIR "random/example.com/example.apk";
+ EXPECT_EQ(0, validate_apk_path(path3))
+ << path3 << " should be allowed as a valid path";
+
const char *badpriv1 = TEST_APP_PRIVATE_DIR "../example.apk";
EXPECT_EQ(-1, validate_apk_path(badpriv1))
<< badpriv1 << " should be rejected as a invalid path";
@@ -172,16 +181,16 @@
EXPECT_EQ(-1, validate_apk_path(badpriv2))
<< badpriv2 << " should be rejected as a invalid path";
- // Only one subdir should be allowed.
- const char *bad_path3 = TEST_APP_PRIVATE_DIR "example.com/subdir/pkg.apk";
+ // Only one or two subdir should be allowed.
+ const char *bad_path3 = TEST_APP_PRIVATE_DIR "random/example.com/subdir/pkg.apk";
EXPECT_EQ(-1, validate_apk_path(bad_path3))
<< bad_path3 << " should be rejected as a invalid path";
- const char *bad_path4 = TEST_APP_PRIVATE_DIR "example.com/subdir/../pkg.apk";
+ const char *bad_path4 = TEST_APP_PRIVATE_DIR "random/example.com/subdir/../pkg.apk";
EXPECT_EQ(-1, validate_apk_path(bad_path4))
<< bad_path4 << " should be rejected as a invalid path";
- const char *bad_path5 = TEST_APP_PRIVATE_DIR "example.com1/../example.com2/pkg.apk";
+ const char *bad_path5 = TEST_APP_PRIVATE_DIR "random/example.com1/../example.com2/pkg.apk";
EXPECT_EQ(-1, validate_apk_path(bad_path5))
<< bad_path5 << " should be rejected as a invalid path";
}
@@ -229,10 +238,16 @@
<< badasec6 << " should be rejected as a invalid path";
}
-TEST_F(UtilsTest, IsValidApkPath_TwoSubdirFail) {
- const char *badasec7 = TEST_ASEC_DIR "com.example.asec/subdir1/pkg.apk";
- EXPECT_EQ(-1, validate_apk_path(badasec7))
- << badasec7 << " should be rejected as a invalid path";
+TEST_F(UtilsTest, IsValidApkPath_TwoSubdir) {
+ const char *badasec7 = TEST_ASEC_DIR "random/com.example.asec/pkg.apk";
+ EXPECT_EQ(0, validate_apk_path(badasec7))
+ << badasec7 << " should be allowed as a valid path";
+}
+
+TEST_F(UtilsTest, IsValidApkPath_ThreeSubdirFail) {
+ const char *badasec8 = TEST_ASEC_DIR "random/com.example.asec/subdir/pkg.apk";
+ EXPECT_EQ(-1, validate_apk_path(badasec8))
+ << badasec8 << " should be rejcted as an invalid path";
}
TEST_F(UtilsTest, CheckSystemApp_Dir1) {
@@ -511,8 +526,8 @@
EXPECT_EQ(0, validate_apk_path("/data/app/com.example"));
EXPECT_EQ(0, validate_apk_path("/data/app/com.example/file"));
EXPECT_EQ(0, validate_apk_path("/data/app/com.example//file"));
- EXPECT_NE(0, validate_apk_path("/data/app/com.example/dir/"));
- EXPECT_NE(0, validate_apk_path("/data/app/com.example/dir/file"));
+ EXPECT_EQ(0, validate_apk_path("/data/app/random/com.example/"));
+ EXPECT_EQ(0, validate_apk_path("/data/app/random/com.example/file"));
EXPECT_NE(0, validate_apk_path("/data/app/com.example/dir/dir/file"));
EXPECT_NE(0, validate_apk_path("/data/app/com.example/dir/dir//file"));
EXPECT_NE(0, validate_apk_path("/data/app/com.example/dir/dir/dir/file"));
@@ -527,8 +542,10 @@
EXPECT_EQ(0, validate_apk_path_subdirs("/data/app/com.example/dir/file"));
EXPECT_EQ(0, validate_apk_path_subdirs("/data/app/com.example/dir/dir/file"));
EXPECT_EQ(0, validate_apk_path_subdirs("/data/app/com.example/dir/dir//file"));
- EXPECT_NE(0, validate_apk_path_subdirs("/data/app/com.example/dir/dir/dir/file"));
- EXPECT_NE(0, validate_apk_path_subdirs("/data/app/com.example/dir/dir/dir//file"));
+ EXPECT_EQ(0, validate_apk_path_subdirs("/data/app/com.example/dir/dir/dir/file"));
+ EXPECT_EQ(0, validate_apk_path_subdirs("/data/app/com.example/dir/dir/dir//file"));
+ EXPECT_NE(0, validate_apk_path_subdirs("/data/app/com.example/dir/dir/dir/dir/file"));
+ EXPECT_NE(0, validate_apk_path_subdirs("/data/app/com.example/dir/dir/dir/dir//file"));
}
TEST_F(UtilsTest, MatchExtension_Valid) {
diff --git a/cmds/installd/utils.cpp b/cmds/installd/utils.cpp
index 6012822..939cf90 100644
--- a/cmds/installd/utils.cpp
+++ b/cmds/installd/utils.cpp
@@ -954,11 +954,11 @@
}
int validate_apk_path(const char* path) {
- return validate_apk_path_internal(path, 1 /* maxSubdirs */);
+ return validate_apk_path_internal(path, 2 /* maxSubdirs */);
}
int validate_apk_path_subdirs(const char* path) {
- return validate_apk_path_internal(path, 3 /* maxSubdirs */);
+ return validate_apk_path_internal(path, 4 /* maxSubdirs */);
}
int ensure_config_user_dirs(userid_t userid) {
diff --git a/cmds/servicemanager/ServiceManager.cpp b/cmds/servicemanager/ServiceManager.cpp
index ae74ac3..abe6436 100644
--- a/cmds/servicemanager/ServiceManager.cpp
+++ b/cmds/servicemanager/ServiceManager.cpp
@@ -423,11 +423,12 @@
void ServiceManager::handleClientCallbacks() {
for (const auto& [name, service] : mNameToService) {
- handleServiceClientCallback(name);
+ handleServiceClientCallback(name, true);
}
}
-ssize_t ServiceManager::handleServiceClientCallback(const std::string& serviceName) {
+ssize_t ServiceManager::handleServiceClientCallback(const std::string& serviceName,
+ bool isCalledOnInterval) {
auto serviceIt = mNameToService.find(serviceName);
if (serviceIt == mNameToService.end() || mNameToClientCallback.count(serviceName) < 1) {
return -1;
@@ -451,14 +452,17 @@
service.guaranteeClient = false;
}
- if (hasClients && !service.hasClients) {
- // client was retrieved in some other way
- sendClientCallbackNotifications(serviceName, true);
- }
+ // only send notifications if this was called via the interval checking workflow
+ if (isCalledOnInterval) {
+ if (hasClients && !service.hasClients) {
+ // client was retrieved in some other way
+ sendClientCallbackNotifications(serviceName, true);
+ }
- // there are no more clients, but the callback has not been called yet
- if (!hasClients && service.hasClients) {
- sendClientCallbackNotifications(serviceName, false);
+ // there are no more clients, but the callback has not been called yet
+ if (!hasClients && service.hasClients) {
+ sendClientCallbackNotifications(serviceName, false);
+ }
}
return count;
@@ -518,7 +522,7 @@
return Status::fromExceptionCode(Status::EX_ILLEGAL_STATE);
}
- int clients = handleServiceClientCallback(name);
+ int clients = handleServiceClientCallback(name, false);
// clients < 0: feature not implemented or other error. Assume clients.
// Otherwise:
@@ -527,7 +531,7 @@
// So, if clients > 2, then at least one other service on the system must hold a refcount.
if (clients < 0 || clients > 2) {
// client callbacks are either disabled or there are other clients
- LOG(INFO) << "Tried to unregister " << name << " but there are clients: " << clients;
+ LOG(INFO) << "Tried to unregister " << name << ", but there are clients: " << clients;
return Status::fromExceptionCode(Status::EX_ILLEGAL_STATE);
}
diff --git a/cmds/servicemanager/ServiceManager.h b/cmds/servicemanager/ServiceManager.h
index 77f5250..a2fc5a8 100644
--- a/cmds/servicemanager/ServiceManager.h
+++ b/cmds/servicemanager/ServiceManager.h
@@ -75,7 +75,7 @@
void removeRegistrationCallback(const wp<IBinder>& who,
ServiceCallbackMap::iterator* it,
bool* found);
- ssize_t handleServiceClientCallback(const std::string& serviceName);
+ ssize_t handleServiceClientCallback(const std::string& serviceName, bool isCalledOnInterval);
// Also updates mHasClients (of what the last callback was)
void sendClientCallbackNotifications(const std::string& serviceName, bool hasClients);
// removes a callback from mNameToClientCallback, deleting the entry if the vector is empty
diff --git a/docs/images/camera2/metadata/android.scaler.rotateAndCrop/crop-region-rotate-90-43-ratio.png b/docs/images/camera2/metadata/android.scaler.rotateAndCrop/crop-region-rotate-90-43-ratio.png
new file mode 100644
index 0000000..2633996
--- /dev/null
+++ b/docs/images/camera2/metadata/android.scaler.rotateAndCrop/crop-region-rotate-90-43-ratio.png
Binary files differ
diff --git a/include/android/imagedecoder.h b/include/android/imagedecoder.h
index 469b088..0d943b7 100644
--- a/include/android/imagedecoder.h
+++ b/include/android/imagedecoder.h
@@ -27,7 +27,7 @@
#define ANDROID_IMAGE_DECODER_H
#include "bitmap.h"
-
+#include <android/rect.h>
#include <stdint.h>
#ifdef __cplusplus
@@ -35,7 +35,6 @@
#endif
struct AAsset;
-struct ARect;
#if __ANDROID_API__ >= 30
@@ -92,7 +91,8 @@
* @return {@link ANDROID_IMAGE_DECODER_SUCCESS} on success or a value
* indicating reason for the failure.
*/
-int AImageDecoder_createFromAAsset(AAsset* asset, AImageDecoder** outDecoder) __INTRODUCED_IN(30);
+int AImageDecoder_createFromAAsset(struct AAsset* asset, AImageDecoder** outDecoder)
+ __INTRODUCED_IN(30);
/**
* Create a new AImageDecoder from a file descriptor.
@@ -133,7 +133,7 @@
/**
* Choose the desired output format.
*
- * @param format AndroidBitmapFormat to use
+ * @param format {@link AndroidBitmapFormat} to use for the output.
* @return {@link ANDROID_IMAGE_DECODER_SUCCESS} if the format is compatible
* with the image and {@link ANDROID_IMAGE_DECODER_INVALID_CONVERSION}
* otherwise. In the latter case, the AImageDecoder uses the
@@ -196,7 +196,7 @@
* pointer is null, width or height is <= 0, or any existing crop is
* not contained by the image dimensions.
*/
-int AImageDecoder_setTargetSize(AImageDecoder*, int width, int height) __INTRODUCED_IN(30);
+int AImageDecoder_setTargetSize(AImageDecoder*, int32_t width, int32_t height) __INTRODUCED_IN(30);
/**
@@ -219,7 +219,7 @@
* @return ANDROID_IMAGE_DECODER result code.
*/
int AImageDecoder_computeSampledSize(const AImageDecoder*, int sampleSize,
- int* width, int* height) __INTRODUCED_IN(30);
+ int32_t* width, int32_t* height) __INTRODUCED_IN(30);
/**
* Specify how to crop the output after scaling (if any).
*
@@ -276,18 +276,12 @@
const AImageDecoderHeaderInfo*) __INTRODUCED_IN(30);
/**
- * Report whether the encoded image represents an animation.
- */
-bool AImageDecoderHeaderInfo_isAnimated(
- const AImageDecoderHeaderInfo*) __INTRODUCED_IN(30);
-
-/**
- * Report the AndroidBitmapFormat the AImageDecoder will decode to
+ * Report the {@link AndroidBitmapFormat} the AImageDecoder will decode to
* by default. AImageDecoder will try to choose one that is sensible
* for the image and the system. Note that this does not indicate the
* encoded format of the image.
*/
-AndroidBitmapFormat AImageDecoderHeaderInfo_getAndroidBitmapFormat(
+int32_t AImageDecoderHeaderInfo_getAndroidBitmapFormat(
const AImageDecoderHeaderInfo*) __INTRODUCED_IN(30);
/**
@@ -337,7 +331,8 @@
* {@link AImageDecoder_getMinimumStride}.
* @param size Size of the pixel buffer in bytes. Must be at least
* stride * (height - 1) +
- * {@link AImageDecoder_getMinimumStride}.
+ * {@link AImageDecoder_getMinimumStride}. Must also be a multiple
+ * of the bytes per pixel of the {@link AndroidBitmapFormat}.
* @return {@link ANDROID_IMAGE_DECODER_SUCCESS} on success, or an error code
* from the same enum describing the failure.
*/
diff --git a/include/android/sensor.h b/include/android/sensor.h
index 3ebe79f..12c00ad 100644
--- a/include/android/sensor.h
+++ b/include/android/sensor.h
@@ -245,6 +245,10 @@
* {@link ASENSOR_TYPE_ACCELEROMETER_UNCALIBRATED}
*/
ASENSOR_TYPE_ACCELEROMETER_UNCALIBRATED = 35,
+ /**
+ * {@link ASENSOR_TYPE_HINGE_ANGLE}
+ */
+ ASENSOR_TYPE_HINGE_ANGLE = 36,
};
/**
diff --git a/include/android/surface_control.h b/include/android/surface_control.h
index 31abb66..eeb8330 100644
--- a/include/android/surface_control.h
+++ b/include/android/surface_control.h
@@ -407,6 +407,33 @@
#endif // __ANDROID_API__ >= 29
+#if __ANDROID_API__ >= 30
+
+/*
+ * Sets the intended frame rate for |surface_control|.
+ *
+ * On devices that are capable of running the display at different refresh rates, the system may
+ * choose a display refresh rate to better match this surface's frame rate. Usage of this API won't
+ * directly affect the application's frame production pipeline. However, because the system may
+ * change the display refresh rate, calls to this function may result in changes to Choreographer
+ * callback timings, and changes to the time interval at which the system releases buffers back to
+ * the application.
+ *
+ * |frameRate| is the intended frame rate of this surface, in frames per second. 0 is a special
+ * value that indicates the app will accept the system's choice for the display frame rate, which is
+ * the default behavior if this function isn't called. The frameRate param does *not* need to be a
+ * valid refresh rate for this device's display - e.g., it's fine to pass 30fps to a device that can
+ * only run the display at 60fps.
+ *
+ * Available since API level 30.
+ */
+void ASurfaceTransaction_setFrameRate(ASurfaceTransaction* transaction,
+ ASurfaceControl* surface_control,
+ float frameRate)
+ __INTRODUCED_IN(30);
+
+#endif // __ANDROID_API__ >= 30
+
__END_DECLS
#endif // ANDROID_SURFACE_CONTROL_H
diff --git a/include/input/Input.h b/include/input/Input.h
index f871847..cf0814c 100644
--- a/include/input/Input.h
+++ b/include/input/Input.h
@@ -31,6 +31,7 @@
#include <utils/RefBase.h>
#include <utils/Timers.h>
#include <utils/Vector.h>
+#include <array>
#include <limits>
#include <queue>
@@ -258,6 +259,11 @@
*/
constexpr float AMOTION_EVENT_INVALID_CURSOR_POSITION = std::numeric_limits<float>::quiet_NaN();
+/**
+ * Invalid value of HMAC - SHA256. Any events with this HMAC value will be marked as not verified.
+ */
+constexpr std::array<uint8_t, 32> INVALID_HMAC = {0};
+
/*
* Pointer coordinate data.
*/
@@ -348,22 +354,25 @@
inline int32_t getDeviceId() const { return mDeviceId; }
- inline int32_t getSource() const { return mSource; }
+ inline uint32_t getSource() const { return mSource; }
- inline void setSource(int32_t source) { mSource = source; }
+ inline void setSource(uint32_t source) { mSource = source; }
inline int32_t getDisplayId() const { return mDisplayId; }
inline void setDisplayId(int32_t displayId) { mDisplayId = displayId; }
+ inline std::array<uint8_t, 32> getHmac() const { return mHmac; }
protected:
- void initialize(int32_t deviceId, int32_t source, int32_t displayId);
+ void initialize(int32_t deviceId, uint32_t source, int32_t displayId,
+ std::array<uint8_t, 32> hmac);
void initialize(const InputEvent& from);
int32_t mDeviceId;
- int32_t mSource;
+ uint32_t mSource;
int32_t mDisplayId;
+ std::array<uint8_t, 32> mHmac;
};
/*
@@ -396,18 +405,10 @@
static const char* getLabel(int32_t keyCode);
static int32_t getKeyCodeFromLabel(const char* label);
- void initialize(
- int32_t deviceId,
- int32_t source,
- int32_t displayId,
- int32_t action,
- int32_t flags,
- int32_t keyCode,
- int32_t scanCode,
- int32_t metaState,
- int32_t repeatCount,
- nsecs_t downTime,
- nsecs_t eventTime);
+ void initialize(int32_t deviceId, uint32_t source, int32_t displayId,
+ std::array<uint8_t, 32> hmac, int32_t action, int32_t flags, int32_t keyCode,
+ int32_t scanCode, int32_t metaState, int32_t repeatCount, nsecs_t downTime,
+ nsecs_t eventTime);
void initialize(const KeyEvent& from);
protected:
@@ -463,6 +464,10 @@
inline void setActionButton(int32_t button) { mActionButton = button; }
+ inline float getXScale() const { return mXScale; }
+
+ inline float getYScale() const { return mYScale; }
+
inline float getXOffset() const { return mXOffset; }
inline float getYOffset() const { return mYOffset; }
@@ -624,9 +629,10 @@
ssize_t findPointerIndex(int32_t pointerId) const;
- void initialize(int32_t deviceId, int32_t source, int32_t displayId, int32_t action,
- int32_t actionButton, int32_t flags, int32_t edgeFlags, int32_t metaState,
- int32_t buttonState, MotionClassification classification, float xOffset,
+ void initialize(int32_t deviceId, uint32_t source, int32_t displayId,
+ std::array<uint8_t, 32> hmac, int32_t action, int32_t actionButton,
+ int32_t flags, int32_t edgeFlags, int32_t metaState, int32_t buttonState,
+ MotionClassification classification, float xScale, float yScale, float xOffset,
float yOffset, float xPrecision, float yPrecision, float rawXCursorPosition,
float rawYCursorPosition, nsecs_t downTime, nsecs_t eventTime,
size_t pointerCount, const PointerProperties* pointerProperties,
@@ -651,7 +657,7 @@
status_t writeToParcel(Parcel* parcel) const;
#endif
- static bool isTouchEvent(int32_t source, int32_t action);
+ static bool isTouchEvent(uint32_t source, int32_t action);
inline bool isTouchEvent() const {
return isTouchEvent(mSource, mAction);
}
@@ -676,6 +682,8 @@
int32_t mMetaState;
int32_t mButtonState;
MotionClassification mClassification;
+ float mXScale;
+ float mYScale;
float mXOffset;
float mYOffset;
float mXPrecision;
diff --git a/include/input/InputTransport.h b/include/input/InputTransport.h
index ae47438..06fd3bb 100644
--- a/include/input/InputTransport.h
+++ b/include/input/InputTransport.h
@@ -76,6 +76,9 @@
} header;
// Body *must* be 8 byte aligned.
+ // For keys and motions, rely on the fact that std::array takes up exactly as much space
+ // as the underlying data. This is not guaranteed by C++, but it simplifies the conversions.
+ static_assert(sizeof(std::array<uint8_t, 32>) == 32);
union Body {
struct Key {
uint32_t seq;
@@ -84,6 +87,7 @@
int32_t deviceId;
int32_t source;
int32_t displayId;
+ std::array<uint8_t, 32> hmac;
int32_t action;
int32_t flags;
int32_t keyCode;
@@ -103,6 +107,7 @@
int32_t deviceId;
int32_t source;
int32_t displayId;
+ std::array<uint8_t, 32> hmac;
int32_t action;
int32_t actionButton;
int32_t flags;
@@ -112,6 +117,8 @@
uint8_t empty2[3]; // 3 bytes to fill gap created by classification
int32_t edgeFlags;
nsecs_t downTime __attribute__((aligned(8)));
+ float xScale;
+ float yScale;
float xOffset;
float yOffset;
float xPrecision;
@@ -269,19 +276,10 @@
* Returns BAD_VALUE if seq is 0.
* Other errors probably indicate that the channel is broken.
*/
- status_t publishKeyEvent(
- uint32_t seq,
- int32_t deviceId,
- int32_t source,
- int32_t displayId,
- int32_t action,
- int32_t flags,
- int32_t keyCode,
- int32_t scanCode,
- int32_t metaState,
- int32_t repeatCount,
- nsecs_t downTime,
- nsecs_t eventTime);
+ status_t publishKeyEvent(uint32_t seq, int32_t deviceId, int32_t source, int32_t displayId,
+ std::array<uint8_t, 32> hmac, int32_t action, int32_t flags,
+ int32_t keyCode, int32_t scanCode, int32_t metaState,
+ int32_t repeatCount, nsecs_t downTime, nsecs_t eventTime);
/* Publishes a motion event to the input channel.
*
@@ -292,9 +290,10 @@
* Other errors probably indicate that the channel is broken.
*/
status_t publishMotionEvent(uint32_t seq, int32_t deviceId, int32_t source, int32_t displayId,
- int32_t action, int32_t actionButton, int32_t flags,
- int32_t edgeFlags, int32_t metaState, int32_t buttonState,
- MotionClassification classification, float xOffset, float yOffset,
+ std::array<uint8_t, 32> hmac, int32_t action, int32_t actionButton,
+ int32_t flags, int32_t edgeFlags, int32_t metaState,
+ int32_t buttonState, MotionClassification classification,
+ float xScale, float yScale, float xOffset, float yOffset,
float xPrecision, float yPrecision, float xCursorPosition,
float yCursorPosition, nsecs_t downTime, nsecs_t eventTime,
uint32_t pointerCount, const PointerProperties* pointerProperties,
diff --git a/libs/binder/BpBinder.cpp b/libs/binder/BpBinder.cpp
index 238c9dc..f16c39c 100644
--- a/libs/binder/BpBinder.cpp
+++ b/libs/binder/BpBinder.cpp
@@ -435,7 +435,8 @@
Vector<Obituary>* obits = mObituaries;
if(obits != nullptr) {
if (!obits->isEmpty()) {
- ALOGI("onLastStrongRef automatically unlinking death recipients");
+ ALOGI("onLastStrongRef automatically unlinking death recipients: %s",
+ mDescriptorCache.size() ? String8(mDescriptorCache).c_str() : "<uncached descriptor>");
}
if (ipc) ipc->clearDeathNotification(mHandle, this);
diff --git a/libs/binder/BufferedTextOutput.cpp b/libs/binder/BufferedTextOutput.cpp
index fb424fd..8cf6097 100644
--- a/libs/binder/BufferedTextOutput.cpp
+++ b/libs/binder/BufferedTextOutput.cpp
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-#include <binder/BufferedTextOutput.h>
+#include "BufferedTextOutput.h"
#include <binder/Debug.h>
#include <cutils/atomic.h>
@@ -49,9 +49,10 @@
}
status_t append(const char* txt, size_t len) {
+ if (len > SIZE_MAX - bufferPos) return NO_MEMORY; // overflow
if ((len+bufferPos) > bufferSize) {
+ if ((len + bufferPos) > SIZE_MAX / 3) return NO_MEMORY; // overflow
size_t newSize = ((len+bufferPos)*3)/2;
- if (newSize < (len+bufferPos)) return NO_MEMORY; // overflow
void* b = realloc(buffer, newSize);
if (!b) return NO_MEMORY;
buffer = (char*)b;
diff --git a/libs/binder/include/binder/BufferedTextOutput.h b/libs/binder/BufferedTextOutput.h
similarity index 100%
rename from libs/binder/include/binder/BufferedTextOutput.h
rename to libs/binder/BufferedTextOutput.h
diff --git a/libs/binder/LazyServiceRegistrar.cpp b/libs/binder/LazyServiceRegistrar.cpp
index dc9482c..f064bd7 100644
--- a/libs/binder/LazyServiceRegistrar.cpp
+++ b/libs/binder/LazyServiceRegistrar.cpp
@@ -53,14 +53,13 @@
struct Service {
sp<IBinder> service;
- std::string name;
bool allowIsolated;
int dumpFlags;
};
/**
- * Number of services that have been registered.
+ * Map of registered names and services
*/
- std::vector<Service> mRegisteredServices;
+ std::map<std::string, Service> mRegisteredServices;
};
bool ClientCounterCallback::registerService(const sp<IBinder>& service, const std::string& name,
@@ -68,20 +67,24 @@
auto manager = interface_cast<AidlServiceManager>(
ProcessState::self()->getContextObject(nullptr));
- ALOGI("Registering service %s", name.c_str());
+ bool reRegister = mRegisteredServices.count(name) > 0;
+ std::string regStr = (reRegister) ? "Re-registering" : "Registering";
+ ALOGI("%s service %s", regStr.c_str(), name.c_str());
if (!manager->addService(name.c_str(), service, allowIsolated, dumpFlags).isOk()) {
ALOGE("Failed to register service %s", name.c_str());
return false;
}
- if (!manager->registerClientCallback(name, service, this).isOk())
- {
- ALOGE("Failed to add client callback for service %s", name.c_str());
- return false;
+ if (!manager->registerClientCallback(name, service, this).isOk()) {
+ ALOGE("Failed to add client callback for service %s", name.c_str());
+ return false;
}
- mRegisteredServices.push_back({service, name, allowIsolated, dumpFlags});
+ if (!reRegister) {
+ // Only add this when a service is added for the first time, as it is not removed
+ mRegisteredServices[name] = {service, allowIsolated, dumpFlags};
+ }
return true;
}
@@ -119,10 +122,11 @@
for (; unRegisterIt != mRegisteredServices.end(); ++unRegisterIt) {
auto& entry = (*unRegisterIt);
- bool success = manager->tryUnregisterService(entry.name, entry.service).isOk();
+ bool success = manager->tryUnregisterService(entry.first, entry.second.service).isOk();
+
if (!success) {
- ALOGI("Failed to unregister service %s", entry.name.c_str());
+ ALOGI("Failed to unregister service %s", entry.first.c_str());
break;
}
}
@@ -137,7 +141,8 @@
auto& entry = (*reRegisterIt);
// re-register entry
- if (!registerService(entry.service, entry.name, entry.allowIsolated, entry.dumpFlags)) {
+ if (!registerService(entry.second.service, entry.first, entry.second.allowIsolated,
+ entry.second.dumpFlags)) {
// Must restart. Otherwise, clients will never be able to get a hold of this service.
ALOGE("Bad state: could not re-register services");
}
diff --git a/libs/binder/Parcel.cpp b/libs/binder/Parcel.cpp
index a9f8ae6..994e3b9 100644
--- a/libs/binder/Parcel.cpp
+++ b/libs/binder/Parcel.cpp
@@ -422,8 +422,10 @@
const sp<ProcessState> proc(ProcessState::self());
// grow objects
if (mObjectsCapacity < mObjectsSize + numObjects) {
+ if ((size_t) numObjects > SIZE_MAX - mObjectsSize) return NO_MEMORY; // overflow
+ if (mObjectsSize + numObjects > SIZE_MAX / 3) return NO_MEMORY; // overflow
size_t newSize = ((mObjectsSize + numObjects)*3)/2;
- if (newSize*sizeof(binder_size_t) < mObjectsSize) return NO_MEMORY; // overflow
+ if (newSize > SIZE_MAX / sizeof(binder_size_t)) return NO_MEMORY; // overflow
binder_size_t *objects =
(binder_size_t*)realloc(mObjects, newSize*sizeof(binder_size_t));
if (objects == (binder_size_t*)nullptr) {
@@ -1276,8 +1278,10 @@
if (err != NO_ERROR) return err;
}
if (!enoughObjects) {
+ if (mObjectsSize > SIZE_MAX - 2) return NO_MEMORY; // overflow
+ if ((mObjectsSize + 2) > SIZE_MAX / 3) return NO_MEMORY; // overflow
size_t newSize = ((mObjectsSize+2)*3)/2;
- if (newSize*sizeof(binder_size_t) < mObjectsSize) return NO_MEMORY; // overflow
+ if (newSize > SIZE_MAX / sizeof(binder_size_t)) return NO_MEMORY; // overflow
binder_size_t* objects = (binder_size_t*)realloc(mObjects, newSize*sizeof(binder_size_t));
if (objects == nullptr) return NO_MEMORY;
mObjects = objects;
@@ -2405,6 +2409,8 @@
return BAD_VALUE;
}
+ if (len > SIZE_MAX - mDataSize) return NO_MEMORY; // overflow
+ if (mDataSize + len > SIZE_MAX / 3) return NO_MEMORY; // overflow
size_t newSize = ((mDataSize+len)*3)/2;
return (newSize <= mDataSize)
? (status_t) NO_MEMORY
diff --git a/libs/binder/Static.cpp b/libs/binder/Static.cpp
index bd40536..7a77f6d 100644
--- a/libs/binder/Static.cpp
+++ b/libs/binder/Static.cpp
@@ -19,7 +19,7 @@
#include "Static.h"
-#include <binder/BufferedTextOutput.h>
+#include "BufferedTextOutput.h"
#include <binder/IPCThreadState.h>
#include <utils/Log.h>
diff --git a/libs/binder/TEST_MAPPING b/libs/binder/TEST_MAPPING
index b3afd81..7489afa 100644
--- a/libs/binder/TEST_MAPPING
+++ b/libs/binder/TEST_MAPPING
@@ -19,7 +19,13 @@
"name": "binderStabilityTest"
},
{
+ "name": "libbinder_ndk_unit_test"
+ },
+ {
"name": "CtsNdkBinderTestCases"
+ },
+ {
+ "name": "aidl_lazy_test"
}
]
}
diff --git a/libs/binder/ndk/runtests.sh b/libs/binder/ndk/runtests.sh
deleted file mode 100755
index a0c49fb..0000000
--- a/libs/binder/ndk/runtests.sh
+++ /dev/null
@@ -1,45 +0,0 @@
-#!/usr/bin/env bash
-
-# Copyright (C) 2018 The Android Open Source Project
-#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-# http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-
-if [ -z $ANDROID_BUILD_TOP ]; then
- echo "You need to source and lunch before you can use this script"
- exit 1
-fi
-
-set -ex
-
-function run_libbinder_ndk_test() {
- adb shell /data/nativetest64/libbinder_ndk_test_server/libbinder_ndk_test_server &
-
- # avoid getService 1s delay for most runs, non-critical
- sleep 0.1
-
- adb shell /data/nativetest64/libbinder_ndk_test_client/libbinder_ndk_test_client; \
- adb shell killall libbinder_ndk_test_server
-}
-
-[ "$1" != "--skip-build" ] && $ANDROID_BUILD_TOP/build/soong/soong_ui.bash --make-mode \
- MODULES-IN-frameworks-native-libs-binder-ndk
-
-adb root
-adb wait-for-device
-adb sync data
-
-# very simple unit tests, tests things outside of the NDK as well
-run_libbinder_ndk_test
-
-# CTS tests (much more comprehensive, new tests should ideally go here)
-atest android.binder.cts
diff --git a/libs/binder/ndk/test/Android.bp b/libs/binder/ndk/test/Android.bp
index daaaa5a..513d8c2 100644
--- a/libs/binder/ndk/test/Android.bp
+++ b/libs/binder/ndk/test/Android.bp
@@ -56,16 +56,17 @@
// specifically the parts which are outside of the NDK. Actual users should
// also instead use AIDL to generate these stubs. See android.binder.cts.
cc_test {
- name: "libbinder_ndk_test_client",
+ name: "libbinder_ndk_unit_test",
defaults: ["test_libbinder_ndk_test_defaults"],
- srcs: ["main_client.cpp"],
-}
+ srcs: ["libbinder_ndk_unit_test.cpp"],
+ static_libs: [
+ "IBinderNdkUnitTest-ndk_platform",
+ ],
+ test_suites: ["general-tests"],
+ require_root: true,
-cc_test {
- name: "libbinder_ndk_test_server",
- defaults: ["test_libbinder_ndk_test_defaults"],
- srcs: ["main_server.cpp"],
- gtest: false,
+ // force since binderVendorDoubleLoadTest has its own
+ auto_gen_config: true,
}
cc_test {
@@ -85,7 +86,7 @@
"libbinder_ndk",
"libutils",
],
- test_suites: ["device-tests"],
+ test_suites: ["general-tests"],
}
aidl_interface {
@@ -95,3 +96,11 @@
"IBinderVendorDoubleLoadTest.aidl",
],
}
+
+aidl_interface {
+ name: "IBinderNdkUnitTest",
+ srcs: [
+ "IBinderNdkUnitTest.aidl",
+ "IEmpty.aidl",
+ ],
+}
diff --git a/libs/binder/ndk/test/IBinderNdkUnitTest.aidl b/libs/binder/ndk/test/IBinderNdkUnitTest.aidl
new file mode 100644
index 0000000..6e8e463
--- /dev/null
+++ b/libs/binder/ndk/test/IBinderNdkUnitTest.aidl
@@ -0,0 +1,27 @@
+/*
+ * Copyright (C) 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.
+ */
+
+// This AIDL is to test things that can't be tested in CtsNdkBinderTestCases
+// because it requires libbinder_ndk implementation details or APIs not
+// available to apps. Please prefer adding tests to CtsNdkBinderTestCases
+// over here.
+
+import IEmpty;
+
+interface IBinderNdkUnitTest {
+ void takeInterface(IEmpty test);
+ void forceFlushCommands();
+}
diff --git a/libs/binder/ndk/test/IEmpty.aidl b/libs/binder/ndk/test/IEmpty.aidl
new file mode 100644
index 0000000..95e4341
--- /dev/null
+++ b/libs/binder/ndk/test/IEmpty.aidl
@@ -0,0 +1,17 @@
+/*
+ * Copyright (C) 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.
+ */
+
+interface IEmpty { }
diff --git a/libs/binder/ndk/test/main_client.cpp b/libs/binder/ndk/test/libbinder_ndk_unit_test.cpp
similarity index 66%
rename from libs/binder/ndk/test/main_client.cpp
rename to libs/binder/ndk/test/libbinder_ndk_unit_test.cpp
index 8467734..51dd169 100644
--- a/libs/binder/ndk/test/main_client.cpp
+++ b/libs/binder/ndk/test/libbinder_ndk_unit_test.cpp
@@ -14,6 +14,8 @@
* limitations under the License.
*/
+#include <aidl/BnBinderNdkUnitTest.h>
+#include <aidl/BnEmpty.h>
#include <android-base/logging.h>
#include <android/binder_ibinder_jni.h>
#include <android/binder_manager.h>
@@ -21,6 +23,11 @@
#include <gtest/gtest.h>
#include <iface/iface.h>
+// warning: this is assuming that libbinder_ndk is using the same copy
+// of libbinder that we are.
+#include <binder/IPCThreadState.h>
+
+#include <sys/prctl.h>
#include <chrono>
#include <condition_variable>
#include <mutex>
@@ -28,6 +35,65 @@
using ::android::sp;
constexpr char kExistingNonNdkService[] = "SurfaceFlinger";
+constexpr char kBinderNdkUnitTestService[] = "BinderNdkUnitTest";
+
+class MyBinderNdkUnitTest : public aidl::BnBinderNdkUnitTest {
+ ndk::ScopedAStatus takeInterface(const std::shared_ptr<aidl::IEmpty>& empty) {
+ (void)empty;
+ return ndk::ScopedAStatus::ok();
+ }
+ ndk::ScopedAStatus forceFlushCommands() {
+ // warning: this is assuming that libbinder_ndk is using the same copy
+ // of libbinder that we are.
+ android::IPCThreadState::self()->flushCommands();
+ return ndk::ScopedAStatus::ok();
+ }
+};
+
+int generatedService() {
+ ABinderProcess_setThreadPoolMaxThreadCount(0);
+
+ auto service = ndk::SharedRefBase::make<MyBinderNdkUnitTest>();
+ binder_status_t status =
+ AServiceManager_addService(service->asBinder().get(), kBinderNdkUnitTestService);
+
+ if (status != STATUS_OK) {
+ LOG(FATAL) << "Could not register: " << status << " " << kBinderNdkUnitTestService;
+ }
+
+ ABinderProcess_joinThreadPool();
+
+ return 1; // should not return
+}
+
+// manually-written parceling class considered bad practice
+class MyFoo : public IFoo {
+ binder_status_t doubleNumber(int32_t in, int32_t* out) override {
+ *out = 2 * in;
+ LOG(INFO) << "doubleNumber (" << in << ") => " << *out;
+ return STATUS_OK;
+ }
+
+ binder_status_t die() override {
+ LOG(FATAL) << "IFoo::die called!";
+ return STATUS_UNKNOWN_ERROR;
+ }
+};
+
+int manualService(const char* instance) {
+ ABinderProcess_setThreadPoolMaxThreadCount(0);
+
+ // Strong reference to MyFoo kept by service manager.
+ binder_status_t status = (new MyFoo)->addService(instance);
+
+ if (status != STATUS_OK) {
+ LOG(FATAL) << "Could not register: " << status << " " << instance;
+ }
+
+ ABinderProcess_joinThreadPool();
+
+ return 1; // should not return
+}
// This is too slow
// TEST(NdkBinder, GetServiceThatDoesntExist) {
@@ -87,14 +153,14 @@
EXPECT_EQ(STATUS_DEAD_OBJECT, foo->die());
foo = nullptr;
- AIBinder_decStrong(binder);
- binder = nullptr;
std::unique_lock<std::mutex> lock(deathMutex);
EXPECT_TRUE(deathCv.wait_for(lock, 1s, [&] { return deathRecieved; }));
EXPECT_TRUE(deathRecieved);
AIBinder_DeathRecipient_delete(recipient);
+ AIBinder_decStrong(binder);
+ binder = nullptr;
}
TEST(NdkBinder, RetrieveNonNdkService) {
@@ -196,9 +262,56 @@
EXPECT_EQ(IFoo::getService(kInstanceName1), IFoo::getService(kInstanceName2));
}
+TEST(NdkBinder, SentAidlBinderCanBeDestroyed) {
+ static volatile bool destroyed = false;
+ static std::mutex dMutex;
+ static std::condition_variable cv;
+
+ class MyEmpty : public aidl::BnEmpty {
+ virtual ~MyEmpty() {
+ destroyed = true;
+ cv.notify_one();
+ }
+ };
+
+ std::shared_ptr<MyEmpty> empty = ndk::SharedRefBase::make<MyEmpty>();
+
+ ndk::SpAIBinder binder(AServiceManager_getService(kBinderNdkUnitTestService));
+ std::shared_ptr<aidl::IBinderNdkUnitTest> service =
+ aidl::IBinderNdkUnitTest::fromBinder(binder);
+
+ EXPECT_FALSE(destroyed);
+
+ service->takeInterface(empty);
+ service->forceFlushCommands();
+ empty = nullptr;
+
+ // give other binder thread time to process commands
+ {
+ using namespace std::chrono_literals;
+ std::unique_lock<std::mutex> lk(dMutex);
+ cv.wait_for(lk, 1s, [] { return destroyed; });
+ }
+
+ EXPECT_TRUE(destroyed);
+}
+
int main(int argc, char* argv[]) {
::testing::InitGoogleTest(&argc, argv);
+ if (fork() == 0) {
+ prctl(PR_SET_PDEATHSIG, SIGHUP);
+ return manualService(IFoo::kInstanceNameToDieFor);
+ }
+ if (fork() == 0) {
+ prctl(PR_SET_PDEATHSIG, SIGHUP);
+ return manualService(IFoo::kSomeInstanceName);
+ }
+ if (fork() == 0) {
+ prctl(PR_SET_PDEATHSIG, SIGHUP);
+ return generatedService();
+ }
+
ABinderProcess_setThreadPoolMaxThreadCount(1); // to recieve death notifications/callbacks
ABinderProcess_startThreadPool();
diff --git a/libs/binder/ndk/test/main_server.cpp b/libs/binder/ndk/test/main_server.cpp
deleted file mode 100644
index a6e17e8..0000000
--- a/libs/binder/ndk/test/main_server.cpp
+++ /dev/null
@@ -1,57 +0,0 @@
-/*
- * Copyright (C) 2018 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#include <android-base/logging.h>
-#include <android/binder_process.h>
-#include <iface/iface.h>
-
-using ::android::sp;
-
-class MyFoo : public IFoo {
- binder_status_t doubleNumber(int32_t in, int32_t* out) override {
- *out = 2 * in;
- LOG(INFO) << "doubleNumber (" << in << ") => " << *out;
- return STATUS_OK;
- }
-
- binder_status_t die() override {
- LOG(FATAL) << "IFoo::die called!";
- return STATUS_UNKNOWN_ERROR;
- }
-};
-
-int service(const char* instance) {
- ABinderProcess_setThreadPoolMaxThreadCount(0);
-
- // Strong reference to MyFoo kept by service manager.
- binder_status_t status = (new MyFoo)->addService(instance);
-
- if (status != STATUS_OK) {
- LOG(FATAL) << "Could not register: " << status << " " << instance;
- }
-
- ABinderProcess_joinThreadPool();
-
- return 1; // should not return
-}
-
-int main() {
- if (fork() == 0) {
- return service(IFoo::kInstanceNameToDieFor);
- }
-
- return service(IFoo::kSomeInstanceName);
-}
diff --git a/libs/cputimeinstate/cputimeinstate.cpp b/libs/cputimeinstate/cputimeinstate.cpp
index ee44cf5..31a399b 100644
--- a/libs/cputimeinstate/cputimeinstate.cpp
+++ b/libs/cputimeinstate/cputimeinstate.cpp
@@ -49,6 +49,8 @@
static std::mutex gInitializedMutex;
static bool gInitialized = false;
+static std::mutex gTrackingMutex;
+static bool gTracking = false;
static uint32_t gNPolicies = 0;
static uint32_t gNCpus = 0;
static std::vector<std::vector<uint32_t>> gPolicyFreqs;
@@ -161,7 +163,9 @@
// This function should *not* be called while tracking is already active; doing so is unnecessary
// and can lead to accounting errors.
bool startTrackingUidTimes() {
+ std::lock_guard<std::mutex> guard(gTrackingMutex);
if (!initGlobals()) return false;
+ if (gTracking) return true;
unique_fd cpuPolicyFd(bpf_obj_get_wronly(BPF_FS_PATH "map_time_in_state_cpu_policy_map"));
if (cpuPolicyFd < 0) return false;
@@ -209,8 +213,9 @@
if (writeToMapEntry(policyFreqIdxFd, &i, &zero, BPF_ANY)) return false;
}
- return attachTracepointProgram("sched", "sched_switch") &&
+ gTracking = attachTracepointProgram("sched", "sched_switch") &&
attachTracepointProgram("power", "cpu_frequency");
+ return gTracking;
}
std::optional<std::vector<std::vector<uint32_t>>> getCpuFreqs() {
diff --git a/libs/dumputils/dump_utils.cpp b/libs/dumputils/dump_utils.cpp
index 875059c..90fded0 100644
--- a/libs/dumputils/dump_utils.cpp
+++ b/libs/dumputils/dump_utils.cpp
@@ -55,6 +55,7 @@
"android.hardware.audio@2.0::IDevicesFactory",
"android.hardware.audio@4.0::IDevicesFactory",
"android.hardware.audio@5.0::IDevicesFactory",
+ "android.hardware.audio@6.0::IDevicesFactory",
"android.hardware.biometrics.face@1.0::IBiometricsFace",
"android.hardware.bluetooth@1.0::IBluetoothHci",
"android.hardware.camera.provider@2.4::ICameraProvider",
diff --git a/libs/gralloc/types/Gralloc4.cpp b/libs/gralloc/types/Gralloc4.cpp
index 4851b44..4e8569f 100644
--- a/libs/gralloc/types/Gralloc4.cpp
+++ b/libs/gralloc/types/Gralloc4.cpp
@@ -42,6 +42,7 @@
using aidl::android::hardware::graphics::common::StandardMetadataType;
using aidl::android::hardware::graphics::common::XyColor;
+using BufferDescriptorInfo = android::hardware::graphics::mapper::V4_0::IMapper::BufferDescriptorInfo;
using MetadataType = android::hardware::graphics::mapper::V4_0::IMapper::MetadataType;
namespace android {
@@ -195,15 +196,32 @@
status_t validateMetadataType(InputHidlVec* input, const MetadataType& expectedMetadataType);
/**
- * encode is the main encoding function. It takes in T and uses the encodeHelper function to turn T
- * into the hidl_vec byte stream.
+ * encode/encodeMetadata are the main encoding functions. They take in T and uses the encodeHelper
+ * function to turn T into the hidl_vec byte stream.
*
- * This function first calls the encodeHelper function to determine how large the hidl_vec
- * needs to be. It resizes the hidl_vec. Finally, it reruns the encodeHelper function which
+ * These functions first call the encodeHelper function to determine how large the hidl_vec
+ * needs to be. They resize the hidl_vec. Finally, it reruns the encodeHelper function which
* encodes T into the hidl_vec byte stream.
*/
template <class T>
-status_t encode(const MetadataType& metadataType, const T& input, hidl_vec<uint8_t>* output,
+status_t encode(const T& input, hidl_vec<uint8_t>* output, EncodeHelper<T> encodeHelper) {
+ OutputHidlVec outputHidlVec{output};
+
+ status_t err = encodeHelper(input, &outputHidlVec);
+ if (err) {
+ return err;
+ }
+
+ err = outputHidlVec.resize();
+ if (err) {
+ return err;
+ }
+
+ return encodeHelper(input, &outputHidlVec);
+}
+
+template <class T>
+status_t encodeMetadata(const MetadataType& metadataType, const T& input, hidl_vec<uint8_t>* output,
EncodeHelper<T> encodeHelper) {
OutputHidlVec outputHidlVec{output};
@@ -231,21 +249,42 @@
}
template <class T>
-status_t encodeOptional(const MetadataType& metadataType, const std::optional<T>& input,
+status_t encodeOptionalMetadata(const MetadataType& metadataType, const std::optional<T>& input,
hidl_vec<uint8_t>* output, EncodeHelper<T> encodeHelper) {
if (!input) {
return NO_ERROR;
}
- return encode(metadataType, *input, output, encodeHelper);
+ return encodeMetadata(metadataType, *input, output, encodeHelper);
}
/**
- * decode is the main decode function. It takes in a hidl_vec and uses the decodeHelper function to
- * turn the hidl_vec byte stream into T. If an error occurs, the errorHandler function cleans up
- * T.
+ * decode/decodeMetadata are the main decoding functions. They take in a hidl_vec and use the
+ * decodeHelper function to turn the hidl_vec byte stream into T. If an error occurs, the
+ * errorHandler function cleans up T.
*/
template <class T>
-status_t decode(const MetadataType& metadataType, const hidl_vec<uint8_t>& input, T* output,
+status_t decode(const hidl_vec<uint8_t>& input, T* output, DecodeHelper<T> decodeHelper,
+ ErrorHandler<T> errorHandler = nullptr) {
+ InputHidlVec inputHidlVec{&input};
+
+ status_t err = decodeHelper(&inputHidlVec, output);
+ if (err) {
+ return err;
+ }
+
+ err = inputHidlVec.hasRemainingData();
+ if (err) {
+ if (errorHandler) {
+ errorHandler(output);
+ }
+ return BAD_VALUE;
+ }
+
+ return NO_ERROR;
+}
+
+template <class T>
+status_t decodeMetadata(const MetadataType& metadataType, const hidl_vec<uint8_t>& input, T* output,
DecodeHelper<T> decodeHelper, ErrorHandler<T> errorHandler = nullptr) {
InputHidlVec inputHidlVec{&input};
@@ -271,7 +310,7 @@
}
template <class T>
-status_t decodeOptional(const MetadataType& metadataType, const hidl_vec<uint8_t>& input,
+status_t decodeOptionalMetadata(const MetadataType& metadataType, const hidl_vec<uint8_t>& input,
std::optional<T>* output, DecodeHelper<T> decodeHelper) {
if (!output) {
return BAD_VALUE;
@@ -281,7 +320,7 @@
return NO_ERROR;
}
T tmp;
- status_t err = decode(metadataType, input, &tmp, decodeHelper);
+ status_t err = decodeMetadata(metadataType, input, &tmp, decodeHelper);
if (!err) {
*output = tmp;
}
@@ -520,6 +559,66 @@
return decodeInteger<int32_t>(input, &output->bottom);
}
+status_t encodeBufferDescriptorInfoHelper(const BufferDescriptorInfo& input,
+ OutputHidlVec* output) {
+ status_t err = encodeString(input.name, output);
+ if (err) {
+ return err;
+ }
+ err = encodeInteger<uint32_t>(input.width, output);
+ if (err) {
+ return err;
+ }
+ err = encodeInteger<uint32_t>(input.height, output);
+ if (err) {
+ return err;
+ }
+ err = encodeInteger<uint32_t>(input.layerCount, output);
+ if (err) {
+ return err;
+ }
+ err = encodeInteger<int32_t>(static_cast<int32_t>(input.format), output);
+ if (err) {
+ return err;
+ }
+ err = encodeInteger<uint64_t>(input.usage, output);
+ if (err) {
+ return err;
+ }
+ return encodeInteger<uint64_t>(input.reservedSize, output);
+}
+
+status_t decodeBufferDescriptorInfoHelper(InputHidlVec* input, BufferDescriptorInfo* output) {
+ std::string name;
+ status_t err = decodeString(input, &name);
+ if (err) {
+ return err;
+ }
+ output->name = name;
+
+ err = decodeInteger<uint32_t>(input, &output->width);
+ if (err) {
+ return err;
+ }
+ err = decodeInteger<uint32_t>(input, &output->height);
+ if (err) {
+ return err;
+ }
+ err = decodeInteger<uint32_t>(input, &output->layerCount);
+ if (err) {
+ return err;
+ }
+ err = decodeInteger<int32_t>(input, reinterpret_cast<int32_t*>(&output->format));
+ if (err) {
+ return err;
+ }
+ err = decodeInteger<uint64_t>(input, &output->usage);
+ if (err) {
+ return err;
+ }
+ return decodeInteger<uint64_t>(input, &output->reservedSize);
+}
+
status_t encodePlaneLayoutComponent(const PlaneLayoutComponent& input, OutputHidlVec* output) {
if (!output) {
return BAD_VALUE;
@@ -799,260 +898,269 @@
/**
* Public API functions
*/
+status_t encodeBufferDescriptorInfo(const BufferDescriptorInfo& bufferDescriptorInfo,
+ hidl_vec<uint8_t>* outBufferDescriptorInfo) {
+ return encode(bufferDescriptorInfo, outBufferDescriptorInfo, encodeBufferDescriptorInfoHelper);
+}
+
+status_t decodeBufferDescriptorInfo(const hidl_vec<uint8_t>& bufferDescriptorInfo,
+ BufferDescriptorInfo* outBufferDescriptorInfo) {
+ return decode(bufferDescriptorInfo, outBufferDescriptorInfo, decodeBufferDescriptorInfoHelper);
+}
status_t encodeBufferId(uint64_t bufferId, hidl_vec<uint8_t>* outBufferId) {
- return encode(MetadataType_BufferId, bufferId, outBufferId, encodeInteger);
+ return encodeMetadata(MetadataType_BufferId, bufferId, outBufferId, encodeInteger);
}
status_t decodeBufferId(const hidl_vec<uint8_t>& bufferId, uint64_t* outBufferId) {
- return decode(MetadataType_BufferId, bufferId, outBufferId, decodeInteger);
+ return decodeMetadata(MetadataType_BufferId, bufferId, outBufferId, decodeInteger);
}
status_t encodeName(const std::string& name, hidl_vec<uint8_t>* outName) {
- return encode(MetadataType_Name, name, outName, encodeString);
+ return encodeMetadata(MetadataType_Name, name, outName, encodeString);
}
status_t decodeName(const hidl_vec<uint8_t>& name, std::string* outName) {
- return decode(MetadataType_Name, name, outName, decodeString);
+ return decodeMetadata(MetadataType_Name, name, outName, decodeString);
}
status_t encodeWidth(uint64_t width, hidl_vec<uint8_t>* outWidth) {
- return encode(MetadataType_Width, width, outWidth, encodeInteger);
+ return encodeMetadata(MetadataType_Width, width, outWidth, encodeInteger);
}
status_t decodeWidth(const hidl_vec<uint8_t>& width, uint64_t* outWidth) {
- return decode(MetadataType_Width, width, outWidth, decodeInteger);
+ return decodeMetadata(MetadataType_Width, width, outWidth, decodeInteger);
}
status_t encodeHeight(uint64_t height, hidl_vec<uint8_t>* outHeight) {
- return encode(MetadataType_Height, height, outHeight, encodeInteger);
+ return encodeMetadata(MetadataType_Height, height, outHeight, encodeInteger);
}
status_t decodeHeight(const hidl_vec<uint8_t>& height, uint64_t* outHeight) {
- return decode(MetadataType_Height, height, outHeight, decodeInteger);
+ return decodeMetadata(MetadataType_Height, height, outHeight, decodeInteger);
}
status_t encodeLayerCount(uint64_t layerCount, hidl_vec<uint8_t>* outLayerCount) {
- return encode(MetadataType_LayerCount, layerCount, outLayerCount, encodeInteger);
+ return encodeMetadata(MetadataType_LayerCount, layerCount, outLayerCount, encodeInteger);
}
status_t decodeLayerCount(const hidl_vec<uint8_t>& layerCount, uint64_t* outLayerCount) {
- return decode(MetadataType_LayerCount, layerCount, outLayerCount, decodeInteger);
+ return decodeMetadata(MetadataType_LayerCount, layerCount, outLayerCount, decodeInteger);
}
status_t encodePixelFormatRequested(const hardware::graphics::common::V1_2::PixelFormat& pixelFormatRequested,
hidl_vec<uint8_t>* outPixelFormatRequested) {
- return encode(MetadataType_PixelFormatRequested, static_cast<int32_t>(pixelFormatRequested),
+ return encodeMetadata(MetadataType_PixelFormatRequested, static_cast<int32_t>(pixelFormatRequested),
outPixelFormatRequested, encodeInteger);
}
status_t decodePixelFormatRequested(const hidl_vec<uint8_t>& pixelFormatRequested,
hardware::graphics::common::V1_2::PixelFormat* outPixelFormatRequested) {
- return decode(MetadataType_PixelFormatRequested, pixelFormatRequested,
+ return decodeMetadata(MetadataType_PixelFormatRequested, pixelFormatRequested,
reinterpret_cast<int32_t*>(outPixelFormatRequested), decodeInteger);
}
status_t encodePixelFormatFourCC(uint32_t pixelFormatFourCC, hidl_vec<uint8_t>* outPixelFormatFourCC) {
- return encode(MetadataType_PixelFormatFourCC, pixelFormatFourCC, outPixelFormatFourCC,
+ return encodeMetadata(MetadataType_PixelFormatFourCC, pixelFormatFourCC, outPixelFormatFourCC,
encodeInteger);
}
status_t decodePixelFormatFourCC(const hidl_vec<uint8_t>& pixelFormatFourCC, uint32_t* outPixelFormatFourCC) {
- return decode(MetadataType_PixelFormatFourCC, pixelFormatFourCC, outPixelFormatFourCC,
+ return decodeMetadata(MetadataType_PixelFormatFourCC, pixelFormatFourCC, outPixelFormatFourCC,
decodeInteger);
}
status_t encodePixelFormatModifier(uint64_t pixelFormatModifier, hidl_vec<uint8_t>* outPixelFormatModifier) {
- return encode(MetadataType_PixelFormatModifier, pixelFormatModifier, outPixelFormatModifier,
+ return encodeMetadata(MetadataType_PixelFormatModifier, pixelFormatModifier, outPixelFormatModifier,
encodeInteger);
}
status_t decodePixelFormatModifier(const hidl_vec<uint8_t>& pixelFormatModifier, uint64_t* outPixelFormatModifier) {
- return decode(MetadataType_PixelFormatModifier, pixelFormatModifier, outPixelFormatModifier,
+ return decodeMetadata(MetadataType_PixelFormatModifier, pixelFormatModifier, outPixelFormatModifier,
decodeInteger);
}
status_t encodeUsage(uint64_t usage, hidl_vec<uint8_t>* outUsage) {
- return encode(MetadataType_Usage, usage, outUsage, encodeInteger);
+ return encodeMetadata(MetadataType_Usage, usage, outUsage, encodeInteger);
}
status_t decodeUsage(const hidl_vec<uint8_t>& usage, uint64_t* outUsage) {
- return decode(MetadataType_Usage, usage, outUsage, decodeInteger);
+ return decodeMetadata(MetadataType_Usage, usage, outUsage, decodeInteger);
}
status_t encodeAllocationSize(uint64_t allocationSize, hidl_vec<uint8_t>* outAllocationSize) {
- return encode(MetadataType_AllocationSize, allocationSize, outAllocationSize, encodeInteger);
+ return encodeMetadata(MetadataType_AllocationSize, allocationSize, outAllocationSize, encodeInteger);
}
status_t decodeAllocationSize(const hidl_vec<uint8_t>& allocationSize, uint64_t* outAllocationSize) {
- return decode(MetadataType_AllocationSize, allocationSize, outAllocationSize, decodeInteger);
+ return decodeMetadata(MetadataType_AllocationSize, allocationSize, outAllocationSize, decodeInteger);
}
status_t encodeProtectedContent(uint64_t protectedContent, hidl_vec<uint8_t>* outProtectedContent) {
- return encode(MetadataType_ProtectedContent, protectedContent, outProtectedContent,
+ return encodeMetadata(MetadataType_ProtectedContent, protectedContent, outProtectedContent,
encodeInteger);
}
status_t decodeProtectedContent(const hidl_vec<uint8_t>& protectedContent, uint64_t* outProtectedContent) {
- return decode(MetadataType_ProtectedContent, protectedContent, outProtectedContent,
+ return decodeMetadata(MetadataType_ProtectedContent, protectedContent, outProtectedContent,
decodeInteger);
}
status_t encodeCompression(const ExtendableType& compression, hidl_vec<uint8_t>* outCompression) {
- return encode(MetadataType_Compression, compression, outCompression, encodeExtendableType);
+ return encodeMetadata(MetadataType_Compression, compression, outCompression, encodeExtendableType);
}
status_t decodeCompression(const hidl_vec<uint8_t>& compression, ExtendableType* outCompression) {
- return decode(MetadataType_Compression, compression, outCompression, decodeExtendableType,
+ return decodeMetadata(MetadataType_Compression, compression, outCompression, decodeExtendableType,
clearExtendableType);
}
status_t encodeInterlaced(const ExtendableType& interlaced, hidl_vec<uint8_t>* outInterlaced) {
- return encode(MetadataType_Interlaced, interlaced, outInterlaced, encodeExtendableType);
+ return encodeMetadata(MetadataType_Interlaced, interlaced, outInterlaced, encodeExtendableType);
}
status_t decodeInterlaced(const hidl_vec<uint8_t>& interlaced, ExtendableType* outInterlaced) {
- return decode(MetadataType_Interlaced, interlaced, outInterlaced, decodeExtendableType,
+ return decodeMetadata(MetadataType_Interlaced, interlaced, outInterlaced, decodeExtendableType,
clearExtendableType);
}
status_t encodeChromaSiting(const ExtendableType& chromaSiting, hidl_vec<uint8_t>* outChromaSiting) {
- return encode(MetadataType_ChromaSiting, chromaSiting, outChromaSiting, encodeExtendableType);
+ return encodeMetadata(MetadataType_ChromaSiting, chromaSiting, outChromaSiting, encodeExtendableType);
}
status_t decodeChromaSiting(const hidl_vec<uint8_t>& chromaSiting, ExtendableType* outChromaSiting) {
- return decode(MetadataType_ChromaSiting, chromaSiting, outChromaSiting, decodeExtendableType,
+ return decodeMetadata(MetadataType_ChromaSiting, chromaSiting, outChromaSiting, decodeExtendableType,
clearExtendableType);
}
status_t encodePlaneLayouts(const std::vector<PlaneLayout>& planeLayouts, hidl_vec<uint8_t>* outPlaneLayouts) {
- return encode(MetadataType_PlaneLayouts, planeLayouts, outPlaneLayouts,
+ return encodeMetadata(MetadataType_PlaneLayouts, planeLayouts, outPlaneLayouts,
encodePlaneLayoutsHelper);
}
status_t decodePlaneLayouts(const hidl_vec<uint8_t>& planeLayouts, std::vector<PlaneLayout>* outPlaneLayouts) {
- return decode(MetadataType_PlaneLayouts, planeLayouts, outPlaneLayouts,
+ return decodeMetadata(MetadataType_PlaneLayouts, planeLayouts, outPlaneLayouts,
decodePlaneLayoutsHelper, clearPlaneLayouts);
}
status_t encodeDataspace(const Dataspace& dataspace, hidl_vec<uint8_t>* outDataspace) {
- return encode(MetadataType_Dataspace, static_cast<int32_t>(dataspace), outDataspace,
+ return encodeMetadata(MetadataType_Dataspace, static_cast<int32_t>(dataspace), outDataspace,
encodeInteger);
}
status_t decodeDataspace(const hidl_vec<uint8_t>& dataspace, Dataspace* outDataspace) {
- return decode(MetadataType_Dataspace, dataspace, reinterpret_cast<int32_t*>(outDataspace),
+ return decodeMetadata(MetadataType_Dataspace, dataspace, reinterpret_cast<int32_t*>(outDataspace),
decodeInteger);
}
status_t encodeBlendMode(const BlendMode& blendMode, hidl_vec<uint8_t>* outBlendMode) {
- return encode(MetadataType_BlendMode, static_cast<int32_t>(blendMode), outBlendMode,
+ return encodeMetadata(MetadataType_BlendMode, static_cast<int32_t>(blendMode), outBlendMode,
encodeInteger);
}
status_t decodeBlendMode(const hidl_vec<uint8_t>& blendMode, BlendMode* outBlendMode) {
- return decode(MetadataType_BlendMode, blendMode, reinterpret_cast<int32_t*>(outBlendMode),
+ return decodeMetadata(MetadataType_BlendMode, blendMode, reinterpret_cast<int32_t*>(outBlendMode),
decodeInteger);
}
status_t encodeSmpte2086(const std::optional<Smpte2086>& smpte2086,
hidl_vec<uint8_t>* outSmpte2086) {
- return encodeOptional(MetadataType_Smpte2086, smpte2086, outSmpte2086, encodeSmpte2086Helper);
+ return encodeOptionalMetadata(MetadataType_Smpte2086, smpte2086, outSmpte2086, encodeSmpte2086Helper);
}
status_t decodeSmpte2086(const hidl_vec<uint8_t>& smpte2086,
std::optional<Smpte2086>* outSmpte2086) {
- return decodeOptional(MetadataType_Smpte2086, smpte2086, outSmpte2086, decodeSmpte2086Helper);
+ return decodeOptionalMetadata(MetadataType_Smpte2086, smpte2086, outSmpte2086, decodeSmpte2086Helper);
}
status_t encodeCta861_3(const std::optional<Cta861_3>& cta861_3, hidl_vec<uint8_t>* outCta861_3) {
- return encodeOptional(MetadataType_Cta861_3, cta861_3, outCta861_3, encodeCta861_3Helper);
+ return encodeOptionalMetadata(MetadataType_Cta861_3, cta861_3, outCta861_3, encodeCta861_3Helper);
}
status_t decodeCta861_3(const hidl_vec<uint8_t>& cta861_3, std::optional<Cta861_3>* outCta861_3) {
- return decodeOptional(MetadataType_Cta861_3, cta861_3, outCta861_3, decodeCta861_3Helper);
+ return decodeOptionalMetadata(MetadataType_Cta861_3, cta861_3, outCta861_3, decodeCta861_3Helper);
}
status_t encodeSmpte2094_40(const std::optional<std::vector<uint8_t>>& smpte2094_40,
hidl_vec<uint8_t>* outSmpte2094_40) {
- return encodeOptional(MetadataType_Smpte2094_40, smpte2094_40, outSmpte2094_40,
+ return encodeOptionalMetadata(MetadataType_Smpte2094_40, smpte2094_40, outSmpte2094_40,
encodeByteVector);
}
status_t decodeSmpte2094_40(const hidl_vec<uint8_t>& smpte2094_40,
std::optional<std::vector<uint8_t>>* outSmpte2094_40) {
- return decodeOptional(MetadataType_Smpte2094_40, smpte2094_40, outSmpte2094_40,
+ return decodeOptionalMetadata(MetadataType_Smpte2094_40, smpte2094_40, outSmpte2094_40,
decodeByteVector);
}
status_t encodeUint32(const MetadataType& metadataType, uint32_t input,
hidl_vec<uint8_t>* output) {
- return encode(metadataType, input, output, encodeInteger);
+ return encodeMetadata(metadataType, input, output, encodeInteger);
}
status_t decodeUint32(const MetadataType& metadataType, const hidl_vec<uint8_t>& input,
uint32_t* output) {
- return decode(metadataType, input, output, decodeInteger);
+ return decodeMetadata(metadataType, input, output, decodeInteger);
}
status_t encodeInt32(const MetadataType& metadataType, int32_t input,
hidl_vec<uint8_t>* output) {
- return encode(metadataType, input, output, encodeInteger);
+ return encodeMetadata(metadataType, input, output, encodeInteger);
}
status_t decodeInt32(const MetadataType& metadataType, const hidl_vec<uint8_t>& input,
int32_t* output) {
- return decode(metadataType, input, output, decodeInteger);
+ return decodeMetadata(metadataType, input, output, decodeInteger);
}
status_t encodeUint64(const MetadataType& metadataType, uint64_t input,
hidl_vec<uint8_t>* output) {
- return encode(metadataType, input, output, encodeInteger);
+ return encodeMetadata(metadataType, input, output, encodeInteger);
}
status_t decodeUint64(const MetadataType& metadataType, const hidl_vec<uint8_t>& input,
uint64_t* output) {
- return decode(metadataType, input, output, decodeInteger);
+ return decodeMetadata(metadataType, input, output, decodeInteger);
}
status_t encodeInt64(const MetadataType& metadataType, int64_t input,
hidl_vec<uint8_t>* output) {
- return encode(metadataType, input, output, encodeInteger);
+ return encodeMetadata(metadataType, input, output, encodeInteger);
}
status_t decodeInt64(const MetadataType& metadataType, const hidl_vec<uint8_t>& input,
int64_t* output) {
- return decode(metadataType, input, output, decodeInteger);
+ return decodeMetadata(metadataType, input, output, decodeInteger);
}
status_t encodeFloat(const MetadataType& metadataType, float input,
hidl_vec<uint8_t>* output) {
- return encode(metadataType, input, output, encodeInteger);
+ return encodeMetadata(metadataType, input, output, encodeInteger);
}
status_t decodeFloat(const MetadataType& metadataType, const hidl_vec<uint8_t>& input,
float* output) {
- return decode(metadataType, input, output, decodeInteger);
+ return decodeMetadata(metadataType, input, output, decodeInteger);
}
status_t encodeDouble(const MetadataType& metadataType, double input,
hidl_vec<uint8_t>* output) {
- return encode(metadataType, input, output, encodeInteger);
+ return encodeMetadata(metadataType, input, output, encodeInteger);
}
status_t decodeDouble(const MetadataType& metadataType, const hidl_vec<uint8_t>& input,
double* output) {
- return decode(metadataType, input, output, decodeInteger);
+ return decodeMetadata(metadataType, input, output, decodeInteger);
}
status_t encodeString(const MetadataType& metadataType, const std::string& input,
hidl_vec<uint8_t>* output) {
- return encode(metadataType, input, output, encodeString);
+ return encodeMetadata(metadataType, input, output, encodeString);
}
status_t decodeString(const MetadataType& metadataType, const hidl_vec<uint8_t>& input,
std::string* output) {
- return decode(metadataType, input, output, decodeString);
+ return decodeMetadata(metadataType, input, output, decodeString);
}
bool isStandardMetadataType(const MetadataType& metadataType) {
diff --git a/libs/gralloc/types/fuzzer/gralloctypes.cpp b/libs/gralloc/types/fuzzer/gralloctypes.cpp
index b18f407..b5644be 100644
--- a/libs/gralloc/types/fuzzer/gralloctypes.cpp
+++ b/libs/gralloc/types/fuzzer/gralloctypes.cpp
@@ -31,6 +31,7 @@
using ::android::status_t;
using MetadataType = android::hardware::graphics::mapper::V4_0::IMapper::MetadataType;
+using BufferDescriptorInfo = android::hardware::graphics::mapper::V4_0::IMapper::BufferDescriptorInfo;
#define GRALLOCTYPES_DECODE(T, FUNC) \
[] (const ::android::hardware::hidl_vec<uint8_t>& vec) {\
@@ -53,6 +54,7 @@
// clang-format off
std::vector<GrallocTypesDecode> GRALLOCTYPES_DECODE_FUNCTIONS {
+ GRALLOCTYPES_DECODE(BufferDescriptorInfo, ::android::gralloc4::decodeBufferDescriptorInfo),
GRALLOCTYPES_DECODE(uint64_t, ::android::gralloc4::decodeBufferId),
GRALLOCTYPES_DECODE(std::string, ::android::gralloc4::decodeName),
GRALLOCTYPES_DECODE(uint64_t, ::android::gralloc4::decodeWidth),
diff --git a/libs/gralloc/types/include/gralloctypes/Gralloc4.h b/libs/gralloc/types/include/gralloctypes/Gralloc4.h
index 42c6e15..d855439 100644
--- a/libs/gralloc/types/include/gralloctypes/Gralloc4.h
+++ b/libs/gralloc/types/include/gralloctypes/Gralloc4.h
@@ -414,6 +414,12 @@
/*---------------------------------------------------------------------------------------------*/
/**
+ * The functions below encode and decode BufferDescriptorInfo into a byte stream.
+ */
+status_t encodeBufferDescriptorInfo(const android::hardware::graphics::mapper::V4_0::IMapper::BufferDescriptorInfo& bufferDescriptorInfo, android::hardware::hidl_vec<uint8_t>* outBufferDescriptorInfo);
+status_t decodeBufferDescriptorInfo(const android::hardware::hidl_vec<uint8_t>& bufferDescriptorInfo, android::hardware::graphics::mapper::V4_0::IMapper::BufferDescriptorInfo* outBufferDescriptorInfo);
+
+/**
* The functions below encode and decode standard metadata into a byte stream. It is STRONGLY
* recommended that both the vendor and system partitions use these functions when getting
* and setting metadata through gralloc 4 (IMapper 4.0).
diff --git a/libs/gralloc/types/tests/Gralloc4_test.cpp b/libs/gralloc/types/tests/Gralloc4_test.cpp
index 3542ed9..dbe41f1 100644
--- a/libs/gralloc/types/tests/Gralloc4_test.cpp
+++ b/libs/gralloc/types/tests/Gralloc4_test.cpp
@@ -25,6 +25,7 @@
using android::hardware::hidl_vec;
using android::hardware::graphics::common::V1_2::PixelFormat;
+using android::hardware::graphics::common::V1_2::BufferUsage;
using aidl::android::hardware::graphics::common::BlendMode;
using aidl::android::hardware::graphics::common::ChromaSiting;
@@ -41,6 +42,7 @@
using aidl::android::hardware::graphics::common::StandardMetadataType;
using aidl::android::hardware::graphics::common::XyColor;
+using BufferDescriptorInfo = android::hardware::graphics::mapper::V4_0::IMapper::BufferDescriptorInfo;
using MetadataType = android::hardware::graphics::mapper::V4_0::IMapper::MetadataType;
namespace android {
@@ -435,6 +437,19 @@
ASSERT_NO_FATAL_FAILURE(testHelperStableAidlTypeOptional(GetParam(), gralloc4::encodeSmpte2094_40, gralloc4::decodeSmpte2094_40));
}
+class Gralloc4TestBufferDescriptorInfo : public testing::TestWithParam<BufferDescriptorInfo> { };
+
+INSTANTIATE_TEST_CASE_P(
+ Gralloc4TestBufferDescriptorInfoParams, Gralloc4TestBufferDescriptorInfo,
+ ::testing::Values(BufferDescriptorInfo{"BufferName", 64, 64, 1,
+ PixelFormat::RGBA_8888,
+ static_cast<uint64_t>(BufferUsage::CPU_READ_OFTEN | BufferUsage::CPU_WRITE_OFTEN),
+ 1024}));
+
+TEST_P(Gralloc4TestBufferDescriptorInfo, BufferDescriptorInfo) {
+ ASSERT_NO_FATAL_FAILURE(testHelperConst(GetParam(), gralloc4::encodeBufferDescriptorInfo, gralloc4::decodeBufferDescriptorInfo));
+}
+
class Gralloc4TestErrors : public testing::Test { };
TEST_F(Gralloc4TestErrors, Gralloc4TestEncodeNull) {
diff --git a/libs/gui/BLASTBufferQueue.cpp b/libs/gui/BLASTBufferQueue.cpp
index 9674e54..4e62da7 100644
--- a/libs/gui/BLASTBufferQueue.cpp
+++ b/libs/gui/BLASTBufferQueue.cpp
@@ -48,7 +48,7 @@
mBufferItemConsumer->setBufferFreedListener(this);
mBufferItemConsumer->setDefaultBufferSize(mWidth, mHeight);
mBufferItemConsumer->setDefaultBufferFormat(PIXEL_FORMAT_RGBA_8888);
- mBufferItemConsumer->setTransformHint(mSurfaceControl->getTransformHint());
+ mTransformHint = mSurfaceControl->getTransformHint();
mNumAcquired = 0;
mNumFrameAvailable = 0;
@@ -62,7 +62,6 @@
mWidth = width;
mHeight = height;
mBufferItemConsumer->setDefaultBufferSize(mWidth, mHeight);
- mBufferItemConsumer->setTransformHint(mSurfaceControl->getTransformHint());
}
static void transactionCallbackThunk(void* context, nsecs_t latchTime,
@@ -84,6 +83,7 @@
if (stats.size() > 0) {
mPendingReleaseItem.releaseFence = stats[0].previousReleaseFence;
mTransformHint = stats[0].transformHint;
+ mBufferItemConsumer->setTransformHint(mTransformHint);
} else {
ALOGE("Warning: no SurfaceControlStats returned in BLASTBufferQueue callback");
mPendingReleaseItem.releaseFence = nullptr;
@@ -152,9 +152,11 @@
bufferItem.mFence ? new Fence(bufferItem.mFence->dup()) : Fence::NO_FENCE);
t->addTransactionCompletedCallback(transactionCallbackThunk, static_cast<void*>(this));
- t->setFrame(mSurfaceControl, {0, 0, (int32_t)buffer->getWidth(), (int32_t)buffer->getHeight()});
+ t->setFrame(mSurfaceControl, {0, 0, mWidth, mHeight});
t->setCrop(mSurfaceControl, computeCrop(bufferItem));
t->setTransform(mSurfaceControl, bufferItem.mTransform);
+ t->setTransformToDisplayInverse(mSurfaceControl, bufferItem.mTransformToDisplayInverse);
+ t->setDesiredPresentTime(bufferItem.mTimestamp);
if (applyTransaction) {
t->apply();
diff --git a/libs/gui/BufferQueueCore.cpp b/libs/gui/BufferQueueCore.cpp
index 3b0120b..9e5d681 100644
--- a/libs/gui/BufferQueueCore.cpp
+++ b/libs/gui/BufferQueueCore.cpp
@@ -84,54 +84,54 @@
return INVALID_OPERATION;
}
-BufferQueueCore::BufferQueueCore() :
- mMutex(),
- mIsAbandoned(false),
- mConsumerControlledByApp(false),
- mConsumerName(getUniqueName()),
- mConsumerListener(),
- mConsumerUsageBits(0),
- mConsumerIsProtected(false),
- mConnectedApi(NO_CONNECTED_API),
- mLinkedToDeath(),
- mConnectedProducerListener(),
- mBufferReleasedCbEnabled(false),
- mSlots(),
- mQueue(),
- mFreeSlots(),
- mFreeBuffers(),
- mUnusedSlots(),
- mActiveBuffers(),
- mDequeueCondition(),
- mDequeueBufferCannotBlock(false),
- mQueueBufferCanDrop(false),
- mLegacyBufferDrop(true),
- mDefaultBufferFormat(PIXEL_FORMAT_RGBA_8888),
- mDefaultWidth(1),
- mDefaultHeight(1),
- mDefaultBufferDataSpace(HAL_DATASPACE_UNKNOWN),
- mMaxBufferCount(BufferQueueDefs::NUM_BUFFER_SLOTS),
- mMaxAcquiredBufferCount(1),
- mMaxDequeuedBufferCount(1),
- mBufferHasBeenQueued(false),
- mFrameCounter(0),
- mTransformHint(0),
- mIsAllocating(false),
- mIsAllocatingCondition(),
- mAllowAllocation(true),
- mBufferAge(0),
- mGenerationNumber(0),
- mAsyncMode(false),
- mSharedBufferMode(false),
- mAutoRefresh(false),
- mSharedBufferSlot(INVALID_BUFFER_SLOT),
- mSharedBufferCache(Rect::INVALID_RECT, 0, NATIVE_WINDOW_SCALING_MODE_FREEZE,
- HAL_DATASPACE_UNKNOWN),
- mLastQueuedSlot(INVALID_BUFFER_SLOT),
- mUniqueId(getUniqueId()),
- mAutoPrerotation(false),
- mTransformHintInUse(0)
-{
+BufferQueueCore::BufferQueueCore()
+ : mMutex(),
+ mIsAbandoned(false),
+ mConsumerControlledByApp(false),
+ mConsumerName(getUniqueName()),
+ mConsumerListener(),
+ mConsumerUsageBits(0),
+ mConsumerIsProtected(false),
+ mConnectedApi(NO_CONNECTED_API),
+ mLinkedToDeath(),
+ mConnectedProducerListener(),
+ mBufferReleasedCbEnabled(false),
+ mSlots(),
+ mQueue(),
+ mFreeSlots(),
+ mFreeBuffers(),
+ mUnusedSlots(),
+ mActiveBuffers(),
+ mDequeueCondition(),
+ mDequeueBufferCannotBlock(false),
+ mQueueBufferCanDrop(false),
+ mLegacyBufferDrop(true),
+ mDefaultBufferFormat(PIXEL_FORMAT_RGBA_8888),
+ mDefaultWidth(1),
+ mDefaultHeight(1),
+ mDefaultBufferDataSpace(HAL_DATASPACE_UNKNOWN),
+ mMaxBufferCount(BufferQueueDefs::NUM_BUFFER_SLOTS),
+ mMaxAcquiredBufferCount(1),
+ mMaxDequeuedBufferCount(1),
+ mBufferHasBeenQueued(false),
+ mFrameCounter(0),
+ mTransformHint(0),
+ mIsAllocating(false),
+ mIsAllocatingCondition(),
+ mAllowAllocation(true),
+ mBufferAge(0),
+ mGenerationNumber(0),
+ mAsyncMode(false),
+ mSharedBufferMode(false),
+ mAutoRefresh(false),
+ mSharedBufferSlot(INVALID_BUFFER_SLOT),
+ mSharedBufferCache(Rect::INVALID_RECT, 0, NATIVE_WINDOW_SCALING_MODE_FREEZE,
+ HAL_DATASPACE_UNKNOWN),
+ mLastQueuedSlot(INVALID_BUFFER_SLOT),
+ mUniqueId(getUniqueId()),
+ mAutoPrerotation(false),
+ mTransformHintInUse(0),
+ mFrameRate(0) {
int numStartingBuffers = getMaxBufferCountLocked();
for (int s = 0; s < numStartingBuffers; s++) {
mFreeSlots.insert(s);
diff --git a/libs/gui/tests/BLASTBufferQueue_test.cpp b/libs/gui/tests/BLASTBufferQueue_test.cpp
index 8fca883..41f0d40 100644
--- a/libs/gui/tests/BLASTBufferQueue_test.cpp
+++ b/libs/gui/tests/BLASTBufferQueue_test.cpp
@@ -237,6 +237,33 @@
ASSERT_EQ(&next, adapter.getNextTransaction());
}
+TEST_F(BLASTBufferQueueTest, onFrameAvailable_ApplyDesiredPresentTime) {
+ BLASTBufferQueueHelper adapter(mSurfaceControl, mDisplayWidth, mDisplayHeight);
+ sp<IGraphicBufferProducer> igbProducer;
+ setUpProducer(adapter, igbProducer);
+
+ int slot;
+ sp<Fence> fence;
+ sp<GraphicBuffer> buf;
+ auto ret = igbProducer->dequeueBuffer(&slot, &fence, mDisplayWidth, mDisplayHeight,
+ PIXEL_FORMAT_RGBA_8888, GRALLOC_USAGE_SW_WRITE_OFTEN,
+ nullptr, nullptr);
+ ASSERT_EQ(IGraphicBufferProducer::BUFFER_NEEDS_REALLOCATION, ret);
+ ASSERT_EQ(OK, igbProducer->requestBuffer(slot, &buf));
+
+ nsecs_t desiredPresentTime = systemTime() + nsecs_t(5 * 1e8);
+ IGraphicBufferProducer::QueueBufferOutput qbOutput;
+ IGraphicBufferProducer::QueueBufferInput input(desiredPresentTime, false, HAL_DATASPACE_UNKNOWN,
+ Rect(mDisplayWidth, mDisplayHeight),
+ NATIVE_WINDOW_SCALING_MODE_FREEZE, 0,
+ Fence::NO_FENCE);
+ igbProducer->queueBuffer(slot, input, &qbOutput);
+ ASSERT_NE(ui::Transform::ROT_INVALID, qbOutput.transformHint);
+
+ adapter.waitForCallbacks();
+ ASSERT_GE(systemTime(), desiredPresentTime);
+}
+
TEST_F(BLASTBufferQueueTest, onFrameAvailable_Apply) {
uint8_t r = 255;
uint8_t g = 0;
diff --git a/libs/input/Input.cpp b/libs/input/Input.cpp
index 8ccbc7f..85b0fd0 100644
--- a/libs/input/Input.cpp
+++ b/libs/input/Input.cpp
@@ -57,16 +57,19 @@
return "UNKNOWN";
}
-void InputEvent::initialize(int32_t deviceId, int32_t source, int32_t displayId) {
+void InputEvent::initialize(int32_t deviceId, uint32_t source, int32_t displayId,
+ std::array<uint8_t, 32> hmac) {
mDeviceId = deviceId;
mSource = source;
mDisplayId = displayId;
+ mHmac = hmac;
}
void InputEvent::initialize(const InputEvent& from) {
mDeviceId = from.mDeviceId;
mSource = from.mSource;
mDisplayId = from.mDisplayId;
+ mHmac = from.mHmac;
}
// --- KeyEvent ---
@@ -79,19 +82,11 @@
return getKeyCodeByLabel(label);
}
-void KeyEvent::initialize(
- int32_t deviceId,
- int32_t source,
- int32_t displayId,
- int32_t action,
- int32_t flags,
- int32_t keyCode,
- int32_t scanCode,
- int32_t metaState,
- int32_t repeatCount,
- nsecs_t downTime,
- nsecs_t eventTime) {
- InputEvent::initialize(deviceId, source, displayId);
+void KeyEvent::initialize(int32_t deviceId, uint32_t source, int32_t displayId,
+ std::array<uint8_t, 32> hmac, int32_t action, int32_t flags,
+ int32_t keyCode, int32_t scanCode, int32_t metaState, int32_t repeatCount,
+ nsecs_t downTime, nsecs_t eventTime) {
+ InputEvent::initialize(deviceId, source, displayId, hmac);
mAction = action;
mFlags = flags;
mKeyCode = keyCode;
@@ -250,15 +245,16 @@
// --- MotionEvent ---
-void MotionEvent::initialize(int32_t deviceId, int32_t source, int32_t displayId, int32_t action,
- int32_t actionButton, int32_t flags, int32_t edgeFlags,
- int32_t metaState, int32_t buttonState,
- MotionClassification classification, float xOffset, float yOffset,
- float xPrecision, float yPrecision, float rawXCursorPosition,
- float rawYCursorPosition, nsecs_t downTime, nsecs_t eventTime,
- size_t pointerCount, const PointerProperties* pointerProperties,
+void MotionEvent::initialize(int32_t deviceId, uint32_t source, int32_t displayId,
+ std::array<uint8_t, 32> hmac, int32_t action, int32_t actionButton,
+ int32_t flags, int32_t edgeFlags, int32_t metaState,
+ int32_t buttonState, MotionClassification classification, float xScale,
+ float yScale, float xOffset, float yOffset, float xPrecision,
+ float yPrecision, float rawXCursorPosition, float rawYCursorPosition,
+ nsecs_t downTime, nsecs_t eventTime, size_t pointerCount,
+ const PointerProperties* pointerProperties,
const PointerCoords* pointerCoords) {
- InputEvent::initialize(deviceId, source, displayId);
+ InputEvent::initialize(deviceId, source, displayId, hmac);
mAction = action;
mActionButton = actionButton;
mFlags = flags;
@@ -266,6 +262,8 @@
mMetaState = metaState;
mButtonState = buttonState;
mClassification = classification;
+ mXScale = xScale;
+ mYScale = yScale;
mXOffset = xOffset;
mYOffset = yOffset;
mXPrecision = xPrecision;
@@ -281,7 +279,7 @@
}
void MotionEvent::copyFrom(const MotionEvent* other, bool keepHistory) {
- InputEvent::initialize(other->mDeviceId, other->mSource, other->mDisplayId);
+ InputEvent::initialize(other->mDeviceId, other->mSource, other->mDisplayId, other->mHmac);
mAction = other->mAction;
mActionButton = other->mActionButton;
mFlags = other->mFlags;
@@ -289,6 +287,8 @@
mMetaState = other->mMetaState;
mButtonState = other->mButtonState;
mClassification = other->mClassification;
+ mXScale = other->mXScale;
+ mYScale = other->mYScale;
mXOffset = other->mXOffset;
mYOffset = other->mYOffset;
mXPrecision = other->mXPrecision;
@@ -321,17 +321,17 @@
float MotionEvent::getXCursorPosition() const {
const float rawX = getRawXCursorPosition();
- return rawX + mXOffset;
+ return rawX * mXScale + mXOffset;
}
float MotionEvent::getYCursorPosition() const {
const float rawY = getRawYCursorPosition();
- return rawY + mYOffset;
+ return rawY * mYScale + mYOffset;
}
void MotionEvent::setCursorPosition(float x, float y) {
- mRawXCursorPosition = x - mXOffset;
- mRawYCursorPosition = y - mYOffset;
+ mRawXCursorPosition = (x - mXOffset) / mXScale;
+ mRawYCursorPosition = (y - mYOffset) / mYScale;
}
const PointerCoords* MotionEvent::getRawPointerCoords(size_t pointerIndex) const {
@@ -346,9 +346,9 @@
float value = getRawPointerCoords(pointerIndex)->getAxisValue(axis);
switch (axis) {
case AMOTION_EVENT_AXIS_X:
- return value + mXOffset;
+ return value * mXScale + mXOffset;
case AMOTION_EVENT_AXIS_Y:
- return value + mYOffset;
+ return value * mYScale + mYOffset;
}
return value;
}
@@ -368,9 +368,9 @@
float value = getHistoricalRawPointerCoords(pointerIndex, historicalIndex)->getAxisValue(axis);
switch (axis) {
case AMOTION_EVENT_AXIS_X:
- return value + mXOffset;
+ return value * mXScale + mXOffset;
case AMOTION_EVENT_AXIS_Y:
- return value + mYOffset;
+ return value * mYScale + mYOffset;
}
return value;
}
@@ -442,11 +442,11 @@
float oldXOffset = mXOffset;
float oldYOffset = mYOffset;
float newX, newY;
- float rawX = getRawX(0);
- float rawY = getRawY(0);
- transformPoint(matrix, rawX + oldXOffset, rawY + oldYOffset, &newX, &newY);
- mXOffset = newX - rawX;
- mYOffset = newY - rawY;
+ float scaledRawX = getRawX(0) * mXScale;
+ float scaledRawY = getRawY(0) * mYScale;
+ transformPoint(matrix, scaledRawX + oldXOffset, scaledRawY + oldYOffset, &newX, &newY);
+ mXOffset = newX - scaledRawX;
+ mYOffset = newY - scaledRawY;
// Determine how the origin is transformed by the matrix so that we
// can transform orientation vectors.
@@ -455,22 +455,22 @@
// Apply the transformation to cursor position.
if (isValidCursorPosition(mRawXCursorPosition, mRawYCursorPosition)) {
- float x = mRawXCursorPosition + oldXOffset;
- float y = mRawYCursorPosition + oldYOffset;
+ float x = mRawXCursorPosition * mXScale + oldXOffset;
+ float y = mRawYCursorPosition * mYScale + oldYOffset;
transformPoint(matrix, x, y, &x, &y);
- mRawXCursorPosition = x - mXOffset;
- mRawYCursorPosition = y - mYOffset;
+ mRawXCursorPosition = (x - mXOffset) / mXScale;
+ mRawYCursorPosition = (y - mYOffset) / mYScale;
}
// Apply the transformation to all samples.
size_t numSamples = mSamplePointerCoords.size();
for (size_t i = 0; i < numSamples; i++) {
PointerCoords& c = mSamplePointerCoords.editItemAt(i);
- float x = c.getAxisValue(AMOTION_EVENT_AXIS_X) + oldXOffset;
- float y = c.getAxisValue(AMOTION_EVENT_AXIS_Y) + oldYOffset;
+ float x = c.getAxisValue(AMOTION_EVENT_AXIS_X) * mXScale + oldXOffset;
+ float y = c.getAxisValue(AMOTION_EVENT_AXIS_Y) * mYScale + oldYOffset;
transformPoint(matrix, x, y, &x, &y);
- c.setAxisValue(AMOTION_EVENT_AXIS_X, x - mXOffset);
- c.setAxisValue(AMOTION_EVENT_AXIS_Y, y - mYOffset);
+ c.setAxisValue(AMOTION_EVENT_AXIS_X, (x - mXOffset) / mXScale);
+ c.setAxisValue(AMOTION_EVENT_AXIS_Y, (y - mYOffset) / mYScale);
float orientation = c.getAxisValue(AMOTION_EVENT_AXIS_ORIENTATION);
c.setAxisValue(AMOTION_EVENT_AXIS_ORIENTATION,
@@ -488,8 +488,14 @@
}
mDeviceId = parcel->readInt32();
- mSource = parcel->readInt32();
+ mSource = parcel->readUint32();
mDisplayId = parcel->readInt32();
+ std::vector<uint8_t> hmac;
+ status_t result = parcel->readByteVector(&hmac);
+ if (result != OK || hmac.size() != 32) {
+ return BAD_VALUE;
+ }
+ std::move(hmac.begin(), hmac.begin() + hmac.size(), mHmac.begin());
mAction = parcel->readInt32();
mActionButton = parcel->readInt32();
mFlags = parcel->readInt32();
@@ -497,6 +503,8 @@
mMetaState = parcel->readInt32();
mButtonState = parcel->readInt32();
mClassification = static_cast<MotionClassification>(parcel->readByte());
+ mXScale = parcel->readFloat();
+ mYScale = parcel->readFloat();
mXOffset = parcel->readFloat();
mYOffset = parcel->readFloat();
mXPrecision = parcel->readFloat();
@@ -541,8 +549,10 @@
parcel->writeInt32(sampleCount);
parcel->writeInt32(mDeviceId);
- parcel->writeInt32(mSource);
+ parcel->writeUint32(mSource);
parcel->writeInt32(mDisplayId);
+ std::vector<uint8_t> hmac(mHmac.begin(), mHmac.end());
+ parcel->writeByteVector(hmac);
parcel->writeInt32(mAction);
parcel->writeInt32(mActionButton);
parcel->writeInt32(mFlags);
@@ -550,6 +560,8 @@
parcel->writeInt32(mMetaState);
parcel->writeInt32(mButtonState);
parcel->writeByte(static_cast<int8_t>(mClassification));
+ parcel->writeFloat(mXScale);
+ parcel->writeFloat(mYScale);
parcel->writeFloat(mXOffset);
parcel->writeFloat(mYOffset);
parcel->writeFloat(mXPrecision);
@@ -578,7 +590,7 @@
}
#endif
-bool MotionEvent::isTouchEvent(int32_t source, int32_t action) {
+bool MotionEvent::isTouchEvent(uint32_t source, int32_t action) {
if (source & AINPUT_SOURCE_CLASS_POINTER) {
// Specifically excludes HOVER_MOVE and SCROLL.
switch (action & AMOTION_EVENT_ACTION_MASK) {
@@ -607,7 +619,7 @@
void FocusEvent::initialize(bool hasFocus, bool inTouchMode) {
InputEvent::initialize(ReservedInputDeviceId::VIRTUAL_KEYBOARD_ID, AINPUT_SOURCE_UNKNOWN,
- ADISPLAY_ID_NONE);
+ ADISPLAY_ID_NONE, INVALID_HMAC);
mHasFocus = hasFocus;
mInTouchMode = inTouchMode;
}
diff --git a/libs/input/InputTransport.cpp b/libs/input/InputTransport.cpp
index d53a557..d25a5cc 100644
--- a/libs/input/InputTransport.cpp
+++ b/libs/input/InputTransport.cpp
@@ -147,6 +147,8 @@
msg->body.key.source = body.key.source;
// int32_t displayId
msg->body.key.displayId = body.key.displayId;
+ // std::array<uint8_t, 32> hmac
+ msg->body.key.hmac = body.key.hmac;
// int32_t action
msg->body.key.action = body.key.action;
// int32_t flags
@@ -174,6 +176,8 @@
msg->body.motion.source = body.motion.source;
// int32_t displayId
msg->body.motion.displayId = body.motion.displayId;
+ // std::array<uint8_t, 32> hmac
+ msg->body.motion.hmac = body.motion.hmac;
// int32_t action
msg->body.motion.action = body.motion.action;
// int32_t actionButton
@@ -190,6 +194,10 @@
msg->body.motion.edgeFlags = body.motion.edgeFlags;
// nsecs_t downTime
msg->body.motion.downTime = body.motion.downTime;
+ // float xScale
+ msg->body.motion.xScale = body.motion.xScale;
+ // float yScale
+ msg->body.motion.yScale = body.motion.yScale;
// float xOffset
msg->body.motion.xOffset = body.motion.xOffset;
// float yOffset
@@ -424,19 +432,11 @@
InputPublisher::~InputPublisher() {
}
-status_t InputPublisher::publishKeyEvent(
- uint32_t seq,
- int32_t deviceId,
- int32_t source,
- int32_t displayId,
- int32_t action,
- int32_t flags,
- int32_t keyCode,
- int32_t scanCode,
- int32_t metaState,
- int32_t repeatCount,
- nsecs_t downTime,
- nsecs_t eventTime) {
+status_t InputPublisher::publishKeyEvent(uint32_t seq, int32_t deviceId, int32_t source,
+ int32_t displayId, std::array<uint8_t, 32> hmac,
+ int32_t action, int32_t flags, int32_t keyCode,
+ int32_t scanCode, int32_t metaState, int32_t repeatCount,
+ nsecs_t downTime, nsecs_t eventTime) {
if (ATRACE_ENABLED()) {
std::string message = StringPrintf("publishKeyEvent(inputChannel=%s, keyCode=%" PRId32 ")",
mChannel->getName().c_str(), keyCode);
@@ -461,6 +461,7 @@
msg.body.key.deviceId = deviceId;
msg.body.key.source = source;
msg.body.key.displayId = displayId;
+ msg.body.key.hmac = hmac;
msg.body.key.action = action;
msg.body.key.flags = flags;
msg.body.key.keyCode = keyCode;
@@ -473,11 +474,12 @@
}
status_t InputPublisher::publishMotionEvent(
- uint32_t seq, int32_t deviceId, int32_t source, int32_t displayId, int32_t action,
- int32_t actionButton, int32_t flags, int32_t edgeFlags, int32_t metaState,
- int32_t buttonState, MotionClassification classification, float xOffset, float yOffset,
- float xPrecision, float yPrecision, float xCursorPosition, float yCursorPosition,
- nsecs_t downTime, nsecs_t eventTime, uint32_t pointerCount,
+ uint32_t seq, int32_t deviceId, int32_t source, int32_t displayId,
+ std::array<uint8_t, 32> hmac, int32_t action, int32_t actionButton, int32_t flags,
+ int32_t edgeFlags, int32_t metaState, int32_t buttonState,
+ MotionClassification classification, float xScale, float yScale, float xOffset,
+ float yOffset, float xPrecision, float yPrecision, float xCursorPosition,
+ float yCursorPosition, nsecs_t downTime, nsecs_t eventTime, uint32_t pointerCount,
const PointerProperties* pointerProperties, const PointerCoords* pointerCoords) {
if (ATRACE_ENABLED()) {
std::string message = StringPrintf(
@@ -489,13 +491,14 @@
ALOGD("channel '%s' publisher ~ publishMotionEvent: seq=%u, deviceId=%d, source=0x%x, "
"displayId=%" PRId32 ", "
"action=0x%x, actionButton=0x%08x, flags=0x%x, edgeFlags=0x%x, "
- "metaState=0x%x, buttonState=0x%x, classification=%s, xOffset=%f, yOffset=%f, "
+ "metaState=0x%x, buttonState=0x%x, classification=%s, xScale=%.1f, yScale=%.1f, "
+ "xOffset=%.1f, yOffset=%.1f, "
"xPrecision=%f, yPrecision=%f, downTime=%" PRId64 ", eventTime=%" PRId64 ", "
"pointerCount=%" PRIu32,
mChannel->getName().c_str(), seq, deviceId, source, displayId, action, actionButton,
flags, edgeFlags, metaState, buttonState,
- motionClassificationToString(classification), xOffset, yOffset, xPrecision,
- yPrecision, downTime, eventTime, pointerCount);
+ motionClassificationToString(classification), xScale, yScale, xOffset, yOffset,
+ xPrecision, yPrecision, downTime, eventTime, pointerCount);
}
if (!seq) {
@@ -515,6 +518,7 @@
msg.body.motion.deviceId = deviceId;
msg.body.motion.source = source;
msg.body.motion.displayId = displayId;
+ msg.body.motion.hmac = hmac;
msg.body.motion.action = action;
msg.body.motion.actionButton = actionButton;
msg.body.motion.flags = flags;
@@ -522,6 +526,8 @@
msg.body.motion.metaState = metaState;
msg.body.motion.buttonState = buttonState;
msg.body.motion.classification = classification;
+ msg.body.motion.xScale = xScale;
+ msg.body.motion.yScale = yScale;
msg.body.motion.xOffset = xOffset;
msg.body.motion.yOffset = yOffset;
msg.body.motion.xPrecision = xPrecision;
@@ -1136,18 +1142,10 @@
}
void InputConsumer::initializeKeyEvent(KeyEvent* event, const InputMessage* msg) {
- event->initialize(
- msg->body.key.deviceId,
- msg->body.key.source,
- msg->body.key.displayId,
- msg->body.key.action,
- msg->body.key.flags,
- msg->body.key.keyCode,
- msg->body.key.scanCode,
- msg->body.key.metaState,
- msg->body.key.repeatCount,
- msg->body.key.downTime,
- msg->body.key.eventTime);
+ event->initialize(msg->body.key.deviceId, msg->body.key.source, msg->body.key.displayId,
+ msg->body.key.hmac, msg->body.key.action, msg->body.key.flags,
+ msg->body.key.keyCode, msg->body.key.scanCode, msg->body.key.metaState,
+ msg->body.key.repeatCount, msg->body.key.downTime, msg->body.key.eventTime);
}
void InputConsumer::initializeFocusEvent(FocusEvent* event, const InputMessage* msg) {
@@ -1164,15 +1162,15 @@
}
event->initialize(msg->body.motion.deviceId, msg->body.motion.source,
- msg->body.motion.displayId, msg->body.motion.action,
+ msg->body.motion.displayId, msg->body.motion.hmac, msg->body.motion.action,
msg->body.motion.actionButton, msg->body.motion.flags,
msg->body.motion.edgeFlags, msg->body.motion.metaState,
msg->body.motion.buttonState, msg->body.motion.classification,
- msg->body.motion.xOffset, msg->body.motion.yOffset,
- msg->body.motion.xPrecision, msg->body.motion.yPrecision,
- msg->body.motion.xCursorPosition, msg->body.motion.yCursorPosition,
- msg->body.motion.downTime, msg->body.motion.eventTime, pointerCount,
- pointerProperties, pointerCoords);
+ msg->body.motion.xScale, msg->body.motion.yScale, msg->body.motion.xOffset,
+ msg->body.motion.yOffset, msg->body.motion.xPrecision,
+ msg->body.motion.yPrecision, msg->body.motion.xCursorPosition,
+ msg->body.motion.yCursorPosition, msg->body.motion.downTime,
+ msg->body.motion.eventTime, pointerCount, pointerProperties, pointerCoords);
}
void InputConsumer::addSample(MotionEvent* event, const InputMessage* msg) {
diff --git a/libs/input/KeyCharacterMap.cpp b/libs/input/KeyCharacterMap.cpp
index e189d20..6f9b162 100644
--- a/libs/input/KeyCharacterMap.cpp
+++ b/libs/input/KeyCharacterMap.cpp
@@ -487,9 +487,9 @@
int32_t deviceId, int32_t keyCode, int32_t metaState, bool down, nsecs_t time) {
outEvents.push();
KeyEvent& event = outEvents.editTop();
- event.initialize(deviceId, AINPUT_SOURCE_KEYBOARD, ADISPLAY_ID_NONE,
- down ? AKEY_EVENT_ACTION_DOWN : AKEY_EVENT_ACTION_UP,
- 0, keyCode, 0, metaState, 0, time, time);
+ event.initialize(deviceId, AINPUT_SOURCE_KEYBOARD, ADISPLAY_ID_NONE, INVALID_HMAC,
+ down ? AKEY_EVENT_ACTION_DOWN : AKEY_EVENT_ACTION_UP, 0, keyCode, 0, metaState,
+ 0, time, time);
}
void KeyCharacterMap::addMetaKeys(Vector<KeyEvent>& outEvents,
diff --git a/libs/input/tests/InputEvent_test.cpp b/libs/input/tests/InputEvent_test.cpp
index b90857c..dce1f29 100644
--- a/libs/input/tests/InputEvent_test.cpp
+++ b/libs/input/tests/InputEvent_test.cpp
@@ -26,7 +26,12 @@
// Default display id.
static constexpr int32_t DISPLAY_ID = ADISPLAY_ID_DEFAULT;
-class BaseTest : public testing::Test { };
+class BaseTest : public testing::Test {
+protected:
+ static constexpr std::array<uint8_t, 32> HMAC = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10,
+ 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21,
+ 22, 23, 24, 25, 26, 27, 28, 29, 30, 31};
+};
// --- PointerCoordsTest ---
@@ -176,16 +181,17 @@
KeyEvent event;
// Initialize and get properties.
- const nsecs_t ARBITRARY_DOWN_TIME = 1;
- const nsecs_t ARBITRARY_EVENT_TIME = 2;
- event.initialize(2, AINPUT_SOURCE_GAMEPAD, DISPLAY_ID, AKEY_EVENT_ACTION_DOWN,
- AKEY_EVENT_FLAG_FROM_SYSTEM, AKEYCODE_BUTTON_X, 121,
- AMETA_ALT_ON, 1, ARBITRARY_DOWN_TIME, ARBITRARY_EVENT_TIME);
+ constexpr nsecs_t ARBITRARY_DOWN_TIME = 1;
+ constexpr nsecs_t ARBITRARY_EVENT_TIME = 2;
+ event.initialize(2, AINPUT_SOURCE_GAMEPAD, DISPLAY_ID, HMAC, AKEY_EVENT_ACTION_DOWN,
+ AKEY_EVENT_FLAG_FROM_SYSTEM, AKEYCODE_BUTTON_X, 121, AMETA_ALT_ON, 1,
+ ARBITRARY_DOWN_TIME, ARBITRARY_EVENT_TIME);
ASSERT_EQ(AINPUT_EVENT_TYPE_KEY, event.getType());
ASSERT_EQ(2, event.getDeviceId());
- ASSERT_EQ(static_cast<int>(AINPUT_SOURCE_GAMEPAD), event.getSource());
+ ASSERT_EQ(AINPUT_SOURCE_GAMEPAD, event.getSource());
ASSERT_EQ(DISPLAY_ID, event.getDisplayId());
+ EXPECT_EQ(HMAC, event.getHmac());
ASSERT_EQ(AKEY_EVENT_ACTION_DOWN, event.getAction());
ASSERT_EQ(AKEY_EVENT_FLAG_FROM_SYSTEM, event.getFlags());
ASSERT_EQ(AKEYCODE_BUTTON_X, event.getKeyCode());
@@ -197,7 +203,7 @@
// Set source.
event.setSource(AINPUT_SOURCE_JOYSTICK);
- ASSERT_EQ(static_cast<int>(AINPUT_SOURCE_JOYSTICK), event.getSource());
+ ASSERT_EQ(AINPUT_SOURCE_JOYSTICK, event.getSource());
// Set display id.
constexpr int32_t newDisplayId = 2;
@@ -210,19 +216,17 @@
class MotionEventTest : public BaseTest {
protected:
- static const nsecs_t ARBITRARY_DOWN_TIME;
- static const nsecs_t ARBITRARY_EVENT_TIME;
- static const float X_OFFSET;
- static const float Y_OFFSET;
+ static constexpr nsecs_t ARBITRARY_DOWN_TIME = 1;
+ static constexpr nsecs_t ARBITRARY_EVENT_TIME = 2;
+ static constexpr float X_SCALE = 2.0;
+ static constexpr float Y_SCALE = 3.0;
+ static constexpr float X_OFFSET = 1;
+ static constexpr float Y_OFFSET = 1.1;
void initializeEventWithHistory(MotionEvent* event);
void assertEqualsEventWithHistory(const MotionEvent* event);
};
-const nsecs_t MotionEventTest::ARBITRARY_DOWN_TIME = 1;
-const nsecs_t MotionEventTest::ARBITRARY_EVENT_TIME = 2;
-const float MotionEventTest::X_OFFSET = 1.0f;
-const float MotionEventTest::Y_OFFSET = 1.1f;
void MotionEventTest::initializeEventWithHistory(MotionEvent* event) {
PointerProperties pointerProperties[2];
@@ -254,12 +258,13 @@
pointerCoords[1].setAxisValue(AMOTION_EVENT_AXIS_TOOL_MAJOR, 26);
pointerCoords[1].setAxisValue(AMOTION_EVENT_AXIS_TOOL_MINOR, 27);
pointerCoords[1].setAxisValue(AMOTION_EVENT_AXIS_ORIENTATION, 28);
- event->initialize(2, AINPUT_SOURCE_TOUCHSCREEN, DISPLAY_ID, AMOTION_EVENT_ACTION_MOVE, 0,
+ event->initialize(2, AINPUT_SOURCE_TOUCHSCREEN, DISPLAY_ID, HMAC, AMOTION_EVENT_ACTION_MOVE, 0,
AMOTION_EVENT_FLAG_WINDOW_IS_OBSCURED, AMOTION_EVENT_EDGE_FLAG_TOP,
AMETA_ALT_ON, AMOTION_EVENT_BUTTON_PRIMARY, MotionClassification::NONE,
- X_OFFSET, Y_OFFSET, 2.0f, 2.1f, AMOTION_EVENT_INVALID_CURSOR_POSITION,
- AMOTION_EVENT_INVALID_CURSOR_POSITION, ARBITRARY_DOWN_TIME,
- ARBITRARY_EVENT_TIME, 2, pointerProperties, pointerCoords);
+ X_SCALE, Y_SCALE, X_OFFSET, Y_OFFSET, 2.0f, 2.1f,
+ AMOTION_EVENT_INVALID_CURSOR_POSITION, AMOTION_EVENT_INVALID_CURSOR_POSITION,
+ ARBITRARY_DOWN_TIME, ARBITRARY_EVENT_TIME, 2, pointerProperties,
+ pointerCoords);
pointerCoords[0].setAxisValue(AMOTION_EVENT_AXIS_X, 110);
pointerCoords[0].setAxisValue(AMOTION_EVENT_AXIS_Y, 111);
@@ -306,14 +311,17 @@
// Check properties.
ASSERT_EQ(AINPUT_EVENT_TYPE_MOTION, event->getType());
ASSERT_EQ(2, event->getDeviceId());
- ASSERT_EQ(static_cast<int>(AINPUT_SOURCE_TOUCHSCREEN), event->getSource());
+ ASSERT_EQ(AINPUT_SOURCE_TOUCHSCREEN, event->getSource());
ASSERT_EQ(DISPLAY_ID, event->getDisplayId());
+ EXPECT_EQ(HMAC, event->getHmac());
ASSERT_EQ(AMOTION_EVENT_ACTION_MOVE, event->getAction());
ASSERT_EQ(AMOTION_EVENT_FLAG_WINDOW_IS_OBSCURED, event->getFlags());
ASSERT_EQ(AMOTION_EVENT_EDGE_FLAG_TOP, event->getEdgeFlags());
ASSERT_EQ(AMETA_ALT_ON, event->getMetaState());
ASSERT_EQ(AMOTION_EVENT_BUTTON_PRIMARY, event->getButtonState());
ASSERT_EQ(MotionClassification::NONE, event->getClassification());
+ EXPECT_EQ(X_SCALE, event->getXScale());
+ EXPECT_EQ(Y_SCALE, event->getYScale());
ASSERT_EQ(X_OFFSET, event->getXOffset());
ASSERT_EQ(Y_OFFSET, event->getYOffset());
ASSERT_EQ(2.0f, event->getXPrecision());
@@ -367,19 +375,19 @@
ASSERT_EQ(211, event->getRawY(0));
ASSERT_EQ(221, event->getRawY(1));
- ASSERT_EQ(X_OFFSET + 10, event->getHistoricalX(0, 0));
- ASSERT_EQ(X_OFFSET + 20, event->getHistoricalX(1, 0));
- ASSERT_EQ(X_OFFSET + 110, event->getHistoricalX(0, 1));
- ASSERT_EQ(X_OFFSET + 120, event->getHistoricalX(1, 1));
- ASSERT_EQ(X_OFFSET + 210, event->getX(0));
- ASSERT_EQ(X_OFFSET + 220, event->getX(1));
+ ASSERT_EQ(X_OFFSET + 10 * X_SCALE, event->getHistoricalX(0, 0));
+ ASSERT_EQ(X_OFFSET + 20 * X_SCALE, event->getHistoricalX(1, 0));
+ ASSERT_EQ(X_OFFSET + 110 * X_SCALE, event->getHistoricalX(0, 1));
+ ASSERT_EQ(X_OFFSET + 120 * X_SCALE, event->getHistoricalX(1, 1));
+ ASSERT_EQ(X_OFFSET + 210 * X_SCALE, event->getX(0));
+ ASSERT_EQ(X_OFFSET + 220 * X_SCALE, event->getX(1));
- ASSERT_EQ(Y_OFFSET + 11, event->getHistoricalY(0, 0));
- ASSERT_EQ(Y_OFFSET + 21, event->getHistoricalY(1, 0));
- ASSERT_EQ(Y_OFFSET + 111, event->getHistoricalY(0, 1));
- ASSERT_EQ(Y_OFFSET + 121, event->getHistoricalY(1, 1));
- ASSERT_EQ(Y_OFFSET + 211, event->getY(0));
- ASSERT_EQ(Y_OFFSET + 221, event->getY(1));
+ ASSERT_EQ(Y_OFFSET + 11 * Y_SCALE, event->getHistoricalY(0, 0));
+ ASSERT_EQ(Y_OFFSET + 21 * Y_SCALE, event->getHistoricalY(1, 0));
+ ASSERT_EQ(Y_OFFSET + 111 * Y_SCALE, event->getHistoricalY(0, 1));
+ ASSERT_EQ(Y_OFFSET + 121 * Y_SCALE, event->getHistoricalY(1, 1));
+ ASSERT_EQ(Y_OFFSET + 211 * Y_SCALE, event->getY(0));
+ ASSERT_EQ(Y_OFFSET + 221 * Y_SCALE, event->getY(1));
ASSERT_EQ(12, event->getHistoricalPressure(0, 0));
ASSERT_EQ(22, event->getHistoricalPressure(1, 0));
@@ -440,7 +448,7 @@
// Set source.
event.setSource(AINPUT_SOURCE_JOYSTICK);
- ASSERT_EQ(static_cast<int>(AINPUT_SOURCE_JOYSTICK), event.getSource());
+ ASSERT_EQ(AINPUT_SOURCE_JOYSTICK, event.getSource());
// Set displayId.
constexpr int32_t newDisplayId = 2;
@@ -505,8 +513,8 @@
ASSERT_EQ(210 * 2, event.getRawX(0));
ASSERT_EQ(211 * 2, event.getRawY(0));
- ASSERT_EQ((X_OFFSET + 210) * 2, event.getX(0));
- ASSERT_EQ((Y_OFFSET + 211) * 2, event.getY(0));
+ ASSERT_EQ((X_OFFSET + 210 * X_SCALE) * 2, event.getX(0));
+ ASSERT_EQ((Y_OFFSET + 211 * Y_SCALE) * 2, event.getY(0));
ASSERT_EQ(212, event.getPressure(0));
ASSERT_EQ(213, event.getSize(0));
ASSERT_EQ(214 * 2, event.getTouchMajor(0));
@@ -570,12 +578,13 @@
pointerCoords[i].setAxisValue(AMOTION_EVENT_AXIS_ORIENTATION, angle);
}
MotionEvent event;
- event.initialize(0 /*deviceId*/, AINPUT_SOURCE_UNKNOWN, DISPLAY_ID, AMOTION_EVENT_ACTION_MOVE,
- 0 /*actionButton*/, 0 /*flags*/, AMOTION_EVENT_EDGE_FLAG_NONE, AMETA_NONE,
- 0 /*buttonState*/, MotionClassification::NONE, 0 /*xOffset*/, 0 /*yOffset*/,
- 0 /*xPrecision*/, 0 /*yPrecision*/, 3 + RADIUS /*xCursorPosition*/,
- 2 /*yCursorPosition*/, 0 /*downTime*/, 0 /*eventTime*/, pointerCount,
- pointerProperties, pointerCoords);
+ event.initialize(0 /*deviceId*/, AINPUT_SOURCE_UNKNOWN, DISPLAY_ID, INVALID_HMAC,
+ AMOTION_EVENT_ACTION_MOVE, 0 /*actionButton*/, 0 /*flags*/,
+ AMOTION_EVENT_EDGE_FLAG_NONE, AMETA_NONE, 0 /*buttonState*/,
+ MotionClassification::NONE, 1 /*xScale*/, 1 /*yScale*/, 0 /*xOffset*/,
+ 0 /*yOffset*/, 0 /*xPrecision*/, 0 /*yPrecision*/,
+ 3 + RADIUS /*xCursorPosition*/, 2 /*yCursorPosition*/, 0 /*downTime*/,
+ 0 /*eventTime*/, pointerCount, pointerProperties, pointerCoords);
float originalRawX = 0 + 3;
float originalRawY = -RADIUS + 2;
@@ -634,9 +643,10 @@
}
for (MotionClassification classification : classifications) {
- event.initialize(0 /*deviceId*/, AINPUT_SOURCE_TOUCHSCREEN, DISPLAY_ID,
+ event.initialize(0 /*deviceId*/, AINPUT_SOURCE_TOUCHSCREEN, DISPLAY_ID, INVALID_HMAC,
AMOTION_EVENT_ACTION_DOWN, 0, 0, AMOTION_EVENT_EDGE_FLAG_NONE, AMETA_NONE,
- 0, classification, 0, 0, 0, 0, AMOTION_EVENT_INVALID_CURSOR_POSITION,
+ 0, classification, 1 /*xScale*/, 1 /*yScale*/, 0, 0, 0, 0,
+ AMOTION_EVENT_INVALID_CURSOR_POSITION,
AMOTION_EVENT_INVALID_CURSOR_POSITION, 0 /*downTime*/, 0 /*eventTime*/,
pointerCount, pointerProperties, pointerCoords);
ASSERT_EQ(classification, event.getClassification());
@@ -654,9 +664,10 @@
pointerCoords[i].clear();
}
- event.initialize(0 /*deviceId*/, AINPUT_SOURCE_MOUSE, DISPLAY_ID, AMOTION_EVENT_ACTION_DOWN, 0,
- 0, AMOTION_EVENT_EDGE_FLAG_NONE, AMETA_NONE, 0, MotionClassification::NONE, 0,
- 0, 0, 0, 280 /*xCursorPosition*/, 540 /*yCursorPosition*/, 0 /*downTime*/,
+ event.initialize(0 /*deviceId*/, AINPUT_SOURCE_MOUSE, DISPLAY_ID, INVALID_HMAC,
+ AMOTION_EVENT_ACTION_DOWN, 0, 0, AMOTION_EVENT_EDGE_FLAG_NONE, AMETA_NONE, 0,
+ MotionClassification::NONE, 1 /*xScale*/, 1 /*yScale*/, 0, 0, 0, 0,
+ 280 /*xCursorPosition*/, 540 /*yCursorPosition*/, 0 /*downTime*/,
0 /*eventTime*/, pointerCount, pointerProperties, pointerCoords);
event.offsetLocation(20, 60);
ASSERT_EQ(280, event.getRawXCursorPosition());
diff --git a/libs/input/tests/InputPublisherAndConsumer_test.cpp b/libs/input/tests/InputPublisherAndConsumer_test.cpp
index 2fc77e9..d4bbf6c 100644
--- a/libs/input/tests/InputPublisherAndConsumer_test.cpp
+++ b/libs/input/tests/InputPublisherAndConsumer_test.cpp
@@ -73,8 +73,11 @@
constexpr uint32_t seq = 15;
constexpr int32_t deviceId = 1;
- constexpr int32_t source = AINPUT_SOURCE_KEYBOARD;
+ constexpr uint32_t source = AINPUT_SOURCE_KEYBOARD;
constexpr int32_t displayId = ADISPLAY_ID_DEFAULT;
+ constexpr std::array<uint8_t, 32> hmac = {31, 30, 29, 28, 27, 26, 25, 24, 23, 22, 21,
+ 20, 19, 18, 17, 16, 15, 14, 13, 12, 11, 10,
+ 9, 8, 7, 6, 5, 4, 3, 2, 1, 0};
constexpr int32_t action = AKEY_EVENT_ACTION_DOWN;
constexpr int32_t flags = AKEY_EVENT_FLAG_FROM_SYSTEM;
constexpr int32_t keyCode = AKEYCODE_ENTER;
@@ -84,8 +87,9 @@
constexpr nsecs_t downTime = 3;
constexpr nsecs_t eventTime = 4;
- status = mPublisher->publishKeyEvent(seq, deviceId, source, displayId, action, flags,
- keyCode, scanCode, metaState, repeatCount, downTime, eventTime);
+ status = mPublisher->publishKeyEvent(seq, deviceId, source, displayId, hmac, action, flags,
+ keyCode, scanCode, metaState, repeatCount, downTime,
+ eventTime);
ASSERT_EQ(OK, status)
<< "publisher publishKeyEvent should return OK";
@@ -105,6 +109,7 @@
EXPECT_EQ(deviceId, keyEvent->getDeviceId());
EXPECT_EQ(source, keyEvent->getSource());
EXPECT_EQ(displayId, keyEvent->getDisplayId());
+ EXPECT_EQ(hmac, keyEvent->getHmac());
EXPECT_EQ(action, keyEvent->getAction());
EXPECT_EQ(flags, keyEvent->getFlags());
EXPECT_EQ(keyCode, keyEvent->getKeyCode());
@@ -134,8 +139,11 @@
constexpr uint32_t seq = 15;
constexpr int32_t deviceId = 1;
- constexpr int32_t source = AINPUT_SOURCE_TOUCHSCREEN;
+ constexpr uint32_t source = AINPUT_SOURCE_TOUCHSCREEN;
constexpr int32_t displayId = ADISPLAY_ID_DEFAULT;
+ constexpr std::array<uint8_t, 32> hmac = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10,
+ 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21,
+ 22, 23, 24, 25, 26, 27, 28, 29, 30, 31};
constexpr int32_t action = AMOTION_EVENT_ACTION_MOVE;
constexpr int32_t actionButton = 0;
constexpr int32_t flags = AMOTION_EVENT_FLAG_WINDOW_IS_OBSCURED;
@@ -143,6 +151,8 @@
constexpr int32_t metaState = AMETA_ALT_LEFT_ON | AMETA_ALT_ON;
constexpr int32_t buttonState = AMOTION_EVENT_BUTTON_PRIMARY;
constexpr MotionClassification classification = MotionClassification::AMBIGUOUS_GESTURE;
+ constexpr float xScale = 2;
+ constexpr float yScale = 3;
constexpr float xOffset = -10;
constexpr float yOffset = -20;
constexpr float xPrecision = 0.25;
@@ -171,12 +181,12 @@
pointerCoords[i].setAxisValue(AMOTION_EVENT_AXIS_ORIENTATION, 3.5 * i);
}
- status =
- mPublisher->publishMotionEvent(seq, deviceId, source, displayId, action, actionButton,
- flags, edgeFlags, metaState, buttonState, classification,
- xOffset, yOffset, xPrecision, yPrecision,
- xCursorPosition, yCursorPosition, downTime, eventTime,
- pointerCount, pointerProperties, pointerCoords);
+ status = mPublisher->publishMotionEvent(seq, deviceId, source, displayId, hmac, action,
+ actionButton, flags, edgeFlags, metaState, buttonState,
+ classification, xScale, yScale, xOffset, yOffset,
+ xPrecision, yPrecision, xCursorPosition,
+ yCursorPosition, downTime, eventTime, pointerCount,
+ pointerProperties, pointerCoords);
ASSERT_EQ(OK, status)
<< "publisher publishMotionEvent should return OK";
@@ -196,18 +206,23 @@
EXPECT_EQ(deviceId, motionEvent->getDeviceId());
EXPECT_EQ(source, motionEvent->getSource());
EXPECT_EQ(displayId, motionEvent->getDisplayId());
+ EXPECT_EQ(hmac, motionEvent->getHmac());
EXPECT_EQ(action, motionEvent->getAction());
EXPECT_EQ(flags, motionEvent->getFlags());
EXPECT_EQ(edgeFlags, motionEvent->getEdgeFlags());
EXPECT_EQ(metaState, motionEvent->getMetaState());
EXPECT_EQ(buttonState, motionEvent->getButtonState());
EXPECT_EQ(classification, motionEvent->getClassification());
+ EXPECT_EQ(xScale, motionEvent->getXScale());
+ EXPECT_EQ(yScale, motionEvent->getYScale());
+ EXPECT_EQ(xOffset, motionEvent->getXOffset());
+ EXPECT_EQ(yOffset, motionEvent->getYOffset());
EXPECT_EQ(xPrecision, motionEvent->getXPrecision());
EXPECT_EQ(yPrecision, motionEvent->getYPrecision());
EXPECT_EQ(xCursorPosition, motionEvent->getRawXCursorPosition());
EXPECT_EQ(yCursorPosition, motionEvent->getRawYCursorPosition());
- EXPECT_EQ(xCursorPosition + xOffset, motionEvent->getXCursorPosition());
- EXPECT_EQ(yCursorPosition + yOffset, motionEvent->getYCursorPosition());
+ EXPECT_EQ(xCursorPosition * xScale + xOffset, motionEvent->getXCursorPosition());
+ EXPECT_EQ(yCursorPosition * yScale + yOffset, motionEvent->getYCursorPosition());
EXPECT_EQ(downTime, motionEvent->getDownTime());
EXPECT_EQ(eventTime, motionEvent->getEventTime());
EXPECT_EQ(pointerCount, motionEvent->getPointerCount());
@@ -222,10 +237,10 @@
motionEvent->getRawX(i));
EXPECT_EQ(pointerCoords[i].getAxisValue(AMOTION_EVENT_AXIS_Y),
motionEvent->getRawY(i));
- EXPECT_EQ(pointerCoords[i].getAxisValue(AMOTION_EVENT_AXIS_X) + xOffset,
- motionEvent->getX(i));
- EXPECT_EQ(pointerCoords[i].getAxisValue(AMOTION_EVENT_AXIS_Y) + yOffset,
- motionEvent->getY(i));
+ EXPECT_EQ(pointerCoords[i].getAxisValue(AMOTION_EVENT_AXIS_X) * xScale + xOffset,
+ motionEvent->getX(i));
+ EXPECT_EQ(pointerCoords[i].getAxisValue(AMOTION_EVENT_AXIS_Y) * yScale + yOffset,
+ motionEvent->getY(i));
EXPECT_EQ(pointerCoords[i].getAxisValue(AMOTION_EVENT_AXIS_PRESSURE),
motionEvent->getPressure(i));
EXPECT_EQ(pointerCoords[i].getAxisValue(AMOTION_EVENT_AXIS_SIZE),
@@ -316,11 +331,12 @@
pointerCoords[i].clear();
}
- status =
- mPublisher->publishMotionEvent(0, 0, 0, 0, 0, 0, 0, 0, 0, 0, MotionClassification::NONE,
- 0, 0, 0, 0, AMOTION_EVENT_INVALID_CURSOR_POSITION,
- AMOTION_EVENT_INVALID_CURSOR_POSITION, 0, 0,
- pointerCount, pointerProperties, pointerCoords);
+ status = mPublisher->publishMotionEvent(0, 0, 0, 0, INVALID_HMAC, 0, 0, 0, 0, 0, 0,
+ MotionClassification::NONE, 1 /* xScale */,
+ 1 /* yScale */, 0, 0, 0, 0,
+ AMOTION_EVENT_INVALID_CURSOR_POSITION,
+ AMOTION_EVENT_INVALID_CURSOR_POSITION, 0, 0,
+ pointerCount, pointerProperties, pointerCoords);
ASSERT_EQ(BAD_VALUE, status)
<< "publisher publishMotionEvent should return BAD_VALUE";
}
@@ -331,11 +347,12 @@
PointerProperties pointerProperties[pointerCount];
PointerCoords pointerCoords[pointerCount];
- status =
- mPublisher->publishMotionEvent(1, 0, 0, 0, 0, 0, 0, 0, 0, 0, MotionClassification::NONE,
- 0, 0, 0, 0, AMOTION_EVENT_INVALID_CURSOR_POSITION,
- AMOTION_EVENT_INVALID_CURSOR_POSITION, 0, 0,
- pointerCount, pointerProperties, pointerCoords);
+ status = mPublisher->publishMotionEvent(1, 0, 0, 0, INVALID_HMAC, 0, 0, 0, 0, 0, 0,
+ MotionClassification::NONE, 1 /* xScale */,
+ 1 /* yScale */, 0, 0, 0, 0,
+ AMOTION_EVENT_INVALID_CURSOR_POSITION,
+ AMOTION_EVENT_INVALID_CURSOR_POSITION, 0, 0,
+ pointerCount, pointerProperties, pointerCoords);
ASSERT_EQ(BAD_VALUE, status)
<< "publisher publishMotionEvent should return BAD_VALUE";
}
@@ -351,11 +368,12 @@
pointerCoords[i].clear();
}
- status =
- mPublisher->publishMotionEvent(1, 0, 0, 0, 0, 0, 0, 0, 0, 0, MotionClassification::NONE,
- 0, 0, 0, 0, AMOTION_EVENT_INVALID_CURSOR_POSITION,
- AMOTION_EVENT_INVALID_CURSOR_POSITION, 0, 0,
- pointerCount, pointerProperties, pointerCoords);
+ status = mPublisher->publishMotionEvent(1, 0, 0, 0, INVALID_HMAC, 0, 0, 0, 0, 0, 0,
+ MotionClassification::NONE, 1 /* xScale */,
+ 1 /* yScale */, 0, 0, 0, 0,
+ AMOTION_EVENT_INVALID_CURSOR_POSITION,
+ AMOTION_EVENT_INVALID_CURSOR_POSITION, 0, 0,
+ pointerCount, pointerProperties, pointerCoords);
ASSERT_EQ(BAD_VALUE, status)
<< "publisher publishMotionEvent should return BAD_VALUE";
}
diff --git a/libs/input/tests/StructLayout_test.cpp b/libs/input/tests/StructLayout_test.cpp
index 9ab0dba..aa8a2d4 100644
--- a/libs/input/tests/StructLayout_test.cpp
+++ b/libs/input/tests/StructLayout_test.cpp
@@ -39,35 +39,39 @@
CHECK_OFFSET(InputMessage::Body::Key, deviceId, 16);
CHECK_OFFSET(InputMessage::Body::Key, source, 20);
CHECK_OFFSET(InputMessage::Body::Key, displayId, 24);
- CHECK_OFFSET(InputMessage::Body::Key, action, 28);
- CHECK_OFFSET(InputMessage::Body::Key, flags, 32);
- CHECK_OFFSET(InputMessage::Body::Key, keyCode, 36);
- CHECK_OFFSET(InputMessage::Body::Key, scanCode, 40);
- CHECK_OFFSET(InputMessage::Body::Key, metaState, 44);
- CHECK_OFFSET(InputMessage::Body::Key, repeatCount, 48);
- CHECK_OFFSET(InputMessage::Body::Key, downTime, 56);
+ CHECK_OFFSET(InputMessage::Body::Key, hmac, 28);
+ CHECK_OFFSET(InputMessage::Body::Key, action, 60);
+ CHECK_OFFSET(InputMessage::Body::Key, flags, 64);
+ CHECK_OFFSET(InputMessage::Body::Key, keyCode, 68);
+ CHECK_OFFSET(InputMessage::Body::Key, scanCode, 72);
+ CHECK_OFFSET(InputMessage::Body::Key, metaState, 76);
+ CHECK_OFFSET(InputMessage::Body::Key, repeatCount, 80);
+ CHECK_OFFSET(InputMessage::Body::Key, downTime, 88);
CHECK_OFFSET(InputMessage::Body::Motion, seq, 0);
CHECK_OFFSET(InputMessage::Body::Motion, eventTime, 8);
CHECK_OFFSET(InputMessage::Body::Motion, deviceId, 16);
CHECK_OFFSET(InputMessage::Body::Motion, source, 20);
CHECK_OFFSET(InputMessage::Body::Motion, displayId, 24);
- CHECK_OFFSET(InputMessage::Body::Motion, action, 28);
- CHECK_OFFSET(InputMessage::Body::Motion, actionButton, 32);
- CHECK_OFFSET(InputMessage::Body::Motion, flags, 36);
- CHECK_OFFSET(InputMessage::Body::Motion, metaState, 40);
- CHECK_OFFSET(InputMessage::Body::Motion, buttonState, 44);
- CHECK_OFFSET(InputMessage::Body::Motion, classification, 48);
- CHECK_OFFSET(InputMessage::Body::Motion, edgeFlags, 52);
- CHECK_OFFSET(InputMessage::Body::Motion, downTime, 56);
- CHECK_OFFSET(InputMessage::Body::Motion, xOffset, 64);
- CHECK_OFFSET(InputMessage::Body::Motion, yOffset, 68);
- CHECK_OFFSET(InputMessage::Body::Motion, xPrecision, 72);
- CHECK_OFFSET(InputMessage::Body::Motion, yPrecision, 76);
- CHECK_OFFSET(InputMessage::Body::Motion, xCursorPosition, 80);
- CHECK_OFFSET(InputMessage::Body::Motion, yCursorPosition, 84);
- CHECK_OFFSET(InputMessage::Body::Motion, pointerCount, 88);
- CHECK_OFFSET(InputMessage::Body::Motion, pointers, 96);
+ CHECK_OFFSET(InputMessage::Body::Motion, hmac, 28);
+ CHECK_OFFSET(InputMessage::Body::Motion, action, 60);
+ CHECK_OFFSET(InputMessage::Body::Motion, actionButton, 64);
+ CHECK_OFFSET(InputMessage::Body::Motion, flags, 68);
+ CHECK_OFFSET(InputMessage::Body::Motion, metaState, 72);
+ CHECK_OFFSET(InputMessage::Body::Motion, buttonState, 76);
+ CHECK_OFFSET(InputMessage::Body::Motion, classification, 80);
+ CHECK_OFFSET(InputMessage::Body::Motion, edgeFlags, 84);
+ CHECK_OFFSET(InputMessage::Body::Motion, downTime, 88);
+ CHECK_OFFSET(InputMessage::Body::Motion, xScale, 96);
+ CHECK_OFFSET(InputMessage::Body::Motion, yScale, 100);
+ CHECK_OFFSET(InputMessage::Body::Motion, xOffset, 104);
+ CHECK_OFFSET(InputMessage::Body::Motion, yOffset, 108);
+ CHECK_OFFSET(InputMessage::Body::Motion, xPrecision, 112);
+ CHECK_OFFSET(InputMessage::Body::Motion, yPrecision, 116);
+ CHECK_OFFSET(InputMessage::Body::Motion, xCursorPosition, 120);
+ CHECK_OFFSET(InputMessage::Body::Motion, yCursorPosition, 124);
+ CHECK_OFFSET(InputMessage::Body::Motion, pointerCount, 128);
+ CHECK_OFFSET(InputMessage::Body::Motion, pointers, 136);
CHECK_OFFSET(InputMessage::Body::Focus, seq, 0);
CHECK_OFFSET(InputMessage::Body::Focus, hasFocus, 4);
@@ -86,7 +90,7 @@
* the Motion type, where "pointerCount" variable affects the size and can change at runtime.
*/
void TestBodySize() {
- static_assert(sizeof(InputMessage::Body::Key) == 64);
+ static_assert(sizeof(InputMessage::Body::Key) == 96);
static_assert(sizeof(InputMessage::Body::Motion) ==
offsetof(InputMessage::Body::Motion, pointers) +
sizeof(InputMessage::Body::Motion::Pointer) * MAX_POINTERS);
diff --git a/libs/input/tests/VelocityTracker_test.cpp b/libs/input/tests/VelocityTracker_test.cpp
index 968e2fa..731eb6a 100644
--- a/libs/input/tests/VelocityTracker_test.cpp
+++ b/libs/input/tests/VelocityTracker_test.cpp
@@ -176,11 +176,11 @@
EXPECT_EQ(pointerIndex, pointerCount);
MotionEvent event;
- event.initialize(0 /*deviceId*/, AINPUT_SOURCE_TOUCHSCREEN, DISPLAY_ID, action,
- 0 /*actionButton*/, 0 /*flags*/, AMOTION_EVENT_EDGE_FLAG_NONE, AMETA_NONE,
- 0 /*buttonState*/, MotionClassification::NONE, 0 /*xOffset*/,
- 0 /*yOffset*/, 0 /*xPrecision*/, 0 /*yPrecision*/,
- AMOTION_EVENT_INVALID_CURSOR_POSITION,
+ event.initialize(0 /*deviceId*/, AINPUT_SOURCE_TOUCHSCREEN, DISPLAY_ID, INVALID_HMAC,
+ action, 0 /*actionButton*/, 0 /*flags*/, AMOTION_EVENT_EDGE_FLAG_NONE,
+ AMETA_NONE, 0 /*buttonState*/, MotionClassification::NONE, 1 /*xScale*/,
+ 1 /*yScale*/, 0 /*xOffset*/, 0 /*yOffset*/, 0 /*xPrecision*/,
+ 0 /*yPrecision*/, AMOTION_EVENT_INVALID_CURSOR_POSITION,
AMOTION_EVENT_INVALID_CURSOR_POSITION, 0 /*downTime*/,
entry.eventTime.count(), pointerCount, properties, coords);
diff --git a/libs/nativedisplay/AChoreographer.cpp b/libs/nativedisplay/AChoreographer.cpp
index 15d937e..58fff8f 100644
--- a/libs/nativedisplay/AChoreographer.cpp
+++ b/libs/nativedisplay/AChoreographer.cpp
@@ -60,7 +60,7 @@
void postFrameCallbackDelayed(AChoreographer_frameCallback cb,
AChoreographer_frameCallback64 cb64, void* data, nsecs_t delay);
void registerRefreshRateCallback(AChoreographer_refreshRateCallback cb, void* data);
- void unregisterRefreshRateCallback(AChoreographer_refreshRateCallback cb);
+ void unregisterRefreshRateCallback(AChoreographer_refreshRateCallback cb, void* data);
enum {
MSG_SCHEDULE_CALLBACKS = 0,
@@ -152,21 +152,34 @@
void Choreographer::registerRefreshRateCallback(AChoreographer_refreshRateCallback cb, void* data) {
{
AutoMutex _l{mLock};
+ for (const auto& callback : mRefreshRateCallbacks) {
+ // Don't re-add callbacks.
+ if (cb == callback.callback && data == callback.data) {
+ return;
+ }
+ }
mRefreshRateCallbacks.emplace_back(RefreshRateCallback{cb, data});
toggleConfigEvents(ISurfaceComposer::ConfigChanged::eConfigChangedDispatch);
}
}
-void Choreographer::unregisterRefreshRateCallback(AChoreographer_refreshRateCallback cb) {
+void Choreographer::unregisterRefreshRateCallback(AChoreographer_refreshRateCallback cb,
+ void* data) {
{
AutoMutex _l{mLock};
mRefreshRateCallbacks.erase(std::remove_if(mRefreshRateCallbacks.begin(),
mRefreshRateCallbacks.end(),
[&](const RefreshRateCallback& callback) {
- return cb == callback.callback;
- }));
+ return cb == callback.callback &&
+ data == callback.data;
+ }),
+ mRefreshRateCallbacks.end());
if (mRefreshRateCallbacks.empty()) {
toggleConfigEvents(ISurfaceComposer::ConfigChanged::eConfigChangedSuppress);
+ // If callbacks are empty then clear out the most recently seen
+ // vsync period so that when another callback is registered then the
+ // up-to-date refresh rate can be communicated to the app again.
+ mVsyncPeriod = 0;
}
}
}
@@ -224,9 +237,9 @@
// on every single configuration change.
if (mVsyncPeriod != vsyncPeriod) {
cb.callback(vsyncPeriod, cb.data);
- mVsyncPeriod = vsyncPeriod;
}
}
+ mVsyncPeriod = vsyncPeriod;
}
}
@@ -285,8 +298,9 @@
AChoreographer_to_Choreographer(choreographer)->registerRefreshRateCallback(callback, data);
}
void AChoreographer_unregisterRefreshRateCallback(AChoreographer* choreographer,
- AChoreographer_refreshRateCallback callback) {
- AChoreographer_to_Choreographer(choreographer)->unregisterRefreshRateCallback(callback);
+ AChoreographer_refreshRateCallback callback,
+ void* data) {
+ AChoreographer_to_Choreographer(choreographer)->unregisterRefreshRateCallback(callback, data);
}
AChoreographer* AChoreographer_create() {
diff --git a/libs/nativedisplay/include/android/choreographer.h b/libs/nativedisplay/include/android/choreographer.h
index 0d97e8c..5fd3de9 100644
--- a/libs/nativedisplay/include/android/choreographer.h
+++ b/libs/nativedisplay/include/android/choreographer.h
@@ -54,6 +54,13 @@
*/
typedef void (*AChoreographer_frameCallback64)(int64_t frameTimeNanos, void* data);
+/**
+ * Prototype of the function that is called when the display refresh rate
+ * changes. It's passed the new vsync period in nanoseconds, as well as the data
+ * pointer provided by the application that registered a callback.
+ */
+typedef void (*AChoreographer_refreshRateCallback)(int64_t vsyncPeriodNanos, void* data);
+
#if __ANDROID_API__ >= 24
/**
@@ -102,6 +109,42 @@
#endif /* __ANDROID_API__ >= 29 */
+#if __ANDROID_API__ >= 30
+
+/**
+ * Registers a callback to be run when the display refresh rate changes. The
+ * data pointer provided will be passed to the callback function when it's
+ * called. The same callback may be registered multiple times, provided that a
+ * different data pointer is provided each time.
+ *
+ * If an application registers a callback for this choreographer instance when
+ * no new callbacks were previously registered, that callback is guaranteed to
+ * be dispatched. However, if the callback and associated data pointer are
+ * unregistered prior to running the callback, then the callback may be silently
+ * dropped.
+ *
+ * This api is thread-safe. Any thread is allowed to register a new refresh
+ * rate callback for the choreographer instance.
+ */
+void AChoreographer_registerRefreshRateCallback(AChoreographer* choreographer,
+ AChoreographer_refreshRateCallback, void* data);
+
+/**
+ * Unregisters a callback to be run when the display refresh rate changes, along
+ * with the data pointer previously provided when registering the callback. The
+ * callback is only unregistered when the data pointer matches one that was
+ * previously registered.
+ *
+ * This api is thread-safe. Any thread is allowed to unregister an existing
+ * refresh rate callback for the choreographer instance. When a refresh rate
+ * callback and associated data pointer are unregistered, then there is a
+ * guarantee that when the unregistration completes that that callback will not
+ * be run with the data pointer passed.
+ */
+void AChoreographer_unregisterRefreshRateCallback(AChoreographer* choreographer,
+ AChoreographer_refreshRateCallback, void* data);
+#endif /* __ANDROID_API__ >= 30 */
+
__END_DECLS
#endif // ANDROID_CHOREOGRAPHER_H
diff --git a/libs/nativedisplay/include/apex/choreographer.h b/libs/nativedisplay/include/apex/choreographer.h
index 5251fd3..b17b497 100644
--- a/libs/nativedisplay/include/apex/choreographer.h
+++ b/libs/nativedisplay/include/apex/choreographer.h
@@ -22,25 +22,6 @@
__BEGIN_DECLS
/**
- * Prototype of the function that is called when the display refresh rate
- * changes. It's passed the new vsync period in nanoseconds, as well as the data
- * pointer provided by the application that registered a callback.
- */
-typedef void (*AChoreographer_refreshRateCallback)(int64_t vsyncPeriodNanos, void* data);
-
-/**
- * Registers a callback to be run when the display refresh rate changes.
- */
-void AChoreographer_registerRefreshRateCallback(AChoreographer* choreographer,
- AChoreographer_refreshRateCallback, void* data);
-
-/**
- * Unregisters a callback to be run when the display refresh rate changes.
- */
-void AChoreographer_unregisterRefreshRateCallback(AChoreographer* choreographer,
- AChoreographer_refreshRateCallback);
-
-/**
* Creates an instance of AChoreographer.
*
* The key differences between this method and AChoreographer_getInstance are:
diff --git a/libs/nativewindow/ANativeWindow.cpp b/libs/nativewindow/ANativeWindow.cpp
index 842af18..a60bc4d 100644
--- a/libs/nativewindow/ANativeWindow.cpp
+++ b/libs/nativewindow/ANativeWindow.cpp
@@ -158,6 +158,13 @@
return query(window, NATIVE_WINDOW_DATASPACE);
}
+int32_t ANativeWindow_setFrameRate(ANativeWindow* window, float frameRate) {
+ if (!window || !query(window, NATIVE_WINDOW_IS_VALID) || frameRate < 0) {
+ return -EINVAL;
+ }
+ return native_window_set_frame_rate(window, frameRate);
+}
+
/**************************************************************************************************
* vndk-stable
**************************************************************************************************/
diff --git a/libs/nativewindow/include/android/native_window.h b/libs/nativewindow/include/android/native_window.h
index 3e436e3..262aee3 100644
--- a/libs/nativewindow/include/android/native_window.h
+++ b/libs/nativewindow/include/android/native_window.h
@@ -230,6 +230,39 @@
#endif // __ANDROID_API__ >= 28
+#if __ANDROID_API__ >= 30
+
+/**
+ * Sets the intended frame rate for this window.
+ *
+ * On devices that are capable of running the display at different refresh
+ * rates, the system may choose a display refresh rate to better match this
+ * window's frame rate. Usage of this API won't introduce frame rate throttling,
+ * or affect other aspects of the application's frame production
+ * pipeline. However, because the system may change the display refresh rate,
+ * calls to this function may result in changes to Choreographer callback
+ * timings, and changes to the time interval at which the system releases
+ * buffers back to the application.
+ *
+ * Note that this only has an effect for windows presented on the display. If
+ * this ANativeWindow is consumed by something other than the system compositor,
+ * e.g. a media codec, this call has no effect.
+ *
+ * Available since API level 30.
+ *
+ * \param frameRate The intended frame rate of this window, in frames per
+ * second. 0 is a special value that indicates the app will accept the system's
+ * choice for the display frame rate, which is the default behavior if this
+ * function isn't called. The frameRate param does *not* need to be a valid
+ * refresh rate for this device's display - e.g., it's fine to pass 30fps to a
+ * device that can only run the display at 60fps.
+ *
+ * \return 0 for success, -EINVAL if the window or frame rate are invalid.
+ */
+int32_t ANativeWindow_setFrameRate(ANativeWindow* window, float frameRate) __INTRODUCED_IN(30);
+
+#endif // __ANDROID_API__ >= 30
+
#ifdef __cplusplus
};
#endif
diff --git a/libs/nativewindow/libnativewindow.map.txt b/libs/nativewindow/libnativewindow.map.txt
index f59e8f0..3002da2 100644
--- a/libs/nativewindow/libnativewindow.map.txt
+++ b/libs/nativewindow/libnativewindow.map.txt
@@ -43,6 +43,7 @@
ANativeWindow_setDequeueTimeout; # apex # introduced=30
ANativeWindow_setSharedBufferMode; # llndk
ANativeWindow_setSwapInterval; # llndk
+ ANativeWindow_setFrameRate; # introduced=30
ANativeWindow_setUsage; # llndk
ANativeWindow_unlockAndPost;
local:
diff --git a/libs/renderengine/gl/GLESRenderEngine.cpp b/libs/renderengine/gl/GLESRenderEngine.cpp
index e257704..98605ba 100644
--- a/libs/renderengine/gl/GLESRenderEngine.cpp
+++ b/libs/renderengine/gl/GLESRenderEngine.cpp
@@ -1003,7 +1003,7 @@
setViewportAndProjection(display.physicalDisplay, display.clip);
} else {
setViewportAndProjection(display.physicalDisplay, display.clip);
- auto status = mBlurFilter->setAsDrawTarget(display);
+ auto status = mBlurFilter->setAsDrawTarget(display, blurLayer->backgroundBlurRadius);
if (status != NO_ERROR) {
ALOGE("Failed to prepare blur filter! Aborting GPU composition for buffer (%p).",
buffer->handle);
@@ -1037,7 +1037,7 @@
.build();
for (auto const layer : layers) {
if (blurLayer == layer) {
- auto status = mBlurFilter->prepare(layer->backgroundBlurRadius);
+ auto status = mBlurFilter->prepare();
if (status != NO_ERROR) {
ALOGE("Failed to render blur effect! Aborting GPU composition for buffer (%p).",
buffer->handle);
diff --git a/libs/renderengine/gl/GLFramebuffer.cpp b/libs/renderengine/gl/GLFramebuffer.cpp
index 091eac9..153935b 100644
--- a/libs/renderengine/gl/GLFramebuffer.cpp
+++ b/libs/renderengine/gl/GLFramebuffer.cpp
@@ -122,6 +122,14 @@
glBindFramebuffer(GL_FRAMEBUFFER, mFramebufferName);
}
+void GLFramebuffer::bindAsReadBuffer() const {
+ glBindFramebuffer(GL_READ_FRAMEBUFFER, mFramebufferName);
+}
+
+void GLFramebuffer::bindAsDrawBuffer() const {
+ glBindFramebuffer(GL_DRAW_FRAMEBUFFER, mFramebufferName);
+}
+
void GLFramebuffer::unbind() const {
glBindFramebuffer(GL_FRAMEBUFFER, 0);
}
diff --git a/libs/renderengine/gl/GLFramebuffer.h b/libs/renderengine/gl/GLFramebuffer.h
index 668685a..69102d6 100644
--- a/libs/renderengine/gl/GLFramebuffer.h
+++ b/libs/renderengine/gl/GLFramebuffer.h
@@ -48,6 +48,8 @@
int32_t getBufferWidth() const { return mBufferWidth; }
GLenum getStatus() const { return mStatus; }
void bind() const;
+ void bindAsReadBuffer() const;
+ void bindAsDrawBuffer() const;
void unbind() const;
private:
diff --git a/libs/renderengine/gl/filters/BlurFilter.cpp b/libs/renderengine/gl/filters/BlurFilter.cpp
index a554687..48c2560 100644
--- a/libs/renderengine/gl/filters/BlurFilter.cpp
+++ b/libs/renderengine/gl/filters/BlurFilter.cpp
@@ -31,20 +31,26 @@
namespace gl {
BlurFilter::BlurFilter(GLESRenderEngine& engine)
- : mEngine(engine), mCompositionFbo(engine), mBlurredFbo(engine), mSimpleProgram(engine) {
- mSimpleProgram.compile(getVertexShader(), getSimpleFragShader());
- mSPosLoc = mSimpleProgram.getAttributeLocation("aPosition");
- mSUvLoc = mSimpleProgram.getAttributeLocation("aUV");
- mSTextureLoc = mSimpleProgram.getUniformLocation("uTexture");
+ : mEngine(engine), mCompositionFbo(engine), mBlurredFbo(engine), mMixProgram(engine) {
+ mMixProgram.compile(getVertexShader(), getMixFragShader());
+ mMPosLoc = mMixProgram.getAttributeLocation("aPosition");
+ mMUvLoc = mMixProgram.getAttributeLocation("aUV");
+ mMTextureLoc = mMixProgram.getUniformLocation("uTexture");
+ mMCompositionTextureLoc = mMixProgram.getUniformLocation("uCompositionTexture");
+ mMMixLoc = mMixProgram.getUniformLocation("uMix");
}
-status_t BlurFilter::setAsDrawTarget(const DisplaySettings& display) {
+status_t BlurFilter::setAsDrawTarget(const DisplaySettings& display, uint32_t radius) {
ATRACE_NAME("BlurFilter::setAsDrawTarget");
+ mRadius = radius;
if (!mTexturesAllocated) {
- const uint32_t fboWidth = floorf(display.physicalDisplay.width() * kFboScale);
- const uint32_t fboHeight = floorf(display.physicalDisplay.height() * kFboScale);
- mCompositionFbo.allocateBuffers(fboWidth, fboHeight);
+ mDisplayWidth = display.physicalDisplay.width();
+ mDisplayHeight = display.physicalDisplay.height();
+ mCompositionFbo.allocateBuffers(mDisplayWidth, mDisplayHeight);
+
+ const uint32_t fboWidth = floorf(mDisplayWidth * kFboScale);
+ const uint32_t fboHeight = floorf(mDisplayHeight * kFboScale);
mBlurredFbo.allocateBuffers(fboWidth, fboHeight);
allocateTextures();
mTexturesAllocated = true;
@@ -83,27 +89,41 @@
status_t BlurFilter::render() {
ATRACE_NAME("BlurFilter::render");
- // Now let's scale our blur up
- mSimpleProgram.useProgram();
+ // Now let's scale our blur up. It will be interpolated with the larger composited
+ // texture for the first frames, to hide downscaling artifacts.
+ GLfloat mix = fmin(1.0, mRadius / kMaxCrossFadeRadius);
+ if (mix >= 1) {
+ mBlurredFbo.bindAsReadBuffer();
+ glBlitFramebuffer(0, 0, mBlurredFbo.getBufferWidth(), mBlurredFbo.getBufferHeight(), 0, 0,
+ mDisplayWidth, mDisplayHeight, GL_COLOR_BUFFER_BIT, GL_LINEAR);
+ glBindFramebuffer(GL_READ_FRAMEBUFFER, 0);
+ return NO_ERROR;
+ }
+
+ mMixProgram.useProgram();
+ glUniform1f(mMMixLoc, mix);
glActiveTexture(GL_TEXTURE0);
glBindTexture(GL_TEXTURE_2D, mBlurredFbo.getTextureName());
- glUniform1i(mSTextureLoc, 0);
+ glUniform1i(mMTextureLoc, 0);
+ glActiveTexture(GL_TEXTURE1);
+ glBindTexture(GL_TEXTURE_2D, mCompositionFbo.getTextureName());
+ glUniform1i(mMCompositionTextureLoc, 1);
mEngine.checkErrors("Setting final pass uniforms");
- drawMesh(mSUvLoc, mSPosLoc);
+ drawMesh(mMUvLoc, mMPosLoc);
glUseProgram(0);
+ glActiveTexture(GL_TEXTURE0);
return NO_ERROR;
}
string BlurFilter::getVertexShader() const {
return R"SHADER(
#version 310 es
- precision lowp float;
in vec2 aPosition;
- in mediump vec2 aUV;
- out mediump vec2 vUV;
+ in highp vec2 aUV;
+ out highp vec2 vUV;
void main() {
vUV = aUV;
@@ -112,18 +132,22 @@
)SHADER";
}
-string BlurFilter::getSimpleFragShader() const {
+string BlurFilter::getMixFragShader() const {
string shader = R"SHADER(
#version 310 es
- precision lowp float;
+ precision mediump float;
- in mediump vec2 vUV;
+ in highp vec2 vUV;
out vec4 fragColor;
+ uniform sampler2D uCompositionTexture;
uniform sampler2D uTexture;
+ uniform float uMix;
void main() {
- fragColor = texture(uTexture, vUV);
+ vec4 blurred = texture(uTexture, vUV);
+ vec4 composition = texture(uCompositionTexture, vUV);
+ fragColor = mix(composition, blurred, uMix);
}
)SHADER";
return shader;
diff --git a/libs/renderengine/gl/filters/BlurFilter.h b/libs/renderengine/gl/filters/BlurFilter.h
index 2b5ea58..6889939 100644
--- a/libs/renderengine/gl/filters/BlurFilter.h
+++ b/libs/renderengine/gl/filters/BlurFilter.h
@@ -30,23 +30,26 @@
class BlurFilter {
public:
// Downsample FBO to improve performance
- static constexpr float kFboScale = 0.35f;
+ static constexpr float kFboScale = 0.25f;
+ // To avoid downscaling artifacts, we interpolate the blurred fbo with the full composited
+ // image, up to this radius.
+ static constexpr float kMaxCrossFadeRadius = 15.0f;
explicit BlurFilter(GLESRenderEngine& engine);
virtual ~BlurFilter(){};
// Set up render targets, redirecting output to offscreen texture.
- status_t setAsDrawTarget(const DisplaySettings&);
+ status_t setAsDrawTarget(const DisplaySettings&, uint32_t radius);
// Allocate any textures needed for the filter.
virtual void allocateTextures() = 0;
// Execute blur passes, rendering to offscreen texture.
- virtual status_t prepare(uint32_t radius) = 0;
+ virtual status_t prepare() = 0;
// Render blur to the bound framebuffer (screen).
status_t render();
protected:
+ uint32_t mRadius;
void drawMesh(GLuint uv, GLuint position);
- string getSimpleFragShader() const;
string getVertexShader() const;
GLESRenderEngine& mEngine;
@@ -54,14 +57,19 @@
GLFramebuffer mCompositionFbo;
// Frame buffer holding the blur result.
GLFramebuffer mBlurredFbo;
+ uint32_t mDisplayWidth;
+ uint32_t mDisplayHeight;
private:
+ string getMixFragShader() const;
bool mTexturesAllocated = false;
- GenericProgram mSimpleProgram;
- GLuint mSPosLoc;
- GLuint mSUvLoc;
- GLuint mSTextureLoc;
+ GenericProgram mMixProgram;
+ GLuint mMPosLoc;
+ GLuint mMUvLoc;
+ GLuint mMMixLoc;
+ GLuint mMTextureLoc;
+ GLuint mMCompositionTextureLoc;
};
} // namespace gl
diff --git a/libs/renderengine/gl/filters/GaussianBlurFilter.cpp b/libs/renderengine/gl/filters/GaussianBlurFilter.cpp
index b1ad72c..4d7bf44 100644
--- a/libs/renderengine/gl/filters/GaussianBlurFilter.cpp
+++ b/libs/renderengine/gl/filters/GaussianBlurFilter.cpp
@@ -26,6 +26,10 @@
#include <utils/Trace.h>
+#define PI 3.14159265359
+#define THETA 0.352
+#define K 1.0 / (2.0 * THETA * THETA)
+
namespace android {
namespace renderengine {
namespace gl {
@@ -39,22 +43,24 @@
mVPosLoc = mVerticalProgram.getAttributeLocation("aPosition");
mVUvLoc = mVerticalProgram.getAttributeLocation("aUV");
mVTextureLoc = mVerticalProgram.getUniformLocation("uTexture");
- mVSizeLoc = mVerticalProgram.getUniformLocation("uSize");
- mVRadiusLoc = mVerticalProgram.getUniformLocation("uRadius");
+ mVIncrementLoc = mVerticalProgram.getUniformLocation("uIncrement");
+ mVNumSamplesLoc = mVerticalProgram.getUniformLocation("uSamples");
+ mVGaussianWeightLoc = mVerticalProgram.getUniformLocation("uGaussianWeights");
mHorizontalProgram.compile(getVertexShader(), getFragmentShader(true));
mHPosLoc = mHorizontalProgram.getAttributeLocation("aPosition");
mHUvLoc = mHorizontalProgram.getAttributeLocation("aUV");
mHTextureLoc = mHorizontalProgram.getUniformLocation("uTexture");
- mHSizeLoc = mHorizontalProgram.getUniformLocation("uSize");
- mHRadiusLoc = mHorizontalProgram.getUniformLocation("uRadius");
+ mHIncrementLoc = mHorizontalProgram.getUniformLocation("uIncrement");
+ mHNumSamplesLoc = mHorizontalProgram.getUniformLocation("uSamples");
+ mHGaussianWeightLoc = mHorizontalProgram.getUniformLocation("uGaussianWeights");
}
void GaussianBlurFilter::allocateTextures() {
mVerticalPassFbo.allocateBuffers(mBlurredFbo.getBufferWidth(), mBlurredFbo.getBufferHeight());
}
-status_t GaussianBlurFilter::prepare(uint32_t radius) {
+status_t GaussianBlurFilter::prepare() {
ATRACE_NAME("GaussianBlurFilter::prepare");
if (mVerticalPassFbo.getStatus() != GL_FRAMEBUFFER_COMPLETE) {
@@ -70,19 +76,38 @@
return GL_INVALID_OPERATION;
}
+ mCompositionFbo.bindAsReadBuffer();
+ mBlurredFbo.bindAsDrawBuffer();
+ glBlitFramebuffer(0, 0, mCompositionFbo.getBufferWidth(), mCompositionFbo.getBufferHeight(), 0,
+ 0, mBlurredFbo.getBufferWidth(), mBlurredFbo.getBufferHeight(),
+ GL_COLOR_BUFFER_BIT, GL_LINEAR);
+ glBindFramebuffer(GL_READ_FRAMEBUFFER, 0);
+ glBindFramebuffer(GL_DRAW_FRAMEBUFFER, 0);
+
// First, we'll apply the vertical pass, that receives the flattened background layers.
mVerticalPassFbo.bind();
mVerticalProgram.useProgram();
+ // Precompute gaussian bell curve, and send it to the shader to avoid
+ // unnecessary computations.
+ auto samples = min(mRadius, kNumSamples);
+ GLfloat gaussianWeights[kNumSamples] = {};
+ for (size_t i = 0; i < samples; i++) {
+ float normalized = float(i) / samples;
+ gaussianWeights[i] = (float)exp(-K * normalized * normalized);
+ }
+
// set uniforms
auto width = mVerticalPassFbo.getBufferWidth();
auto height = mVerticalPassFbo.getBufferHeight();
+ auto radiusF = fmax(1.0f, mRadius * kFboScale);
glViewport(0, 0, width, height);
glActiveTexture(GL_TEXTURE0);
- glBindTexture(GL_TEXTURE_2D, mCompositionFbo.getTextureName());
+ glBindTexture(GL_TEXTURE_2D, mBlurredFbo.getTextureName());
glUniform1i(mVTextureLoc, 0);
- glUniform2f(mVSizeLoc, width, height);
- glUniform1f(mVRadiusLoc, radius * kFboScale);
+ glUniform2f(mVIncrementLoc, radiusF / (width * 2.0f), radiusF / (height * 2.0f));
+ glUniform1i(mVNumSamplesLoc, samples);
+ glUniform1fv(mVGaussianWeightLoc, kNumSamples, gaussianWeights);
mEngine.checkErrors("Setting vertical-diagonal pass uniforms");
drawMesh(mVUvLoc, mVPosLoc);
@@ -95,8 +120,9 @@
glActiveTexture(GL_TEXTURE0);
glBindTexture(GL_TEXTURE_2D, mVerticalPassFbo.getTextureName());
glUniform1i(mHTextureLoc, 0);
- glUniform2f(mHSizeLoc, width, height);
- glUniform1f(mHRadiusLoc, radius * kFboScale);
+ glUniform2f(mHIncrementLoc, radiusF / (width * 2.0f), radiusF / (height * 2.0f));
+ glUniform1i(mHNumSamplesLoc, samples);
+ glUniform1fv(mHGaussianWeightLoc, kNumSamples, gaussianWeights);
mEngine.checkErrors("Setting vertical pass uniforms");
drawMesh(mHUvLoc, mHPosLoc);
@@ -113,43 +139,31 @@
}
string GaussianBlurFilter::getFragmentShader(bool horizontal) const {
- string shader = "#version 310 es\n#define DIRECTION ";
- shader += (horizontal ? "1" : "0");
- shader += R"SHADER(
- precision lowp float;
+ stringstream shader;
+ shader << "#version 310 es\n"
+ << "#define DIRECTION " << (horizontal ? "1" : "0") << "\n"
+ << "#define NUM_SAMPLES " << kNumSamples <<
+ R"SHADER(
+ precision mediump float;
uniform sampler2D uTexture;
- uniform vec2 uSize;
- uniform float uRadius;
+ uniform vec2 uIncrement;
+ uniform float[NUM_SAMPLES] uGaussianWeights;
+ uniform int uSamples;
- in mediump vec2 vUV;
-
+ highp in vec2 vUV;
out vec4 fragColor;
- #define PI 3.14159265359
- #define THETA 0.352
- #define MU 0.0
- #define A 1.0 / (THETA * sqrt(2.0 * PI))
- #define K 1.0 / (2.0 * THETA * THETA)
- #define MAX_SAMPLES 12
-
- float gaussianBellCurve(float x) {
- float tmp = (x - MU);
- return exp(-K * tmp * tmp);
- }
-
- vec3 gaussianBlur(sampler2D texture, mediump vec2 uv, float size,
- vec2 direction, float radius) {
+ vec3 gaussianBlur(sampler2D texture, highp vec2 uv, float inc, vec2 direction) {
float totalWeight = 0.0;
- vec3 blurred = vec3(0.);
- int samples = min(int(floor(radius / 2.0)), MAX_SAMPLES);
- float inc = radius / (size * 2.0);
+ vec3 blurred = vec3(0.0);
+ float fSamples = 1.0 / float(uSamples);
- for (int i = -samples; i <= samples; i++) {
- float normalized = (float(i) / float(samples));
- float weight = gaussianBellCurve(normalized);
+ for (int i = -uSamples; i <= uSamples; i++) {
+ float weight = uGaussianWeights[abs(i)];
+ float normalized = float(i) * fSamples;
float radInc = inc * normalized;
- blurred += weight * (texture(texture, uv + radInc * direction)).rgb;;
+ blurred += weight * (texture(texture, radInc * direction + uv, 0.0)).rgb;
totalWeight += weight;
}
@@ -158,15 +172,15 @@
void main() {
#if DIRECTION == 1
- vec3 color = gaussianBlur(uTexture, vUV, uSize.x, vec2(1.0, 0.0), uRadius);
+ vec3 color = gaussianBlur(uTexture, vUV, uIncrement.x, vec2(1.0, 0.0));
#else
- vec3 color = gaussianBlur(uTexture, vUV, uSize.y, vec2(0.0, 1.0), uRadius);
+ vec3 color = gaussianBlur(uTexture, vUV, uIncrement.y, vec2(0.0, 1.0));
#endif
- fragColor = vec4(color.r, color.g, color.b, texture(uTexture, vUV).a);
+ fragColor = vec4(color, 1.0);
}
)SHADER";
- return shader;
+ return shader.str();
}
} // namespace gl
diff --git a/libs/renderengine/gl/filters/GaussianBlurFilter.h b/libs/renderengine/gl/filters/GaussianBlurFilter.h
index acf0f07..8580522 100644
--- a/libs/renderengine/gl/filters/GaussianBlurFilter.h
+++ b/libs/renderengine/gl/filters/GaussianBlurFilter.h
@@ -30,8 +30,10 @@
class GaussianBlurFilter : public BlurFilter {
public:
+ static constexpr uint32_t kNumSamples = 12;
+
explicit GaussianBlurFilter(GLESRenderEngine& engine);
- status_t prepare(uint32_t radius) override;
+ status_t prepare() override;
void allocateTextures() override;
private:
@@ -45,16 +47,18 @@
GLuint mVPosLoc;
GLuint mVUvLoc;
GLuint mVTextureLoc;
- GLuint mVSizeLoc;
- GLuint mVRadiusLoc;
+ GLuint mVIncrementLoc;
+ GLuint mVNumSamplesLoc;
+ GLuint mVGaussianWeightLoc;
// Horizontal pass and its uniforms
GenericProgram mHorizontalProgram;
GLuint mHPosLoc;
GLuint mHUvLoc;
GLuint mHTextureLoc;
- GLuint mHSizeLoc;
- GLuint mHRadiusLoc;
+ GLuint mHIncrementLoc;
+ GLuint mHNumSamplesLoc;
+ GLuint mHGaussianWeightLoc;
};
} // namespace gl
diff --git a/libs/renderengine/gl/filters/LensBlurFilter.cpp b/libs/renderengine/gl/filters/LensBlurFilter.cpp
index 386bd91..fb29fbb 100644
--- a/libs/renderengine/gl/filters/LensBlurFilter.cpp
+++ b/libs/renderengine/gl/filters/LensBlurFilter.cpp
@@ -62,7 +62,7 @@
mBlurredFbo.getBufferHeight());
}
-status_t LensBlurFilter::prepare(uint32_t radius) {
+status_t LensBlurFilter::prepare() {
ATRACE_NAME("LensBlurFilter::prepare");
if (mVerticalDiagonalPassFbo.getStatus() != GL_FRAMEBUFFER_COMPLETE) {
@@ -86,12 +86,13 @@
// set uniforms
auto width = mVerticalDiagonalPassFbo.getBufferWidth();
auto height = mVerticalDiagonalPassFbo.getBufferHeight();
+ auto radiusF = fmax(1.0f, mRadius * kFboScale);
glViewport(0, 0, width, height);
glActiveTexture(GL_TEXTURE0);
glBindTexture(GL_TEXTURE_2D, mCompositionFbo.getTextureName());
glUniform1i(mVDTexture0Loc, 0);
- glUniform2f(mVDSizeLoc, width, height);
- glUniform1f(mVDRadiusLoc, radius * kFboScale);
+ glUniform2f(mVDSizeLoc, mDisplayWidth, mDisplayHeight);
+ glUniform1f(mVDRadiusLoc, radiusF);
glUniform1i(mVDNumSamplesLoc, kNumSamples);
mEngine.checkErrors("Setting vertical-diagonal pass uniforms");
@@ -108,8 +109,8 @@
glActiveTexture(GL_TEXTURE1);
glBindTexture(GL_TEXTURE_2D, mVerticalDiagonalPassFbo.getSecondaryTextureName());
glUniform1i(mCTexture1Loc, 1);
- glUniform2f(mCSizeLoc, width, height);
- glUniform1f(mCRadiusLoc, radius * kFboScale);
+ glUniform2f(mCSizeLoc, mDisplayWidth, mDisplayHeight);
+ glUniform1f(mCRadiusLoc, radiusF);
glUniform1i(mCNumSamplesLoc, kNumSamples);
mEngine.checkErrors("Setting vertical pass uniforms");
@@ -132,9 +133,7 @@
string shader = "#version 310 es\n#define DIRECTION ";
shader += (forComposition ? "1" : "0");
shader += R"SHADER(
- precision lowp float;
-
- #define BOKEH_ANGLE 0.0
+ precision mediump float;
#define PI 3.14159265359
uniform sampler2D uTexture0;
@@ -142,7 +141,7 @@
uniform float uRadius;
uniform int uNumSamples;
- in mediump vec2 vUV;
+ highp in vec2 vUV;
#if DIRECTION == 0
layout(location = 0) out vec4 fragColor0;
@@ -152,61 +151,55 @@
out vec4 fragColor;
#endif
- vec4 blur(const sampler2D tex, in vec2 uv, const vec2 direction, float radius,
- in int samples, float intensity) {
- vec4 finalColor = vec4(vec3(0.0), 1.0);
- float blurAmount = 0.0;
+ const vec2 verticalMult = vec2(cos(PI / 2.0), sin(PI / 2.0));
+ const vec2 diagonalMult = vec2(cos(-PI / 6.0), sin(-PI / 6.0));
+ const vec2 diagonal2Mult = vec2(cos(-5.0 * PI / 6.0), sin(-5.0 * PI / 6.0));
+
+ vec3 blur(const sampler2D tex, vec2 uv, const vec2 direction, float radius,
+ int samples, float intensity) {
+ vec3 finalColor = vec3(0.0);
uv += direction * 0.5;
for (int i = 0; i < samples; i++){
float delta = radius * float(i) / float(samples);
- vec4 color = texture(tex, uv + direction * delta);
+ vec3 color = texture(tex, uv + direction * delta).rgb;
color.rgb *= intensity;
- color *= color.a;
- blurAmount += color.a;
finalColor += color;
}
- return finalColor / blurAmount;
+ return finalColor / float(samples);
}
- vec4 blur(const sampler2D tex, in vec2 uv, const vec2 direction, float radius,
- in int samples) {
+ vec3 blur(const sampler2D tex, vec2 uv, const vec2 direction, float radius,
+ int samples) {
return blur(tex, uv, direction, radius, samples, 1.0);
}
vec4[2] verticalDiagonalLensBlur (vec2 uv, sampler2D texture, vec2 resolution,
float radius, int samples) {
- float coc = texture(texture, uv).a;
-
// Vertical Blur
- vec2 blurDirV = (coc / resolution.xy) * vec2(cos(BOKEH_ANGLE + PI / 2.0),
- sin(BOKEH_ANGLE + PI / 2.0));
- vec3 colorV = blur(texture, uv, blurDirV, radius, samples).rgb * coc;
+ vec2 blurDirV = 1.0 / resolution.xy * verticalMult;
+ vec3 colorV = blur(texture, uv, blurDirV, radius, samples);
// Diagonal Blur
- vec2 blurDirD = (coc / resolution.xy) * vec2(cos(BOKEH_ANGLE - PI / 6.0),
- sin(BOKEH_ANGLE - PI / 6.0));
- vec3 colorD = blur(texture, uv, blurDirD, radius, samples).rgb * coc;
+ vec2 blurDirD = 1.0 / resolution.xy * diagonalMult;
+ vec3 colorD = blur(texture, uv, blurDirD, radius, samples);
vec4 composed[2];
- composed[0] = vec4(colorV, coc);
+ composed[0] = vec4(colorV, 1.0);
// added * 0.5, to remap
- composed[1] = vec4((colorD + colorV) * 0.5, coc);
+ composed[1] = vec4((colorD + colorV) * 0.5, 1.0);
return composed;
}
vec4 rhombiLensBlur (vec2 uv, sampler2D texture0, sampler2D texture1, vec2 resolution,
float radius, int samples) {
- float coc1 = texture(texture0, uv).a;
- float coc2 = texture(texture1, uv).a;
+ vec2 blurDirection1 = 1.0 / resolution.xy * diagonalMult;
+ vec3 color1 = blur(texture0, uv, blurDirection1, radius, samples);
- vec2 blurDirection1 = coc1 / resolution.xy * vec2(cos(BOKEH_ANGLE - PI / 6.0), sin(BOKEH_ANGLE - PI / 6.0));
- vec3 color1 = blur(texture0, uv, blurDirection1, radius, samples).rgb * coc1;
-
- vec2 blurDirection2 = coc2 / resolution.xy * vec2(cos(BOKEH_ANGLE - 5.0 * PI / 6.0), sin(BOKEH_ANGLE - 5.0 * PI / 6.0));
- vec3 color2 = blur(texture1, uv, blurDirection2, radius, samples, 2.0).rgb * coc2;
+ vec2 blurDirection2 = 1.0 / resolution.xy * diagonal2Mult;
+ vec3 color2 = blur(texture1, uv, blurDirection2, radius, samples, 2.0);
return vec4((color1 + color2) * 0.33, 1.0);
}
diff --git a/libs/renderengine/gl/filters/LensBlurFilter.h b/libs/renderengine/gl/filters/LensBlurFilter.h
index 8543f0d..1620c5a 100644
--- a/libs/renderengine/gl/filters/LensBlurFilter.h
+++ b/libs/renderengine/gl/filters/LensBlurFilter.h
@@ -31,7 +31,7 @@
class LensBlurFilter : public BlurFilter {
public:
explicit LensBlurFilter(GLESRenderEngine& engine);
- status_t prepare(uint32_t radius) override;
+ status_t prepare() override;
void allocateTextures() override;
private:
diff --git a/libs/sensor/Sensor.cpp b/libs/sensor/Sensor.cpp
index abc9103..9d817ae 100644
--- a/libs/sensor/Sensor.cpp
+++ b/libs/sensor/Sensor.cpp
@@ -268,6 +268,10 @@
mStringType = SENSOR_STRING_TYPE_ACCELEROMETER_UNCALIBRATED;
mFlags |= SENSOR_FLAG_CONTINUOUS_MODE;
break;
+ case SENSOR_TYPE_HINGE_ANGLE:
+ mStringType = SENSOR_STRING_TYPE_HINGE_ANGLE;
+ mFlags |= SENSOR_FLAG_ON_CHANGE_MODE;
+ break;
default:
// Only pipe the stringType, requiredPermission and flags for custom sensors.
if (halVersion > SENSORS_DEVICE_API_VERSION_1_0 && hwSensor.stringType) {
diff --git a/libs/sensor/SensorManager.cpp b/libs/sensor/SensorManager.cpp
index bf8b9f7..a4a5d13 100644
--- a/libs/sensor/SensorManager.cpp
+++ b/libs/sensor/SensorManager.cpp
@@ -209,7 +209,7 @@
type == SENSOR_TYPE_TILT_DETECTOR || type == SENSOR_TYPE_WAKE_GESTURE ||
type == SENSOR_TYPE_GLANCE_GESTURE || type == SENSOR_TYPE_PICK_UP_GESTURE ||
type == SENSOR_TYPE_WRIST_TILT_GESTURE ||
- type == SENSOR_TYPE_LOW_LATENCY_OFFBODY_DETECT) {
+ type == SENSOR_TYPE_LOW_LATENCY_OFFBODY_DETECT || type == SENSOR_TYPE_HINGE_ANGLE) {
wakeUpSensor = true;
}
// For now we just return the first sensor of that type we find.
diff --git a/services/gpuservice/GpuService.cpp b/services/gpuservice/GpuService.cpp
index 55a0c0a..be4a462 100644
--- a/services/gpuservice/GpuService.cpp
+++ b/services/gpuservice/GpuService.cpp
@@ -28,12 +28,7 @@
#include <utils/String8.h>
#include <utils/Trace.h>
-#include <array>
-#include <fstream>
-#include <sstream>
-#include <sys/types.h>
#include <vkjson.h>
-#include <unistd.h>
#include "gpustats/GpuStats.h"
@@ -45,7 +40,6 @@
status_t cmdHelp(int out);
status_t cmdVkjson(int out, int err);
void dumpGameDriverInfo(std::string* result);
-void dumpMemoryInfo(std::string* result, const GpuMemoryMap& memories, uint32_t pid);
} // namespace
const String16 sDump("android.permission.DUMP");
@@ -78,147 +72,6 @@
mGpuStats->insertTargetStats(appPackageName, driverVersionCode, stats, value);
}
-bool isExpectedFormat(const char* str) {
- // Should match in order:
- // gpuaddr useraddr size id flags type usage sglen mapsize eglsrf eglimg
- std::istringstream iss;
- iss.str(str);
-
- std::string word;
- iss >> word;
- if (word != "gpuaddr") { return false; }
- iss >> word;
- if (word != "useraddr") { return false; }
- iss >> word;
- if (word != "size") { return false; }
- iss >> word;
- if (word != "id") { return false; }
- iss >> word;
- if (word != "flags") { return false; }
- iss >> word;
- if (word != "type") { return false; }
- iss >> word;
- if (word != "usage") { return false; }
- iss >> word;
- if (word != "sglen") { return false; }
- iss >> word;
- if (word != "mapsize") { return false; }
- iss >> word;
- if (word != "eglsrf") { return false; }
- iss >> word;
- if (word != "eglimg") { return false; }
- return true;
-}
-
-
-// Queries gpu memory via Qualcomm's /d/kgsl/proc/*/mem interface.
-status_t GpuService::getQCommGpuMemoryInfo(GpuMemoryMap* memories, std::string* result, int32_t dumpPid) const {
- const std::string kDirectoryPath = "/d/kgsl/proc";
- DIR* directory = opendir(kDirectoryPath.c_str());
- if (!directory) { return PERMISSION_DENIED; }
-
- // File Format:
- // gpuaddr useraddr size id flags type usage sglen mapsize eglsrf eglimg
- // 0000000000000000 0000000000000000 8359936 23 --w--pY-- gpumem VK/others( 38) 0 0 0 0
- // 0000000000000000 0000000000000000 16293888 24 --wL--N-- ion surface 41 0 0 1
-
- const bool dumpAll = dumpPid == 0;
- static constexpr size_t kMaxLineLength = 1024;
- static char line[kMaxLineLength];
- while(dirent* subdir = readdir(directory)) {
- // Skip "." and ".." in directory.
- if (strcmp(subdir->d_name, ".") == 0 || strcmp(subdir->d_name, "..") == 0 ) { continue; }
-
- std::string pid_str(subdir->d_name);
- const uint32_t pid(stoi(pid_str));
-
- if (!dumpAll && dumpPid != pid) {
- continue;
- }
-
- std::string filepath(kDirectoryPath + "/" + pid_str + "/mem");
- std::ifstream file(filepath);
-
- // Check first line
- file.getline(line, kMaxLineLength);
- if (!isExpectedFormat(line)) {
- continue;
- }
-
- if (result) {
- StringAppendF(result, "%d:\n%s\n", pid, line);
- }
-
- while( file.getline(line, kMaxLineLength) ) {
- if (result) {
- StringAppendF(result, "%s\n", line);
- }
-
- std::istringstream iss;
- iss.str(line);
-
- // Skip gpuaddr, useraddr.
- const char delimiter = ' ';
- iss >> std::ws;
- iss.ignore(kMaxLineLength, delimiter);
- iss >> std::ws;
- iss.ignore(kMaxLineLength, delimiter);
-
- // Get size.
- int64_t memsize;
- iss >> memsize;
-
- // Skip id, flags.
- iss >> std::ws;
- iss.ignore(kMaxLineLength, delimiter);
- iss >> std::ws;
- iss.ignore(kMaxLineLength, delimiter);
-
- // Get type, usage.
- std::string memtype;
- std::string usage;
- iss >> memtype >> usage;
-
- // Adjust for the space in VK/others( #)
- if (usage == "VK/others(") {
- std::string vkTypeEnd;
- iss >> vkTypeEnd;
- usage.append(vkTypeEnd);
- }
-
- // Skip sglen.
- iss >> std::ws;
- iss.ignore(kMaxLineLength, delimiter);
-
- // Get mapsize.
- int64_t mapsize;
- iss >> mapsize;
-
- if (memsize == 0 && mapsize == 0) {
- continue;
- }
-
- if (memtype == "gpumem") {
- (*memories)[pid][usage].gpuMemory += memsize;
- } else {
- (*memories)[pid][usage].ionMemory += memsize;
- }
-
- if (mapsize > 0) {
- (*memories)[pid][usage].mappedMemory += mapsize;
- }
- }
-
- if (result) {
- StringAppendF(result, "\n");
- }
- }
-
- closedir(directory);
-
- return OK;
-}
-
status_t GpuService::shellCommand(int /*in*/, int out, int err, std::vector<String16>& args) {
ATRACE_CALL();
@@ -248,9 +101,7 @@
bool dumpAll = true;
bool dumpDriverInfo = false;
bool dumpStats = false;
- bool dumpMemory = false;
size_t numArgs = args.size();
- int32_t pid = 0;
if (numArgs) {
dumpAll = false;
@@ -259,11 +110,6 @@
dumpStats = true;
} else if (args[index] == String16("--gpudriverinfo")) {
dumpDriverInfo = true;
- } else if (args[index] == String16("--gpumem")) {
- dumpMemory = true;
- } else if (args[index].startsWith(String16("--gpumem="))) {
- dumpMemory = true;
- pid = atoi(String8(&args[index][9]));
}
}
}
@@ -276,14 +122,6 @@
mGpuStats->dump(args, &result);
result.append("\n");
}
- if (dumpAll || dumpMemory) {
- GpuMemoryMap memories;
- // Currently only queries Qualcomm gpu memory. More will be added later.
- if (getQCommGpuMemoryInfo(&memories, &result, pid) == OK) {
- dumpMemoryInfo(&result, memories, pid);
- result.append("\n");
- }
- }
}
write(fd, result.c_str(), result.size());
@@ -335,34 +173,6 @@
StringAppendF(result, "Pre-release Game Driver: %s\n", preReleaseGameDriver);
}
-// Read and print all memory info for each process from /d/kgsl/proc/<pid>/mem.
-void dumpMemoryInfo(std::string* result, const GpuMemoryMap& memories, uint32_t pid) {
- if (!result) return;
-
- // Write results.
- StringAppendF(result, "GPU Memory Summary:\n");
- for(auto& mem : memories) {
- uint32_t process = mem.first;
- if (pid != 0 && pid != process) {
- continue;
- }
-
- StringAppendF(result, "%d:\n", process);
- for(auto& memStruct : mem.second) {
- StringAppendF(result, " %s", memStruct.first.c_str());
-
- if(memStruct.second.gpuMemory > 0)
- StringAppendF(result, ", GPU memory = %" PRId64, memStruct.second.gpuMemory);
- if(memStruct.second.mappedMemory > 0)
- StringAppendF(result, ", Mapped memory = %" PRId64, memStruct.second.mappedMemory);
- if(memStruct.second.ionMemory > 0)
- StringAppendF(result, ", Ion memory = %" PRId64, memStruct.second.ionMemory);
-
- StringAppendF(result, "\n");
- }
- }
-}
-
} // anonymous namespace
} // namespace android
diff --git a/services/inputflinger/Android.bp b/services/inputflinger/Android.bp
index f6b5935..308e93a 100644
--- a/services/inputflinger/Android.bp
+++ b/services/inputflinger/Android.bp
@@ -83,6 +83,7 @@
srcs: [
"InputListener.cpp",
"InputReaderBase.cpp",
+ "InputThread.cpp",
],
shared_libs: [
diff --git a/services/inputflinger/InputReaderBase.cpp b/services/inputflinger/InputReaderBase.cpp
index 2d6f2c1..b2dadf8 100644
--- a/services/inputflinger/InputReaderBase.cpp
+++ b/services/inputflinger/InputReaderBase.cpp
@@ -125,6 +125,16 @@
return std::nullopt;
}
+std::optional<DisplayViewport> InputReaderConfiguration::getDisplayViewportById(
+ int32_t displayId) const {
+ for (const DisplayViewport& currentViewport : mDisplays) {
+ if (currentViewport.displayId == displayId) {
+ return std::make_optional(currentViewport);
+ }
+ }
+ return std::nullopt;
+}
+
void InputReaderConfiguration::setDisplayViewports(const std::vector<DisplayViewport>& viewports) {
mDisplays = viewports;
}
@@ -151,4 +161,4 @@
y = newY;
}
-} // namespace android
\ No newline at end of file
+} // namespace android
diff --git a/services/inputflinger/InputThread.cpp b/services/inputflinger/InputThread.cpp
new file mode 100644
index 0000000..b87f7a1
--- /dev/null
+++ b/services/inputflinger/InputThread.cpp
@@ -0,0 +1,60 @@
+/*
+ * Copyright (C) 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.
+ */
+
+#include "InputThread.h"
+
+namespace android {
+
+namespace {
+
+// Implementation of Thread from libutils.
+class InputThreadImpl : public Thread {
+public:
+ explicit InputThreadImpl(std::function<void()> loop)
+ : Thread(/* canCallJava */ true), mThreadLoop(loop) {}
+
+ ~InputThreadImpl() {}
+
+private:
+ std::function<void()> mThreadLoop;
+
+ bool threadLoop() override {
+ mThreadLoop();
+ return true;
+ }
+};
+
+} // namespace
+
+InputThread::InputThread(std::string name, std::function<void()> loop, std::function<void()> wake)
+ : mName(name), mThreadWake(wake) {
+ mThread = new InputThreadImpl(loop);
+ mThread->run(mName.c_str(), ANDROID_PRIORITY_URGENT_DISPLAY);
+}
+
+InputThread::~InputThread() {
+ mThread->requestExit();
+ if (mThreadWake) {
+ mThreadWake();
+ }
+ mThread->requestExitAndWait();
+}
+
+bool InputThread::isCallingThread() {
+ return gettid() == mThread->getTid();
+}
+
+} // namespace android
\ No newline at end of file
diff --git a/services/inputflinger/benchmarks/InputDispatcher_benchmarks.cpp b/services/inputflinger/benchmarks/InputDispatcher_benchmarks.cpp
index 246e735..9a6ef21 100644
--- a/services/inputflinger/benchmarks/InputDispatcher_benchmarks.cpp
+++ b/services/inputflinger/benchmarks/InputDispatcher_benchmarks.cpp
@@ -203,9 +203,10 @@
const nsecs_t currentTime = now();
MotionEvent event;
- event.initialize(DEVICE_ID, AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT,
+ event.initialize(DEVICE_ID, AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT, INVALID_HMAC,
AMOTION_EVENT_ACTION_DOWN, /* actionButton */ 0, /* flags */ 0,
/* edgeFlags */ 0, AMETA_NONE, /* buttonState */ 0, MotionClassification::NONE,
+ 1 /* xScale */, 1 /* yScale */,
/* xOffset */ 0, /* yOffset */ 0, /* xPrecision */ 0,
/* yPrecision */ 0, AMOTION_EVENT_INVALID_CURSOR_POSITION,
AMOTION_EVENT_INVALID_CURSOR_POSITION, currentTime, currentTime,
diff --git a/services/inputflinger/dispatcher/InputDispatcher.cpp b/services/inputflinger/dispatcher/InputDispatcher.cpp
index 30fdf90..b2b5145 100644
--- a/services/inputflinger/dispatcher/InputDispatcher.cpp
+++ b/services/inputflinger/dispatcher/InputDispatcher.cpp
@@ -325,24 +325,6 @@
return dispatchEntry;
}
-// --- InputDispatcherThread ---
-
-class InputDispatcher::InputDispatcherThread : public Thread {
-public:
- explicit InputDispatcherThread(InputDispatcher* dispatcher)
- : Thread(/* canCallJava */ true), mDispatcher(dispatcher) {}
-
- ~InputDispatcherThread() {}
-
-private:
- InputDispatcher* mDispatcher;
-
- virtual bool threadLoop() override {
- mDispatcher->dispatchOnce();
- return true;
- }
-};
-
// --- InputDispatcher ---
InputDispatcher::InputDispatcher(const sp<InputDispatcherPolicyInterface>& policy)
@@ -367,8 +349,6 @@
mKeyRepeatState.lastKeyEntry = nullptr;
policy->getDispatcherConfiguration(&mConfig);
-
- mThread = new InputDispatcherThread(this);
}
InputDispatcher::~InputDispatcher() {
@@ -387,25 +367,21 @@
}
status_t InputDispatcher::start() {
- if (mThread->isRunning()) {
+ if (mThread) {
return ALREADY_EXISTS;
}
- return mThread->run("InputDispatcher", PRIORITY_URGENT_DISPLAY);
+ mThread = std::make_unique<InputThread>(
+ "InputDispatcher", [this]() { dispatchOnce(); }, [this]() { mLooper->wake(); });
+ return OK;
}
status_t InputDispatcher::stop() {
- if (!mThread->isRunning()) {
- return OK;
- }
- if (gettid() == mThread->getTid()) {
- ALOGE("InputDispatcher can only be stopped from outside of the InputDispatcherThread!");
+ if (mThread && mThread->isCallingThread()) {
+ ALOGE("InputDispatcher cannot be stopped from its own thread!");
return INVALID_OPERATION;
}
- // Directly calling requestExitAndWait() causes the thread to not exit
- // if mLooper is waiting for a long timeout.
- mThread->requestExit();
- mLooper->wake();
- return mThread->requestExitAndWait();
+ mThread.reset();
+ return OK;
}
void InputDispatcher::dispatchOnce() {
@@ -2410,7 +2386,7 @@
status = connection->inputPublisher
.publishKeyEvent(dispatchEntry->seq, keyEntry->deviceId,
keyEntry->source, keyEntry->displayId,
- dispatchEntry->resolvedAction,
+ INVALID_HMAC, dispatchEntry->resolvedAction,
dispatchEntry->resolvedFlags, keyEntry->keyCode,
keyEntry->scanCode, keyEntry->metaState,
keyEntry->repeatCount, keyEntry->downTime,
@@ -2424,26 +2400,28 @@
PointerCoords scaledCoords[MAX_POINTERS];
const PointerCoords* usingCoords = motionEntry->pointerCoords;
- // Set the X and Y offset depending on the input source.
- float xOffset, yOffset;
+ // Set the X and Y offset and X and Y scale depending on the input source.
+ float xOffset = 0.0f, yOffset = 0.0f;
+ float xScale = 1.0f, yScale = 1.0f;
if ((motionEntry->source & AINPUT_SOURCE_CLASS_POINTER) &&
!(dispatchEntry->targetFlags & InputTarget::FLAG_ZERO_COORDS)) {
float globalScaleFactor = dispatchEntry->globalScaleFactor;
- float wxs = dispatchEntry->windowXScale;
- float wys = dispatchEntry->windowYScale;
- xOffset = dispatchEntry->xOffset * wxs;
- yOffset = dispatchEntry->yOffset * wys;
- if (wxs != 1.0f || wys != 1.0f || globalScaleFactor != 1.0f) {
+ xScale = dispatchEntry->windowXScale;
+ yScale = dispatchEntry->windowYScale;
+ xOffset = dispatchEntry->xOffset * xScale;
+ yOffset = dispatchEntry->yOffset * yScale;
+ if (globalScaleFactor != 1.0f) {
for (uint32_t i = 0; i < motionEntry->pointerCount; i++) {
scaledCoords[i] = motionEntry->pointerCoords[i];
- scaledCoords[i].scale(globalScaleFactor, wxs, wys);
+ // Don't apply window scale here since we don't want scale to affect raw
+ // coordinates. The scale will be sent back to the client and applied
+ // later when requesting relative coordinates.
+ scaledCoords[i].scale(globalScaleFactor, 1 /* windowXScale */,
+ 1 /* windowYScale */);
}
usingCoords = scaledCoords;
}
} else {
- xOffset = 0.0f;
- yOffset = 0.0f;
-
// We don't want the dispatch target to know.
if (dispatchEntry->targetFlags & InputTarget::FLAG_ZERO_COORDS) {
for (uint32_t i = 0; i < motionEntry->pointerCount; i++) {
@@ -2457,13 +2435,13 @@
status = connection->inputPublisher
.publishMotionEvent(dispatchEntry->seq, motionEntry->deviceId,
motionEntry->source, motionEntry->displayId,
- dispatchEntry->resolvedAction,
+ INVALID_HMAC, dispatchEntry->resolvedAction,
motionEntry->actionButton,
dispatchEntry->resolvedFlags,
motionEntry->edgeFlags, motionEntry->metaState,
motionEntry->buttonState,
- motionEntry->classification, xOffset, yOffset,
- motionEntry->xPrecision,
+ motionEntry->classification, xScale, yScale,
+ xOffset, yOffset, motionEntry->xPrecision,
motionEntry->yPrecision,
motionEntry->xCursorPosition,
motionEntry->yCursorPosition,
@@ -2699,59 +2677,59 @@
std::vector<EventEntry*> cancelationEvents =
connection->inputState.synthesizeCancelationEvents(currentTime, options);
- if (!cancelationEvents.empty()) {
+ if (cancelationEvents.empty()) {
+ return;
+ }
#if DEBUG_OUTBOUND_EVENT_DETAILS
- ALOGD("channel '%s' ~ Synthesized %zu cancelation events to bring channel back in sync "
- "with reality: %s, mode=%d.",
- connection->getInputChannelName().c_str(), cancelationEvents.size(), options.reason,
- options.mode);
+ ALOGD("channel '%s' ~ Synthesized %zu cancelation events to bring channel back in sync "
+ "with reality: %s, mode=%d.",
+ connection->getInputChannelName().c_str(), cancelationEvents.size(), options.reason,
+ options.mode);
#endif
- for (size_t i = 0; i < cancelationEvents.size(); i++) {
- EventEntry* cancelationEventEntry = cancelationEvents[i];
- switch (cancelationEventEntry->type) {
- case EventEntry::Type::KEY: {
- logOutboundKeyDetails("cancel - ",
- static_cast<const KeyEntry&>(*cancelationEventEntry));
- break;
- }
- case EventEntry::Type::MOTION: {
- logOutboundMotionDetails("cancel - ",
- static_cast<const MotionEntry&>(
- *cancelationEventEntry));
- break;
- }
- case EventEntry::Type::FOCUS: {
- LOG_ALWAYS_FATAL("Canceling focus events is not supported");
- break;
- }
- case EventEntry::Type::CONFIGURATION_CHANGED:
- case EventEntry::Type::DEVICE_RESET: {
- LOG_ALWAYS_FATAL("%s event should not be found inside Connections's queue",
- EventEntry::typeToString(cancelationEventEntry->type));
- break;
- }
+ for (size_t i = 0; i < cancelationEvents.size(); i++) {
+ EventEntry* cancelationEventEntry = cancelationEvents[i];
+ switch (cancelationEventEntry->type) {
+ case EventEntry::Type::KEY: {
+ logOutboundKeyDetails("cancel - ",
+ static_cast<const KeyEntry&>(*cancelationEventEntry));
+ break;
}
-
- InputTarget target;
- sp<InputWindowHandle> windowHandle =
- getWindowHandleLocked(connection->inputChannel->getConnectionToken());
- if (windowHandle != nullptr) {
- const InputWindowInfo* windowInfo = windowHandle->getInfo();
- target.setDefaultPointerInfo(-windowInfo->frameLeft, -windowInfo->frameTop,
- windowInfo->windowXScale, windowInfo->windowYScale);
- target.globalScaleFactor = windowInfo->globalScaleFactor;
+ case EventEntry::Type::MOTION: {
+ logOutboundMotionDetails("cancel - ",
+ static_cast<const MotionEntry&>(*cancelationEventEntry));
+ break;
}
- target.inputChannel = connection->inputChannel;
- target.flags = InputTarget::FLAG_DISPATCH_AS_IS;
-
- enqueueDispatchEntryLocked(connection, cancelationEventEntry, // increments ref
- target, InputTarget::FLAG_DISPATCH_AS_IS);
-
- cancelationEventEntry->release();
+ case EventEntry::Type::FOCUS: {
+ LOG_ALWAYS_FATAL("Canceling focus events is not supported");
+ break;
+ }
+ case EventEntry::Type::CONFIGURATION_CHANGED:
+ case EventEntry::Type::DEVICE_RESET: {
+ LOG_ALWAYS_FATAL("%s event should not be found inside Connections's queue",
+ EventEntry::typeToString(cancelationEventEntry->type));
+ break;
+ }
}
- startDispatchCycleLocked(currentTime, connection);
+ InputTarget target;
+ sp<InputWindowHandle> windowHandle =
+ getWindowHandleLocked(connection->inputChannel->getConnectionToken());
+ if (windowHandle != nullptr) {
+ const InputWindowInfo* windowInfo = windowHandle->getInfo();
+ target.setDefaultPointerInfo(-windowInfo->frameLeft, -windowInfo->frameTop,
+ windowInfo->windowXScale, windowInfo->windowYScale);
+ target.globalScaleFactor = windowInfo->globalScaleFactor;
+ }
+ target.inputChannel = connection->inputChannel;
+ target.flags = InputTarget::FLAG_DISPATCH_AS_IS;
+
+ enqueueDispatchEntryLocked(connection, cancelationEventEntry, // increments ref
+ target, InputTarget::FLAG_DISPATCH_AS_IS);
+
+ cancelationEventEntry->release();
}
+
+ startDispatchCycleLocked(currentTime, connection);
}
MotionEntry* InputDispatcher::splitMotionEvent(const MotionEntry& originalMotionEntry,
@@ -2930,8 +2908,9 @@
accelerateMetaShortcuts(args->deviceId, args->action, keyCode, metaState);
KeyEvent event;
- event.initialize(args->deviceId, args->source, args->displayId, args->action, flags, keyCode,
- args->scanCode, metaState, repeatCount, args->downTime, args->eventTime);
+ event.initialize(args->deviceId, args->source, args->displayId, INVALID_HMAC, args->action,
+ flags, keyCode, args->scanCode, metaState, repeatCount, args->downTime,
+ args->eventTime);
android::base::Timer t;
mPolicy->interceptKeyBeforeQueueing(&event, /*byref*/ policyFlags);
@@ -3024,9 +3003,10 @@
mLock.unlock();
MotionEvent event;
- event.initialize(args->deviceId, args->source, args->displayId, args->action,
- args->actionButton, args->flags, args->edgeFlags, args->metaState,
- args->buttonState, args->classification, 0, 0, args->xPrecision,
+ event.initialize(args->deviceId, args->source, args->displayId, INVALID_HMAC,
+ args->action, args->actionButton, args->flags, args->edgeFlags,
+ args->metaState, args->buttonState, args->classification, 1 /*xScale*/,
+ 1 /*yScale*/, 0 /* xOffset */, 0 /* yOffset */, args->xPrecision,
args->yPrecision, args->xCursorPosition, args->yCursorPosition,
args->downTime, args->eventTime, args->pointerCount,
args->pointerProperties, args->pointerCoords);
@@ -3126,7 +3106,7 @@
accelerateMetaShortcuts(keyEvent.getDeviceId(), action,
/*byref*/ keyCode, /*byref*/ metaState);
keyEvent.initialize(keyEvent.getDeviceId(), keyEvent.getSource(),
- keyEvent.getDisplayId(), action, flags, keyCode,
+ keyEvent.getDisplayId(), INVALID_HMAC, action, flags, keyCode,
keyEvent.getScanCode(), metaState, keyEvent.getRepeatCount(),
keyEvent.getDownTime(), keyEvent.getEventTime());
@@ -4682,8 +4662,8 @@
KeyEvent InputDispatcher::createKeyEvent(const KeyEntry& entry) {
KeyEvent event;
- event.initialize(entry.deviceId, entry.source, entry.displayId, entry.action, entry.flags,
- entry.keyCode, entry.scanCode, entry.metaState, entry.repeatCount,
+ event.initialize(entry.deviceId, entry.source, entry.displayId, INVALID_HMAC, entry.action,
+ entry.flags, entry.keyCode, entry.scanCode, entry.metaState, entry.repeatCount,
entry.downTime, entry.eventTime);
return event;
}
diff --git a/services/inputflinger/dispatcher/InputDispatcher.h b/services/inputflinger/dispatcher/InputDispatcher.h
index a4ba0de..93de18d 100644
--- a/services/inputflinger/dispatcher/InputDispatcher.h
+++ b/services/inputflinger/dispatcher/InputDispatcher.h
@@ -25,6 +25,7 @@
#include "InputDispatcherPolicyInterface.h"
#include "InputState.h"
#include "InputTarget.h"
+#include "InputThread.h"
#include "Monitor.h"
#include "TouchState.h"
#include "TouchedWindow.h"
@@ -124,8 +125,7 @@
STALE,
};
- class InputDispatcherThread;
- sp<InputDispatcherThread> mThread;
+ std::unique_ptr<InputThread> mThread;
sp<InputDispatcherPolicyInterface> mPolicy;
android::InputDispatcherConfiguration mConfig;
diff --git a/services/inputflinger/include/InputReaderBase.h b/services/inputflinger/include/InputReaderBase.h
index 56c0a73..f8d5351 100644
--- a/services/inputflinger/include/InputReaderBase.h
+++ b/services/inputflinger/include/InputReaderBase.h
@@ -169,6 +169,9 @@
// Used to determine which DisplayViewport should be tied to which InputDevice.
std::unordered_map<std::string, uint8_t> portAssociations;
+ // The suggested display ID to show the cursor.
+ int32_t defaultPointerDisplayId;
+
// Velocity control parameters for mouse pointer movements.
VelocityControlParameters pointerVelocityControlParameters;
@@ -273,6 +276,7 @@
std::optional<DisplayViewport> getDisplayViewportByUniqueId(const std::string& uniqueDisplayId)
const;
std::optional<DisplayViewport> getDisplayViewportByPort(uint8_t physicalPort) const;
+ std::optional<DisplayViewport> getDisplayViewportById(int32_t displayId) const;
void setDisplayViewports(const std::vector<DisplayViewport>& viewports);
@@ -352,4 +356,4 @@
} // namespace android
-#endif // _UI_INPUT_READER_COMMON_H
\ No newline at end of file
+#endif // _UI_INPUT_READER_COMMON_H
diff --git a/services/inputflinger/include/InputThread.h b/services/inputflinger/include/InputThread.h
new file mode 100644
index 0000000..407365a
--- /dev/null
+++ b/services/inputflinger/include/InputThread.h
@@ -0,0 +1,46 @@
+/*
+ * Copyright (C) 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.
+ */
+
+#ifndef _UI_INPUT_THREAD_H
+#define _UI_INPUT_THREAD_H
+
+#include <utils/Thread.h>
+
+namespace android {
+
+/* A thread that loops continuously until destructed to process input events.
+ *
+ * Creating the InputThread starts it immediately. The thread begins looping the loop
+ * function until the InputThread is destroyed. The wake function is used to wake anything
+ * that sleeps in the loop when it is time for the thread to be destroyed.
+ */
+class InputThread {
+public:
+ explicit InputThread(std::string name, std::function<void()> loop,
+ std::function<void()> wake = nullptr);
+ virtual ~InputThread();
+
+ bool isCallingThread();
+
+private:
+ std::string mName;
+ std::function<void()> mThreadWake;
+ sp<Thread> mThread;
+};
+
+} // namespace android
+
+#endif // _UI_INPUT_THREAD_H
\ No newline at end of file
diff --git a/services/inputflinger/include/PointerControllerInterface.h b/services/inputflinger/include/PointerControllerInterface.h
index 0ff28e4..194c665 100644
--- a/services/inputflinger/include/PointerControllerInterface.h
+++ b/services/inputflinger/include/PointerControllerInterface.h
@@ -17,6 +17,7 @@
#ifndef _INPUTFLINGER_POINTER_CONTROLLER_INTERFACE_H
#define _INPUTFLINGER_POINTER_CONTROLLER_INTERFACE_H
+#include <input/DisplayViewport.h>
#include <input/Input.h>
#include <utils/BitSet.h>
#include <utils/RefBase.h>
@@ -101,6 +102,9 @@
/* Gets the id of the display where the pointer should be shown. */
virtual int32_t getDisplayId() const = 0;
+
+ /* Sets the associated display of this pointer. Pointer should show on that display. */
+ virtual void setDisplayViewport(const DisplayViewport& displayViewport) = 0;
};
} // namespace android
diff --git a/services/inputflinger/reader/InputReader.cpp b/services/inputflinger/reader/InputReader.cpp
index 05f0db1..2023c6e 100644
--- a/services/inputflinger/reader/InputReader.cpp
+++ b/services/inputflinger/reader/InputReader.cpp
@@ -49,25 +49,6 @@
namespace android {
-// --- InputReader::InputReaderThread ---
-
-/* Thread that reads raw events from the event hub and processes them, endlessly. */
-class InputReader::InputReaderThread : public Thread {
-public:
- explicit InputReaderThread(InputReader* reader)
- : Thread(/* canCallJava */ true), mReader(reader) {}
-
- ~InputReaderThread() {}
-
-private:
- InputReader* mReader;
-
- bool threadLoop() override {
- mReader->loopOnce();
- return true;
- }
-};
-
// --- InputReader ---
InputReader::InputReader(std::shared_ptr<EventHubInterface> eventHub,
@@ -83,7 +64,6 @@
mNextTimeout(LLONG_MAX),
mConfigurationChangesToRefresh(0) {
mQueuedListener = new QueuedInputListener(listener);
- mThread = new InputReaderThread(this);
{ // acquire lock
AutoMutex _l(mLock);
@@ -100,25 +80,21 @@
}
status_t InputReader::start() {
- if (mThread->isRunning()) {
+ if (mThread) {
return ALREADY_EXISTS;
}
- return mThread->run("InputReader", PRIORITY_URGENT_DISPLAY);
+ mThread = std::make_unique<InputThread>(
+ "InputReader", [this]() { loopOnce(); }, [this]() { mEventHub->wake(); });
+ return OK;
}
status_t InputReader::stop() {
- if (!mThread->isRunning()) {
- return OK;
- }
- if (gettid() == mThread->getTid()) {
- ALOGE("InputReader can only be stopped from outside of the InputReaderThread!");
+ if (mThread && mThread->isCallingThread()) {
+ ALOGE("InputReader cannot be stopped from its own thread!");
return INVALID_OPERATION;
}
- // Directly calling requestExitAndWait() causes the thread to not exit
- // if mEventHub is waiting for a long timeout.
- mThread->requestExit();
- mEventHub->wake();
- return mThread->requestExitAndWait();
+ mThread.reset();
+ return OK;
}
void InputReader::loopOnce() {
diff --git a/services/inputflinger/reader/include/InputReader.h b/services/inputflinger/reader/include/InputReader.h
index 5024906..02957cd 100644
--- a/services/inputflinger/reader/include/InputReader.h
+++ b/services/inputflinger/reader/include/InputReader.h
@@ -21,6 +21,7 @@
#include "InputListener.h"
#include "InputReaderBase.h"
#include "InputReaderContext.h"
+#include "InputThread.h"
#include <utils/Condition.h>
#include <utils/Mutex.h>
@@ -116,8 +117,7 @@
friend class ContextImpl;
private:
- class InputReaderThread;
- sp<InputReaderThread> mThread;
+ std::unique_ptr<InputThread> mThread;
Mutex mLock;
diff --git a/services/inputflinger/reader/mapper/CursorInputMapper.cpp b/services/inputflinger/reader/mapper/CursorInputMapper.cpp
index f69138e..69a75ba 100644
--- a/services/inputflinger/reader/mapper/CursorInputMapper.cpp
+++ b/services/inputflinger/reader/mapper/CursorInputMapper.cpp
@@ -189,12 +189,32 @@
// Update the PointerController if viewports changed.
if (mParameters.mode == Parameters::MODE_POINTER) {
- getPolicy()->obtainPointerController(getDeviceId());
+ updatePointerControllerDisplayViewport(*config);
}
bumpGeneration();
}
}
+void CursorInputMapper::updatePointerControllerDisplayViewport(
+ const InputReaderConfiguration& config) {
+ std::optional<DisplayViewport> viewport =
+ config.getDisplayViewportById(config.defaultPointerDisplayId);
+ if (!viewport) {
+ ALOGW("Can't find the designated viewport with ID %" PRId32 " to update cursor input "
+ "mapper. Fall back to default display",
+ config.defaultPointerDisplayId);
+ viewport = config.getDisplayViewportById(ADISPLAY_ID_DEFAULT);
+ }
+
+ if (!viewport) {
+ ALOGE("Still can't find a viable viewport to update cursor input mapper. Skip setting it to"
+ " PointerController.");
+ return;
+ }
+
+ mPointerController->setDisplayViewport(*viewport);
+}
+
void CursorInputMapper::configureParameters() {
mParameters.mode = Parameters::MODE_POINTER;
String8 cursorModeString;
diff --git a/services/inputflinger/reader/mapper/CursorInputMapper.h b/services/inputflinger/reader/mapper/CursorInputMapper.h
index 77d122a..d56f9be 100644
--- a/services/inputflinger/reader/mapper/CursorInputMapper.h
+++ b/services/inputflinger/reader/mapper/CursorInputMapper.h
@@ -117,8 +117,9 @@
void dumpParameters(std::string& dump);
void sync(nsecs_t when);
+ void updatePointerControllerDisplayViewport(const InputReaderConfiguration& config);
};
} // namespace android
-#endif // _UI_INPUTREADER_CURSOR_INPUT_MAPPER_H
\ No newline at end of file
+#endif // _UI_INPUTREADER_CURSOR_INPUT_MAPPER_H
diff --git a/services/inputflinger/reader/mapper/TouchInputMapper.cpp b/services/inputflinger/reader/mapper/TouchInputMapper.cpp
index c80a2dc..b66caca 100644
--- a/services/inputflinger/reader/mapper/TouchInputMapper.cpp
+++ b/services/inputflinger/reader/mapper/TouchInputMapper.cpp
@@ -552,9 +552,10 @@
* Determine which DisplayViewport to use.
* 1. If display port is specified, return the matching viewport. If matching viewport not
* found, then return.
- * 2. If a device has associated display, get the matching viewport by either unique id or by
+ * 2. Always use the suggested viewport from WindowManagerService for pointers.
+ * 3. If a device has associated display, get the matching viewport by either unique id or by
* the display type (internal or external).
- * 3. Otherwise, use a non-display viewport.
+ * 4. Otherwise, use a non-display viewport.
*/
std::optional<DisplayViewport> TouchInputMapper::findViewport() {
if (mParameters.hasAssociatedDisplay) {
@@ -564,6 +565,17 @@
return mDevice->getAssociatedViewport();
}
+ if (mDeviceMode == DEVICE_MODE_POINTER) {
+ std::optional<DisplayViewport> viewport =
+ mConfig.getDisplayViewportById(mConfig.defaultPointerDisplayId);
+ if (viewport) {
+ return viewport;
+ } else {
+ ALOGW("Can't find designated display viewport with ID %" PRId32 " for pointers.",
+ mConfig.defaultPointerDisplayId);
+ }
+ }
+
// Check if uniqueDisplayId is specified in idc file.
if (!mParameters.uniqueDisplayId.empty()) {
return mConfig.getDisplayViewportByUniqueId(mParameters.uniqueDisplayId);
@@ -749,6 +761,7 @@
(mDeviceMode == DEVICE_MODE_DIRECT && mConfig.showTouches)) {
if (mPointerController == nullptr || viewportChanged) {
mPointerController = getPolicy()->obtainPointerController(getDeviceId());
+ mPointerController->setDisplayViewport(mViewport);
}
} else {
mPointerController.clear();
diff --git a/services/inputflinger/tests/InputDispatcher_test.cpp b/services/inputflinger/tests/InputDispatcher_test.cpp
index 98ebf50..094452a 100644
--- a/services/inputflinger/tests/InputDispatcher_test.cpp
+++ b/services/inputflinger/tests/InputDispatcher_test.cpp
@@ -225,18 +225,18 @@
KeyEvent event;
// Rejects undefined key actions.
- event.initialize(DEVICE_ID, AINPUT_SOURCE_KEYBOARD, ADISPLAY_ID_NONE,
- /*action*/ -1, 0,
- AKEYCODE_A, KEY_A, AMETA_NONE, 0, ARBITRARY_TIME, ARBITRARY_TIME);
+ event.initialize(DEVICE_ID, AINPUT_SOURCE_KEYBOARD, ADISPLAY_ID_NONE, INVALID_HMAC,
+ /*action*/ -1, 0, AKEYCODE_A, KEY_A, AMETA_NONE, 0, ARBITRARY_TIME,
+ ARBITRARY_TIME);
ASSERT_EQ(INPUT_EVENT_INJECTION_FAILED, mDispatcher->injectInputEvent(
&event,
INJECTOR_PID, INJECTOR_UID, INPUT_EVENT_INJECTION_SYNC_NONE, 0, 0))
<< "Should reject key events with undefined action.";
// Rejects ACTION_MULTIPLE since it is not supported despite being defined in the API.
- event.initialize(DEVICE_ID, AINPUT_SOURCE_KEYBOARD, ADISPLAY_ID_NONE,
- AKEY_EVENT_ACTION_MULTIPLE, 0,
- AKEYCODE_A, KEY_A, AMETA_NONE, 0, ARBITRARY_TIME, ARBITRARY_TIME);
+ event.initialize(DEVICE_ID, AINPUT_SOURCE_KEYBOARD, ADISPLAY_ID_NONE, INVALID_HMAC,
+ AKEY_EVENT_ACTION_MULTIPLE, 0, AKEYCODE_A, KEY_A, AMETA_NONE, 0,
+ ARBITRARY_TIME, ARBITRARY_TIME);
ASSERT_EQ(INPUT_EVENT_INJECTION_FAILED, mDispatcher->injectInputEvent(
&event,
INJECTOR_PID, INJECTOR_UID, INPUT_EVENT_INJECTION_SYNC_NONE, 0, 0))
@@ -260,10 +260,10 @@
constexpr MotionClassification classification = MotionClassification::NONE;
// Rejects undefined motion actions.
- event.initialize(DEVICE_ID, source, DISPLAY_ID,
- /*action*/ -1, 0, 0, edgeFlags, metaState, 0, classification, 0, 0, 0, 0,
- AMOTION_EVENT_INVALID_CURSOR_POSITION, AMOTION_EVENT_INVALID_CURSOR_POSITION,
- ARBITRARY_TIME, ARBITRARY_TIME,
+ event.initialize(DEVICE_ID, source, DISPLAY_ID, INVALID_HMAC,
+ /*action*/ -1, 0, 0, edgeFlags, metaState, 0, classification, 1 /* xScale */,
+ 1 /* yScale */, 0, 0, 0, 0, AMOTION_EVENT_INVALID_CURSOR_POSITION,
+ AMOTION_EVENT_INVALID_CURSOR_POSITION, ARBITRARY_TIME, ARBITRARY_TIME,
/*pointerCount*/ 1, pointerProperties, pointerCoords);
ASSERT_EQ(INPUT_EVENT_INJECTION_FAILED, mDispatcher->injectInputEvent(
&event,
@@ -271,24 +271,24 @@
<< "Should reject motion events with undefined action.";
// Rejects pointer down with invalid index.
- event.initialize(DEVICE_ID, source, DISPLAY_ID,
+ event.initialize(DEVICE_ID, source, DISPLAY_ID, INVALID_HMAC,
AMOTION_EVENT_ACTION_POINTER_DOWN |
(1 << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT),
- 0, 0, edgeFlags, metaState, 0, classification, 0, 0, 0, 0,
- AMOTION_EVENT_INVALID_CURSOR_POSITION, AMOTION_EVENT_INVALID_CURSOR_POSITION,
- ARBITRARY_TIME, ARBITRARY_TIME,
+ 0, 0, edgeFlags, metaState, 0, classification, 1 /* xScale */, 1 /* yScale */,
+ 0, 0, 0, 0, AMOTION_EVENT_INVALID_CURSOR_POSITION,
+ AMOTION_EVENT_INVALID_CURSOR_POSITION, ARBITRARY_TIME, ARBITRARY_TIME,
/*pointerCount*/ 1, pointerProperties, pointerCoords);
ASSERT_EQ(INPUT_EVENT_INJECTION_FAILED, mDispatcher->injectInputEvent(
&event,
INJECTOR_PID, INJECTOR_UID, INPUT_EVENT_INJECTION_SYNC_NONE, 0, 0))
<< "Should reject motion events with pointer down index too large.";
- event.initialize(DEVICE_ID, source, DISPLAY_ID,
+ event.initialize(DEVICE_ID, source, DISPLAY_ID, INVALID_HMAC,
AMOTION_EVENT_ACTION_POINTER_DOWN |
(~0U << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT),
- 0, 0, edgeFlags, metaState, 0, classification, 0, 0, 0, 0,
- AMOTION_EVENT_INVALID_CURSOR_POSITION, AMOTION_EVENT_INVALID_CURSOR_POSITION,
- ARBITRARY_TIME, ARBITRARY_TIME,
+ 0, 0, edgeFlags, metaState, 0, classification, 1 /* xScale */, 1 /* yScale */,
+ 0, 0, 0, 0, AMOTION_EVENT_INVALID_CURSOR_POSITION,
+ AMOTION_EVENT_INVALID_CURSOR_POSITION, ARBITRARY_TIME, ARBITRARY_TIME,
/*pointerCount*/ 1, pointerProperties, pointerCoords);
ASSERT_EQ(INPUT_EVENT_INJECTION_FAILED, mDispatcher->injectInputEvent(
&event,
@@ -296,24 +296,24 @@
<< "Should reject motion events with pointer down index too small.";
// Rejects pointer up with invalid index.
- event.initialize(DEVICE_ID, source, DISPLAY_ID,
+ event.initialize(DEVICE_ID, source, DISPLAY_ID, INVALID_HMAC,
AMOTION_EVENT_ACTION_POINTER_UP |
(1 << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT),
- 0, 0, edgeFlags, metaState, 0, classification, 0, 0, 0, 0,
- AMOTION_EVENT_INVALID_CURSOR_POSITION, AMOTION_EVENT_INVALID_CURSOR_POSITION,
- ARBITRARY_TIME, ARBITRARY_TIME,
+ 0, 0, edgeFlags, metaState, 0, classification, 1 /* xScale */, 1 /* yScale */,
+ 0, 0, 0, 0, AMOTION_EVENT_INVALID_CURSOR_POSITION,
+ AMOTION_EVENT_INVALID_CURSOR_POSITION, ARBITRARY_TIME, ARBITRARY_TIME,
/*pointerCount*/ 1, pointerProperties, pointerCoords);
ASSERT_EQ(INPUT_EVENT_INJECTION_FAILED, mDispatcher->injectInputEvent(
&event,
INJECTOR_PID, INJECTOR_UID, INPUT_EVENT_INJECTION_SYNC_NONE, 0, 0))
<< "Should reject motion events with pointer up index too large.";
- event.initialize(DEVICE_ID, source, DISPLAY_ID,
+ event.initialize(DEVICE_ID, source, DISPLAY_ID, INVALID_HMAC,
AMOTION_EVENT_ACTION_POINTER_UP |
(~0U << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT),
- 0, 0, edgeFlags, metaState, 0, classification, 0, 0, 0, 0,
- AMOTION_EVENT_INVALID_CURSOR_POSITION, AMOTION_EVENT_INVALID_CURSOR_POSITION,
- ARBITRARY_TIME, ARBITRARY_TIME,
+ 0, 0, edgeFlags, metaState, 0, classification, 1 /* xScale */, 1 /* yScale */,
+ 0, 0, 0, 0, AMOTION_EVENT_INVALID_CURSOR_POSITION,
+ AMOTION_EVENT_INVALID_CURSOR_POSITION, ARBITRARY_TIME, ARBITRARY_TIME,
/*pointerCount*/ 1, pointerProperties, pointerCoords);
ASSERT_EQ(INPUT_EVENT_INJECTION_FAILED, mDispatcher->injectInputEvent(
&event,
@@ -321,20 +321,20 @@
<< "Should reject motion events with pointer up index too small.";
// Rejects motion events with invalid number of pointers.
- event.initialize(DEVICE_ID, source, DISPLAY_ID, AMOTION_EVENT_ACTION_DOWN, 0, 0, edgeFlags,
- metaState, 0, classification, 0, 0, 0, 0,
- AMOTION_EVENT_INVALID_CURSOR_POSITION, AMOTION_EVENT_INVALID_CURSOR_POSITION,
- ARBITRARY_TIME, ARBITRARY_TIME,
+ event.initialize(DEVICE_ID, source, DISPLAY_ID, INVALID_HMAC, AMOTION_EVENT_ACTION_DOWN, 0, 0,
+ edgeFlags, metaState, 0, classification, 1 /* xScale */, 1 /* yScale */, 0, 0,
+ 0, 0, AMOTION_EVENT_INVALID_CURSOR_POSITION,
+ AMOTION_EVENT_INVALID_CURSOR_POSITION, ARBITRARY_TIME, ARBITRARY_TIME,
/*pointerCount*/ 0, pointerProperties, pointerCoords);
ASSERT_EQ(INPUT_EVENT_INJECTION_FAILED, mDispatcher->injectInputEvent(
&event,
INJECTOR_PID, INJECTOR_UID, INPUT_EVENT_INJECTION_SYNC_NONE, 0, 0))
<< "Should reject motion events with 0 pointers.";
- event.initialize(DEVICE_ID, source, DISPLAY_ID, AMOTION_EVENT_ACTION_DOWN, 0, 0, edgeFlags,
- metaState, 0, classification, 0, 0, 0, 0,
- AMOTION_EVENT_INVALID_CURSOR_POSITION, AMOTION_EVENT_INVALID_CURSOR_POSITION,
- ARBITRARY_TIME, ARBITRARY_TIME,
+ event.initialize(DEVICE_ID, source, DISPLAY_ID, INVALID_HMAC, AMOTION_EVENT_ACTION_DOWN, 0, 0,
+ edgeFlags, metaState, 0, classification, 1 /* xScale */, 1 /* yScale */, 0, 0,
+ 0, 0, AMOTION_EVENT_INVALID_CURSOR_POSITION,
+ AMOTION_EVENT_INVALID_CURSOR_POSITION, ARBITRARY_TIME, ARBITRARY_TIME,
/*pointerCount*/ MAX_POINTERS + 1, pointerProperties, pointerCoords);
ASSERT_EQ(INPUT_EVENT_INJECTION_FAILED, mDispatcher->injectInputEvent(
&event,
@@ -343,10 +343,10 @@
// Rejects motion events with invalid pointer ids.
pointerProperties[0].id = -1;
- event.initialize(DEVICE_ID, source, DISPLAY_ID, AMOTION_EVENT_ACTION_DOWN, 0, 0, edgeFlags,
- metaState, 0, classification, 0, 0, 0, 0,
- AMOTION_EVENT_INVALID_CURSOR_POSITION, AMOTION_EVENT_INVALID_CURSOR_POSITION,
- ARBITRARY_TIME, ARBITRARY_TIME,
+ event.initialize(DEVICE_ID, source, DISPLAY_ID, INVALID_HMAC, AMOTION_EVENT_ACTION_DOWN, 0, 0,
+ edgeFlags, metaState, 0, classification, 1 /* xScale */, 1 /* yScale */, 0, 0,
+ 0, 0, AMOTION_EVENT_INVALID_CURSOR_POSITION,
+ AMOTION_EVENT_INVALID_CURSOR_POSITION, ARBITRARY_TIME, ARBITRARY_TIME,
/*pointerCount*/ 1, pointerProperties, pointerCoords);
ASSERT_EQ(INPUT_EVENT_INJECTION_FAILED, mDispatcher->injectInputEvent(
&event,
@@ -354,10 +354,10 @@
<< "Should reject motion events with pointer ids less than 0.";
pointerProperties[0].id = MAX_POINTER_ID + 1;
- event.initialize(DEVICE_ID, source, DISPLAY_ID, AMOTION_EVENT_ACTION_DOWN, 0, 0, edgeFlags,
- metaState, 0, classification, 0, 0, 0, 0,
- AMOTION_EVENT_INVALID_CURSOR_POSITION, AMOTION_EVENT_INVALID_CURSOR_POSITION,
- ARBITRARY_TIME, ARBITRARY_TIME,
+ event.initialize(DEVICE_ID, source, DISPLAY_ID, INVALID_HMAC, AMOTION_EVENT_ACTION_DOWN, 0, 0,
+ edgeFlags, metaState, 0, classification, 1 /* xScale */, 1 /* yScale */, 0, 0,
+ 0, 0, AMOTION_EVENT_INVALID_CURSOR_POSITION,
+ AMOTION_EVENT_INVALID_CURSOR_POSITION, ARBITRARY_TIME, ARBITRARY_TIME,
/*pointerCount*/ 1, pointerProperties, pointerCoords);
ASSERT_EQ(INPUT_EVENT_INJECTION_FAILED, mDispatcher->injectInputEvent(
&event,
@@ -367,10 +367,10 @@
// Rejects motion events with duplicate pointer ids.
pointerProperties[0].id = 1;
pointerProperties[1].id = 1;
- event.initialize(DEVICE_ID, source, DISPLAY_ID, AMOTION_EVENT_ACTION_DOWN, 0, 0, edgeFlags,
- metaState, 0, classification, 0, 0, 0, 0,
- AMOTION_EVENT_INVALID_CURSOR_POSITION, AMOTION_EVENT_INVALID_CURSOR_POSITION,
- ARBITRARY_TIME, ARBITRARY_TIME,
+ event.initialize(DEVICE_ID, source, DISPLAY_ID, INVALID_HMAC, AMOTION_EVENT_ACTION_DOWN, 0, 0,
+ edgeFlags, metaState, 0, classification, 1 /* xScale */, 1 /* yScale */, 0, 0,
+ 0, 0, AMOTION_EVENT_INVALID_CURSOR_POSITION,
+ AMOTION_EVENT_INVALID_CURSOR_POSITION, ARBITRARY_TIME, ARBITRARY_TIME,
/*pointerCount*/ 2, pointerProperties, pointerCoords);
ASSERT_EQ(INPUT_EVENT_INJECTION_FAILED, mDispatcher->injectInputEvent(
&event,
@@ -645,9 +645,9 @@
nsecs_t currentTime = systemTime(SYSTEM_TIME_MONOTONIC);
// Define a valid key down event.
- event.initialize(DEVICE_ID, AINPUT_SOURCE_KEYBOARD, displayId,
- AKEY_EVENT_ACTION_DOWN, /* flags */ 0,
- AKEYCODE_A, KEY_A, AMETA_NONE, /* repeatCount */ 0, currentTime, currentTime);
+ event.initialize(DEVICE_ID, AINPUT_SOURCE_KEYBOARD, displayId, INVALID_HMAC,
+ AKEY_EVENT_ACTION_DOWN, /* flags */ 0, AKEYCODE_A, KEY_A, AMETA_NONE,
+ /* repeatCount */ 0, currentTime, currentTime);
// Inject event until dispatch out.
return dispatcher->injectInputEvent(
@@ -674,10 +674,12 @@
nsecs_t currentTime = systemTime(SYSTEM_TIME_MONOTONIC);
// Define a valid motion down event.
- event.initialize(DEVICE_ID, source, displayId, action, /* actionButton */ 0, /* flags */ 0,
+ event.initialize(DEVICE_ID, source, displayId, INVALID_HMAC, action, /* actionButton */ 0,
+ /* flags */ 0,
/* edgeFlags */ 0, AMETA_NONE, /* buttonState */ 0, MotionClassification::NONE,
- /* xOffset */ 0, /* yOffset */ 0, /* xPrecision */ 0,
- /* yPrecision */ 0, xCursorPosition, yCursorPosition, currentTime, currentTime,
+ /* xScale */ 1, /* yScale */ 1, /* xOffset */ 0, /* yOffset */ 0,
+ /* xPrecision */ 0, /* yPrecision */ 0, xCursorPosition, yCursorPosition,
+ currentTime, currentTime,
/*pointerCount*/ 1, pointerProperties, pointerCoords);
// Inject event until dispatch out.
diff --git a/services/inputflinger/tests/InputReader_test.cpp b/services/inputflinger/tests/InputReader_test.cpp
index a9d7793..8ca7e4a 100644
--- a/services/inputflinger/tests/InputReader_test.cpp
+++ b/services/inputflinger/tests/InputReader_test.cpp
@@ -90,10 +90,6 @@
mMaxY = maxY;
}
- void setDisplayId(int32_t displayId) {
- mDisplayId = displayId;
- }
-
virtual void setPosition(float x, float y) {
mX = x;
mY = y;
@@ -116,6 +112,10 @@
return mDisplayId;
}
+ virtual void setDisplayViewport(const DisplayViewport& viewport) {
+ mDisplayId = viewport.displayId;
+ }
+
const std::map<int32_t, std::vector<int32_t>>& getSpots() {
return mSpotsByDisplay;
}
@@ -280,6 +280,10 @@
mConfig.showTouches = enabled;
}
+ void setDefaultPointerDisplayId(int32_t pointerDisplayId) {
+ mConfig.defaultPointerDisplayId = pointerDisplayId;
+ }
+
private:
DisplayViewport createDisplayViewport(int32_t displayId, int32_t width, int32_t height,
int32_t orientation, const std::string& uniqueId, std::optional<uint8_t> physicalPort,
@@ -3432,12 +3436,18 @@
CursorInputMapper* mapper = new CursorInputMapper(mDevice);
addMapperAndConfigure(mapper);
- // Setup PointerController for second display.
+ // Setup for second display.
constexpr int32_t SECOND_DISPLAY_ID = 1;
+ const std::string SECOND_DISPLAY_UNIQUE_ID = "local:1";
+ mFakePolicy->addDisplayViewport(SECOND_DISPLAY_ID, 800, 480, DISPLAY_ORIENTATION_0,
+ SECOND_DISPLAY_UNIQUE_ID, NO_PORT,
+ ViewportType::VIEWPORT_EXTERNAL);
+ mFakePolicy->setDefaultPointerDisplayId(SECOND_DISPLAY_ID);
+ configureDevice(InputReaderConfiguration::CHANGE_DISPLAY_INFO);
+
mFakePointerController->setBounds(0, 0, 800 - 1, 480 - 1);
mFakePointerController->setPosition(100, 200);
mFakePointerController->setButtonState(0);
- mFakePointerController->setDisplayId(SECOND_DISPLAY_ID);
NotifyMotionArgs args;
process(mapper, ARBITRARY_TIME, EV_REL, REL_X, 10);
@@ -6539,14 +6549,16 @@
}
TEST_F(MultiTouchInputMapperTest, Process_Pointer_ShouldHandleDisplayId) {
- // Setup PointerController for second display.
+ // Setup for second display.
sp<FakePointerController> fakePointerController = new FakePointerController();
- fakePointerController->setBounds(0, 0, 800 - 1, 480 - 1);
+ fakePointerController->setBounds(0, 0, DISPLAY_WIDTH - 1, DISPLAY_HEIGHT - 1);
fakePointerController->setPosition(100, 200);
fakePointerController->setButtonState(0);
- fakePointerController->setDisplayId(SECONDARY_DISPLAY_ID);
mFakePolicy->setPointerController(mDevice->getId(), fakePointerController);
+ mFakePolicy->setDefaultPointerDisplayId(SECONDARY_DISPLAY_ID);
+ prepareSecondaryDisplay(ViewportType::VIEWPORT_EXTERNAL);
+
MultiTouchInputMapper* mapper = new MultiTouchInputMapper(mDevice);
prepareDisplay(DISPLAY_ORIENTATION_0);
prepareAxes(POSITION);
diff --git a/services/sensorservice/SensorServiceUtils.cpp b/services/sensorservice/SensorServiceUtils.cpp
index 34cd8dd..fdd56b3 100644
--- a/services/sensorservice/SensorServiceUtils.cpp
+++ b/services/sensorservice/SensorServiceUtils.cpp
@@ -55,6 +55,7 @@
case SENSOR_TYPE_MOTION_DETECT:
case SENSOR_TYPE_HEART_BEAT:
case SENSOR_TYPE_LOW_LATENCY_OFFBODY_DETECT:
+ case SENSOR_TYPE_HINGE_ANGLE:
return 1;
default:
diff --git a/services/surfaceflinger/Android.bp b/services/surfaceflinger/Android.bp
index 9a7eabd..1b1e889 100644
--- a/services/surfaceflinger/Android.bp
+++ b/services/surfaceflinger/Android.bp
@@ -165,7 +165,9 @@
"Scheduler/EventThread.cpp",
"Scheduler/OneShotTimer.cpp",
"Scheduler/LayerHistory.cpp",
+ "Scheduler/LayerHistoryV2.cpp",
"Scheduler/LayerInfo.cpp",
+ "Scheduler/LayerInfoV2.cpp",
"Scheduler/MessageQueue.cpp",
"Scheduler/PhaseOffsets.cpp",
"Scheduler/RefreshRateConfigs.cpp",
diff --git a/services/surfaceflinger/BufferQueueLayer.cpp b/services/surfaceflinger/BufferQueueLayer.cpp
index b55e62b..46bbbe1 100644
--- a/services/surfaceflinger/BufferQueueLayer.cpp
+++ b/services/surfaceflinger/BufferQueueLayer.cpp
@@ -129,8 +129,13 @@
return frameRateChanged;
}
-float BufferQueueLayer::getFrameRate() const {
- return mLatchedFrameRate;
+std::optional<float> BufferQueueLayer::getFrameRate() const {
+ const auto frameRate = mLatchedFrameRate.load();
+ if (frameRate > 0.f || frameRate == FRAME_RATE_NO_VOTE) {
+ return frameRate;
+ }
+
+ return {};
}
// -----------------------------------------------------------------------
diff --git a/services/surfaceflinger/BufferQueueLayer.h b/services/surfaceflinger/BufferQueueLayer.h
index 0777953..2bd1e3d 100644
--- a/services/surfaceflinger/BufferQueueLayer.h
+++ b/services/surfaceflinger/BufferQueueLayer.h
@@ -57,7 +57,7 @@
bool shouldPresentNow(nsecs_t expectedPresentTime) const override;
bool setFrameRate(float frameRate) override;
- float getFrameRate() const override;
+ std::optional<float> getFrameRate() const override;
// -----------------------------------------------------------------------
diff --git a/services/surfaceflinger/BufferStateLayer.cpp b/services/surfaceflinger/BufferStateLayer.cpp
index 287fe89..c86c538 100644
--- a/services/surfaceflinger/BufferStateLayer.cpp
+++ b/services/surfaceflinger/BufferStateLayer.cpp
@@ -239,7 +239,7 @@
mReleasePreviousBuffer = true;
}
- mFrameCounter++;
+ mCurrentState.frameNumber++;
mCurrentState.buffer = buffer;
mCurrentState.clientCacheId = clientCacheId;
@@ -247,13 +247,15 @@
setTransactionFlags(eTransactionNeeded);
const int32_t layerId = getSequence();
- mFlinger->mTimeStats->setPostTime(layerId, mFrameNumber, getName().c_str(), postTime);
+ mFlinger->mTimeStats->setPostTime(layerId, mCurrentState.frameNumber, getName().c_str(),
+ postTime);
mFlinger->mFrameTracer->traceNewLayer(layerId, getName().c_str());
- mFlinger->mFrameTracer->traceTimestamp(layerId, buffer->getId(), mFrameNumber, postTime,
- FrameTracer::FrameEvent::POST);
+ mFlinger->mFrameTracer->traceTimestamp(layerId, buffer->getId(), mCurrentState.frameNumber,
+ postTime, FrameTracer::FrameEvent::POST);
mCurrentState.desiredPresentTime = desiredPresentTime;
- mFlinger->mScheduler->recordLayerHistory(this, desiredPresentTime);
+ mFlinger->mScheduler->recordLayerHistory(this,
+ desiredPresentTime <= 0 ? 0 : desiredPresentTime);
return true;
}
@@ -413,7 +415,7 @@
}
uint64_t BufferStateLayer::getFrameNumber(nsecs_t /*expectedPresentTime*/) const {
- return mFrameNumber;
+ return mDrawingState.frameNumber;
}
bool BufferStateLayer::getAutoRefresh() const {
@@ -490,7 +492,7 @@
ALOGE("[%s] rejecting buffer: "
"bufferWidth=%d, bufferHeight=%d, front.active.{w=%d, h=%d}",
getDebugName(), bufferWidth, bufferHeight, s.active.w, s.active.h);
- mFlinger->mTimeStats->removeTimeRecord(layerId, mFrameNumber);
+ mFlinger->mTimeStats->removeTimeRecord(layerId, mDrawingState.frameNumber);
return BAD_VALUE;
}
@@ -498,8 +500,6 @@
handle->latchTime = latchTime;
}
- mFrameNumber = mFrameCounter;
-
if (!SyncFeatures::getInstance().useNativeFenceSync()) {
// Bind the new buffer to the GL texture.
//
@@ -516,11 +516,13 @@
}
const uint64_t bufferID = getCurrentBufferId();
- mFlinger->mTimeStats->setAcquireFence(layerId, mFrameNumber, mBufferInfo.mFenceTime);
- mFlinger->mFrameTracer->traceFence(layerId, bufferID, mFrameNumber, mBufferInfo.mFenceTime,
+ mFlinger->mTimeStats->setAcquireFence(layerId, mDrawingState.frameNumber,
+ mBufferInfo.mFenceTime);
+ mFlinger->mFrameTracer->traceFence(layerId, bufferID, mDrawingState.frameNumber,
+ mBufferInfo.mFenceTime,
FrameTracer::FrameEvent::ACQUIRE_FENCE);
- mFlinger->mTimeStats->setLatchTime(layerId, mFrameNumber, latchTime);
- mFlinger->mFrameTracer->traceTimestamp(layerId, bufferID, mFrameNumber, latchTime,
+ mFlinger->mTimeStats->setLatchTime(layerId, mDrawingState.frameNumber, latchTime);
+ mFlinger->mFrameTracer->traceTimestamp(layerId, bufferID, mDrawingState.frameNumber, latchTime,
FrameTracer::FrameEvent::LATCH);
mCurrentStateModified = false;
@@ -547,7 +549,7 @@
status_t BufferStateLayer::updateFrameNumber(nsecs_t /*latchTime*/) {
// TODO(marissaw): support frame history events
mPreviousFrameNumber = mCurrentFrameNumber;
- mCurrentFrameNumber = mFrameNumber;
+ mCurrentFrameNumber = mDrawingState.frameNumber;
return NO_ERROR;
}
diff --git a/services/surfaceflinger/ColorLayer.cpp b/services/surfaceflinger/ColorLayer.cpp
index 04854d0..dbdfd5b 100644
--- a/services/surfaceflinger/ColorLayer.cpp
+++ b/services/surfaceflinger/ColorLayer.cpp
@@ -61,7 +61,7 @@
}
bool ColorLayer::isVisible() const {
- return !isHiddenByPolicy() && getAlpha() > 0.0f;
+ return !isHiddenByPolicy() && getAlpha() > 0.0_hf;
}
bool ColorLayer::setColor(const half3& color) {
@@ -104,7 +104,9 @@
}
bool ColorLayer::isOpaque(const Layer::State& s) const {
- return (s.flags & layer_state_t::eLayerOpaque) != 0;
+ // Consider the layer to be opaque if its opaque flag is set or its effective
+ // alpha (considering the alpha of its parents as well) is 1.0;
+ return (s.flags & layer_state_t::eLayerOpaque) != 0 || getAlpha() == 1.0_hf;
}
ui::Dataspace ColorLayer::getDataSpace() const {
diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/DisplayColorProfile.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/DisplayColorProfile.h
index d93bfa3..67e6deb 100644
--- a/services/surfaceflinger/CompositionEngine/include/compositionengine/DisplayColorProfile.h
+++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/DisplayColorProfile.h
@@ -19,8 +19,15 @@
#include <cstdint>
#include <string>
+// TODO(b/129481165): remove the #pragma below and fix conversion issues
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Wconversion"
+
#include <ui/GraphicTypes.h>
+// TODO(b/129481165): remove the #pragma below and fix conversion issues
+#pragma clang diagnostic pop // ignored "-Wconversion"
+
namespace android {
class HdrCapabilities;
diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/DisplayColorProfileCreationArgs.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/DisplayColorProfileCreationArgs.h
index ef0f925..7eb8eb1 100644
--- a/services/surfaceflinger/CompositionEngine/include/compositionengine/DisplayColorProfileCreationArgs.h
+++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/DisplayColorProfileCreationArgs.h
@@ -20,7 +20,15 @@
#include <unordered_map>
#include <vector>
+// TODO(b/129481165): remove the #pragma below and fix conversion issues
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Wconversion"
+
#include <ui/GraphicTypes.h>
+
+// TODO(b/129481165): remove the #pragma below and fix conversion issues
+#pragma clang diagnostic pop // ignored "-Wconversion"
+
#include <ui/HdrCapabilities.h>
namespace android::compositionengine {
diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/LayerFE.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/LayerFE.h
index f34509f..26442d9 100644
--- a/services/surfaceflinger/CompositionEngine/include/compositionengine/LayerFE.h
+++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/LayerFE.h
@@ -20,7 +20,15 @@
#include <ostream>
#include <unordered_set>
+// TODO(b/129481165): remove the #pragma below and fix conversion issues
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Wconversion"
+
#include <renderengine/LayerSettings.h>
+
+// TODO(b/129481165): remove the #pragma below and fix conversion issues
+#pragma clang diagnostic pop // ignored "-Wconversion"
+
#include <utils/RefBase.h>
#include <utils/Timers.h>
diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/LayerFECompositionState.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/LayerFECompositionState.h
index 3dbd25e..1af99c5 100644
--- a/services/surfaceflinger/CompositionEngine/include/compositionengine/LayerFECompositionState.h
+++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/LayerFECompositionState.h
@@ -18,18 +18,26 @@
#include <cstdint>
-#include <gui/BufferQueue.h>
#include <gui/HdrMetadata.h>
#include <math/mat4.h>
#include <ui/FloatRect.h>
-#include <ui/GraphicBuffer.h>
-#include <ui/GraphicTypes.h>
#include <ui/Rect.h>
#include <ui/Region.h>
#include <ui/Transform.h>
+// TODO(b/129481165): remove the #pragma below and fix conversion issues
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Wconversion"
+
+#include <gui/BufferQueue.h>
+#include <ui/GraphicBuffer.h>
+#include <ui/GraphicTypes.h>
+
#include "DisplayHardware/ComposerHal.h"
+// TODO(b/129481165): remove the #pragma below and fix conversion issues
+#pragma clang diagnostic pop // ignored "-Wconversion"
+
namespace android::compositionengine {
/*
diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/Output.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/Output.h
index a5da0b1..4ab7082 100644
--- a/services/surfaceflinger/CompositionEngine/include/compositionengine/Output.h
+++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/Output.h
@@ -163,7 +163,8 @@
// Sets the projection state to use
virtual void setProjection(const ui::Transform&, uint32_t orientation, const Rect& frame,
- const Rect& viewport, const Rect& scissor, bool needsFiltering) = 0;
+ const Rect& viewport, const Rect& sourceClip,
+ const Rect& destinationClip, bool needsFiltering) = 0;
// Sets the bounds to use
virtual void setBounds(const ui::Size&) = 0;
diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/OutputLayer.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/OutputLayer.h
index a9a95cd..a466561 100644
--- a/services/surfaceflinger/CompositionEngine/include/compositionengine/OutputLayer.h
+++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/OutputLayer.h
@@ -21,9 +21,16 @@
#include <utils/StrongPointer.h>
+// TODO(b/129481165): remove the #pragma below and fix conversion issues
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Wconversion"
+
#include "DisplayHardware/ComposerHal.h"
#include "DisplayHardware/DisplayIdentification.h"
+// TODO(b/129481165): remove the #pragma below and fix conversion issues
+#pragma clang diagnostic pop // ignored "-Wconversion"
+
namespace HWC2 {
class Layer;
} // namespace HWC2
diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/HwcBufferCache.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/HwcBufferCache.h
index 8eec035..2864c10 100644
--- a/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/HwcBufferCache.h
+++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/HwcBufferCache.h
@@ -19,7 +19,15 @@
#include <cstdint>
#include <vector>
+// TODO(b/129481165): remove the #pragma below and fix conversion issues
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Wconversion"
+
#include <gui/BufferQueue.h>
+
+// TODO(b/129481165): remove the #pragma below and fix conversion issues
+#pragma clang diagnostic pop // ignored "-Wconversion"
+
#include <utils/StrongPointer.h>
namespace android {
diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/Output.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/Output.h
index f469e62..8dc440c 100644
--- a/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/Output.h
+++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/Output.h
@@ -39,7 +39,8 @@
std::optional<DisplayId> getDisplayId() const override;
void setCompositionEnabled(bool) override;
void setProjection(const ui::Transform&, uint32_t orientation, const Rect& frame,
- const Rect& viewport, const Rect& scissor, bool needsFiltering) override;
+ const Rect& viewport, const Rect& sourceClip, const Rect& destinationClip,
+ bool needsFiltering) override;
void setBounds(const ui::Size&) override;
void setLayerStackFilter(uint32_t layerStackId, bool isInternal) override;
diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/OutputCompositionState.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/OutputCompositionState.h
index 34dadb5..66ed2b6 100644
--- a/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/OutputCompositionState.h
+++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/OutputCompositionState.h
@@ -19,7 +19,16 @@
#include <cstdint>
#include <math/mat4.h>
+
+// TODO(b/129481165): remove the #pragma below and fix conversion issues
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Wconversion"
+
#include <ui/GraphicTypes.h>
+
+// TODO(b/129481165): remove the #pragma below and fix conversion issues
+#pragma clang diagnostic pop // ignored "-Wconversion"
+
#include <ui/Rect.h>
#include <ui/Region.h>
#include <ui/Transform.h>
@@ -70,8 +79,11 @@
// The logical space user viewport rectangle
Rect viewport;
- // The physical space scissor rectangle
- Rect scissor;
+ // The physical space source clip rectangle
+ Rect sourceClip;
+
+ // The physical space destination clip rectangle
+ Rect destinationClip;
// If true, RenderEngine filtering should be enabled
bool needsFiltering{false};
diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/OutputLayerCompositionState.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/OutputLayerCompositionState.h
index 00baa89..b0a9bc9 100644
--- a/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/OutputLayerCompositionState.h
+++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/OutputLayerCompositionState.h
@@ -27,8 +27,15 @@
#include <ui/Rect.h>
#include <ui/Region.h>
+// TODO(b/129481165): remove the #pragma below and fix conversion issues
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Wconversion"
+
#include "DisplayHardware/ComposerHal.h"
+// TODO(b/129481165): remove the #pragma below and fix conversion issues
+#pragma clang diagnostic pop // ignored "-Wconversion"
+
namespace HWC2 {
class Layer;
} // namespace HWC2
diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/mock/Output.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/mock/Output.h
index c41302d..59906b9 100644
--- a/services/surfaceflinger/CompositionEngine/include/compositionengine/mock/Output.h
+++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/mock/Output.h
@@ -37,8 +37,9 @@
MOCK_CONST_METHOD0(getDisplayId, std::optional<DisplayId>());
MOCK_METHOD1(setCompositionEnabled, void(bool));
- MOCK_METHOD6(setProjection,
- void(const ui::Transform&, uint32_t, const Rect&, const Rect&, const Rect&, bool));
+ MOCK_METHOD7(setProjection,
+ void(const ui::Transform&, uint32_t, const Rect&, const Rect&, const Rect&,
+ const Rect&, bool));
MOCK_METHOD1(setBounds, void(const ui::Size&));
MOCK_METHOD2(setLayerStackFilter, void(uint32_t, bool));
diff --git a/services/surfaceflinger/CompositionEngine/mock/Display.cpp b/services/surfaceflinger/CompositionEngine/mock/Display.cpp
index dc303d7..01cf112 100644
--- a/services/surfaceflinger/CompositionEngine/mock/Display.cpp
+++ b/services/surfaceflinger/CompositionEngine/mock/Display.cpp
@@ -14,10 +14,6 @@
* limitations under the License.
*/
-// TODO(b/129481165): remove the #pragma below and fix conversion issues
-#pragma clang diagnostic push
-#pragma clang diagnostic ignored "-Wconversion"
-
#include <compositionengine/mock/Display.h>
namespace android::compositionengine::mock {
@@ -28,6 +24,3 @@
Display::~Display() = default;
} // namespace android::compositionengine::mock
-
-// TODO(b/129481165): remove the #pragma below and fix conversion issues
-#pragma clang diagnostic pop // ignored "-Wconversion"
diff --git a/services/surfaceflinger/CompositionEngine/mock/Layer.cpp b/services/surfaceflinger/CompositionEngine/mock/Layer.cpp
index a733bac..08483cb 100644
--- a/services/surfaceflinger/CompositionEngine/mock/Layer.cpp
+++ b/services/surfaceflinger/CompositionEngine/mock/Layer.cpp
@@ -14,10 +14,6 @@
* limitations under the License.
*/
-// TODO(b/129481165): remove the #pragma below and fix conversion issues
-#pragma clang diagnostic push
-#pragma clang diagnostic ignored "-Wconversion"
-
#include <compositionengine/mock/Layer.h>
namespace android::compositionengine::mock {
@@ -28,6 +24,3 @@
Layer::~Layer() = default;
} // namespace android::compositionengine::mock
-
-// TODO(b/129481165): remove the #pragma below and fix conversion issues
-#pragma clang diagnostic pop // ignored "-Wconversion"
diff --git a/services/surfaceflinger/CompositionEngine/mock/LayerFE.cpp b/services/surfaceflinger/CompositionEngine/mock/LayerFE.cpp
index 232756f..607eaad 100644
--- a/services/surfaceflinger/CompositionEngine/mock/LayerFE.cpp
+++ b/services/surfaceflinger/CompositionEngine/mock/LayerFE.cpp
@@ -14,10 +14,6 @@
* limitations under the License.
*/
-// TODO(b/129481165): remove the #pragma below and fix conversion issues
-#pragma clang diagnostic push
-#pragma clang diagnostic ignored "-Wconversion"
-
#include <compositionengine/mock/LayerFE.h>
namespace android::compositionengine::mock {
@@ -28,6 +24,3 @@
LayerFE::~LayerFE() = default;
} // namespace android::compositionengine::mock
-
-// TODO(b/129481165): remove the #pragma below and fix conversion issues
-#pragma clang diagnostic pop // ignored "-Wconversion"
diff --git a/services/surfaceflinger/CompositionEngine/mock/Output.cpp b/services/surfaceflinger/CompositionEngine/mock/Output.cpp
index 2608ef0..44df4c3 100644
--- a/services/surfaceflinger/CompositionEngine/mock/Output.cpp
+++ b/services/surfaceflinger/CompositionEngine/mock/Output.cpp
@@ -14,10 +14,6 @@
* limitations under the License.
*/
-// TODO(b/129481165): remove the #pragma below and fix conversion issues
-#pragma clang diagnostic push
-#pragma clang diagnostic ignored "-Wconversion"
-
#include <compositionengine/mock/Output.h>
namespace android::compositionengine::mock {
@@ -28,6 +24,3 @@
Output::~Output() = default;
} // namespace android::compositionengine::mock
-
-// TODO(b/129481165): remove the #pragma below and fix conversion issues
-#pragma clang diagnostic pop // ignored "-Wconversion"
diff --git a/services/surfaceflinger/CompositionEngine/mock/OutputLayer.cpp b/services/surfaceflinger/CompositionEngine/mock/OutputLayer.cpp
index c1153e3..4da9377 100644
--- a/services/surfaceflinger/CompositionEngine/mock/OutputLayer.cpp
+++ b/services/surfaceflinger/CompositionEngine/mock/OutputLayer.cpp
@@ -14,10 +14,6 @@
* limitations under the License.
*/
-// TODO(b/129481165): remove the #pragma below and fix conversion issues
-#pragma clang diagnostic push
-#pragma clang diagnostic ignored "-Wconversion"
-
#include <compositionengine/mock/OutputLayer.h>
namespace android::compositionengine::mock {
@@ -28,6 +24,3 @@
OutputLayer::~OutputLayer() = default;
} // namespace android::compositionengine::mock
-
-// TODO(b/129481165): remove the #pragma below and fix conversion issues
-#pragma clang diagnostic pop // ignored "-Wconversion"
diff --git a/services/surfaceflinger/CompositionEngine/src/CompositionEngine.cpp b/services/surfaceflinger/CompositionEngine/src/CompositionEngine.cpp
index 030c703..aeaa18a 100644
--- a/services/surfaceflinger/CompositionEngine/src/CompositionEngine.cpp
+++ b/services/surfaceflinger/CompositionEngine/src/CompositionEngine.cpp
@@ -14,21 +14,25 @@
* limitations under the License.
*/
-// TODO(b/129481165): remove the #pragma below and fix conversion issues
-#pragma clang diagnostic push
-#pragma clang diagnostic ignored "-Wconversion"
-
#include <compositionengine/CompositionRefreshArgs.h>
#include <compositionengine/LayerFE.h>
#include <compositionengine/OutputLayer.h>
#include <compositionengine/impl/CompositionEngine.h>
#include <compositionengine/impl/Display.h>
#include <compositionengine/impl/Layer.h>
+
#include <renderengine/RenderEngine.h>
#include <utils/Trace.h>
+// TODO(b/129481165): remove the #pragma below and fix conversion issues
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Wconversion"
+
#include "DisplayHardware/HWComposer.h"
+// TODO(b/129481165): remove the #pragma below and fix conversion issues
+#pragma clang diagnostic pop // ignored "-Wconversion"
+
namespace android::compositionengine {
CompositionEngine::~CompositionEngine() = default;
@@ -159,6 +163,3 @@
} // namespace impl
} // namespace android::compositionengine
-
-// TODO(b/129481165): remove the #pragma below and fix conversion issues
-#pragma clang diagnostic pop // ignored "-Wconversion"
diff --git a/services/surfaceflinger/CompositionEngine/src/Display.cpp b/services/surfaceflinger/CompositionEngine/src/Display.cpp
index 13f5137..ccd6572 100644
--- a/services/surfaceflinger/CompositionEngine/src/Display.cpp
+++ b/services/surfaceflinger/CompositionEngine/src/Display.cpp
@@ -14,10 +14,6 @@
* limitations under the License.
*/
-// TODO(b/129481165): remove the #pragma below and fix conversion issues
-#pragma clang diagnostic push
-#pragma clang diagnostic ignored "-Wconversion"
-
#include <android-base/stringprintf.h>
#include <compositionengine/CompositionEngine.h>
#include <compositionengine/CompositionRefreshArgs.h>
@@ -29,9 +25,18 @@
#include <compositionengine/impl/DumpHelpers.h>
#include <compositionengine/impl/OutputLayer.h>
#include <compositionengine/impl/RenderSurface.h>
+
#include <utils/Trace.h>
+// TODO(b/129481165): remove the #pragma below and fix conversion issues
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Wconversion"
+
#include "DisplayHardware/HWComposer.h"
+
+// TODO(b/129481165): remove the #pragma below and fix conversion issues
+#pragma clang diagnostic pop // ignored "-Wconversion"
+
#include "DisplayHardware/PowerAdvisor.h"
namespace android::compositionengine::impl {
@@ -345,6 +350,3 @@
}
} // namespace android::compositionengine::impl
-
-// TODO(b/129481165): remove the #pragma below and fix conversion issues
-#pragma clang diagnostic pop // ignored "-Wconversion"
diff --git a/services/surfaceflinger/CompositionEngine/src/HwcBufferCache.cpp b/services/surfaceflinger/CompositionEngine/src/HwcBufferCache.cpp
index 1e7c97c..cedc333 100644
--- a/services/surfaceflinger/CompositionEngine/src/HwcBufferCache.cpp
+++ b/services/surfaceflinger/CompositionEngine/src/HwcBufferCache.cpp
@@ -14,14 +14,18 @@
* limitations under the License.
*/
+#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 <compositionengine/impl/HwcBufferCache.h>
#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() {
@@ -52,6 +56,3 @@
}
} // namespace android::compositionengine::impl
-
-// TODO(b/129481165): remove the #pragma below and fix conversion issues
-#pragma clang diagnostic pop // ignored "-Wconversion"
diff --git a/services/surfaceflinger/CompositionEngine/src/Layer.cpp b/services/surfaceflinger/CompositionEngine/src/Layer.cpp
index 22ecd33..ecacaee 100644
--- a/services/surfaceflinger/CompositionEngine/src/Layer.cpp
+++ b/services/surfaceflinger/CompositionEngine/src/Layer.cpp
@@ -14,10 +14,6 @@
* limitations under the License.
*/
-// TODO(b/129481165): remove the #pragma below and fix conversion issues
-#pragma clang diagnostic push
-#pragma clang diagnostic ignored "-Wconversion"
-
#include <android-base/stringprintf.h>
#include <compositionengine/LayerFE.h>
#include <compositionengine/LayerFECompositionState.h>
@@ -45,6 +41,3 @@
} // namespace impl
} // namespace android::compositionengine
-
-// TODO(b/129481165): remove the #pragma below and fix conversion issues
-#pragma clang diagnostic pop // ignored "-Wconversion"
diff --git a/services/surfaceflinger/CompositionEngine/src/LayerFECompositionState.cpp b/services/surfaceflinger/CompositionEngine/src/LayerFECompositionState.cpp
index 8065e65..016084f 100644
--- a/services/surfaceflinger/CompositionEngine/src/LayerFECompositionState.cpp
+++ b/services/surfaceflinger/CompositionEngine/src/LayerFECompositionState.cpp
@@ -14,10 +14,6 @@
* limitations under the License.
*/
-// TODO(b/129481165): remove the #pragma below and fix conversion issues
-#pragma clang diagnostic push
-#pragma clang diagnostic ignored "-Wconversion"
-
#include <android-base/stringprintf.h>
#include <compositionengine/LayerFECompositionState.h>
#include <compositionengine/impl/DumpHelpers.h>
@@ -91,6 +87,3 @@
}
} // namespace android::compositionengine
-
-// TODO(b/129481165): remove the #pragma below and fix conversion issues
-#pragma clang diagnostic pop // ignored "-Wconversion"
diff --git a/services/surfaceflinger/CompositionEngine/src/Output.cpp b/services/surfaceflinger/CompositionEngine/src/Output.cpp
index 61dadfd..ce4b84a 100644
--- a/services/surfaceflinger/CompositionEngine/src/Output.cpp
+++ b/services/surfaceflinger/CompositionEngine/src/Output.cpp
@@ -14,10 +14,6 @@
* limitations under the License.
*/
-// TODO(b/129481165): remove the #pragma below and fix conversion issues
-#pragma clang diagnostic push
-#pragma clang diagnostic ignored "-Wconversion"
-
#include <thread>
#include <android-base/stringprintf.h>
@@ -32,8 +28,17 @@
#include <compositionengine/impl/OutputCompositionState.h>
#include <compositionengine/impl/OutputLayer.h>
#include <compositionengine/impl/OutputLayerCompositionState.h>
+
+// TODO(b/129481165): remove the #pragma below and fix conversion issues
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Wconversion"
+
#include <renderengine/DisplaySettings.h>
#include <renderengine/RenderEngine.h>
+
+// TODO(b/129481165): remove the #pragma below and fix conversion issues
+#pragma clang diagnostic pop // ignored "-Wconversion"
+
#include <ui/DebugUtils.h>
#include <ui/HdrCapabilities.h>
#include <utils/Trace.h>
@@ -102,11 +107,13 @@
}
void Output::setProjection(const ui::Transform& transform, uint32_t orientation, const Rect& frame,
- const Rect& viewport, const Rect& scissor, bool needsFiltering) {
+ const Rect& viewport, const Rect& sourceClip,
+ const Rect& destinationClip, bool needsFiltering) {
auto& outputState = editState();
outputState.transform = transform;
outputState.orientation = orientation;
- outputState.scissor = scissor;
+ outputState.sourceClip = sourceClip;
+ outputState.destinationClip = destinationClip;
outputState.frame = frame;
outputState.viewport = viewport;
outputState.needsFiltering = needsFiltering;
@@ -829,8 +836,8 @@
const bool supportsProtectedContent = renderEngine.supportsProtectedContent();
renderengine::DisplaySettings clientCompositionDisplay;
- clientCompositionDisplay.physicalDisplay = outputState.scissor;
- clientCompositionDisplay.clip = outputState.scissor;
+ clientCompositionDisplay.physicalDisplay = outputState.destinationClip;
+ clientCompositionDisplay.clip = outputState.sourceClip;
clientCompositionDisplay.globalTransform = outputState.transform.asMatrix4();
clientCompositionDisplay.orientation = outputState.orientation;
clientCompositionDisplay.outputDataspace = mDisplayColorProfile->hasWideColorGamut()
@@ -898,8 +905,7 @@
// GPU composition can finish in time. We must reset GPU frequency afterwards,
// because high frequency consumes extra battery.
const bool expensiveRenderingExpected =
- clientCompositionDisplay.outputDataspace == ui::Dataspace::DISPLAY_P3 ||
- mLayerRequestingBackgroundBlur != nullptr;
+ clientCompositionDisplay.outputDataspace == ui::Dataspace::DISPLAY_P3;
if (expensiveRenderingExpected) {
setExpensiveRenderingExpected(true);
}
@@ -1120,6 +1126,3 @@
} // namespace impl
} // namespace android::compositionengine
-
-// TODO(b/129481165): remove the #pragma below and fix conversion issues
-#pragma clang diagnostic pop // ignored "-Wconversion"
diff --git a/services/surfaceflinger/CompositionEngine/src/OutputCompositionState.cpp b/services/surfaceflinger/CompositionEngine/src/OutputCompositionState.cpp
index 84d79f7..ca5be48 100644
--- a/services/surfaceflinger/CompositionEngine/src/OutputCompositionState.cpp
+++ b/services/surfaceflinger/CompositionEngine/src/OutputCompositionState.cpp
@@ -40,7 +40,8 @@
dumpVal(out, "frame", frame);
dumpVal(out, "viewport", viewport);
- dumpVal(out, "scissor", scissor);
+ dumpVal(out, "sourceClip", sourceClip);
+ dumpVal(out, "destinationClip", destinationClip);
dumpVal(out, "needsFiltering", needsFiltering);
out.append("\n ");
diff --git a/services/surfaceflinger/CompositionEngine/src/OutputLayer.cpp b/services/surfaceflinger/CompositionEngine/src/OutputLayer.cpp
index 914a047..d92b7ef 100644
--- a/services/surfaceflinger/CompositionEngine/src/OutputLayer.cpp
+++ b/services/surfaceflinger/CompositionEngine/src/OutputLayer.cpp
@@ -14,10 +14,6 @@
* limitations under the License.
*/
-// TODO(b/129481165): remove the #pragma below and fix conversion issues
-#pragma clang diagnostic push
-#pragma clang diagnostic ignored "-Wconversion"
-
#include <android-base/stringprintf.h>
#include <compositionengine/DisplayColorProfile.h>
#include <compositionengine/Layer.h>
@@ -28,8 +24,15 @@
#include <compositionengine/impl/OutputLayer.h>
#include <compositionengine/impl/OutputLayerCompositionState.h>
+// TODO(b/129481165): remove the #pragma below and fix conversion issues
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Wconversion"
+
#include "DisplayHardware/HWComposer.h"
+// TODO(b/129481165): remove the #pragma below and fix conversion issues
+#pragma clang diagnostic pop // ignored "-Wconversion"
+
namespace android::compositionengine {
OutputLayer::~OutputLayer() = default;
@@ -651,5 +654,3 @@
} // namespace impl
} // namespace android::compositionengine
-// TODO(b/129481165): remove the #pragma below and fix conversion issues
-#pragma clang diagnostic pop // ignored "-Wconversion"
diff --git a/services/surfaceflinger/CompositionEngine/src/OutputLayerCompositionState.cpp b/services/surfaceflinger/CompositionEngine/src/OutputLayerCompositionState.cpp
index 20c8f9a..165e320 100644
--- a/services/surfaceflinger/CompositionEngine/src/OutputLayerCompositionState.cpp
+++ b/services/surfaceflinger/CompositionEngine/src/OutputLayerCompositionState.cpp
@@ -14,15 +14,18 @@
* limitations under the License.
*/
+#include <compositionengine/impl/DumpHelpers.h>
+#include <compositionengine/impl/OutputLayerCompositionState.h>
+
// TODO(b/129481165): remove the #pragma below and fix conversion issues
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wconversion"
-#include <compositionengine/impl/DumpHelpers.h>
-#include <compositionengine/impl/OutputLayerCompositionState.h>
-
#include "DisplayHardware/HWC2.h"
+// TODO(b/129481165): remove the #pragma below and fix conversion issues
+#pragma clang diagnostic pop // ignored "-Wconversion"
+
namespace android::compositionengine::impl {
namespace {
@@ -74,6 +77,3 @@
}
} // namespace android::compositionengine::impl
-
-// TODO(b/129481165): remove the #pragma below and fix conversion issues
-#pragma clang diagnostic pop // ignored "-Wconversion"
diff --git a/services/surfaceflinger/CompositionEngine/src/RenderSurface.cpp b/services/surfaceflinger/CompositionEngine/src/RenderSurface.cpp
index e981172..660baff 100644
--- a/services/surfaceflinger/CompositionEngine/src/RenderSurface.cpp
+++ b/services/surfaceflinger/CompositionEngine/src/RenderSurface.cpp
@@ -14,10 +14,6 @@
* limitations under the License.
*/
-// TODO(b/129481165): remove the #pragma below and fix conversion issues
-#pragma clang diagnostic push
-#pragma clang diagnostic ignored "-Wconversion"
-
#define ATRACE_TAG ATRACE_TAG_GRAPHICS
#include <android-base/stringprintf.h>
@@ -29,6 +25,7 @@
#include <compositionengine/impl/DumpHelpers.h>
#include <compositionengine/impl/OutputCompositionState.h>
#include <compositionengine/impl/RenderSurface.h>
+
#include <log/log.h>
#include <renderengine/RenderEngine.h>
#include <sync/sync.h>
@@ -37,8 +34,15 @@
#include <ui/Rect.h>
#include <utils/Trace.h>
+// TODO(b/129481165): remove the #pragma below and fix conversion issues
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Wconversion"
+
#include "DisplayHardware/HWComposer.h"
+// TODO(b/129481165): remove the #pragma below and fix conversion issues
+#pragma clang diagnostic pop // ignored "-Wconversion"
+
namespace android::compositionengine {
RenderSurface::~RenderSurface() = default;
@@ -254,6 +258,3 @@
} // namespace impl
} // namespace android::compositionengine
-
-// TODO(b/129481165): remove the #pragma below and fix conversion issues
-#pragma clang diagnostic pop // ignored "-Wconversion"
diff --git a/services/surfaceflinger/CompositionEngine/tests/CompositionEngineTest.cpp b/services/surfaceflinger/CompositionEngine/tests/CompositionEngineTest.cpp
index bcaa529..c1faa90 100644
--- a/services/surfaceflinger/CompositionEngine/tests/CompositionEngineTest.cpp
+++ b/services/surfaceflinger/CompositionEngine/tests/CompositionEngineTest.cpp
@@ -14,10 +14,6 @@
* limitations under the License.
*/
-// TODO(b/129481165): remove the #pragma below and fix conversion issues
-#pragma clang diagnostic push
-#pragma clang diagnostic ignored "-Wconversion"
-
#include <compositionengine/CompositionRefreshArgs.h>
#include <compositionengine/LayerFECompositionState.h>
#include <compositionengine/impl/CompositionEngine.h>
@@ -302,6 +298,3 @@
} // namespace
} // namespace android::compositionengine
-
-// TODO(b/129481165): remove the #pragma below and fix conversion issues
-#pragma clang diagnostic pop // ignored "-Wconversion"
diff --git a/services/surfaceflinger/CompositionEngine/tests/DisplayTest.cpp b/services/surfaceflinger/CompositionEngine/tests/DisplayTest.cpp
index 0fe5843..ae93969 100644
--- a/services/surfaceflinger/CompositionEngine/tests/DisplayTest.cpp
+++ b/services/surfaceflinger/CompositionEngine/tests/DisplayTest.cpp
@@ -14,10 +14,6 @@
* limitations under the License.
*/
-// TODO(b/129481165): remove the #pragma below and fix conversion issues
-#pragma clang diagnostic push
-#pragma clang diagnostic ignored "-Wconversion"
-
#include <cmath>
#include <compositionengine/DisplayColorProfileCreationArgs.h>
@@ -837,6 +833,3 @@
} // namespace
} // namespace android::compositionengine
-
-// TODO(b/129481165): remove the #pragma below and fix conversion issues
-#pragma clang diagnostic pop // ignored "-Wconversion"
diff --git a/services/surfaceflinger/CompositionEngine/tests/LayerTest.cpp b/services/surfaceflinger/CompositionEngine/tests/LayerTest.cpp
index 44df289..787f973 100644
--- a/services/surfaceflinger/CompositionEngine/tests/LayerTest.cpp
+++ b/services/surfaceflinger/CompositionEngine/tests/LayerTest.cpp
@@ -14,10 +14,6 @@
* limitations under the License.
*/
-// TODO(b/129481165): remove the #pragma below and fix conversion issues
-#pragma clang diagnostic push
-#pragma clang diagnostic ignored "-Wconversion"
-
#include <gtest/gtest.h>
#include <compositionengine/LayerCreationArgs.h>
@@ -63,6 +59,3 @@
} // namespace
} // namespace android::compositionengine
-
-// TODO(b/129481165): remove the #pragma below and fix conversion issues
-#pragma clang diagnostic pop // ignored "-Wconversion"
diff --git a/services/surfaceflinger/CompositionEngine/tests/MockHWC2.h b/services/surfaceflinger/CompositionEngine/tests/MockHWC2.h
index 7fd6541..a51cc67 100644
--- a/services/surfaceflinger/CompositionEngine/tests/MockHWC2.h
+++ b/services/surfaceflinger/CompositionEngine/tests/MockHWC2.h
@@ -20,13 +20,21 @@
#include <ui/Fence.h>
#include <ui/FloatRect.h>
#include <ui/GraphicBuffer.h>
-#include <ui/GraphicTypes.h>
+
#include <ui/Rect.h>
#include <ui/Region.h>
#include <ui/Transform.h>
+// TODO(b/129481165): remove the #pragma below and fix conversion issues
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Wconversion"
+
+#include <ui/GraphicTypes.h>
#include "DisplayHardware/HWC2.h"
+// TODO(b/129481165): remove the #pragma below and fix conversion issues
+#pragma clang diagnostic pop // ignored "-Wconversion"
+
namespace HWC2 {
namespace mock {
diff --git a/services/surfaceflinger/CompositionEngine/tests/MockHWComposer.h b/services/surfaceflinger/CompositionEngine/tests/MockHWComposer.h
index 9971791..502a33f 100644
--- a/services/surfaceflinger/CompositionEngine/tests/MockHWComposer.h
+++ b/services/surfaceflinger/CompositionEngine/tests/MockHWComposer.h
@@ -19,8 +19,15 @@
#include <compositionengine/Output.h>
#include <gmock/gmock.h>
+// TODO(b/129481165): remove the #pragma below and fix conversion issues
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Wconversion"
+
#include "DisplayHardware/HWComposer.h"
+// TODO(b/129481165): remove the #pragma below and fix conversion issues
+#pragma clang diagnostic pop // ignored "-Wconversion"
+
namespace android {
namespace mock {
diff --git a/services/surfaceflinger/CompositionEngine/tests/OutputLayerTest.cpp b/services/surfaceflinger/CompositionEngine/tests/OutputLayerTest.cpp
index bd830be..0e579fa 100644
--- a/services/surfaceflinger/CompositionEngine/tests/OutputLayerTest.cpp
+++ b/services/surfaceflinger/CompositionEngine/tests/OutputLayerTest.cpp
@@ -14,10 +14,6 @@
* limitations under the License.
*/
-// TODO(b/129481165): remove the #pragma below and fix conversion issues
-#pragma clang diagnostic push
-#pragma clang diagnostic ignored "-Wconversion"
-
#include <compositionengine/impl/OutputLayer.h>
#include <compositionengine/impl/OutputLayerCompositionState.h>
#include <compositionengine/mock/CompositionEngine.h>
@@ -1058,6 +1054,3 @@
} // namespace
} // namespace android::compositionengine
-
-// TODO(b/129481165): remove the #pragma below and fix conversion issues
-#pragma clang diagnostic pop // ignored "-Wconversion"
diff --git a/services/surfaceflinger/CompositionEngine/tests/OutputTest.cpp b/services/surfaceflinger/CompositionEngine/tests/OutputTest.cpp
index 9a2889e..463d095 100644
--- a/services/surfaceflinger/CompositionEngine/tests/OutputTest.cpp
+++ b/services/surfaceflinger/CompositionEngine/tests/OutputTest.cpp
@@ -14,10 +14,6 @@
* limitations under the License.
*/
-// TODO(b/129481165): remove the #pragma below and fix conversion issues
-#pragma clang diagnostic push
-#pragma clang diagnostic ignored "-Wconversion"
-
#include <cmath>
#include <android-base/stringprintf.h>
@@ -253,16 +249,19 @@
const int32_t orientation = 123;
const Rect frame{1, 2, 3, 4};
const Rect viewport{5, 6, 7, 8};
- const Rect scissor{9, 10, 11, 12};
+ const Rect sourceClip{9, 10, 11, 12};
+ const Rect destinationClip{13, 14, 15, 16};
const bool needsFiltering = true;
- mOutput->setProjection(transform, orientation, frame, viewport, scissor, needsFiltering);
+ mOutput->setProjection(transform, orientation, frame, viewport, sourceClip, destinationClip,
+ needsFiltering);
EXPECT_THAT(mOutput->getState().transform, transform);
EXPECT_EQ(orientation, mOutput->getState().orientation);
EXPECT_EQ(frame, mOutput->getState().frame);
EXPECT_EQ(viewport, mOutput->getState().viewport);
- EXPECT_EQ(scissor, mOutput->getState().scissor);
+ EXPECT_EQ(sourceClip, mOutput->getState().sourceClip);
+ EXPECT_EQ(destinationClip, mOutput->getState().destinationClip);
EXPECT_EQ(needsFiltering, mOutput->getState().needsFiltering);
}
@@ -2782,7 +2781,8 @@
mOutput.mState.frame = kDefaultOutputFrame;
mOutput.mState.viewport = kDefaultOutputViewport;
- mOutput.mState.scissor = kDefaultOutputScissor;
+ mOutput.mState.sourceClip = kDefaultOutputSourceClip;
+ mOutput.mState.destinationClip = kDefaultOutputDestinationClip;
mOutput.mState.transform = ui::Transform{kDefaultOutputOrientation};
mOutput.mState.orientation = kDefaultOutputOrientation;
mOutput.mState.dataspace = kDefaultOutputDataspace;
@@ -2826,7 +2826,8 @@
static const Rect kDefaultOutputFrame;
static const Rect kDefaultOutputViewport;
- static const Rect kDefaultOutputScissor;
+ static const Rect kDefaultOutputSourceClip;
+ static const Rect kDefaultOutputDestinationClip;
static const mat4 kDefaultColorTransformMat;
static const Region kDebugRegion;
@@ -2846,7 +2847,8 @@
const Rect OutputComposeSurfacesTest::kDefaultOutputFrame{1001, 1002, 1003, 1004};
const Rect OutputComposeSurfacesTest::kDefaultOutputViewport{1005, 1006, 1007, 1008};
-const Rect OutputComposeSurfacesTest::kDefaultOutputScissor{1009, 1010, 1011, 1012};
+const Rect OutputComposeSurfacesTest::kDefaultOutputSourceClip{1009, 1010, 1011, 1012};
+const Rect OutputComposeSurfacesTest::kDefaultOutputDestinationClip{1013, 1014, 1015, 1016};
const mat4 OutputComposeSurfacesTest::kDefaultColorTransformMat{mat4() * 0.5f};
const Region OutputComposeSurfacesTest::kDebugRegion{Rect{100, 101, 102, 103}};
const HdrCapabilities OutputComposeSurfacesTest::
@@ -3089,9 +3091,10 @@
verify().ifMixedCompositionIs(true)
.andIfUsesHdr(true)
.andIfSkipColorTransform(false)
- .thenExpectDisplaySettingsUsed({kDefaultOutputScissor, kDefaultOutputScissor, mat4(),
- kDefaultMaxLuminance, kDefaultOutputDataspace, mat4(),
- Region::INVALID_REGION, kDefaultOutputOrientation})
+ .thenExpectDisplaySettingsUsed({kDefaultOutputDestinationClip, kDefaultOutputSourceClip,
+ mat4(), kDefaultMaxLuminance, kDefaultOutputDataspace,
+ mat4(), Region::INVALID_REGION,
+ kDefaultOutputOrientation})
.execute()
.expectAFenceWasReturned();
}
@@ -3100,9 +3103,10 @@
verify().ifMixedCompositionIs(true)
.andIfUsesHdr(false)
.andIfSkipColorTransform(false)
- .thenExpectDisplaySettingsUsed({kDefaultOutputScissor, kDefaultOutputScissor, mat4(),
- kDefaultMaxLuminance, kDefaultOutputDataspace, mat4(),
- Region::INVALID_REGION, kDefaultOutputOrientation})
+ .thenExpectDisplaySettingsUsed({kDefaultOutputDestinationClip, kDefaultOutputSourceClip,
+ mat4(), kDefaultMaxLuminance, kDefaultOutputDataspace,
+ mat4(), Region::INVALID_REGION,
+ kDefaultOutputOrientation})
.execute()
.expectAFenceWasReturned();
}
@@ -3111,8 +3115,8 @@
verify().ifMixedCompositionIs(false)
.andIfUsesHdr(true)
.andIfSkipColorTransform(false)
- .thenExpectDisplaySettingsUsed({kDefaultOutputScissor, kDefaultOutputScissor, mat4(),
- kDefaultMaxLuminance, kDefaultOutputDataspace,
+ .thenExpectDisplaySettingsUsed({kDefaultOutputDestinationClip, kDefaultOutputSourceClip,
+ mat4(), kDefaultMaxLuminance, kDefaultOutputDataspace,
kDefaultColorTransformMat, Region::INVALID_REGION,
kDefaultOutputOrientation})
.execute()
@@ -3123,8 +3127,8 @@
verify().ifMixedCompositionIs(false)
.andIfUsesHdr(false)
.andIfSkipColorTransform(false)
- .thenExpectDisplaySettingsUsed({kDefaultOutputScissor, kDefaultOutputScissor, mat4(),
- kDefaultMaxLuminance, kDefaultOutputDataspace,
+ .thenExpectDisplaySettingsUsed({kDefaultOutputDestinationClip, kDefaultOutputSourceClip,
+ mat4(), kDefaultMaxLuminance, kDefaultOutputDataspace,
kDefaultColorTransformMat, Region::INVALID_REGION,
kDefaultOutputOrientation})
.execute()
@@ -3136,9 +3140,10 @@
verify().ifMixedCompositionIs(false)
.andIfUsesHdr(true)
.andIfSkipColorTransform(true)
- .thenExpectDisplaySettingsUsed({kDefaultOutputScissor, kDefaultOutputScissor, mat4(),
- kDefaultMaxLuminance, kDefaultOutputDataspace, mat4(),
- Region::INVALID_REGION, kDefaultOutputOrientation})
+ .thenExpectDisplaySettingsUsed({kDefaultOutputDestinationClip, kDefaultOutputSourceClip,
+ mat4(), kDefaultMaxLuminance, kDefaultOutputDataspace,
+ mat4(), Region::INVALID_REGION,
+ kDefaultOutputOrientation})
.execute()
.expectAFenceWasReturned();
}
@@ -3349,7 +3354,8 @@
GenerateClientCompositionRequestsTest_ThreeLayers() {
mOutput.mState.frame = kDisplayFrame;
mOutput.mState.viewport = kDisplayViewport;
- mOutput.mState.scissor = kDisplayScissor;
+ mOutput.mState.sourceClip = kDisplaySourceClip;
+ mOutput.mState.destinationClip = kDisplayDestinationClip;
mOutput.mState.transform = ui::Transform{kDisplayOrientation};
mOutput.mState.orientation = kDisplayOrientation;
mOutput.mState.needsFiltering = false;
@@ -3380,14 +3386,17 @@
static const Rect kDisplayFrame;
static const Rect kDisplayViewport;
- static const Rect kDisplayScissor;
+ static const Rect kDisplaySourceClip;
+ static const Rect kDisplayDestinationClip;
std::array<Layer, 3> mLayers;
};
const Rect GenerateClientCompositionRequestsTest_ThreeLayers::kDisplayFrame(0, 0, 100, 200);
const Rect GenerateClientCompositionRequestsTest_ThreeLayers::kDisplayViewport(0, 0, 101, 201);
-const Rect GenerateClientCompositionRequestsTest_ThreeLayers::kDisplayScissor(0, 0, 102, 202);
+const Rect GenerateClientCompositionRequestsTest_ThreeLayers::kDisplaySourceClip(0, 0, 102, 202);
+const Rect GenerateClientCompositionRequestsTest_ThreeLayers::kDisplayDestinationClip(0, 0, 103,
+ 203);
TEST_F(GenerateClientCompositionRequestsTest_ThreeLayers, handlesNoClientCompostionLayers) {
EXPECT_CALL(mLayers[0].mOutputLayer, requiresClientComposition()).WillOnce(Return(false));
@@ -3793,13 +3802,15 @@
const Rect kPortraitFrame(0, 0, 1000, 2000);
const Rect kPortraitViewport(0, 0, 2000, 1000);
- const Rect kPortraitScissor(0, 0, 1000, 2000);
+ const Rect kPortraitSourceClip(0, 0, 1000, 2000);
+ const Rect kPortraitDestinationClip(0, 0, 1000, 2000);
const uint32_t kPortraitOrientation = TR_ROT_90;
constexpr ui::Dataspace kOutputDataspace = ui::Dataspace::DISPLAY_P3;
mOutput.mState.frame = kPortraitFrame;
mOutput.mState.viewport = kPortraitViewport;
- mOutput.mState.scissor = kPortraitScissor;
+ mOutput.mState.sourceClip = kPortraitSourceClip;
+ mOutput.mState.destinationClip = kPortraitDestinationClip;
mOutput.mState.transform = ui::Transform{kPortraitOrientation};
mOutput.mState.orientation = kPortraitOrientation;
mOutput.mState.needsFiltering = false;
@@ -3934,6 +3945,3 @@
} // namespace
} // namespace android::compositionengine
-
-// TODO(b/129481165): remove the #pragma below and fix conversion issues
-#pragma clang diagnostic pop // ignored "-Wconversion"
diff --git a/services/surfaceflinger/CompositionEngine/tests/RenderSurfaceTest.cpp b/services/surfaceflinger/CompositionEngine/tests/RenderSurfaceTest.cpp
index 4fba10b..fd47e45 100644
--- a/services/surfaceflinger/CompositionEngine/tests/RenderSurfaceTest.cpp
+++ b/services/surfaceflinger/CompositionEngine/tests/RenderSurfaceTest.cpp
@@ -14,10 +14,6 @@
* limitations under the License.
*/
-// TODO(b/129481165): remove the #pragma below and fix conversion issues
-#pragma clang diagnostic push
-#pragma clang diagnostic ignored "-Wconversion"
-
#include <cstdarg>
#include <cstdint>
@@ -354,6 +350,3 @@
} // namespace
} // namespace android::compositionengine
-
-// TODO(b/129481165): remove the #pragma below and fix conversion issues
-#pragma clang diagnostic pop // ignored "-Wconversion"
diff --git a/services/surfaceflinger/DisplayDevice.cpp b/services/surfaceflinger/DisplayDevice.cpp
index 4ae6dad..f3fe159 100644
--- a/services/surfaceflinger/DisplayDevice.cpp
+++ b/services/surfaceflinger/DisplayDevice.cpp
@@ -222,10 +222,13 @@
const bool needsFiltering =
(!globalTransform.preserveRects() || (type >= ui::Transform::SCALE));
- Rect scissor = globalTransform.transform(viewport);
- if (scissor.isEmpty()) {
- scissor = displayBounds;
+ Rect sourceClip = globalTransform.transform(viewport);
+ if (sourceClip.isEmpty()) {
+ sourceClip = displayBounds;
}
+ // For normal display use we always set the source and destination clip
+ // rectangles to the same values.
+ const Rect& destinationClip = sourceClip;
uint32_t transformOrientation;
@@ -236,8 +239,8 @@
transformOrientation = ui::Transform::toRotationFlags(orientation);
}
- getCompositionDisplay()->setProjection(globalTransform, transformOrientation,
- frame, viewport, scissor, needsFiltering);
+ getCompositionDisplay()->setProjection(globalTransform, transformOrientation, frame, viewport,
+ sourceClip, destinationClip, needsFiltering);
}
ui::Transform::RotationFlags DisplayDevice::getPrimaryDisplayRotationFlags() {
@@ -302,8 +305,8 @@
return mCompositionDisplay->getState().frame;
}
-const Rect& DisplayDevice::getScissor() const {
- return mCompositionDisplay->getState().scissor;
+const Rect& DisplayDevice::getSourceClip() const {
+ return mCompositionDisplay->getState().sourceClip;
}
bool DisplayDevice::hasWideColorGamut() const {
diff --git a/services/surfaceflinger/DisplayDevice.h b/services/surfaceflinger/DisplayDevice.h
index 605e7c8..75c709e 100644
--- a/services/surfaceflinger/DisplayDevice.h
+++ b/services/surfaceflinger/DisplayDevice.h
@@ -96,7 +96,7 @@
const ui::Transform& getTransform() const;
const Rect& getViewport() const;
const Rect& getFrame() const;
- const Rect& getScissor() const;
+ const Rect& getSourceClip() const;
bool needsFiltering() const;
uint32_t getLayerStack() const;
@@ -227,8 +227,10 @@
public:
DisplayRenderArea(const sp<const DisplayDevice>& display,
RotationFlags rotation = ui::Transform::ROT_0)
- : DisplayRenderArea(display, display->getBounds(), display->getWidth(),
- display->getHeight(), display->getCompositionDataSpace(), rotation) {}
+ : DisplayRenderArea(display, display->getBounds(),
+ static_cast<uint32_t>(display->getWidth()),
+ static_cast<uint32_t>(display->getHeight()),
+ display->getCompositionDataSpace(), rotation) {}
DisplayRenderArea(sp<const DisplayDevice> display, const Rect& sourceCrop, uint32_t reqWidth,
uint32_t reqHeight, ui::Dataspace reqDataSpace, RotationFlags rotation,
@@ -269,7 +271,7 @@
Rect getSourceCrop() const override {
// use the projected display viewport by default.
if (mSourceCrop.isEmpty()) {
- return mDisplay->getScissor();
+ return mDisplay->getSourceClip();
}
// Recompute the device transformation for the source crop.
@@ -278,14 +280,14 @@
ui::Transform translateLogical;
ui::Transform scale;
const Rect& viewport = mDisplay->getViewport();
- const Rect& scissor = mDisplay->getScissor();
+ const Rect& sourceClip = mDisplay->getSourceClip();
const Rect& frame = mDisplay->getFrame();
const auto flags = ui::Transform::toRotationFlags(mDisplay->getPhysicalOrientation());
rotation.set(flags, getWidth(), getHeight());
translateLogical.set(-viewport.left, -viewport.top);
- translatePhysical.set(scissor.left, scissor.top);
+ translatePhysical.set(sourceClip.left, sourceClip.top);
scale.set(frame.getWidth() / float(viewport.getWidth()), 0, 0,
frame.getHeight() / float(viewport.getHeight()));
const ui::Transform finalTransform =
diff --git a/services/surfaceflinger/DisplayHardware/ComposerHal.h b/services/surfaceflinger/DisplayHardware/ComposerHal.h
index eef0c8b..aa43f09 100644
--- a/services/surfaceflinger/DisplayHardware/ComposerHal.h
+++ b/services/surfaceflinger/DisplayHardware/ComposerHal.h
@@ -23,6 +23,10 @@
#include <utility>
#include <vector>
+// TODO(b/129481165): remove the #pragma below and fix conversion issues
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Wconversion"
+
#if defined(USE_VR_COMPOSER) && USE_VR_COMPOSER
#include <android/frameworks/vr/composer/2.0/IVrComposerClient.h>
#endif // defined(USE_VR_COMPOSER) && USE_VR_COMPOSER
@@ -36,6 +40,9 @@
#include <ui/GraphicBuffer.h>
#include <utils/StrongPointer.h>
+// TODO(b/129481165): remove the #pragma below and fix conversion issues
+#pragma clang diagnostic pop // ignored "-Wconversion"
+
namespace android {
namespace Hwc2 {
diff --git a/services/surfaceflinger/DisplayHardware/HWComposer.h b/services/surfaceflinger/DisplayHardware/HWComposer.h
index a0dabb4..effe43b 100644
--- a/services/surfaceflinger/DisplayHardware/HWComposer.h
+++ b/services/surfaceflinger/DisplayHardware/HWComposer.h
@@ -27,7 +27,13 @@
#include <android-base/thread_annotations.h>
#include <ui/Fence.h>
+
+// TODO(b/129481165): remove the #pragma below and fix conversion issues
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Wconversion"
#include <ui/GraphicTypes.h>
+#pragma clang diagnostic pop
+
#include <utils/StrongPointer.h>
#include <utils/Timers.h>
diff --git a/services/surfaceflinger/Layer.cpp b/services/surfaceflinger/Layer.cpp
index 11ae46d..cedab59 100644
--- a/services/surfaceflinger/Layer.cpp
+++ b/services/surfaceflinger/Layer.cpp
@@ -101,6 +101,7 @@
mCurrentState.active.w = UINT32_MAX;
mCurrentState.active.h = UINT32_MAX;
mCurrentState.active.transform.set(0, 0);
+ mCurrentState.frameNumber = 0;
mCurrentState.transform = 0;
mCurrentState.transformToDisplayInverse = false;
mCurrentState.crop.makeInvalid();
@@ -144,7 +145,7 @@
mFlinger->onLayerDestroyed(this);
}
-LayerCreationArgs::LayerCreationArgs(SurfaceFlinger* flinger, const sp<Client>& client,
+LayerCreationArgs::LayerCreationArgs(SurfaceFlinger* flinger, const sp<Client> client,
std::string name, uint32_t w, uint32_t h, uint32_t flags,
LayerMetadata metadata)
: flinger(flinger),
@@ -1256,8 +1257,11 @@
return true;
}
-float Layer::getFrameRate() const {
- return getDrawingState().frameRate;
+std::optional<float> Layer::getFrameRate() const {
+ const auto frameRate = getDrawingState().frameRate;
+ if (frameRate > 0.f || frameRate == FRAME_RATE_NO_VOTE) return frameRate;
+
+ return {};
}
void Layer::deferTransactionUntil_legacy(const sp<Layer>& barrierLayer, uint64_t frameNumber) {
@@ -1800,6 +1804,15 @@
}
}
+void Layer::traverse(LayerVector::StateSet state, const LayerVector::Visitor& visitor) {
+ visitor(this);
+ const LayerVector& children =
+ state == LayerVector::StateSet::Drawing ? mDrawingChildren : mCurrentChildren;
+ for (const sp<Layer>& child : children) {
+ child->traverse(state, visitor);
+ }
+}
+
LayerVector Layer::makeChildrenTraversalList(LayerVector::StateSet stateSet,
const std::vector<Layer*>& layersInTree) {
LOG_ALWAYS_FATAL_IF(stateSet == LayerVector::StateSet::Invalid,
diff --git a/services/surfaceflinger/Layer.h b/services/surfaceflinger/Layer.h
index 6acf5fb..da3df8f 100644
--- a/services/surfaceflinger/Layer.h
+++ b/services/surfaceflinger/Layer.h
@@ -78,11 +78,11 @@
// ---------------------------------------------------------------------------
struct LayerCreationArgs {
- LayerCreationArgs(SurfaceFlinger* flinger, const sp<Client>& client, std::string name,
+ LayerCreationArgs(SurfaceFlinger* flinger, const sp<Client> client, std::string name,
uint32_t w, uint32_t h, uint32_t flags, LayerMetadata metadata);
SurfaceFlinger* flinger;
- const sp<Client>& client;
+ const sp<Client> client;
std::string name;
uint32_t w;
uint32_t h;
@@ -189,6 +189,7 @@
ui::Dataspace dataspace;
// The fields below this point are only used by BufferStateLayer
+ uint64_t frameNumber;
Geometry active;
uint32_t transform;
@@ -678,6 +679,15 @@
renderengine::ShadowSettings getShadowSettings(const Rect& viewport) const;
+ /**
+ * Traverse this layer and it's hierarchy of children directly. Unlike traverseInZOrder
+ * which will not emit children who have relativeZOrder to another layer, this method
+ * just directly emits all children. It also emits them in no particular order.
+ * So this method is not suitable for graphical operations, as it doesn't represent
+ * the scene state, but it's also more efficient than traverseInZOrder and so useful for
+ * book-keeping.
+ */
+ void traverse(LayerVector::StateSet stateSet, const LayerVector::Visitor& visitor);
void traverseInReverseZOrder(LayerVector::StateSet stateSet,
const LayerVector::Visitor& visitor);
void traverseInZOrder(LayerVector::StateSet stateSet, const LayerVector::Visitor& visitor);
@@ -744,8 +754,9 @@
*/
Rect getCroppedBufferSize(const Layer::State& s) const;
+ constexpr static auto FRAME_RATE_NO_VOTE = -1.0f;
virtual bool setFrameRate(float frameRate);
- virtual float getFrameRate() const;
+ virtual std::optional<float> getFrameRate() const;
protected:
// constant
diff --git a/services/surfaceflinger/LayerVector.cpp b/services/surfaceflinger/LayerVector.cpp
index 7c959b9..9b94920 100644
--- a/services/surfaceflinger/LayerVector.cpp
+++ b/services/surfaceflinger/LayerVector.cpp
@@ -86,6 +86,14 @@
layer->traverseInReverseZOrder(stateSet, visitor);
}
}
+
+void LayerVector::traverse(const Visitor& visitor) const {
+ for (auto i = static_cast<int64_t>(size()) - 1; i >= 0; i--) {
+ const auto& layer = (*this)[i];
+ layer->traverse(mStateSet, visitor);
+ }
+}
+
} // namespace android
// TODO(b/129481165): remove the #pragma below and fix conversion issues
diff --git a/services/surfaceflinger/LayerVector.h b/services/surfaceflinger/LayerVector.h
index 88d7711..a531f4f 100644
--- a/services/surfaceflinger/LayerVector.h
+++ b/services/surfaceflinger/LayerVector.h
@@ -50,7 +50,7 @@
using Visitor = std::function<void(Layer*)>;
void traverseInReverseZOrder(StateSet stateSet, const Visitor& visitor) const;
void traverseInZOrder(StateSet stateSet, const Visitor& visitor) const;
-
+ void traverse(const Visitor& visitor) const;
private:
const StateSet mStateSet;
};
diff --git a/services/surfaceflinger/RefreshRateOverlay.cpp b/services/surfaceflinger/RefreshRateOverlay.cpp
index 33e5796..d3d9d3a 100644
--- a/services/surfaceflinger/RefreshRateOverlay.cpp
+++ b/services/surfaceflinger/RefreshRateOverlay.cpp
@@ -155,6 +155,7 @@
Mutex::Autolock _l(mFlinger.mStateLock);
mLayer = mClient->getLayerUser(mIBinder);
+ mLayer->setFrameRate(Layer::FRAME_RATE_NO_VOTE);
// setting Layer's Z requires resorting layersSortedByZ
ssize_t idx = mFlinger.mCurrentState.layersSortedByZ.indexOf(mLayer);
diff --git a/services/surfaceflinger/RenderArea.h b/services/surfaceflinger/RenderArea.h
index a7a6dd5..9b3a9f4 100644
--- a/services/surfaceflinger/RenderArea.h
+++ b/services/surfaceflinger/RenderArea.h
@@ -71,8 +71,8 @@
RotationFlags getRotationFlags() const { return mRotationFlags; }
// Returns the size of the physical render area.
- int getReqWidth() const { return mReqWidth; }
- int getReqHeight() const { return mReqHeight; }
+ int getReqWidth() const { return static_cast<int>(mReqWidth); }
+ int getReqHeight() const { return static_cast<int>(mReqHeight); }
// Returns the composition data space of the render area.
ui::Dataspace getReqDataSpace() const { return mReqDataSpace; }
diff --git a/services/surfaceflinger/Scheduler/LayerHistory.cpp b/services/surfaceflinger/Scheduler/LayerHistory.cpp
index abf0cd6..b976523 100644
--- a/services/surfaceflinger/Scheduler/LayerHistory.cpp
+++ b/services/surfaceflinger/Scheduler/LayerHistory.cpp
@@ -14,10 +14,6 @@
* limitations under the License.
*/
-// TODO(b/129481165): remove the #pragma below and fix conversion issues
-#pragma clang diagnostic push
-#pragma clang diagnostic ignored "-Wconversion"
-
#undef LOG_TAG
#define LOG_TAG "LayerHistory"
#define ATRACE_TAG ATRACE_TAG_GRAPHICS
@@ -43,7 +39,7 @@
namespace {
bool isLayerActive(const Layer& layer, const LayerInfo& info, nsecs_t threshold) {
- if (layer.getFrameRate() > .0f) {
+ if (layer.getFrameRate().has_value()) {
return layer.isVisible();
}
return layer.isVisible() && info.getLastUpdatedTime() >= threshold;
@@ -77,7 +73,8 @@
: mTraceEnabled(traceEnabled()), mUseFrameRatePriority(useFrameRatePriority()) {}
LayerHistory::~LayerHistory() = default;
-void LayerHistory::registerLayer(Layer* layer, float lowRefreshRate, float highRefreshRate) {
+void LayerHistory::registerLayer(Layer* layer, float lowRefreshRate, float highRefreshRate,
+ LayerVoteType /*type*/) {
auto info = std::make_unique<LayerInfo>(lowRefreshRate, highRefreshRate);
std::lock_guard lock(mLock);
mLayerInfos.emplace_back(layer, std::move(info));
@@ -101,8 +98,6 @@
}
LayerHistory::Summary LayerHistory::summarize(nsecs_t now) {
- float maxRefreshRate = 0;
-
std::lock_guard lock(mLock);
partitionLayers(now);
@@ -113,7 +108,7 @@
if (recent || CC_UNLIKELY(mTraceEnabled)) {
const float refreshRate = info->getRefreshRate(now);
- if (recent && refreshRate > maxRefreshRate) {
+ if (recent && refreshRate > 0.0f) {
if (const auto layer = activeLayer.promote(); layer) {
const int32_t priority = layer->getFrameRateSelectionPriority();
// TODO(b/142507166): This is where the scoring algorithm should live.
@@ -124,36 +119,30 @@
}
}
+ LayerHistory::Summary summary;
for (const auto& [weakLayer, info] : activeLayers()) {
const bool recent = info->isRecentlyActive(now);
auto layer = weakLayer.promote();
// Only use the layer if the reference still exists.
if (layer || CC_UNLIKELY(mTraceEnabled)) {
- float refreshRate = 0.f;
- // Default content refresh rate is only used when dealing with recent layers.
- if (recent) {
- refreshRate = info->getRefreshRate(now);
- }
// Check if frame rate was set on layer.
- float frameRate = layer->getFrameRate();
- if (frameRate > 0.f) {
- // Override content detection refresh rate, if it was set.
- refreshRate = frameRate;
- }
- if (refreshRate > maxRefreshRate) {
- maxRefreshRate = refreshRate;
+ auto frameRate = layer->getFrameRate();
+ if (frameRate.has_value() && frameRate.value() > 0.f) {
+ summary.push_back(
+ {layer->getName(), LayerVoteType::Explicit, *frameRate, /* weight */ 1.0f});
+ } else if (recent) {
+ frameRate = info->getRefreshRate(now);
+ summary.push_back({layer->getName(), LayerVoteType::Heuristic, *frameRate,
+ /* weight */ 1.0f});
}
if (CC_UNLIKELY(mTraceEnabled)) {
- trace(weakLayer, std::round(refreshRate));
+ trace(weakLayer, round<int>(*frameRate));
}
}
}
- if (CC_UNLIKELY(mTraceEnabled)) {
- ALOGD("%s: maxRefreshRate=%.2f", __FUNCTION__, maxRefreshRate);
- }
- return {maxRefreshRate};
+ return summary;
}
void LayerHistory::partitionLayers(nsecs_t now) {
@@ -186,7 +175,7 @@
}
}
- mLayerInfos.erase(mLayerInfos.begin() + end, mLayerInfos.end());
+ mLayerInfos.erase(mLayerInfos.begin() + static_cast<long>(end), mLayerInfos.end());
}
void LayerHistory::clear() {
@@ -199,23 +188,5 @@
mActiveLayersEnd = 0;
}
-bool LayerHistory::hasClientSpecifiedFrameRate() {
- std::lock_guard lock(mLock);
- for (const auto& [weakLayer, info] : activeLayers()) {
- auto layer = weakLayer.promote();
- if (layer) {
- float frameRate = layer->getFrameRate();
- // Found a layer that has a frame rate set on it.
- if (fabs(frameRate) > 0.f) {
- return true;
- }
- }
- }
- // Did not find any layers that have frame rate.
- return false;
-}
-
} // namespace android::scheduler::impl
-// TODO(b/129481165): remove the #pragma below and fix conversion issues
-#pragma clang diagnostic pop // ignored "-Wconversion"
diff --git a/services/surfaceflinger/Scheduler/LayerHistory.h b/services/surfaceflinger/Scheduler/LayerHistory.h
index f217134..a1ae35c 100644
--- a/services/surfaceflinger/Scheduler/LayerHistory.h
+++ b/services/surfaceflinger/Scheduler/LayerHistory.h
@@ -25,6 +25,8 @@
#include <utility>
#include <vector>
+#include "RefreshRateConfigs.h"
+
namespace android {
class Layer;
@@ -33,29 +35,32 @@
namespace scheduler {
class LayerHistoryTest;
+class LayerHistoryTestV2;
class LayerInfo;
+class LayerInfoV2;
class LayerHistory {
public:
+ using LayerVoteType = RefreshRateConfigs::LayerVoteType;
+
virtual ~LayerHistory() = default;
// Layers are unregistered when the weak reference expires.
- virtual void registerLayer(Layer*, float lowRefreshRate, float highRefreshRate) = 0;
+ virtual void registerLayer(Layer*, float lowRefreshRate, float highRefreshRate,
+ LayerVoteType type) = 0;
+
+ // Sets the display size. Client is responsible for synchronization.
+ virtual void setDisplayArea(uint32_t displayArea) = 0;
// Marks the layer as active, and records the given state to its history.
virtual void record(Layer*, nsecs_t presentTime, nsecs_t now) = 0;
- struct Summary {
- float maxRefreshRate; // Maximum refresh rate among recently active layers.
- };
+ using Summary = std::vector<RefreshRateConfigs::LayerRequirement>;
// Rebuilds sets of active/inactive layers, and accumulates stats for active layers.
virtual Summary summarize(nsecs_t now) = 0;
virtual void clear() = 0;
-
- // Checks whether any of the active layers have a desired frame rate bit set on them.
- virtual bool hasClientSpecifiedFrameRate() = 0;
};
namespace impl {
@@ -68,7 +73,10 @@
virtual ~LayerHistory();
// Layers are unregistered when the weak reference expires.
- void registerLayer(Layer*, float lowRefreshRate, float highRefreshRate) override;
+ void registerLayer(Layer*, float lowRefreshRate, float highRefreshRate,
+ LayerVoteType type) override;
+
+ void setDisplayArea(uint32_t /*displayArea*/) override {}
// Marks the layer as active, and records the given state to its history.
void record(Layer*, nsecs_t presentTime, nsecs_t now) override;
@@ -78,10 +86,6 @@
void clear() override;
- // Traverses all active layers and checks whether any of them have a desired frame
- // rate bit set on them.
- bool hasClientSpecifiedFrameRate() override;
-
private:
friend class android::scheduler::LayerHistoryTest;
friend TestableScheduler;
@@ -94,7 +98,7 @@
const size_t index;
auto begin() { return infos.begin(); }
- auto end() { return begin() + index; }
+ auto end() { return begin() + static_cast<long>(index); }
};
ActiveLayers activeLayers() REQUIRES(mLock) { return {mLayerInfos, mActiveLayersEnd}; }
@@ -118,6 +122,64 @@
const bool mUseFrameRatePriority;
};
+class LayerHistoryV2 : public android::scheduler::LayerHistory {
+public:
+ LayerHistoryV2();
+ virtual ~LayerHistoryV2();
+
+ // Layers are unregistered when the weak reference expires.
+ void registerLayer(Layer*, float lowRefreshRate, float highRefreshRate,
+ LayerVoteType type) override;
+
+ // Sets the display size. Client is responsible for synchronization.
+ void setDisplayArea(uint32_t displayArea) override { mDisplayArea = displayArea; }
+
+ // Marks the layer as active, and records the given state to its history.
+ void record(Layer*, nsecs_t presentTime, nsecs_t now) override;
+
+ // Rebuilds sets of active/inactive layers, and accumulates stats for active layers.
+ android::scheduler::LayerHistory::Summary summarize(nsecs_t /*now*/) override;
+
+ void clear() override;
+
+private:
+ friend android::scheduler::LayerHistoryTestV2;
+ friend TestableScheduler;
+
+ using LayerPair = std::pair<wp<Layer>, std::unique_ptr<LayerInfoV2>>;
+ using LayerInfos = std::vector<LayerPair>;
+
+ struct ActiveLayers {
+ LayerInfos& infos;
+ const size_t index;
+
+ auto begin() { return infos.begin(); }
+ auto end() { return begin() + static_cast<long>(index); }
+ };
+
+ ActiveLayers activeLayers() REQUIRES(mLock) { return {mLayerInfos, mActiveLayersEnd}; }
+
+ // Iterates over layers in a single pass, swapping pairs such that active layers precede
+ // inactive layers, and inactive layers precede expired layers. Removes expired layers by
+ // truncating after inactive layers.
+ void partitionLayers(nsecs_t now) REQUIRES(mLock);
+
+ mutable std::mutex mLock;
+
+ // Partitioned such that active layers precede inactive layers. For fast lookup, the few active
+ // layers are at the front, and weak pointers are stored in contiguous memory to hit the cache.
+ LayerInfos mLayerInfos GUARDED_BY(mLock);
+ size_t mActiveLayersEnd GUARDED_BY(mLock) = 0;
+
+ uint32_t mDisplayArea = 0;
+
+ // Whether to emit systrace output and debug logs.
+ const bool mTraceEnabled;
+
+ // Whether to use priority sent from WindowManager to determine the relevancy of the layer.
+ const bool mUseFrameRatePriority;
+};
+
} // namespace impl
} // namespace scheduler
} // namespace android
diff --git a/services/surfaceflinger/Scheduler/LayerHistoryV2.cpp b/services/surfaceflinger/Scheduler/LayerHistoryV2.cpp
new file mode 100644
index 0000000..a6d2c74
--- /dev/null
+++ b/services/surfaceflinger/Scheduler/LayerHistoryV2.cpp
@@ -0,0 +1,206 @@
+/*
+ * 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 "LayerHistoryV2"
+#define ATRACE_TAG ATRACE_TAG_GRAPHICS
+
+#include "LayerHistory.h"
+
+#include <cutils/properties.h>
+#include <utils/Log.h>
+#include <utils/Timers.h>
+#include <utils/Trace.h>
+
+#include <algorithm>
+#include <cmath>
+#include <string>
+#include <utility>
+
+#include "../Layer.h"
+#include "SchedulerUtils.h"
+
+#include "LayerInfoV2.h"
+
+namespace android::scheduler::impl {
+
+namespace {
+
+bool isLayerActive(const Layer& layer, const LayerInfoV2& info, nsecs_t threshold) {
+ if (layer.getFrameRate().has_value()) {
+ return layer.isVisible();
+ }
+ return layer.isVisible() && info.getLastUpdatedTime() >= threshold;
+}
+
+bool traceEnabled() {
+ return property_get_bool("debug.sf.layer_history_trace", false);
+}
+
+bool useFrameRatePriority() {
+ char value[PROPERTY_VALUE_MAX];
+ property_get("debug.sf.use_frame_rate_priority", value, "1");
+ return atoi(value);
+}
+
+void trace(const wp<Layer>& weak, LayerHistory::LayerVoteType type, int fps) {
+ const auto layer = weak.promote();
+ if (!layer) return;
+
+ const auto& name = layer->getName();
+ const auto noVoteTag = "LFPS NoVote " + name;
+ const auto heuristicVoteTag = "LFPS Heuristic " + name;
+ const auto explicitVoteTag = "LFPS Explicit " + name;
+ const auto minVoteTag = "LFPS Min " + name;
+ const auto maxVoteTag = "LFPS Max " + name;
+
+ ATRACE_INT(noVoteTag.c_str(), type == LayerHistory::LayerVoteType::NoVote ? 1 : 0);
+ ATRACE_INT(heuristicVoteTag.c_str(), type == LayerHistory::LayerVoteType::Heuristic ? fps : 0);
+ ATRACE_INT(explicitVoteTag.c_str(), type == LayerHistory::LayerVoteType::Explicit ? fps : 0);
+ ATRACE_INT(minVoteTag.c_str(), type == LayerHistory::LayerVoteType::Min ? 1 : 0);
+ ATRACE_INT(maxVoteTag.c_str(), type == LayerHistory::LayerVoteType::Max ? 1 : 0);
+
+ ALOGD("%s: %s @ %d Hz", __FUNCTION__, name.c_str(), fps);
+}
+
+} // namespace
+
+LayerHistoryV2::LayerHistoryV2()
+ : mTraceEnabled(traceEnabled()), mUseFrameRatePriority(useFrameRatePriority()) {}
+LayerHistoryV2::~LayerHistoryV2() = default;
+
+void LayerHistoryV2::registerLayer(Layer* layer, float /*lowRefreshRate*/, float highRefreshRate,
+ LayerVoteType type) {
+ const nsecs_t highRefreshRatePeriod = static_cast<nsecs_t>(1e9f / highRefreshRate);
+ auto info = std::make_unique<LayerInfoV2>(highRefreshRatePeriod, type);
+ std::lock_guard lock(mLock);
+ mLayerInfos.emplace_back(layer, std::move(info));
+}
+
+void LayerHistoryV2::record(Layer* layer, nsecs_t presentTime, nsecs_t now) {
+ std::lock_guard lock(mLock);
+
+ const auto it = std::find_if(mLayerInfos.begin(), mLayerInfos.end(),
+ [layer](const auto& pair) { return pair.first == layer; });
+ LOG_FATAL_IF(it == mLayerInfos.end(), "%s: unknown layer %p", __FUNCTION__, layer);
+
+ const auto& info = it->second;
+ info->setLastPresentTime(presentTime, now);
+
+ // Activate layer if inactive.
+ if (const auto end = activeLayers().end(); it >= end) {
+ std::iter_swap(it, end);
+ mActiveLayersEnd++;
+ }
+}
+
+LayerHistoryV2::Summary LayerHistoryV2::summarize(nsecs_t now) {
+ LayerHistory::Summary summary;
+
+ std::lock_guard lock(mLock);
+
+ partitionLayers(now);
+
+ for (const auto& [layer, info] : activeLayers()) {
+ const auto strong = layer.promote();
+ if (!strong) {
+ continue;
+ }
+
+ const bool recent = info->isRecentlyActive(now);
+ if (recent) {
+ const auto [type, refreshRate] = info->getRefreshRate(now);
+ // Skip NoVote layer as those don't have any requirements
+ if (type == LayerHistory::LayerVoteType::NoVote) {
+ continue;
+ }
+
+ // Compute the layer's position on the screen
+ const Rect bounds = Rect(strong->getBounds());
+ const ui::Transform transform = strong->getTransform();
+ constexpr bool roundOutwards = true;
+ Rect transformed = transform.transform(bounds, roundOutwards);
+
+ const float layerArea = transformed.getWidth() * transformed.getHeight();
+ float weight = mDisplayArea ? layerArea / mDisplayArea : 0.0f;
+ summary.push_back({strong->getName(), type, refreshRate, weight});
+
+ if (CC_UNLIKELY(mTraceEnabled)) {
+ trace(layer, type, static_cast<int>(std::round(refreshRate)));
+ }
+ } else if (CC_UNLIKELY(mTraceEnabled)) {
+ trace(layer, LayerHistory::LayerVoteType::NoVote, 0);
+ }
+ }
+
+ return summary;
+}
+
+void LayerHistoryV2::partitionLayers(nsecs_t now) {
+ const nsecs_t threshold = getActiveLayerThreshold(now);
+
+ // Collect expired and inactive layers after active layers.
+ size_t i = 0;
+ while (i < mActiveLayersEnd) {
+ auto& [weak, info] = mLayerInfos[i];
+ if (const auto layer = weak.promote(); layer && isLayerActive(*layer, *info, threshold)) {
+ i++;
+ // Set layer vote if set
+ const auto frameRate = layer->getFrameRate();
+ if (frameRate.has_value()) {
+ if (*frameRate == Layer::FRAME_RATE_NO_VOTE) {
+ info->setLayerVote(LayerVoteType::NoVote, 0.f);
+ } else {
+ info->setLayerVote(LayerVoteType::Explicit, *frameRate);
+ }
+ } else {
+ info->resetLayerVote();
+ }
+ continue;
+ }
+
+ if (CC_UNLIKELY(mTraceEnabled)) {
+ trace(weak, LayerHistory::LayerVoteType::NoVote, 0);
+ }
+
+ info->clearHistory();
+ std::swap(mLayerInfos[i], mLayerInfos[--mActiveLayersEnd]);
+ }
+
+ // Collect expired layers after inactive layers.
+ size_t end = mLayerInfos.size();
+ while (i < end) {
+ if (mLayerInfos[i].first.promote()) {
+ i++;
+ } else {
+ std::swap(mLayerInfos[i], mLayerInfos[--end]);
+ }
+ }
+
+ mLayerInfos.erase(mLayerInfos.begin() + static_cast<long>(end), mLayerInfos.end());
+}
+
+void LayerHistoryV2::clear() {
+ std::lock_guard lock(mLock);
+
+ for (const auto& [layer, info] : activeLayers()) {
+ info->clearHistory();
+ }
+
+ mActiveLayersEnd = 0;
+}
+
+} // namespace android::scheduler::impl
diff --git a/services/surfaceflinger/Scheduler/LayerInfoV2.cpp b/services/surfaceflinger/Scheduler/LayerInfoV2.cpp
new file mode 100644
index 0000000..f309d4d
--- /dev/null
+++ b/services/surfaceflinger/Scheduler/LayerInfoV2.cpp
@@ -0,0 +1,138 @@
+/*
+ * 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.
+ */
+
+// #define LOG_NDEBUG 0
+
+#include "LayerInfoV2.h"
+
+#include <algorithm>
+#include <utility>
+
+#undef LOG_TAG
+#define LOG_TAG "LayerInfoV2"
+#define ATRACE_TAG ATRACE_TAG_GRAPHICS
+
+namespace android::scheduler {
+
+LayerInfoV2::LayerInfoV2(nsecs_t highRefreshRatePeriod, LayerHistory::LayerVoteType defaultVote)
+ : mHighRefreshRatePeriod(highRefreshRatePeriod),
+ mDefaultVote(defaultVote),
+ mLayerVote({defaultVote, 0.0f}) {}
+
+void LayerInfoV2::setLastPresentTime(nsecs_t lastPresentTime, nsecs_t now) {
+ lastPresentTime = std::max(lastPresentTime, static_cast<nsecs_t>(0));
+
+ mLastUpdatedTime = std::max(lastPresentTime, now);
+
+ FrameTimeData frameTime = {.presetTime = lastPresentTime, .queueTime = mLastUpdatedTime};
+
+ mFrameTimes.push_back(frameTime);
+ if (mFrameTimes.size() > HISTORY_SIZE) {
+ mFrameTimes.pop_front();
+ }
+}
+
+// Returns whether the earliest present time is within the active threshold.
+bool LayerInfoV2::isRecentlyActive(nsecs_t now) const {
+ if (mFrameTimes.empty()) {
+ return false;
+ }
+
+ return mFrameTimes.back().queueTime >= getActiveLayerThreshold(now);
+}
+
+bool LayerInfoV2::isFrequent(nsecs_t now) const {
+ // Assume layer is infrequent if too few present times have been recorded.
+ if (mFrameTimes.size() < FREQUENT_LAYER_WINDOW_SIZE) {
+ return false;
+ }
+
+ // Layer is frequent if the earliest value in the window of most recent present times is
+ // within threshold.
+ const auto it = mFrameTimes.end() - FREQUENT_LAYER_WINDOW_SIZE;
+ const nsecs_t threshold = now - MAX_FREQUENT_LAYER_PERIOD_NS.count();
+ return it->queueTime >= threshold;
+}
+
+bool LayerInfoV2::hasEnoughDataForHeuristic() const {
+ // The layer had to publish at least HISTORY_SIZE or HISTORY_TIME of updates
+ if (mFrameTimes.size() < HISTORY_SIZE &&
+ mFrameTimes.back().queueTime - mFrameTimes.front().queueTime < HISTORY_TIME.count()) {
+ return false;
+ }
+
+ return true;
+}
+
+std::optional<float> LayerInfoV2::calculateRefreshRateIfPossible() {
+ static constexpr float MARGIN = 1.0f; // 1Hz
+
+ if (!hasEnoughDataForHeuristic()) {
+ ALOGV("Not enough data");
+ return std::nullopt;
+ }
+
+ // Calculate the refresh rate by finding the average delta between frames
+ nsecs_t totalPresentTimeDeltas = 0;
+ for (auto it = mFrameTimes.begin(); it != mFrameTimes.end() - 1; ++it) {
+ // If there are no presentation timestamp provided we can't calculate the refresh rate
+ if (it->presetTime == 0 || (it + 1)->presetTime == 0) {
+ return std::nullopt;
+ }
+
+ totalPresentTimeDeltas +=
+ std::max(((it + 1)->presetTime - it->presetTime), mHighRefreshRatePeriod);
+ }
+ const float averageFrameTime =
+ static_cast<float>(totalPresentTimeDeltas) / (mFrameTimes.size() - 1);
+
+ // Now once we calculated the refresh rate we need to make sure that all the frames we captured
+ // are evenly distributed and we don't calculate the average across some burst of frames.
+ for (auto it = mFrameTimes.begin(); it != mFrameTimes.end() - 1; ++it) {
+ const nsecs_t presentTimeDeltas =
+ std::max(((it + 1)->presetTime - it->presetTime), mHighRefreshRatePeriod);
+ if (std::abs(presentTimeDeltas - averageFrameTime) > 2 * averageFrameTime) {
+ return std::nullopt;
+ }
+ }
+
+ const auto refreshRate = 1e9f / averageFrameTime;
+ if (std::abs(refreshRate - mLastReportedRefreshRate) > MARGIN) {
+ mLastReportedRefreshRate = refreshRate;
+ }
+
+ ALOGV("Refresh rate: %.2f", mLastReportedRefreshRate);
+ return mLastReportedRefreshRate;
+}
+
+std::pair<LayerHistory::LayerVoteType, float> LayerInfoV2::getRefreshRate(nsecs_t now) {
+ if (mLayerVote.type != LayerHistory::LayerVoteType::Heuristic) {
+ return {mLayerVote.type, mLayerVote.fps};
+ }
+
+ if (!isFrequent(now)) {
+ return {LayerHistory::LayerVoteType::Min, 0};
+ }
+
+ auto refreshRate = calculateRefreshRateIfPossible();
+ if (refreshRate.has_value()) {
+ return {LayerHistory::LayerVoteType::Heuristic, refreshRate.value()};
+ }
+
+ return {LayerHistory::LayerVoteType::Max, 0};
+}
+
+} // namespace android::scheduler
diff --git a/services/surfaceflinger/Scheduler/LayerInfoV2.h b/services/surfaceflinger/Scheduler/LayerInfoV2.h
new file mode 100644
index 0000000..564f05e
--- /dev/null
+++ b/services/surfaceflinger/Scheduler/LayerInfoV2.h
@@ -0,0 +1,117 @@
+/*
+ * 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.
+ */
+
+#pragma once
+
+#include <utils/Timers.h>
+
+#include <chrono>
+#include <deque>
+
+#include "LayerHistory.h"
+#include "RefreshRateConfigs.h"
+#include "SchedulerUtils.h"
+
+namespace android {
+
+class Layer;
+
+namespace scheduler {
+
+using namespace std::chrono_literals;
+
+// Maximum period between presents for a layer to be considered active.
+constexpr std::chrono::nanoseconds MAX_ACTIVE_LAYER_PERIOD_NS = 1200ms;
+
+// Earliest present time for a layer to be considered active.
+constexpr nsecs_t getActiveLayerThreshold(nsecs_t now) {
+ return now - MAX_ACTIVE_LAYER_PERIOD_NS.count();
+}
+
+// Stores history of present times and refresh rates for a layer.
+class LayerInfoV2 {
+ // Layer is considered frequent if the earliest value in the window of most recent present times
+ // is within a threshold. If a layer is infrequent, its average refresh rate is disregarded in
+ // favor of a low refresh rate.
+ static constexpr size_t FREQUENT_LAYER_WINDOW_SIZE = 3;
+ static constexpr std::chrono::nanoseconds MAX_FREQUENT_LAYER_PERIOD_NS = 250ms;
+
+ friend class LayerHistoryTestV2;
+
+public:
+ LayerInfoV2(nsecs_t highRefreshRatePeriod, LayerHistory::LayerVoteType defaultVote);
+
+ LayerInfoV2(const LayerInfo&) = delete;
+ LayerInfoV2& operator=(const LayerInfoV2&) = delete;
+
+ // Records the last requested present time. It also stores information about when
+ // the layer was last updated. If the present time is farther in the future than the
+ // updated time, the updated time is the present time.
+ void setLastPresentTime(nsecs_t lastPresentTime, nsecs_t now);
+
+ bool isRecentlyActive(nsecs_t now) const;
+
+ // Sets an explicit layer vote. This usually comes directly from the application via
+ // ANativeWindow_setFrameRate API
+ void setLayerVote(LayerHistory::LayerVoteType type, float fps) { mLayerVote = {type, fps}; }
+
+ // Sets the default layer vote. This will be the layer vote after calling to resetLayerVote().
+ // This is used for layers that called to setLayerVote() and then removed the vote, so that the
+ // layer can go back to whatever vote it had before the app voted for it.
+ void setDefaultLayerVote(LayerHistory::LayerVoteType type) { mDefaultVote = type; }
+
+ // Resets the layer vote to its default.
+ void resetLayerVote() { mLayerVote = {mDefaultVote, 0.0f}; }
+
+ std::pair<LayerHistory::LayerVoteType, float> getRefreshRate(nsecs_t now);
+
+ // Return the last updated time. If the present time is farther in the future than the
+ // updated time, the updated time is the present time.
+ nsecs_t getLastUpdatedTime() const { return mLastUpdatedTime; }
+
+ void clearHistory() { mFrameTimes.clear(); }
+
+private:
+ bool isFrequent(nsecs_t now) const;
+ bool hasEnoughDataForHeuristic() const;
+ std::optional<float> calculateRefreshRateIfPossible();
+
+ // Used for sanitizing the heuristic data
+ const nsecs_t mHighRefreshRatePeriod;
+ LayerHistory::LayerVoteType mDefaultVote;
+
+ nsecs_t mLastUpdatedTime = 0;
+
+ float mLastReportedRefreshRate = 0.0f;
+
+ // Holds information about the layer vote
+ struct {
+ LayerHistory::LayerVoteType type;
+ float fps;
+ } mLayerVote;
+
+ // Used to store the layer timestamps
+ struct FrameTimeData {
+ nsecs_t presetTime; // desiredPresentTime, if provided
+ nsecs_t queueTime; // buffer queue time
+ };
+ std::deque<FrameTimeData> mFrameTimes;
+ static constexpr size_t HISTORY_SIZE = 90;
+ static constexpr std::chrono::nanoseconds HISTORY_TIME = 1s;
+};
+
+} // namespace scheduler
+} // namespace android
diff --git a/services/surfaceflinger/Scheduler/PhaseOffsets.cpp b/services/surfaceflinger/Scheduler/PhaseOffsets.cpp
index 106aa9b..63d9c4b 100644
--- a/services/surfaceflinger/Scheduler/PhaseOffsets.cpp
+++ b/services/surfaceflinger/Scheduler/PhaseOffsets.cpp
@@ -14,10 +14,6 @@
* limitations under the License.
*/
-// TODO(b/129481165): remove the #pragma below and fix conversion issues
-#pragma clang diagnostic push
-#pragma clang diagnostic ignored "-Wconversion"
-
#include "PhaseOffsets.h"
#include <cutils/properties.h>
@@ -119,9 +115,9 @@
}
PhaseOffsets::Offsets PhaseOffsets::getHighFpsOffsets(nsecs_t vsyncDuration) const {
- const int highFpsLateAppOffsetNs =
+ const auto highFpsLateAppOffsetNs =
getProperty("debug.sf.high_fps_late_app_phase_offset_ns").value_or(2000000);
- const int highFpsLateSfOffsetNs =
+ const auto highFpsLateSfOffsetNs =
getProperty("debug.sf.high_fps_late_sf_phase_offset_ns").value_or(1000000);
const auto highFpsEarlySfOffsetNs = getProperty("debug.sf.high_fps_early_phase_offset_ns");
@@ -209,6 +205,32 @@
: vsyncDuration - (appDuration + sfDuration) % vsyncDuration;
}
+PhaseDurations::Offsets PhaseDurations::constructOffsets(nsecs_t vsyncDuration) const {
+ return Offsets{
+ {
+ mSfEarlyDuration < vsyncDuration
+ ? sfDurationToOffset(mSfEarlyDuration, vsyncDuration)
+ : sfDurationToOffset(mSfEarlyDuration, vsyncDuration) - vsyncDuration,
+
+ appDurationToOffset(mAppEarlyDuration, mSfEarlyDuration, vsyncDuration),
+ },
+ {
+ mSfEarlyGlDuration < vsyncDuration
+ ? sfDurationToOffset(mSfEarlyGlDuration, vsyncDuration)
+ : sfDurationToOffset(mSfEarlyGlDuration, vsyncDuration) - vsyncDuration,
+
+ appDurationToOffset(mAppEarlyGlDuration, mSfEarlyGlDuration, vsyncDuration),
+ },
+ {
+ mSfDuration < vsyncDuration
+ ? sfDurationToOffset(mSfDuration, vsyncDuration)
+ : sfDurationToOffset(mSfDuration, vsyncDuration) - vsyncDuration,
+
+ appDurationToOffset(mAppDuration, mSfDuration, vsyncDuration),
+ },
+ };
+}
+
static std::vector<float> getRefreshRatesFromConfigs(
const android::scheduler::RefreshRateConfigs& refreshRateConfigs) {
const auto& allRefreshRates = refreshRateConfigs.getAllRefreshRates();
@@ -227,41 +249,7 @@
std::unordered_map<float, Offsets> offsets;
for (const auto fps : refreshRates) {
- const nsecs_t vsyncDuration = static_cast<nsecs_t>(1e9f / fps);
- offsets.emplace(fps,
- Offsets{
- {
- mSfEarlyDuration < vsyncDuration
- ? sfDurationToOffset(mSfEarlyDuration,
- vsyncDuration)
- : sfDurationToOffset(mSfEarlyDuration,
- vsyncDuration) -
- vsyncDuration,
-
- appDurationToOffset(mAppEarlyDuration, mSfEarlyDuration,
- vsyncDuration),
- },
- {
- mSfEarlyGlDuration < vsyncDuration
- ? sfDurationToOffset(mSfEarlyGlDuration,
- vsyncDuration)
- : sfDurationToOffset(mSfEarlyGlDuration,
- vsyncDuration) -
- vsyncDuration,
-
- appDurationToOffset(mAppEarlyGlDuration, mSfEarlyGlDuration,
- vsyncDuration),
- },
- {
- mSfDuration < vsyncDuration
- ? sfDurationToOffset(mSfDuration, vsyncDuration)
- : sfDurationToOffset(mSfDuration, vsyncDuration) -
- vsyncDuration,
-
- appDurationToOffset(mAppDuration, mSfDuration,
- vsyncDuration),
- },
- });
+ offsets.emplace(fps, constructOffsets(static_cast<nsecs_t>(1e9f / fps)));
}
return offsets;
}
@@ -295,8 +283,16 @@
const auto iter = std::find_if(mOffsets.begin(), mOffsets.end(), [=](const auto& candidateFps) {
return fpsEqualsWithMargin(fps, candidateFps.first);
});
- LOG_ALWAYS_FATAL_IF(iter == mOffsets.end());
- return iter->second;
+
+ if (iter != mOffsets.end()) {
+ return iter->second;
+ }
+
+ // Unknown refresh rate. This might happen if we get a hotplug event for the default display.
+ // This happens only during tests and not during regular device operation.
+ // In this case just construct the offset.
+ ALOGW("Can't find offset for %.2f fps", fps);
+ return constructOffsets(static_cast<nsecs_t>(1e9f / fps));
}
void PhaseDurations::dump(std::string& result) const {
@@ -334,6 +330,3 @@
} // namespace impl
} // namespace android::scheduler
-
-// TODO(b/129481165): remove the #pragma below and fix conversion issues
-#pragma clang diagnostic pop // ignored "-Wconversion"
diff --git a/services/surfaceflinger/Scheduler/PhaseOffsets.h b/services/surfaceflinger/Scheduler/PhaseOffsets.h
index 7b1bdfd..b7d4eae 100644
--- a/services/surfaceflinger/Scheduler/PhaseOffsets.h
+++ b/services/surfaceflinger/Scheduler/PhaseOffsets.h
@@ -108,6 +108,7 @@
private:
std::unordered_map<float, Offsets> initializeOffsets(const std::vector<float>&) const;
+ PhaseDurations::Offsets constructOffsets(nsecs_t vsyncDuration) const;
const nsecs_t mSfDuration;
const nsecs_t mAppDuration;
diff --git a/services/surfaceflinger/Scheduler/RefreshRateConfigs.cpp b/services/surfaceflinger/Scheduler/RefreshRateConfigs.cpp
index 45d1f23..e4b0287 100644
--- a/services/surfaceflinger/Scheduler/RefreshRateConfigs.cpp
+++ b/services/surfaceflinger/Scheduler/RefreshRateConfigs.cpp
@@ -14,24 +14,47 @@
* limitations under the License.
*/
-// TODO(b/129481165): remove the #pragma below and fix conversion issues
-#pragma clang diagnostic push
-#pragma clang diagnostic ignored "-Wconversion"
-
// #define LOG_NDEBUG 0
+#define ATRACE_TAG ATRACE_TAG_GRAPHICS
+
#include "RefreshRateConfigs.h"
+#include <android-base/stringprintf.h>
+#include <utils/Trace.h>
+#include <chrono>
+#include <cmath>
+
+using namespace std::chrono_literals;
namespace android::scheduler {
using AllRefreshRatesMapType = RefreshRateConfigs::AllRefreshRatesMapType;
using RefreshRate = RefreshRateConfigs::RefreshRate;
-// Returns the refresh rate map. This map won't be modified at runtime, so it's safe to access
-// from multiple threads. This can only be called if refreshRateSwitching() returns true.
-// TODO(b/122916473): Get this information from configs prepared by vendors, instead of
-// baking them in.
-const RefreshRate& RefreshRateConfigs::getRefreshRateForContent(float contentFramerate) const {
+const RefreshRate& RefreshRateConfigs::getRefreshRateForContent(
+ const std::vector<LayerRequirement>& layers) const {
std::lock_guard lock(mLock);
+ int contentFramerate = 0;
+ int explicitContentFramerate = 0;
+ for (const auto& layer : layers) {
+ const auto desiredRefreshRateRound = round<int>(layer.desiredRefreshRate);
+ if (layer.vote == LayerVoteType::Explicit) {
+ if (desiredRefreshRateRound > explicitContentFramerate) {
+ explicitContentFramerate = desiredRefreshRateRound;
+ }
+ } else {
+ if (desiredRefreshRateRound > contentFramerate) {
+ contentFramerate = desiredRefreshRateRound;
+ }
+ }
+ }
+
+ if (explicitContentFramerate != 0) {
+ contentFramerate = explicitContentFramerate;
+ } else if (contentFramerate == 0) {
+ contentFramerate = round<int>(mMaxSupportedRefreshRate->fps);
+ }
+ ATRACE_INT("ContentFPS", contentFramerate);
+
// Find the appropriate refresh rate with minimal error
auto iter = min_element(mAvailableRefreshRates.cbegin(), mAvailableRefreshRates.cend(),
[contentFramerate](const auto& lhs, const auto& rhs) -> bool {
@@ -60,6 +83,116 @@
return *bestSoFar;
}
+const RefreshRate& RefreshRateConfigs::getRefreshRateForContentV2(
+ const std::vector<LayerRequirement>& layers) const {
+ constexpr nsecs_t MARGIN = std::chrono::nanoseconds(800us).count();
+ ATRACE_CALL();
+ ALOGV("getRefreshRateForContent %zu layers", layers.size());
+
+ std::lock_guard lock(mLock);
+
+ int noVoteLayers = 0;
+ int minVoteLayers = 0;
+ int maxVoteLayers = 0;
+ int explicitVoteLayers = 0;
+ for (const auto& layer : layers) {
+ if (layer.vote == LayerVoteType::NoVote)
+ noVoteLayers++;
+ else if (layer.vote == LayerVoteType::Min)
+ minVoteLayers++;
+ else if (layer.vote == LayerVoteType::Max)
+ maxVoteLayers++;
+ else if (layer.vote == LayerVoteType::Explicit)
+ explicitVoteLayers++;
+ }
+
+ // Only if all layers want Min we should return Min
+ if (noVoteLayers + minVoteLayers == layers.size()) {
+ return *mAvailableRefreshRates.front();
+ }
+
+ // If we have some Max layers and no Explicit we should return Max
+ if (maxVoteLayers > 0 && explicitVoteLayers == 0) {
+ return *mAvailableRefreshRates.back();
+ }
+
+ // Find the best refresh rate based on score
+ std::vector<std::pair<const RefreshRate*, float>> scores;
+ scores.reserve(mAvailableRefreshRates.size());
+
+ for (const auto refreshRate : mAvailableRefreshRates) {
+ scores.emplace_back(refreshRate, 0.0f);
+ }
+
+ for (const auto& layer : layers) {
+ ALOGV("Calculating score for %s (type: %d)", layer.name.c_str(), layer.vote);
+ if (layer.vote == LayerVoteType::NoVote || layer.vote == LayerVoteType::Min ||
+ layer.vote == LayerVoteType::Max) {
+ continue;
+ }
+
+ // If we have Explicit layers, ignore the Hueristic ones
+ if (explicitVoteLayers > 0 && layer.vote == LayerVoteType::Heuristic) {
+ continue;
+ }
+
+ for (auto& [refreshRate, overallScore] : scores) {
+ const auto displayPeriod = refreshRate->vsyncPeriod;
+ const auto layerPeriod = round<nsecs_t>(1e9f / layer.desiredRefreshRate);
+
+ // Calculate how many display vsyncs we need to present a single frame for this layer
+ auto [displayFramesQuot, displayFramesRem] = std::div(layerPeriod, displayPeriod);
+ if (displayFramesRem <= MARGIN ||
+ std::abs(displayFramesRem - displayPeriod) <= MARGIN) {
+ displayFramesQuot++;
+ displayFramesRem = 0;
+ }
+
+ float layerScore;
+ static constexpr size_t MAX_FRAMES_TO_FIT = 10; // Stop calculating when score < 0.1
+ if (displayFramesRem == 0) {
+ // Layer desired refresh rate matches the display rate.
+ layerScore = layer.weight * 1.0f;
+ } else if (displayFramesQuot == 0) {
+ // Layer desired refresh rate is higher the display rate.
+ layerScore = layer.weight *
+ (static_cast<float>(layerPeriod) / static_cast<float>(displayPeriod)) *
+ (1.0f / (MAX_FRAMES_TO_FIT + 1));
+ } else {
+ // Layer desired refresh rate is lower the display rate. Check how well it fits the
+ // cadence
+ auto diff = std::abs(displayFramesRem - (displayPeriod - displayFramesRem));
+ int iter = 2;
+ while (diff > MARGIN && iter < MAX_FRAMES_TO_FIT) {
+ diff = diff - (displayPeriod - diff);
+ iter++;
+ }
+
+ layerScore = layer.weight * (1.0f / iter);
+ }
+
+ ALOGV("%s (weight %.2f) %.2fHz gives %s score of %.2f", layer.name.c_str(),
+ layer.weight, 1e9f / layerPeriod, refreshRate->name.c_str(), layerScore);
+ overallScore += layerScore;
+ }
+ }
+
+ float max = 0;
+ const RefreshRate* bestRefreshRate = nullptr;
+ for (const auto [refreshRate, score] : scores) {
+ ALOGV("%s scores %.2f", refreshRate->name.c_str(), score);
+
+ ATRACE_INT(refreshRate->name.c_str(), round<int>(score * 100));
+
+ if (score > max) {
+ max = score;
+ bestRefreshRate = refreshRate;
+ }
+ }
+
+ return bestRefreshRate == nullptr ? *mCurrentRefreshRate : *bestRefreshRate;
+}
+
const AllRefreshRatesMapType& RefreshRateConfigs::getAllRefreshRates() const {
return mRefreshRates;
}
@@ -105,11 +238,10 @@
HwcConfigIndexType currentConfigId)
: mRefreshRateSwitching(refreshRateSwitching) {
std::vector<InputConfig> inputConfigs;
- for (auto configId = HwcConfigIndexType(0); configId < HwcConfigIndexType(configs.size());
- ++configId) {
- auto configGroup = HwcConfigGroupType(configs[configId.value()]->getConfigGroup());
- inputConfigs.push_back(
- {configId, configGroup, configs[configId.value()]->getVsyncPeriod()});
+ for (size_t configId = 0; configId < configs.size(); ++configId) {
+ auto configGroup = HwcConfigGroupType(configs[configId]->getConfigGroup());
+ inputConfigs.push_back({HwcConfigIndexType(static_cast<int>(configId)), configGroup,
+ configs[configId]->getVsyncPeriod()});
}
init(inputConfigs, currentConfigId);
}
@@ -180,14 +312,21 @@
void RefreshRateConfigs::constructAvailableRefreshRates() {
// Filter configs based on current policy and sort based on vsync period
HwcConfigGroupType group = mRefreshRates.at(mDefaultConfig).configGroup;
- ALOGV("constructRefreshRateMap: default %d group %d min %.2f max %.2f", mDefaultConfig.value(),
- group.value(), mMinRefreshRateFps, mMaxRefreshRateFps);
+ ALOGV("constructAvailableRefreshRates: default %d group %d min %.2f max %.2f",
+ mDefaultConfig.value(), group.value(), mMinRefreshRateFps, mMaxRefreshRateFps);
getSortedRefreshRateList(
[&](const RefreshRate& refreshRate) REQUIRES(mLock) {
return refreshRate.configGroup == group &&
refreshRate.inPolicy(mMinRefreshRateFps, mMaxRefreshRateFps);
},
&mAvailableRefreshRates);
+
+ std::string availableRefreshRates;
+ for (const auto& refreshRate : mAvailableRefreshRates) {
+ base::StringAppendF(&availableRefreshRates, "%s ", refreshRate->name.c_str());
+ }
+
+ ALOGV("Available refresh rates: %s", availableRefreshRates.c_str());
LOG_ALWAYS_FATAL_IF(mAvailableRefreshRates.empty(),
"No compatible display configs for default=%d min=%.0f max=%.0f",
mDefaultConfig.value(), mMinRefreshRateFps, mMaxRefreshRateFps);
@@ -221,5 +360,3 @@
}
} // namespace android::scheduler
-// TODO(b/129481165): remove the #pragma below and fix conversion issues
-#pragma clang diagnostic pop // ignored "-Wconversion"
diff --git a/services/surfaceflinger/Scheduler/RefreshRateConfigs.h b/services/surfaceflinger/Scheduler/RefreshRateConfigs.h
index 0c3369a..80d42cc 100644
--- a/services/surfaceflinger/Scheduler/RefreshRateConfigs.h
+++ b/services/surfaceflinger/Scheduler/RefreshRateConfigs.h
@@ -97,8 +97,39 @@
// Returns true if this device is doing refresh rate switching. This won't change at runtime.
bool refreshRateSwitchingSupported() const { return mRefreshRateSwitching; }
+ // Describes the different options the layer voted for refresh rate
+ enum class LayerVoteType {
+ NoVote, // Doesn't care about the refresh rate
+ Min, // Minimal refresh rate available
+ Max, // Maximal refresh rate available
+ Heuristic, // Specific refresh rate that was calculated by platform using a heuristic
+ Explicit, // Specific refresh rate that was provided by the app
+ };
+
+ // Captures the layer requirements for a refresh rate. This will be used to determine the
+ // display refresh rate.
+ struct LayerRequirement {
+ std::string name; // Layer's name. Used for debugging purposes.
+ LayerVoteType vote; // Layer vote type.
+ float desiredRefreshRate; // Layer's desired refresh rate, if applicable.
+ float weight; // Layer's weight in the range of [0, 1]. The higher the weight the more
+ // impact this layer would have on choosing the refresh rate.
+
+ bool operator==(const LayerRequirement& other) const {
+ return name == other.name && vote == other.vote &&
+ desiredRefreshRate == other.desiredRefreshRate && weight == other.weight;
+ }
+
+ bool operator!=(const LayerRequirement& other) const { return !(*this == other); }
+ };
+
// Returns all available refresh rates according to the current policy.
- const RefreshRate& getRefreshRateForContent(float contentFramerate) const EXCLUDES(mLock);
+ const RefreshRate& getRefreshRateForContent(const std::vector<LayerRequirement>& layers) const
+ EXCLUDES(mLock);
+
+ // Returns all available refresh rates according to the current policy.
+ const RefreshRate& getRefreshRateForContentV2(const std::vector<LayerRequirement>& layers) const
+ EXCLUDES(mLock);
// Returns all the refresh rates supported by the device. This won't change at runtime.
const AllRefreshRatesMapType& getAllRefreshRates() const EXCLUDES(mLock);
diff --git a/services/surfaceflinger/Scheduler/RefreshRateStats.h b/services/surfaceflinger/Scheduler/RefreshRateStats.h
index a384dbe..e44cd52 100644
--- a/services/surfaceflinger/Scheduler/RefreshRateStats.h
+++ b/services/surfaceflinger/Scheduler/RefreshRateStats.h
@@ -114,7 +114,8 @@
mConfigModesTotalTime[mCurrentConfigMode] = 0;
}
mConfigModesTotalTime[mCurrentConfigMode] += timeElapsedMs;
- fps = mRefreshRateConfigs.getRefreshRateFromConfigId(mCurrentConfigMode).fps;
+ fps = static_cast<uint32_t>(std::round(
+ mRefreshRateConfigs.getRefreshRateFromConfigId(mCurrentConfigMode).fps));
} else {
mScreenOffTime += timeElapsedMs;
}
diff --git a/services/surfaceflinger/Scheduler/Scheduler.cpp b/services/surfaceflinger/Scheduler/Scheduler.cpp
index 0b645c4..7de35af 100644
--- a/services/surfaceflinger/Scheduler/Scheduler.cpp
+++ b/services/surfaceflinger/Scheduler/Scheduler.cpp
@@ -14,10 +14,6 @@
* limitations under the License.
*/
-// TODO(b/129481165): remove the #pragma below and fix conversion issues
-#pragma clang diagnostic push
-#pragma clang diagnostic ignored "-Wconversion"
-
#undef LOG_TAG
#define LOG_TAG "Scheduler"
#define ATRACE_TAG ATRACE_TAG_GRAPHICS
@@ -103,16 +99,21 @@
Scheduler::Scheduler(impl::EventControlThread::SetVSyncEnabledFunction function,
const scheduler::RefreshRateConfigs& refreshRateConfig,
- ISchedulerCallback& schedulerCallback)
+ ISchedulerCallback& schedulerCallback, bool useContentDetectionV2)
: mPrimaryDispSync(createDispSync()),
mEventControlThread(new impl::EventControlThread(std::move(function))),
mSupportKernelTimer(sysprop::support_kernel_idle_timer(false)),
mSchedulerCallback(schedulerCallback),
- mRefreshRateConfigs(refreshRateConfig) {
+ mRefreshRateConfigs(refreshRateConfig),
+ mUseContentDetectionV2(useContentDetectionV2) {
using namespace sysprop;
if (property_get_bool("debug.sf.use_smart_90_for_video", 0) || use_smart_90_for_video(false)) {
- mLayerHistory = std::make_unique<scheduler::impl::LayerHistory>();
+ if (mUseContentDetectionV2) {
+ mLayerHistory = std::make_unique<scheduler::impl::LayerHistoryV2>();
+ } else {
+ mLayerHistory = std::make_unique<scheduler::impl::LayerHistory>();
+ }
}
const int setIdleTimerMs = property_get_int32("debug.sf.set_idle_timer_ms", 0);
@@ -120,7 +121,6 @@
if (const auto millis = setIdleTimerMs ? setIdleTimerMs : set_idle_timer_ms(0); millis > 0) {
const auto callback = mSupportKernelTimer ? &Scheduler::kernelIdleTimerCallback
: &Scheduler::idleTimerCallback;
-
mIdleTimer.emplace(
std::chrono::milliseconds(millis),
[this, callback] { std::invoke(callback, this, TimerState::Reset); },
@@ -149,12 +149,13 @@
Scheduler::Scheduler(std::unique_ptr<DispSync> primaryDispSync,
std::unique_ptr<EventControlThread> eventControlThread,
const scheduler::RefreshRateConfigs& configs,
- ISchedulerCallback& schedulerCallback)
+ ISchedulerCallback& schedulerCallback, bool useContentDetectionV2)
: mPrimaryDispSync(std::move(primaryDispSync)),
mEventControlThread(std::move(eventControlThread)),
mSupportKernelTimer(false),
mSchedulerCallback(schedulerCallback),
- mRefreshRateConfigs(configs) {}
+ mRefreshRateConfigs(configs),
+ mUseContentDetectionV2(useContentDetectionV2) {}
Scheduler::~Scheduler() {
// Ensure the OneShotTimer threads are joined before we start destroying state.
@@ -375,12 +376,42 @@
void Scheduler::registerLayer(Layer* layer) {
if (!mLayerHistory) return;
- const auto lowFps = mRefreshRateConfigs.getMinRefreshRate().fps;
- const auto highFps = layer->getWindowType() == InputWindowInfo::TYPE_WALLPAPER
- ? lowFps
- : mRefreshRateConfigs.getMaxRefreshRate().fps;
+ if (!mUseContentDetectionV2) {
+ const auto lowFps = mRefreshRateConfigs.getMinRefreshRate().fps;
+ const auto highFps = layer->getWindowType() == InputWindowInfo::TYPE_WALLPAPER
+ ? lowFps
+ : mRefreshRateConfigs.getMaxRefreshRate().fps;
- mLayerHistory->registerLayer(layer, lowFps, highFps);
+ mLayerHistory->registerLayer(layer, lowFps, highFps,
+ scheduler::LayerHistory::LayerVoteType::Heuristic);
+ } else {
+ if (layer->getWindowType() == InputWindowInfo::TYPE_WALLPAPER) {
+ mLayerHistory->registerLayer(layer, mRefreshRateConfigs.getMinRefreshRate().fps,
+ mRefreshRateConfigs.getMaxRefreshRate().fps,
+ scheduler::LayerHistory::LayerVoteType::Min);
+ } else if (layer->getWindowType() == InputWindowInfo::TYPE_STATUS_BAR) {
+ mLayerHistory->registerLayer(layer, mRefreshRateConfigs.getMinRefreshRate().fps,
+ mRefreshRateConfigs.getMaxRefreshRate().fps,
+ scheduler::LayerHistory::LayerVoteType::NoVote);
+ } else {
+ mLayerHistory->registerLayer(layer, mRefreshRateConfigs.getMinRefreshRate().fps,
+ mRefreshRateConfigs.getMaxRefreshRate().fps,
+ scheduler::LayerHistory::LayerVoteType::Heuristic);
+ }
+
+ // TODO(146935143): Simulate youtube app vote. This should be removed once youtube calls the
+ // API to set desired rate
+ {
+ const auto vote = property_get_int32("experimental.sf.force_youtube_vote", 0);
+ if (vote != 0 &&
+ layer->getName() ==
+ "SurfaceView - "
+ "com.google.android.youtube/"
+ "com.google.android.apps.youtube.app.WatchWhileActivity#0") {
+ layer->setFrameRate(vote);
+ }
+ }
+ }
}
void Scheduler::recordLayerHistory(Layer* layer, nsecs_t presentTime) {
@@ -392,27 +423,27 @@
void Scheduler::chooseRefreshRateForContent() {
if (!mLayerHistory) return;
- auto [refreshRate] = mLayerHistory->summarize(systemTime());
- const uint32_t refreshRateRound = std::round(refreshRate);
+ ATRACE_CALL();
+
+ scheduler::LayerHistory::Summary summary = mLayerHistory->summarize(systemTime());
HwcConfigIndexType newConfigId;
{
std::lock_guard<std::mutex> lock(mFeatureStateLock);
- if (mFeatures.contentRefreshRate == refreshRateRound) {
+ if (mFeatures.contentRequirements == summary) {
return;
}
- mFeatures.contentRefreshRate = refreshRateRound;
- ATRACE_INT("ContentFPS", refreshRateRound);
-
+ mFeatures.contentRequirements = summary;
mFeatures.contentDetection =
- refreshRateRound > 0 ? ContentDetectionState::On : ContentDetectionState::Off;
+ !summary.empty() ? ContentDetectionState::On : ContentDetectionState::Off;
+
newConfigId = calculateRefreshRateType();
if (mFeatures.configId == newConfigId) {
return;
}
mFeatures.configId = newConfigId;
- };
- auto newRefreshRate = mRefreshRateConfigs.getRefreshRateFromConfigId(newConfigId);
- mSchedulerCallback.changeRefreshRate(newRefreshRate, ConfigEvent::Changed);
+ auto newRefreshRate = mRefreshRateConfigs.getRefreshRateFromConfigId(newConfigId);
+ mSchedulerCallback.changeRefreshRate(newRefreshRate, ConfigEvent::Changed);
+ }
}
void Scheduler::resetIdleTimer() {
@@ -422,16 +453,17 @@
}
void Scheduler::notifyTouchEvent() {
+ if (!mTouchTimer) return;
+
// Touch event will boost the refresh rate to performance.
// Clear Layer History to get fresh FPS detection.
// NOTE: Instead of checking all the layers, we should be checking the layer
// that is currently on top. b/142507166 will give us this capability.
- if (mLayerHistory && !mLayerHistory->hasClientSpecifiedFrameRate()) {
+ std::lock_guard<std::mutex> lock(mFeatureStateLock);
+ if (mLayerHistory && !layerHistoryHasClientSpecifiedFrameRate()) {
mLayerHistory->clear();
- if (mTouchTimer) {
- mTouchTimer->reset();
- }
+ mTouchTimer->reset();
if (mSupportKernelTimer && mIdleTimer) {
mIdleTimer->reset();
@@ -530,6 +562,16 @@
mSchedulerCallback.changeRefreshRate(newRefreshRate, event);
}
+bool Scheduler::layerHistoryHasClientSpecifiedFrameRate() {
+ for (const auto& layer : mFeatures.contentRequirements) {
+ if (layer.vote == scheduler::RefreshRateConfigs::LayerVoteType::Explicit) {
+ return true;
+ }
+ }
+
+ return false;
+}
+
HwcConfigIndexType Scheduler::calculateRefreshRateType() {
if (!mRefreshRateConfigs.refreshRateSwitchingSupported()) {
return mRefreshRateConfigs.getCurrentRefreshRate().configId;
@@ -538,7 +580,7 @@
// If the layer history doesn't have the frame rate specified, use the old path. NOTE:
// if we remove the kernel idle timer, and use our internal idle timer, this code will have to
// be refactored.
- if (!mLayerHistory->hasClientSpecifiedFrameRate()) {
+ if (!layerHistoryHasClientSpecifiedFrameRate()) {
// If Display Power is not in normal operation we want to be in performance mode.
// When coming back to normal mode, a grace period is given with DisplayPowerTimer
if (!mFeatures.isDisplayPowerStateNormal ||
@@ -555,17 +597,26 @@
if (mFeatures.idleTimer == TimerState::Expired) {
return mRefreshRateConfigs.getMinRefreshRateByPolicy().configId;
}
+ }
+ if (!mUseContentDetectionV2) {
// If content detection is off we choose performance as we don't know the content fps
if (mFeatures.contentDetection == ContentDetectionState::Off) {
return mRefreshRateConfigs.getMaxRefreshRateByPolicy().configId;
}
+
+ // Content detection is on, find the appropriate refresh rate with minimal error
+ return mRefreshRateConfigs.getRefreshRateForContent(mFeatures.contentRequirements).configId;
}
// Content detection is on, find the appropriate refresh rate with minimal error
- return mRefreshRateConfigs
- .getRefreshRateForContent(static_cast<float>(mFeatures.contentRefreshRate))
- .configId;
+ if (mFeatures.contentDetection == ContentDetectionState::On) {
+ return mRefreshRateConfigs.getRefreshRateForContentV2(mFeatures.contentRequirements)
+ .configId;
+ }
+
+ // There are no signals for refresh rate, just leave it as is
+ return mRefreshRateConfigs.getCurrentRefreshRate().configId;
}
std::optional<HwcConfigIndexType> Scheduler::getPreferredConfigId() {
@@ -606,7 +657,10 @@
}
}
-} // namespace android
+void Scheduler::onPrimaryDisplayAreaChanged(uint32_t displayArea) {
+ if (mLayerHistory) {
+ mLayerHistory->setDisplayArea(displayArea);
+ }
+}
-// TODO(b/129481165): remove the #pragma below and fix conversion issues
-#pragma clang diagnostic pop // ignored "-Wconversion"
+} // namespace android
diff --git a/services/surfaceflinger/Scheduler/Scheduler.h b/services/surfaceflinger/Scheduler/Scheduler.h
index c6430c3..01062f8 100644
--- a/services/surfaceflinger/Scheduler/Scheduler.h
+++ b/services/surfaceflinger/Scheduler/Scheduler.h
@@ -23,7 +23,11 @@
#include <optional>
#include <unordered_map>
+// TODO(b/129481165): remove the #pragma below and fix conversion issues
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Wconversion"
#include <ui/GraphicTypes.h>
+#pragma clang diagnostic pop
#include "EventControlThread.h"
#include "EventThread.h"
@@ -58,7 +62,8 @@
enum class TransactionStart { EARLY, NORMAL };
Scheduler(impl::EventControlThread::SetVSyncEnabledFunction,
- const scheduler::RefreshRateConfigs&, ISchedulerCallback& schedulerCallback);
+ const scheduler::RefreshRateConfigs&, ISchedulerCallback& schedulerCallback,
+ bool useContentDetectionV2);
virtual ~Scheduler();
@@ -136,6 +141,9 @@
// Notifies the scheduler when the display was refreshed
void onDisplayRefreshed(nsecs_t timestamp);
+ // Notifies the scheduler when the display size has changed. Called from SF's main thread
+ void onPrimaryDisplayAreaChanged(uint32_t displayArea);
+
private:
friend class TestableScheduler;
@@ -147,7 +155,8 @@
// Used by tests to inject mocks.
Scheduler(std::unique_ptr<DispSync>, std::unique_ptr<EventControlThread>,
- const scheduler::RefreshRateConfigs&, ISchedulerCallback& schedulerCallback);
+ const scheduler::RefreshRateConfigs&, ISchedulerCallback& schedulerCallback,
+ bool useContentDetectionV2);
std::unique_ptr<VSyncSource> makePrimaryDispSyncSource(const char* name, nsecs_t phaseOffsetNs);
@@ -170,6 +179,8 @@
HwcConfigIndexType calculateRefreshRateType() REQUIRES(mFeatureStateLock);
+ bool layerHistoryHasClientSpecifiedFrameRate() REQUIRES(mFeatureStateLock);
+
// Stores EventThread associated with a given VSyncSource, and an initial EventThreadConnection.
struct Connection {
sp<EventThreadConnection> connection;
@@ -218,7 +229,7 @@
TimerState displayPowerTimer = TimerState::Expired;
std::optional<HwcConfigIndexType> configId;
- uint32_t contentRefreshRate = 0;
+ scheduler::LayerHistory::Summary contentRequirements;
bool isDisplayPowerStateNormal = true;
} mFeatures GUARDED_BY(mFeatureStateLock);
@@ -229,6 +240,8 @@
std::optional<HWC2::VsyncPeriodChangeTimeline> mLastVsyncPeriodChangeTimeline
GUARDED_BY(mVsyncTimelineLock);
static constexpr std::chrono::nanoseconds MAX_VSYNC_APPLIED_TIME = 200ms;
+
+ const bool mUseContentDetectionV2;
};
} // namespace android
diff --git a/services/surfaceflinger/Scheduler/SchedulerUtils.cpp b/services/surfaceflinger/Scheduler/SchedulerUtils.cpp
index 27120be..e8e0444 100644
--- a/services/surfaceflinger/Scheduler/SchedulerUtils.cpp
+++ b/services/surfaceflinger/Scheduler/SchedulerUtils.cpp
@@ -14,10 +14,6 @@
* limitations under the License.
*/
-// TODO(b/129481165): remove the #pragma below and fix conversion issues
-#pragma clang diagnostic push
-#pragma clang diagnostic ignored "-Wconversion"
-
#include "SchedulerUtils.h"
#include <cinttypes>
@@ -34,12 +30,10 @@
}
size_t n = v->size() / 2;
- nth_element(v->begin(), v->begin() + n, v->end());
+ nth_element(v->begin(), v->begin() + static_cast<long>(n), v->end());
return v->at(n);
}
} // namespace scheduler
} // namespace android
-// TODO(b/129481165): remove the #pragma below and fix conversion issues
-#pragma clang diagnostic pop // ignored "-Wconversion"
diff --git a/services/surfaceflinger/Scheduler/SchedulerUtils.h b/services/surfaceflinger/Scheduler/SchedulerUtils.h
index e7e73a3..04a4cd1 100644
--- a/services/surfaceflinger/Scheduler/SchedulerUtils.h
+++ b/services/surfaceflinger/Scheduler/SchedulerUtils.h
@@ -78,6 +78,11 @@
static constexpr size_t max64print = std::numeric_limits<nsecs_t>::digits10 + 1;
+template <typename T>
+static inline T round(float f) {
+ return static_cast<T>(std::round(f));
+}
+
} // namespace android::scheduler
namespace std {
diff --git a/services/surfaceflinger/Scheduler/VSyncPredictor.cpp b/services/surfaceflinger/Scheduler/VSyncPredictor.cpp
index ac32633..e688415 100644
--- a/services/surfaceflinger/Scheduler/VSyncPredictor.cpp
+++ b/services/surfaceflinger/Scheduler/VSyncPredictor.cpp
@@ -71,12 +71,12 @@
return std::get<0>(mRateMap.find(mIdealPeriod)->second);
}
-void VSyncPredictor::addVsyncTimestamp(nsecs_t timestamp) {
+bool VSyncPredictor::addVsyncTimestamp(nsecs_t timestamp) {
std::lock_guard<std::mutex> lk(mMutex);
if (!validate(timestamp)) {
ALOGV("timestamp was too far off the last known timestamp");
- return;
+ return false;
}
if (timestamps.size() != kHistorySize) {
@@ -89,7 +89,7 @@
if (timestamps.size() < kMinimumSamplesForPrediction) {
mRateMap[mIdealPeriod] = {mIdealPeriod, 0};
- return;
+ return true;
}
// This is a 'simple linear regression' calculation of Y over X, with Y being the
@@ -143,7 +143,7 @@
if (CC_UNLIKELY(bottom == 0)) {
it->second = {mIdealPeriod, 0};
- return;
+ return false;
}
nsecs_t const anticipatedPeriod = top / bottom * kScalingFactor;
@@ -156,6 +156,7 @@
ALOGV("model update ts: %" PRId64 " slope: %" PRId64 " intercept: %" PRId64, timestamp,
anticipatedPeriod, intercept);
+ return true;
}
nsecs_t VSyncPredictor::nextAnticipatedVSyncTimeFrom(nsecs_t timePoint) const {
diff --git a/services/surfaceflinger/Scheduler/VSyncPredictor.h b/services/surfaceflinger/Scheduler/VSyncPredictor.h
index e366555..532fe9e 100644
--- a/services/surfaceflinger/Scheduler/VSyncPredictor.h
+++ b/services/surfaceflinger/Scheduler/VSyncPredictor.h
@@ -38,7 +38,7 @@
uint32_t outlierTolerancePercent);
~VSyncPredictor();
- void addVsyncTimestamp(nsecs_t timestamp) final;
+ bool addVsyncTimestamp(nsecs_t timestamp) final;
nsecs_t nextAnticipatedVSyncTimeFrom(nsecs_t timePoint) const final;
nsecs_t currentPeriod() const final;
void resetModel() final;
diff --git a/services/surfaceflinger/Scheduler/VSyncReactor.cpp b/services/surfaceflinger/Scheduler/VSyncReactor.cpp
index 53fa212..70e4760 100644
--- a/services/surfaceflinger/Scheduler/VSyncReactor.cpp
+++ b/services/surfaceflinger/Scheduler/VSyncReactor.cpp
@@ -130,10 +130,11 @@
}
std::lock_guard<std::mutex> lk(mMutex);
- if (mIgnorePresentFences) {
+ if (mExternalIgnoreFences || mInternalIgnoreFences) {
return true;
}
+ bool timestampAccepted = true;
for (auto it = mUnfiredFences.begin(); it != mUnfiredFences.end();) {
auto const time = (*it)->getCachedSignalTime();
if (time == Fence::SIGNAL_TIME_PENDING) {
@@ -141,7 +142,8 @@
} else if (time == Fence::SIGNAL_TIME_INVALID) {
it = mUnfiredFences.erase(it);
} else {
- mTracker->addVsyncTimestamp(time);
+ timestampAccepted &= mTracker->addVsyncTimestamp(time);
+
it = mUnfiredFences.erase(it);
}
}
@@ -152,7 +154,13 @@
}
mUnfiredFences.push_back(fence);
} else {
- mTracker->addVsyncTimestamp(signalTime);
+ timestampAccepted &= mTracker->addVsyncTimestamp(signalTime);
+ }
+
+ if (!timestampAccepted) {
+ mMoreSamplesNeeded = true;
+ setIgnorePresentFencesInternal(true);
+ mPeriodConfirmationInProgress = true;
}
return mMoreSamplesNeeded;
@@ -160,8 +168,17 @@
void VSyncReactor::setIgnorePresentFences(bool ignoration) {
std::lock_guard<std::mutex> lk(mMutex);
- mIgnorePresentFences = ignoration;
- if (mIgnorePresentFences == true) {
+ mExternalIgnoreFences = ignoration;
+ updateIgnorePresentFencesInternal();
+}
+
+void VSyncReactor::setIgnorePresentFencesInternal(bool ignoration) {
+ mInternalIgnoreFences = ignoration;
+ updateIgnorePresentFencesInternal();
+}
+
+void VSyncReactor::updateIgnorePresentFencesInternal() {
+ if (mExternalIgnoreFences || mInternalIgnoreFences) {
mUnfiredFences.clear();
}
}
@@ -177,14 +194,18 @@
}
void VSyncReactor::startPeriodTransition(nsecs_t newPeriod) {
+ mPeriodConfirmationInProgress = true;
mPeriodTransitioningTo = newPeriod;
mMoreSamplesNeeded = true;
+ setIgnorePresentFencesInternal(true);
}
void VSyncReactor::endPeriodTransition() {
- mPeriodTransitioningTo.reset();
- mLastHwVsync.reset();
+ setIgnorePresentFencesInternal(false);
mMoreSamplesNeeded = false;
+ mPeriodTransitioningTo.reset();
+ mPeriodConfirmationInProgress = false;
+ mLastHwVsync.reset();
}
void VSyncReactor::setPeriod(nsecs_t period) {
@@ -208,27 +229,33 @@
void VSyncReactor::endResync() {}
-bool VSyncReactor::periodChangeDetected(nsecs_t vsync_timestamp) {
- if (!mLastHwVsync || !mPeriodTransitioningTo) {
+bool VSyncReactor::periodConfirmed(nsecs_t vsync_timestamp) {
+ if (!mLastHwVsync || !mPeriodConfirmationInProgress) {
return false;
}
+ auto const period = mPeriodTransitioningTo ? *mPeriodTransitioningTo : getPeriod();
+
+ static constexpr int allowancePercent = 10;
+ static constexpr std::ratio<allowancePercent, 100> allowancePercentRatio;
+ auto const allowance = period * allowancePercentRatio.num / allowancePercentRatio.den;
auto const distance = vsync_timestamp - *mLastHwVsync;
- return std::abs(distance - *mPeriodTransitioningTo) < std::abs(distance - getPeriod());
+ return std::abs(distance - period) < allowance;
}
bool VSyncReactor::addResyncSample(nsecs_t timestamp, bool* periodFlushed) {
assert(periodFlushed);
std::lock_guard<std::mutex> lk(mMutex);
- if (periodChangeDetected(timestamp)) {
- mTracker->setPeriod(*mPeriodTransitioningTo);
- for (auto& entry : mCallbacks) {
- entry.second->setPeriod(*mPeriodTransitioningTo);
+ if (periodConfirmed(timestamp)) {
+ if (mPeriodTransitioningTo) {
+ mTracker->setPeriod(*mPeriodTransitioningTo);
+ for (auto& entry : mCallbacks) {
+ entry.second->setPeriod(*mPeriodTransitioningTo);
+ }
+ *periodFlushed = true;
}
-
endPeriodTransition();
- *periodFlushed = true;
- } else if (mPeriodTransitioningTo) {
+ } else if (mPeriodConfirmationInProgress) {
mLastHwVsync = timestamp;
mMoreSamplesNeeded = true;
*periodFlushed = false;
diff --git a/services/surfaceflinger/Scheduler/VSyncReactor.h b/services/surfaceflinger/Scheduler/VSyncReactor.h
index f318dcb..5b79f35 100644
--- a/services/surfaceflinger/Scheduler/VSyncReactor.h
+++ b/services/surfaceflinger/Scheduler/VSyncReactor.h
@@ -61,9 +61,11 @@
void reset() final;
private:
+ void setIgnorePresentFencesInternal(bool ignoration) REQUIRES(mMutex);
+ void updateIgnorePresentFencesInternal() REQUIRES(mMutex);
void startPeriodTransition(nsecs_t newPeriod) REQUIRES(mMutex);
void endPeriodTransition() REQUIRES(mMutex);
- bool periodChangeDetected(nsecs_t vsync_timestamp) REQUIRES(mMutex);
+ bool periodConfirmed(nsecs_t vsync_timestamp) REQUIRES(mMutex);
std::unique_ptr<Clock> const mClock;
std::unique_ptr<VSyncTracker> const mTracker;
@@ -71,10 +73,12 @@
size_t const mPendingLimit;
std::mutex mMutex;
- bool mIgnorePresentFences GUARDED_BY(mMutex) = false;
+ bool mInternalIgnoreFences GUARDED_BY(mMutex) = false;
+ bool mExternalIgnoreFences GUARDED_BY(mMutex) = false;
std::vector<std::shared_ptr<FenceTime>> mUnfiredFences GUARDED_BY(mMutex);
bool mMoreSamplesNeeded GUARDED_BY(mMutex) = false;
+ bool mPeriodConfirmationInProgress GUARDED_BY(mMutex) = false;
std::optional<nsecs_t> mPeriodTransitioningTo GUARDED_BY(mMutex);
std::optional<nsecs_t> mLastHwVsync GUARDED_BY(mMutex);
diff --git a/services/surfaceflinger/Scheduler/VSyncTracker.h b/services/surfaceflinger/Scheduler/VSyncTracker.h
index 2b27884..a25b8a9 100644
--- a/services/surfaceflinger/Scheduler/VSyncTracker.h
+++ b/services/surfaceflinger/Scheduler/VSyncTracker.h
@@ -33,8 +33,10 @@
* to the model.
*
* \param [in] timestamp The timestamp when the vsync signal was.
+ * \return True if the timestamp was consistent with the internal model,
+ * False otherwise
*/
- virtual void addVsyncTimestamp(nsecs_t timestamp) = 0;
+ virtual bool addVsyncTimestamp(nsecs_t timestamp) = 0;
/*
* Access the next anticipated vsync time such that the anticipated time >= timePoint.
diff --git a/services/surfaceflinger/SurfaceFlinger.cpp b/services/surfaceflinger/SurfaceFlinger.cpp
index 8c271f9..61d197c 100644
--- a/services/surfaceflinger/SurfaceFlinger.cpp
+++ b/services/surfaceflinger/SurfaceFlinger.cpp
@@ -353,7 +353,7 @@
property_get("debug.sf.luma_sampling", value, "1");
mLumaSampling = atoi(value);
- property_get("debug.sf.disable_client_composition_cache", value, "1");
+ property_get("debug.sf.disable_client_composition_cache", value, "0");
mDisableClientCompositionCache = atoi(value);
// We should be reading 'persist.sys.sf.color_saturation' here
@@ -543,6 +543,11 @@
const auto& performanceRefreshRate = mRefreshRateConfigs->getMaxRefreshRateByPolicy();
changeRefreshRateLocked(performanceRefreshRate, Scheduler::ConfigEvent::None);
}
+
+ if (property_get_bool("sf.debug.show_refresh_rate_overlay", false)) {
+ mRefreshRateOverlay = std::make_unique<RefreshRateOverlay>(*this);
+ mRefreshRateOverlay->changeRefreshRate(mRefreshRateConfigs->getCurrentRefreshRate());
+ }
}));
}
@@ -1782,7 +1787,13 @@
// Layers need to get updated (in the previous line) before we can use them for
// choosing the refresh rate.
- mScheduler->chooseRefreshRateForContent();
+ // Hold mStateLock as chooseRefreshRateForContent promotes wp<Layer> to sp<Layer>
+ // and may eventually call to ~Layer() if it holds the last reference
+ {
+ Mutex::Autolock _l(mStateLock);
+ mScheduler->chooseRefreshRateForContent();
+ }
+
if (performSetActiveConfig()) {
break;
}
@@ -2048,7 +2059,7 @@
compositorTiming = getBE().mCompositorTiming;
}
- mDrawingState.traverseInZOrder([&](Layer* layer) {
+ mDrawingState.traverse([&](Layer* layer) {
bool frameLatched = layer->onPostComposition(displayDevice, glCompositionDoneFenceTime,
presentFenceTime, compositorTiming);
if (frameLatched) {
@@ -2391,6 +2402,9 @@
}
if (state.width != draw[i].width || state.height != draw[i].height) {
display->setDisplaySize(state.width, state.height);
+ if (display->isPrimary()) {
+ mScheduler->onPrimaryDisplayAreaChanged(state.width * state.height);
+ }
}
}
}
@@ -2463,6 +2477,12 @@
LOG_ALWAYS_FATAL_IF(!displayId);
dispatchDisplayHotplugEvent(displayId->value, true);
}
+
+ const auto displayDevice = mDisplays[displayToken];
+ if (displayDevice->isPrimary()) {
+ mScheduler->onPrimaryDisplayAreaChanged(displayDevice->getWidth() *
+ displayDevice->getHeight());
+ }
}
}
}
@@ -2476,7 +2496,7 @@
const nsecs_t expectedPresentTime = mExpectedPresentTime.load();
// Notify all layers of available frames
- mCurrentState.traverseInZOrder([expectedPresentTime](Layer* layer) {
+ mCurrentState.traverse([expectedPresentTime](Layer* layer) {
layer->notifyAvailableFrames(expectedPresentTime);
});
@@ -2486,7 +2506,7 @@
*/
if ((transactionFlags & eTraversalNeeded) || mTraversalNeededMainThread) {
- mCurrentState.traverseInZOrder([&](Layer* layer) {
+ mCurrentState.traverse([&](Layer* layer) {
uint32_t trFlags = layer->getTransactionFlags(eTransactionNeeded);
if (!trFlags) return;
@@ -2533,7 +2553,7 @@
sp<const DisplayDevice> hintDisplay;
uint32_t currentlayerStack = 0;
bool first = true;
- mCurrentState.traverseInZOrder([&](Layer* layer) {
+ mCurrentState.traverse([&](Layer* layer) {
// NOTE: we rely on the fact that layers are sorted by
// layerStack first (so we don't have to traverse the list
// of displays for every layer).
@@ -2660,8 +2680,12 @@
}
void SurfaceFlinger::changeRefreshRate(const RefreshRate& refreshRate,
- Scheduler::ConfigEvent event) {
- Mutex::Autolock lock(mStateLock);
+ Scheduler::ConfigEvent event) NO_THREAD_SAFETY_ANALYSIS {
+ // If this is called from the main thread mStateLock must be locked before
+ // Currently the only way to call this function from the main thread is from
+ // Sheduler::chooseRefreshRateForContent
+
+ ConditionalLock lock(mStateLock, std::this_thread::get_id() != mMainThreadId);
changeRefreshRateLocked(refreshRate, event);
}
@@ -2757,7 +2781,7 @@
// clear the "changed" flags in current state
mCurrentState.colorMatrixChanged = false;
- mDrawingState.traverseInZOrder([&](Layer* layer) {
+ mDrawingState.traverse([&](Layer* layer) {
layer->commitChildList();
// If the layer can be reached when traversing mDrawingState, then the layer is no
@@ -2768,7 +2792,7 @@
});
commitOffscreenLayers();
- mDrawingState.traverseInZOrder([&](Layer* layer) { layer->updateMirrorInfo(); });
+ mDrawingState.traverse([&](Layer* layer) { layer->updateMirrorInfo(); });
}
void SurfaceFlinger::withTracingLock(std::function<void()> lockedOperation) {
@@ -2793,7 +2817,7 @@
void SurfaceFlinger::commitOffscreenLayers() {
for (Layer* offscreenLayer : mOffscreenLayers) {
- offscreenLayer->traverseInZOrder(LayerVector::StateSet::Drawing, [](Layer* layer) {
+ offscreenLayer->traverse(LayerVector::StateSet::Drawing, [](Layer* layer) {
uint32_t trFlags = layer->getTransactionFlags(eTransactionNeeded);
if (!trFlags) return;
@@ -2834,7 +2858,7 @@
// 3.) Layer 1 is latched.
// Display is now waiting on Layer 1's frame, which is behind layer 0's
// second frame. But layer 0's second frame could be waiting on display.
- mDrawingState.traverseInZOrder([&](Layer* layer) {
+ mDrawingState.traverse([&](Layer* layer) {
if (layer->hasReadyFrame()) {
frameQueued = true;
if (layer->shouldPresentNow(expectedPresentTime)) {
@@ -2852,7 +2876,7 @@
// be shown on screen. Therefore, we need to latch and release buffers of offscreen
// layers to ensure dequeueBuffer doesn't block indefinitely.
for (Layer* offscreenLayer : mOffscreenLayers) {
- offscreenLayer->traverseInZOrder(LayerVector::StateSet::Drawing,
+ offscreenLayer->traverse(LayerVector::StateSet::Drawing,
[&](Layer* l) { l->latchAndReleaseBuffer(); });
}
@@ -2887,7 +2911,7 @@
mBootStage = BootStage::BOOTANIMATION;
}
- mDrawingState.traverseInZOrder([&](Layer* layer) { layer->updateCloneBufferInfo(); });
+ mDrawingState.traverse([&](Layer* layer) { layer->updateCloneBufferInfo(); });
// Only continue with the refresh if there is actually new work to do
return !mLayersWithQueuedFrames.empty() && newDataLatched;
@@ -3709,7 +3733,7 @@
bool matchFound = true;
while (matchFound) {
matchFound = false;
- mCurrentState.traverseInZOrder([&](Layer* layer) {
+ mCurrentState.traverse([&](Layer* layer) {
if (layer->getName() == uniqueName) {
matchFound = true;
uniqueName = base::StringPrintf("%s#%u", name, ++dupeCounter);
@@ -4080,7 +4104,7 @@
const bool clearAll = args.size() < 2;
const auto name = clearAll ? String8() : String8(args[1]);
- mCurrentState.traverseInZOrder([&](Layer* layer) {
+ mCurrentState.traverse([&](Layer* layer) {
if (clearAll || layer->getName() == name.string()) {
layer->clearFrameStats();
}
@@ -4096,7 +4120,7 @@
// This should only be called from the main thread. Otherwise it would need
// the lock and should use mCurrentState rather than mDrawingState.
void SurfaceFlinger::logFrameStats() {
- mDrawingState.traverseInZOrder([&](Layer* layer) {
+ mDrawingState.traverse([&](Layer* layer) {
layer->logFrameStats();
});
@@ -4331,7 +4355,7 @@
result.append("Offscreen Layers:\n");
postMessageSync(new LambdaMessage([&]() {
for (Layer* offscreenLayer : mOffscreenLayers) {
- offscreenLayer->traverseInZOrder(LayerVector::StateSet::Drawing, [&](Layer* layer) {
+ offscreenLayer->traverse(LayerVector::StateSet::Drawing, [&](Layer* layer) {
layer->dumpCallingUidPid(result);
});
}
@@ -5564,6 +5588,10 @@
// ---------------------------------------------------------------------------
+void SurfaceFlinger::State::traverse(const LayerVector::Visitor& visitor) const {
+ layersSortedByZ.traverse(visitor);
+}
+
void SurfaceFlinger::State::traverseInZOrder(const LayerVector::Visitor& visitor) const {
layersSortedByZ.traverseInZOrder(stateSet, visitor);
}
@@ -5620,11 +5648,10 @@
repaintEverythingForHWC();
}
- auto configId = HwcConfigIndexType(defaultConfig);
- display->setActiveConfig(configId);
+ display->setActiveConfig(defaultConfig);
const nsecs_t vsyncPeriod =
- mRefreshRateConfigs->getRefreshRateFromConfigId(configId).vsyncPeriod;
- mScheduler->onConfigChanged(mAppConnectionHandle, display->getId()->value, configId,
+ getHwComposer().getConfigs(*displayId)[defaultConfig.value()]->getVsyncPeriod();
+ mScheduler->onConfigChanged(mAppConnectionHandle, display->getId()->value, defaultConfig,
vsyncPeriod);
return NO_ERROR;
}
diff --git a/services/surfaceflinger/SurfaceFlinger.h b/services/surfaceflinger/SurfaceFlinger.h
index f8980a5..4c8775d 100644
--- a/services/surfaceflinger/SurfaceFlinger.h
+++ b/services/surfaceflinger/SurfaceFlinger.h
@@ -384,6 +384,7 @@
renderengine::ShadowSettings globalShadowSettings;
+ void traverse(const LayerVector::Visitor& visitor) const;
void traverseInZOrder(const LayerVector::Visitor& visitor) const;
void traverseInReverseZOrder(const LayerVector::Visitor& visitor) const;
};
diff --git a/services/surfaceflinger/SurfaceFlingerDefaultFactory.cpp b/services/surfaceflinger/SurfaceFlingerDefaultFactory.cpp
index e12d31a..45889a5 100644
--- a/services/surfaceflinger/SurfaceFlingerDefaultFactory.cpp
+++ b/services/surfaceflinger/SurfaceFlingerDefaultFactory.cpp
@@ -75,7 +75,9 @@
std::unique_ptr<Scheduler> DefaultFactory::createScheduler(
SetVSyncEnabled setVSyncEnabled, const scheduler::RefreshRateConfigs& configs,
ISchedulerCallback& schedulerCallback) {
- return std::make_unique<Scheduler>(std::move(setVSyncEnabled), configs, schedulerCallback);
+ return std::make_unique<Scheduler>(std::move(setVSyncEnabled), configs, schedulerCallback,
+ property_get_bool("debug.sf.use_content_detection_v2",
+ false));
}
std::unique_ptr<SurfaceInterceptor> DefaultFactory::createSurfaceInterceptor(
diff --git a/services/surfaceflinger/SurfaceTracing.cpp b/services/surfaceflinger/SurfaceTracing.cpp
index 6fd4e46..eb5c7de 100644
--- a/services/surfaceflinger/SurfaceTracing.cpp
+++ b/services/surfaceflinger/SurfaceTracing.cpp
@@ -188,8 +188,11 @@
ALOGE("Could not save the proto file! Permission denied");
mLastErr = PERMISSION_DENIED;
}
- if (!android::base::WriteStringToFile(output, kDefaultFileName, S_IRWXU | S_IRGRP, getuid(),
- getgid(), true)) {
+
+ // -rw-r--r--
+ const mode_t mode = S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH;
+ if (!android::base::WriteStringToFile(output, kDefaultFileName, mode, getuid(), getgid(),
+ true)) {
ALOGE("Could not save the proto file! There are missing fields");
mLastErr = PERMISSION_DENIED;
}
diff --git a/services/surfaceflinger/TEST_MAPPING b/services/surfaceflinger/TEST_MAPPING
index cab33ae..eed975e 100644
--- a/services/surfaceflinger/TEST_MAPPING
+++ b/services/surfaceflinger/TEST_MAPPING
@@ -1,7 +1,13 @@
{
"presubmit": [
{
- "name": "libsurfaceflinger_unittest"
+ "name": "libsurfaceflinger_unittest",
+ // TODO(b/148517641): re-enable once this test is fixed
+ "options": [
+ {
+ "exclude-filter": "FrameTracerTest.*"
+ }
+ ]
},
{
"name": "libcompositionengine_test"
diff --git a/services/surfaceflinger/TimeStats/TimeStats.cpp b/services/surfaceflinger/TimeStats/TimeStats.cpp
index 12c98da..130e99a 100644
--- a/services/surfaceflinger/TimeStats/TimeStats.cpp
+++ b/services/surfaceflinger/TimeStats/TimeStats.cpp
@@ -40,13 +40,18 @@
status_pull_atom_return_t TimeStats::pullAtomCallback(int32_t atom_tag,
pulled_stats_event_list* data, void* cookie) {
impl::TimeStats* timeStats = reinterpret_cast<impl::TimeStats*>(cookie);
+ status_pull_atom_return_t result = STATS_PULL_SKIP;
if (atom_tag == android::util::SURFACEFLINGER_STATS_GLOBAL_INFO) {
- return timeStats->populateGlobalAtom(data);
+ result = timeStats->populateGlobalAtom(data);
} else if (atom_tag == android::util::SURFACEFLINGER_STATS_LAYER_INFO) {
- return timeStats->populateLayerAtom(data);
+ result = timeStats->populateLayerAtom(data);
}
- return STATS_PULL_SKIP;
+ // Enable timestats now. The first full pull for a given build is expected to
+ // have empty or very little stats, as stats are first enabled after the
+ // first pull is completed for either the global or layer stats.
+ timeStats->enable();
+ return result;
}
status_pull_atom_return_t TimeStats::populateGlobalAtom(pulled_stats_event_list* data) {
@@ -167,13 +172,19 @@
}
}
+TimeStats::~TimeStats() {
+ std::lock_guard<std::mutex> lock(mMutex);
+ mStatsDelegate->unregisterStatsPullAtomCallback(
+ android::util::SURFACEFLINGER_STATS_GLOBAL_INFO);
+ mStatsDelegate->unregisterStatsPullAtomCallback(android::util::SURFACEFLINGER_STATS_LAYER_INFO);
+}
+
void TimeStats::onBootFinished() {
- // Temporarily enable TimeStats by default. Telemetry is disabled while
- // we move onto statsd, so TimeStats is currently not exercised at all
- // during testing without enabling by default.
- // TODO: remove this, as we should only be paying this overhead on devices
- // where statsd exists.
- enable();
+ std::lock_guard<std::mutex> lock(mMutex);
+ mStatsDelegate->registerStatsPullAtomCallback(android::util::SURFACEFLINGER_STATS_GLOBAL_INFO,
+ TimeStats::pullAtomCallback, nullptr, this);
+ mStatsDelegate->registerStatsPullAtomCallback(android::util::SURFACEFLINGER_STATS_LAYER_INFO,
+ TimeStats::pullAtomCallback, nullptr, this);
}
void TimeStats::parseArgs(bool asProto, const Vector<String16>& args, std::string& result) {
@@ -735,10 +746,6 @@
mEnabled.store(true);
mTimeStats.statsStart = static_cast<int64_t>(std::time(0));
mPowerTime.prevTime = systemTime();
- mStatsDelegate->registerStatsPullAtomCallback(android::util::SURFACEFLINGER_STATS_GLOBAL_INFO,
- TimeStats::pullAtomCallback, nullptr, this);
- mStatsDelegate->registerStatsPullAtomCallback(android::util::SURFACEFLINGER_STATS_LAYER_INFO,
- TimeStats::pullAtomCallback, nullptr, this);
ALOGD("Enabled");
}
@@ -751,9 +758,6 @@
flushPowerTimeLocked();
mEnabled.store(false);
mTimeStats.statsEnd = static_cast<int64_t>(std::time(0));
- mStatsDelegate->unregisterStatsPullAtomCallback(
- android::util::SURFACEFLINGER_STATS_GLOBAL_INFO);
- mStatsDelegate->unregisterStatsPullAtomCallback(android::util::SURFACEFLINGER_STATS_LAYER_INFO);
ALOGD("Disabled");
}
diff --git a/services/surfaceflinger/TimeStats/TimeStats.h b/services/surfaceflinger/TimeStats/TimeStats.h
index 71f06af..67b9d10 100644
--- a/services/surfaceflinger/TimeStats/TimeStats.h
+++ b/services/surfaceflinger/TimeStats/TimeStats.h
@@ -182,6 +182,8 @@
std::optional<size_t> maxPulledLayers,
std::optional<size_t> maxPulledHistogramBuckets);
+ ~TimeStats() override;
+
void onBootFinished() override;
void parseArgs(bool asProto, const Vector<String16>& args, std::string& result) override;
bool isEnabled() override;
diff --git a/services/surfaceflinger/tests/SurfaceFlinger_test.filter b/services/surfaceflinger/tests/SurfaceFlinger_test.filter
index e5a7774..2bedd7d 100644
--- a/services/surfaceflinger/tests/SurfaceFlinger_test.filter
+++ b/services/surfaceflinger/tests/SurfaceFlinger_test.filter
@@ -1,5 +1,5 @@
{
"presubmit": {
- "filter": "*:-LayerTypeAndRenderTypeTransactionTests/LayerTypeAndRenderTypeTransactionTest.SetCornerRadius/2:LayerTypeAndRenderTypeTransactionTests/LayerTypeAndRenderTypeTransactionTest.SetCornerRadius/3:LayerTypeAndRenderTypeTransactionTests/LayerTypeAndRenderTypeTransactionTest.SetCornerRadiusChildCrop/2:LayerTypeAndRenderTypeTransactionTests/LayerTypeAndRenderTypeTransactionTest.SetCornerRadiusChildCrop/3:MirrorLayerTest.MirrorBufferLayer"
+ "filter": "*:-LayerTypeAndRenderTypeTransactionTests/LayerTypeAndRenderTypeTransactionTest.SetCornerRadius/2:LayerTypeAndRenderTypeTransactionTests/LayerTypeAndRenderTypeTransactionTest.SetCornerRadius/3:LayerTypeAndRenderTypeTransactionTests/LayerTypeAndRenderTypeTransactionTest.SetCornerRadiusChildCrop/2:LayerTypeAndRenderTypeTransactionTests/LayerTypeAndRenderTypeTransactionTest.SetCornerRadiusChildCrop/3"
}
}
diff --git a/services/surfaceflinger/tests/unittests/Android.bp b/services/surfaceflinger/tests/unittests/Android.bp
index 1eaf2dd..7681283 100644
--- a/services/surfaceflinger/tests/unittests/Android.bp
+++ b/services/surfaceflinger/tests/unittests/Android.bp
@@ -32,6 +32,7 @@
// option to false temporarily.
address: true,
},
+ data: ["libsurfaceflinger_unittest.filter"],
srcs: [
":libsurfaceflinger_sources",
"libsurfaceflinger_unittest_main.cpp",
@@ -44,6 +45,7 @@
"EventThreadTest.cpp",
"OneShotTimerTest.cpp",
"LayerHistoryTest.cpp",
+ "LayerHistoryTestV2.cpp",
"LayerMetadataTest.cpp",
"PhaseOffsetsTest.cpp",
"SchedulerTest.cpp",
diff --git a/services/surfaceflinger/tests/unittests/DisplayTransactionTest.cpp b/services/surfaceflinger/tests/unittests/DisplayTransactionTest.cpp
index 9680a17..dddad92 100644
--- a/services/surfaceflinger/tests/unittests/DisplayTransactionTest.cpp
+++ b/services/surfaceflinger/tests/unittests/DisplayTransactionTest.cpp
@@ -1357,7 +1357,8 @@
mHardwareDisplaySize.height),
compositionState.transform);
EXPECT_EQ(TRANSFORM_FLAGS_ROT_0, compositionState.orientation);
- EXPECT_EQ(Rect(mHardwareDisplaySize), compositionState.scissor);
+ EXPECT_EQ(Rect(mHardwareDisplaySize), compositionState.sourceClip);
+ EXPECT_EQ(Rect(mHardwareDisplaySize), compositionState.destinationClip);
EXPECT_EQ(Rect(mHardwareDisplaySize), compositionState.frame);
EXPECT_EQ(Rect(mHardwareDisplaySize), compositionState.viewport);
EXPECT_EQ(false, compositionState.needsFiltering);
@@ -1369,7 +1370,8 @@
mHardwareDisplaySize.height),
compositionState.transform);
EXPECT_EQ(TRANSFORM_FLAGS_ROT_90, compositionState.orientation);
- EXPECT_EQ(Rect(mHardwareDisplaySize), compositionState.scissor);
+ EXPECT_EQ(Rect(mHardwareDisplaySize), compositionState.sourceClip);
+ EXPECT_EQ(Rect(mHardwareDisplaySize), compositionState.destinationClip);
// For 90, the frame and viewport have the hardware display size width and height swapped
EXPECT_EQ(Rect(SwapWH(mHardwareDisplaySize)), compositionState.frame);
EXPECT_EQ(Rect(SwapWH(mHardwareDisplaySize)), compositionState.viewport);
@@ -1382,7 +1384,8 @@
mHardwareDisplaySize.height),
compositionState.transform);
EXPECT_EQ(TRANSFORM_FLAGS_ROT_180, compositionState.orientation);
- EXPECT_EQ(Rect(mHardwareDisplaySize), compositionState.scissor);
+ EXPECT_EQ(Rect(mHardwareDisplaySize), compositionState.sourceClip);
+ EXPECT_EQ(Rect(mHardwareDisplaySize), compositionState.destinationClip);
EXPECT_EQ(Rect(mHardwareDisplaySize), compositionState.frame);
EXPECT_EQ(Rect(mHardwareDisplaySize), compositionState.viewport);
EXPECT_EQ(false, compositionState.needsFiltering);
@@ -1394,7 +1397,8 @@
mHardwareDisplaySize.height),
compositionState.transform);
EXPECT_EQ(TRANSFORM_FLAGS_ROT_270, compositionState.orientation);
- EXPECT_EQ(Rect(mHardwareDisplaySize), compositionState.scissor);
+ EXPECT_EQ(Rect(mHardwareDisplaySize), compositionState.sourceClip);
+ EXPECT_EQ(Rect(mHardwareDisplaySize), compositionState.destinationClip);
// For 270, the frame and viewport have the hardware display size width and height swapped
EXPECT_EQ(Rect(SwapWH(mHardwareDisplaySize)), compositionState.frame);
EXPECT_EQ(Rect(SwapWH(mHardwareDisplaySize)), compositionState.viewport);
diff --git a/services/surfaceflinger/tests/unittests/LayerHistoryTest.cpp b/services/surfaceflinger/tests/unittests/LayerHistoryTest.cpp
index ca51cb6..9ca1b70 100644
--- a/services/surfaceflinger/tests/unittests/LayerHistoryTest.cpp
+++ b/services/surfaceflinger/tests/unittests/LayerHistoryTest.cpp
@@ -73,7 +73,7 @@
HI_FPS_PERIOD},
},
HwcConfigIndexType(0)};
- TestableScheduler* const mScheduler{new TestableScheduler(mConfigs)};
+ TestableScheduler* const mScheduler{new TestableScheduler(mConfigs, false)};
TestableSurfaceFlinger mFlinger;
const nsecs_t mTime = systemTime();
@@ -85,53 +85,36 @@
const auto layer = createLayer();
EXPECT_CALL(*layer, isVisible()).WillRepeatedly(Return(true));
EXPECT_CALL(*layer, getFrameSelectionPriority()).WillRepeatedly(Return(1));
+ EXPECT_CALL(*layer, getFrameRate()).WillRepeatedly(Return(std::nullopt));
EXPECT_EQ(1, layerCount());
EXPECT_EQ(0, activeLayerCount());
- // 0 FPS is returned if no layers are active.
- EXPECT_FLOAT_EQ(0, history().summarize(mTime).maxRefreshRate);
+ // no layers are returned if no layers are active.
+ ASSERT_TRUE(history().summarize(mTime).empty());
EXPECT_EQ(0, activeLayerCount());
- // 0 FPS is returned if active layers have insufficient history.
+ // no layers are returned if active layers have insufficient history.
for (int i = 0; i < PRESENT_TIME_HISTORY_SIZE - 1; i++) {
history().record(layer.get(), 0, mTime);
- EXPECT_FLOAT_EQ(0, history().summarize(mTime).maxRefreshRate);
+ ASSERT_TRUE(history().summarize(mTime).empty());
EXPECT_EQ(1, activeLayerCount());
}
// High FPS is returned once enough history has been recorded.
for (int i = 0; i < 10; i++) {
history().record(layer.get(), 0, mTime);
- EXPECT_FLOAT_EQ(HI_FPS, history().summarize(mTime).maxRefreshRate);
+ ASSERT_EQ(1, history().summarize(mTime).size());
+ EXPECT_FLOAT_EQ(HI_FPS, history().summarize(mTime)[0].desiredRefreshRate);
EXPECT_EQ(1, activeLayerCount());
}
}
-TEST_F(LayerHistoryTest, oneHDRLayer) {
- const auto layer = createLayer();
- EXPECT_CALL(*layer, isVisible()).WillRepeatedly(Return(true));
- EXPECT_CALL(*layer, getFrameSelectionPriority()).WillRepeatedly(Return(1));
-
- EXPECT_EQ(1, layerCount());
- EXPECT_EQ(0, activeLayerCount());
-
- history().record(layer.get(), 0, mTime);
- auto summary = history().summarize(mTime);
- EXPECT_FLOAT_EQ(0, summary.maxRefreshRate);
- EXPECT_EQ(1, activeLayerCount());
-
- EXPECT_CALL(*layer, isVisible()).WillRepeatedly(Return(false));
-
- summary = history().summarize(mTime);
- EXPECT_FLOAT_EQ(0, summary.maxRefreshRate);
- EXPECT_EQ(0, activeLayerCount());
-}
-
TEST_F(LayerHistoryTest, explicitTimestamp) {
const auto layer = createLayer();
EXPECT_CALL(*layer, isVisible()).WillRepeatedly(Return(true));
EXPECT_CALL(*layer, getFrameSelectionPriority()).WillRepeatedly(Return(1));
+ EXPECT_CALL(*layer, getFrameRate()).WillRepeatedly(Return(std::nullopt));
EXPECT_EQ(1, layerCount());
EXPECT_EQ(0, activeLayerCount());
@@ -142,7 +125,8 @@
time += LO_FPS_PERIOD;
}
- EXPECT_FLOAT_EQ(LO_FPS, history().summarize(mTime).maxRefreshRate);
+ ASSERT_EQ(1, history().summarize(mTime).size());
+ EXPECT_FLOAT_EQ(LO_FPS, history().summarize(mTime)[0].desiredRefreshRate);
EXPECT_EQ(1, activeLayerCount());
EXPECT_EQ(1, frequentLayerCount(time));
}
@@ -154,13 +138,15 @@
EXPECT_CALL(*layer1, isVisible()).WillRepeatedly(Return(true));
EXPECT_CALL(*layer1, getFrameSelectionPriority()).WillRepeatedly(Return(1));
+ EXPECT_CALL(*layer1, getFrameRate()).WillRepeatedly(Return(std::nullopt));
EXPECT_CALL(*layer2, isVisible()).WillRepeatedly(Return(true));
EXPECT_CALL(*layer2, getFrameSelectionPriority()).WillRepeatedly(Return(1));
+ EXPECT_CALL(*layer2, getFrameRate()).WillRepeatedly(Return(std::nullopt));
EXPECT_CALL(*layer3, isVisible()).WillRepeatedly(Return(true));
EXPECT_CALL(*layer3, getFrameSelectionPriority()).WillRepeatedly(Return(1));
-
+ EXPECT_CALL(*layer3, getFrameRate()).WillRepeatedly(Return(std::nullopt));
nsecs_t time = mTime;
EXPECT_EQ(3, layerCount());
@@ -173,7 +159,8 @@
time += MAX_FREQUENT_LAYER_PERIOD_NS.count();
}
- EXPECT_FLOAT_EQ(LO_FPS, history().summarize(time).maxRefreshRate);
+ ASSERT_EQ(1, history().summarize(time).size());
+ EXPECT_FLOAT_EQ(LO_FPS, history().summarize(time)[0].desiredRefreshRate);
EXPECT_EQ(1, activeLayerCount());
EXPECT_EQ(0, frequentLayerCount(time));
@@ -186,7 +173,9 @@
// layer1 is still active but infrequent.
history().record(layer1.get(), time, time);
- EXPECT_FLOAT_EQ(HI_FPS, history().summarize(time).maxRefreshRate);
+ ASSERT_EQ(2, history().summarize(time).size());
+ EXPECT_FLOAT_EQ(LO_FPS, history().summarize(time)[0].desiredRefreshRate);
+ EXPECT_FLOAT_EQ(HI_FPS, history().summarize(time)[1].desiredRefreshRate);
EXPECT_EQ(2, activeLayerCount());
EXPECT_EQ(1, frequentLayerCount(time));
@@ -197,7 +186,8 @@
time += LO_FPS_PERIOD;
}
- EXPECT_FLOAT_EQ(LO_FPS, history().summarize(time).maxRefreshRate);
+ ASSERT_EQ(1, history().summarize(time).size());
+ EXPECT_FLOAT_EQ(LO_FPS, history().summarize(time)[0].desiredRefreshRate);
EXPECT_EQ(1, activeLayerCount());
EXPECT_EQ(1, frequentLayerCount(time));
@@ -213,19 +203,24 @@
time += HI_FPS_PERIOD;
}
- EXPECT_FLOAT_EQ(LO_FPS, history().summarize(time).maxRefreshRate);
+ ASSERT_EQ(1, history().summarize(time).size());
+ EXPECT_FLOAT_EQ(LO_FPS, history().summarize(time)[0].desiredRefreshRate);
EXPECT_EQ(2, activeLayerCount());
EXPECT_EQ(2, frequentLayerCount(time));
// layer3 becomes recently active.
history().record(layer3.get(), time, time);
- EXPECT_FLOAT_EQ(HI_FPS, history().summarize(time).maxRefreshRate);
+ ASSERT_EQ(2, history().summarize(time).size());
+ EXPECT_FLOAT_EQ(LO_FPS, history().summarize(time)[0].desiredRefreshRate);
+ EXPECT_FLOAT_EQ(HI_FPS, history().summarize(time)[1].desiredRefreshRate);
EXPECT_EQ(2, activeLayerCount());
EXPECT_EQ(2, frequentLayerCount(time));
// layer1 expires.
layer1.clear();
- EXPECT_FLOAT_EQ(HI_FPS, history().summarize(time).maxRefreshRate);
+ ASSERT_EQ(2, history().summarize(time).size());
+ EXPECT_FLOAT_EQ(LO_FPS, history().summarize(time)[0].desiredRefreshRate);
+ EXPECT_FLOAT_EQ(HI_FPS, history().summarize(time)[1].desiredRefreshRate);
EXPECT_EQ(2, layerCount());
EXPECT_EQ(2, activeLayerCount());
EXPECT_EQ(2, frequentLayerCount(time));
@@ -237,13 +232,14 @@
time += LO_FPS_PERIOD;
}
- EXPECT_FLOAT_EQ(LO_FPS, history().summarize(time).maxRefreshRate);
+ ASSERT_EQ(1, history().summarize(time).size());
+ EXPECT_FLOAT_EQ(LO_FPS, history().summarize(time)[0].desiredRefreshRate);
EXPECT_EQ(1, activeLayerCount());
EXPECT_EQ(1, frequentLayerCount(time));
// layer2 expires.
layer2.clear();
- EXPECT_FLOAT_EQ(0, history().summarize(time).maxRefreshRate);
+ ASSERT_TRUE(history().summarize(time).empty());
EXPECT_EQ(1, layerCount());
EXPECT_EQ(0, activeLayerCount());
EXPECT_EQ(0, frequentLayerCount(time));
@@ -254,14 +250,15 @@
time += HI_FPS_PERIOD;
}
- EXPECT_FLOAT_EQ(HI_FPS, history().summarize(time).maxRefreshRate);
+ ASSERT_EQ(1, history().summarize(time).size());
+ EXPECT_FLOAT_EQ(HI_FPS, history().summarize(time)[0].desiredRefreshRate);
EXPECT_EQ(1, layerCount());
EXPECT_EQ(1, activeLayerCount());
EXPECT_EQ(1, frequentLayerCount(time));
// layer3 expires.
layer3.clear();
- EXPECT_FLOAT_EQ(0, history().summarize(time).maxRefreshRate);
+ ASSERT_TRUE(history().summarize(time).empty());
EXPECT_EQ(0, layerCount());
EXPECT_EQ(0, activeLayerCount());
EXPECT_EQ(0, frequentLayerCount(time));
diff --git a/services/surfaceflinger/tests/unittests/LayerHistoryTestV2.cpp b/services/surfaceflinger/tests/unittests/LayerHistoryTestV2.cpp
new file mode 100644
index 0000000..922966a
--- /dev/null
+++ b/services/surfaceflinger/tests/unittests/LayerHistoryTestV2.cpp
@@ -0,0 +1,423 @@
+/*
+ * 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 "LayerHistoryTestV2"
+
+#include <gmock/gmock.h>
+#include <gtest/gtest.h>
+#include <log/log.h>
+
+#include "Scheduler/LayerHistory.h"
+#include "Scheduler/LayerInfoV2.h"
+#include "TestableScheduler.h"
+#include "TestableSurfaceFlinger.h"
+#include "mock/MockLayer.h"
+
+using testing::_;
+using testing::Return;
+
+namespace android::scheduler {
+
+class LayerHistoryTestV2 : public testing::Test {
+protected:
+ static constexpr auto FREQUENT_LAYER_WINDOW_SIZE = LayerInfoV2::FREQUENT_LAYER_WINDOW_SIZE;
+ static constexpr auto PRESENT_TIME_HISTORY_SIZE = LayerInfoV2::HISTORY_SIZE;
+ static constexpr auto MAX_FREQUENT_LAYER_PERIOD_NS = LayerInfoV2::MAX_FREQUENT_LAYER_PERIOD_NS;
+
+ static constexpr float LO_FPS = 30.f;
+ static constexpr auto LO_FPS_PERIOD = static_cast<nsecs_t>(1e9f / LO_FPS);
+
+ static constexpr float HI_FPS = 90.f;
+ static constexpr auto HI_FPS_PERIOD = static_cast<nsecs_t>(1e9f / HI_FPS);
+
+ LayerHistoryTestV2() { mFlinger.resetScheduler(mScheduler); }
+
+ impl::LayerHistoryV2& history() { return *mScheduler->mutableLayerHistoryV2(); }
+ const impl::LayerHistoryV2& history() const { return *mScheduler->mutableLayerHistoryV2(); }
+
+ size_t layerCount() const { return mScheduler->layerHistorySize(); }
+ size_t activeLayerCount() const NO_THREAD_SAFETY_ANALYSIS { return history().mActiveLayersEnd; }
+
+ auto frequentLayerCount(nsecs_t now) const NO_THREAD_SAFETY_ANALYSIS {
+ const auto& infos = history().mLayerInfos;
+ return std::count_if(infos.begin(),
+ infos.begin() + static_cast<long>(history().mActiveLayersEnd),
+ [now](const auto& pair) { return pair.second->isFrequent(now); });
+ }
+
+ void setLayerInfoVote(Layer* layer,
+ LayerHistory::LayerVoteType vote) NO_THREAD_SAFETY_ANALYSIS {
+ for (auto& [weak, info] : history().mLayerInfos) {
+ if (auto strong = weak.promote(); strong && strong.get() == layer) {
+ info->setDefaultLayerVote(vote);
+ info->setLayerVote(vote, 0);
+ return;
+ }
+ }
+ }
+
+ auto createLayer() { return sp<mock::MockLayer>(new mock::MockLayer(mFlinger.flinger())); }
+
+ RefreshRateConfigs mConfigs{true,
+ {
+ RefreshRateConfigs::InputConfig{HwcConfigIndexType(0),
+ HwcConfigGroupType(0),
+ LO_FPS_PERIOD},
+ RefreshRateConfigs::InputConfig{HwcConfigIndexType(1),
+ HwcConfigGroupType(0),
+ HI_FPS_PERIOD},
+ },
+ HwcConfigIndexType(0)};
+ TestableScheduler* const mScheduler{new TestableScheduler(mConfigs, true)};
+ TestableSurfaceFlinger mFlinger;
+
+ const nsecs_t mTime = systemTime();
+};
+
+namespace {
+
+TEST_F(LayerHistoryTestV2, oneLayer) {
+ const auto layer = createLayer();
+ EXPECT_CALL(*layer, isVisible()).WillRepeatedly(Return(true));
+ EXPECT_CALL(*layer, getFrameRate()).WillRepeatedly(Return(std::nullopt));
+
+ EXPECT_EQ(1, layerCount());
+ EXPECT_EQ(0, activeLayerCount());
+
+ // No layers returned if no layers are active.
+ EXPECT_TRUE(history().summarize(mTime).empty());
+ EXPECT_EQ(0, activeLayerCount());
+
+ // Max returned if active layers have insufficient history.
+ for (int i = 0; i < PRESENT_TIME_HISTORY_SIZE - 1; i++) {
+ history().record(layer.get(), 0, mTime);
+ ASSERT_EQ(1, history().summarize(mTime).size());
+ const auto expectedType = (i + 1 < FREQUENT_LAYER_WINDOW_SIZE)
+ ? LayerHistory::LayerVoteType::Min
+ : LayerHistory::LayerVoteType::Max;
+ EXPECT_EQ(expectedType, history().summarize(mTime)[0].vote);
+ EXPECT_EQ(1, activeLayerCount());
+ }
+
+ // Max is returned since we have enough history but there is no timestamp votes.
+ for (int i = 0; i < 10; i++) {
+ history().record(layer.get(), 0, mTime);
+ ASSERT_EQ(1, history().summarize(mTime).size());
+ EXPECT_EQ(LayerHistory::LayerVoteType::Max, history().summarize(mTime)[0].vote);
+ EXPECT_EQ(1, activeLayerCount());
+ }
+}
+
+TEST_F(LayerHistoryTestV2, oneInvisibleLayer) {
+ const auto layer = createLayer();
+ EXPECT_CALL(*layer, isVisible()).WillRepeatedly(Return(true));
+ EXPECT_CALL(*layer, getFrameRate()).WillRepeatedly(Return(std::nullopt));
+
+ EXPECT_EQ(1, layerCount());
+ EXPECT_EQ(0, activeLayerCount());
+
+ history().record(layer.get(), 0, mTime);
+ auto summary = history().summarize(mTime);
+ ASSERT_EQ(1, history().summarize(mTime).size());
+ // Layer is still considered inactive so we expect to get Min
+ EXPECT_EQ(LayerHistory::LayerVoteType::Min, history().summarize(mTime)[0].vote);
+ EXPECT_EQ(1, activeLayerCount());
+
+ EXPECT_CALL(*layer, isVisible()).WillRepeatedly(Return(false));
+
+ summary = history().summarize(mTime);
+ EXPECT_TRUE(history().summarize(mTime).empty());
+ EXPECT_EQ(0, activeLayerCount());
+}
+
+TEST_F(LayerHistoryTestV2, explicitTimestamp) {
+ const auto layer = createLayer();
+ EXPECT_CALL(*layer, isVisible()).WillRepeatedly(Return(true));
+ EXPECT_CALL(*layer, getFrameRate()).WillRepeatedly(Return(std::nullopt));
+
+ EXPECT_EQ(1, layerCount());
+ EXPECT_EQ(0, activeLayerCount());
+
+ nsecs_t time = mTime;
+ for (int i = 0; i < PRESENT_TIME_HISTORY_SIZE; i++) {
+ history().record(layer.get(), time, time);
+ time += LO_FPS_PERIOD;
+ }
+
+ ASSERT_EQ(1, history().summarize(time).size());
+ EXPECT_EQ(LayerHistory::LayerVoteType::Heuristic, history().summarize(time)[0].vote);
+ EXPECT_FLOAT_EQ(LO_FPS, history().summarize(time)[0].desiredRefreshRate);
+ EXPECT_EQ(1, activeLayerCount());
+ EXPECT_EQ(1, frequentLayerCount(time));
+}
+
+TEST_F(LayerHistoryTestV2, oneLayerNoVote) {
+ const auto layer = createLayer();
+ EXPECT_CALL(*layer, isVisible()).WillRepeatedly(Return(true));
+ EXPECT_CALL(*layer, getFrameRate()).WillRepeatedly(Return(std::nullopt));
+
+ setLayerInfoVote(layer.get(), LayerHistory::LayerVoteType::NoVote);
+
+ EXPECT_EQ(1, layerCount());
+ EXPECT_EQ(0, activeLayerCount());
+
+ nsecs_t time = mTime;
+ for (int i = 0; i < PRESENT_TIME_HISTORY_SIZE; i++) {
+ history().record(layer.get(), time, time);
+ time += HI_FPS_PERIOD;
+ }
+
+ ASSERT_TRUE(history().summarize(time).empty());
+ EXPECT_EQ(1, activeLayerCount());
+ EXPECT_EQ(1, frequentLayerCount(time));
+
+ // layer became inactive
+ time += MAX_ACTIVE_LAYER_PERIOD_NS.count();
+ ASSERT_TRUE(history().summarize(time).empty());
+ EXPECT_EQ(0, activeLayerCount());
+ EXPECT_EQ(0, frequentLayerCount(time));
+}
+
+TEST_F(LayerHistoryTestV2, oneLayerMinVote) {
+ const auto layer = createLayer();
+ EXPECT_CALL(*layer, isVisible()).WillRepeatedly(Return(true));
+ EXPECT_CALL(*layer, getFrameRate()).WillRepeatedly(Return(std::nullopt));
+
+ setLayerInfoVote(layer.get(), LayerHistory::LayerVoteType::Min);
+
+ EXPECT_EQ(1, layerCount());
+ EXPECT_EQ(0, activeLayerCount());
+
+ nsecs_t time = mTime;
+ for (int i = 0; i < PRESENT_TIME_HISTORY_SIZE; i++) {
+ history().record(layer.get(), time, time);
+ time += HI_FPS_PERIOD;
+ }
+
+ ASSERT_EQ(1, history().summarize(time).size());
+ EXPECT_EQ(LayerHistory::LayerVoteType::Min, history().summarize(time)[0].vote);
+ EXPECT_EQ(1, activeLayerCount());
+ EXPECT_EQ(1, frequentLayerCount(time));
+
+ // layer became inactive
+ time += MAX_ACTIVE_LAYER_PERIOD_NS.count();
+ ASSERT_TRUE(history().summarize(time).empty());
+ EXPECT_EQ(0, activeLayerCount());
+ EXPECT_EQ(0, frequentLayerCount(time));
+}
+
+TEST_F(LayerHistoryTestV2, oneLayerMaxVote) {
+ const auto layer = createLayer();
+ EXPECT_CALL(*layer, isVisible()).WillRepeatedly(Return(true));
+ EXPECT_CALL(*layer, getFrameRate()).WillRepeatedly(Return(std::nullopt));
+
+ setLayerInfoVote(layer.get(), LayerHistory::LayerVoteType::Max);
+
+ EXPECT_EQ(1, layerCount());
+ EXPECT_EQ(0, activeLayerCount());
+
+ nsecs_t time = mTime;
+ for (int i = 0; i < PRESENT_TIME_HISTORY_SIZE; i++) {
+ history().record(layer.get(), time, time);
+ time += LO_FPS_PERIOD;
+ }
+
+ ASSERT_EQ(1, history().summarize(time).size());
+ EXPECT_EQ(LayerHistory::LayerVoteType::Max, history().summarize(time)[0].vote);
+ EXPECT_EQ(1, activeLayerCount());
+ EXPECT_EQ(1, frequentLayerCount(time));
+
+ // layer became inactive
+ time += MAX_ACTIVE_LAYER_PERIOD_NS.count();
+ ASSERT_TRUE(history().summarize(time).empty());
+ EXPECT_EQ(0, activeLayerCount());
+ EXPECT_EQ(0, frequentLayerCount(time));
+}
+
+TEST_F(LayerHistoryTestV2, oneLayerExplicitVote) {
+ auto layer = createLayer();
+ EXPECT_CALL(*layer, isVisible()).WillRepeatedly(Return(true));
+ EXPECT_CALL(*layer, getFrameRate()).WillRepeatedly(Return(73.4f));
+
+ EXPECT_EQ(1, layerCount());
+ EXPECT_EQ(0, activeLayerCount());
+
+ nsecs_t time = mTime;
+ for (int i = 0; i < PRESENT_TIME_HISTORY_SIZE; i++) {
+ history().record(layer.get(), time, time);
+ time += HI_FPS_PERIOD;
+ }
+
+ ASSERT_EQ(1, history().summarize(time).size());
+ EXPECT_EQ(LayerHistory::LayerVoteType::Explicit, history().summarize(time)[0].vote);
+ EXPECT_FLOAT_EQ(73.4f, history().summarize(time)[0].desiredRefreshRate);
+ EXPECT_EQ(1, activeLayerCount());
+ EXPECT_EQ(1, frequentLayerCount(time));
+
+ // layer became inactive
+ setLayerInfoVote(layer.get(), LayerHistory::LayerVoteType::Heuristic);
+ time += MAX_ACTIVE_LAYER_PERIOD_NS.count();
+ ASSERT_TRUE(history().summarize(time).empty());
+ // TODO: activeLayerCount() should be 0 but it is 1 since getFrameRate() returns a value > 0
+ EXPECT_EQ(1, activeLayerCount());
+ EXPECT_EQ(0, frequentLayerCount(time));
+}
+
+TEST_F(LayerHistoryTestV2, multipleLayers) {
+ auto layer1 = createLayer();
+ auto layer2 = createLayer();
+ auto layer3 = createLayer();
+
+ EXPECT_CALL(*layer1, isVisible()).WillRepeatedly(Return(true));
+ EXPECT_CALL(*layer1, getFrameRate()).WillRepeatedly(Return(std::nullopt));
+
+ EXPECT_CALL(*layer2, isVisible()).WillRepeatedly(Return(true));
+ EXPECT_CALL(*layer2, getFrameRate()).WillRepeatedly(Return(std::nullopt));
+
+ EXPECT_CALL(*layer3, isVisible()).WillRepeatedly(Return(true));
+ EXPECT_CALL(*layer3, getFrameRate()).WillRepeatedly(Return(std::nullopt));
+
+ nsecs_t time = mTime;
+
+ EXPECT_EQ(3, layerCount());
+ EXPECT_EQ(0, activeLayerCount());
+ EXPECT_EQ(0, frequentLayerCount(time));
+
+ // layer1 is active but infrequent.
+ for (int i = 0; i < PRESENT_TIME_HISTORY_SIZE; i++) {
+ history().record(layer1.get(), time, time);
+ time += MAX_FREQUENT_LAYER_PERIOD_NS.count();
+ }
+
+ ASSERT_EQ(1, history().summarize(time).size());
+ EXPECT_EQ(LayerHistory::LayerVoteType::Min, history().summarize(time)[0].vote);
+ EXPECT_EQ(1, activeLayerCount());
+ EXPECT_EQ(0, frequentLayerCount(time));
+
+ // layer2 is frequent and has high refresh rate.
+ for (int i = 0; i < PRESENT_TIME_HISTORY_SIZE; i++) {
+ history().record(layer2.get(), time, time);
+ time += HI_FPS_PERIOD;
+ }
+
+ // layer1 is still active but infrequent.
+ history().record(layer1.get(), time, time);
+
+ ASSERT_EQ(2, history().summarize(time).size());
+ EXPECT_EQ(LayerHistory::LayerVoteType::Min, history().summarize(time)[0].vote);
+ EXPECT_EQ(LayerHistory::LayerVoteType::Heuristic, history().summarize(time)[1].vote);
+ EXPECT_FLOAT_EQ(HI_FPS, history().summarize(time)[1].desiredRefreshRate);
+ EXPECT_EQ(2, activeLayerCount());
+ EXPECT_EQ(1, frequentLayerCount(time));
+
+ // layer1 is no longer active.
+ // layer2 is frequent and has low refresh rate.
+ for (int i = 0; i < PRESENT_TIME_HISTORY_SIZE; i++) {
+ history().record(layer2.get(), time, time);
+ time += LO_FPS_PERIOD;
+ }
+
+ ASSERT_EQ(1, history().summarize(time).size());
+ EXPECT_EQ(LayerHistory::LayerVoteType::Heuristic, history().summarize(time)[0].vote);
+ EXPECT_FLOAT_EQ(LO_FPS, history().summarize(time)[0].desiredRefreshRate);
+ EXPECT_EQ(1, activeLayerCount());
+ EXPECT_EQ(1, frequentLayerCount(time));
+
+ // layer2 still has low refresh rate.
+ // layer3 has high refresh rate but not enough history.
+ constexpr int RATIO = LO_FPS_PERIOD / HI_FPS_PERIOD;
+ for (int i = 0; i < PRESENT_TIME_HISTORY_SIZE - 1; i++) {
+ if (i % RATIO == 0) {
+ history().record(layer2.get(), time, time);
+ }
+
+ history().record(layer3.get(), time, time);
+ time += HI_FPS_PERIOD;
+ }
+
+ ASSERT_EQ(2, history().summarize(time).size());
+ EXPECT_EQ(LayerHistory::LayerVoteType::Heuristic, history().summarize(time)[0].vote);
+ EXPECT_FLOAT_EQ(LO_FPS, history().summarize(time)[0].desiredRefreshRate);
+ EXPECT_EQ(LayerHistory::LayerVoteType::Max, history().summarize(time)[1].vote);
+ EXPECT_EQ(2, activeLayerCount());
+ EXPECT_EQ(2, frequentLayerCount(time));
+
+ // layer3 becomes recently active.
+ history().record(layer3.get(), time, time);
+ ASSERT_EQ(2, history().summarize(time).size());
+ EXPECT_EQ(LayerHistory::LayerVoteType::Heuristic, history().summarize(time)[0].vote);
+ EXPECT_FLOAT_EQ(LO_FPS, history().summarize(time)[0].desiredRefreshRate);
+ EXPECT_EQ(LayerHistory::LayerVoteType::Heuristic, history().summarize(time)[1].vote);
+ EXPECT_FLOAT_EQ(HI_FPS, history().summarize(time)[1].desiredRefreshRate);
+ EXPECT_EQ(2, activeLayerCount());
+ EXPECT_EQ(2, frequentLayerCount(time));
+
+ // layer1 expires.
+ layer1.clear();
+ ASSERT_EQ(2, history().summarize(time).size());
+ EXPECT_EQ(LayerHistory::LayerVoteType::Heuristic, history().summarize(time)[0].vote);
+ EXPECT_EQ(LayerHistory::LayerVoteType::Heuristic, history().summarize(time)[0].vote);
+ EXPECT_FLOAT_EQ(LO_FPS, history().summarize(time)[0].desiredRefreshRate);
+ EXPECT_EQ(LayerHistory::LayerVoteType::Heuristic, history().summarize(time)[1].vote);
+ EXPECT_FLOAT_EQ(HI_FPS, history().summarize(time)[1].desiredRefreshRate);
+ EXPECT_EQ(2, layerCount());
+ EXPECT_EQ(2, activeLayerCount());
+ EXPECT_EQ(2, frequentLayerCount(time));
+
+ // layer2 still has low refresh rate.
+ // layer3 becomes inactive.
+ for (int i = 0; i < PRESENT_TIME_HISTORY_SIZE; i++) {
+ history().record(layer2.get(), time, time);
+ time += LO_FPS_PERIOD;
+ }
+
+ ASSERT_EQ(1, history().summarize(time).size());
+ EXPECT_EQ(LayerHistory::LayerVoteType::Heuristic, history().summarize(time)[0].vote);
+ EXPECT_FLOAT_EQ(LO_FPS, history().summarize(time)[0].desiredRefreshRate);
+ EXPECT_EQ(1, activeLayerCount());
+ EXPECT_EQ(1, frequentLayerCount(time));
+
+ // layer2 expires.
+ layer2.clear();
+ EXPECT_TRUE(history().summarize(time).empty());
+ EXPECT_EQ(1, layerCount());
+ EXPECT_EQ(0, activeLayerCount());
+ EXPECT_EQ(0, frequentLayerCount(time));
+
+ // layer3 becomes active and has high refresh rate.
+ for (int i = 0; i < PRESENT_TIME_HISTORY_SIZE; i++) {
+ history().record(layer3.get(), time, time);
+ time += HI_FPS_PERIOD;
+ }
+
+ ASSERT_EQ(1, history().summarize(time).size());
+ EXPECT_EQ(LayerHistory::LayerVoteType::Heuristic, history().summarize(time)[0].vote);
+ EXPECT_FLOAT_EQ(HI_FPS, history().summarize(time)[0].desiredRefreshRate);
+ EXPECT_EQ(1, layerCount());
+ EXPECT_EQ(1, activeLayerCount());
+ EXPECT_EQ(1, frequentLayerCount(time));
+
+ // layer3 expires.
+ layer3.clear();
+ EXPECT_TRUE(history().summarize(time).empty());
+ EXPECT_EQ(0, layerCount());
+ EXPECT_EQ(0, activeLayerCount());
+ EXPECT_EQ(0, frequentLayerCount(time));
+}
+
+} // namespace
+} // namespace android::scheduler
diff --git a/services/surfaceflinger/tests/unittests/RefreshRateConfigsTest.cpp b/services/surfaceflinger/tests/unittests/RefreshRateConfigsTest.cpp
index 86aa8fb..19a58dc 100644
--- a/services/surfaceflinger/tests/unittests/RefreshRateConfigsTest.cpp
+++ b/services/surfaceflinger/tests/unittests/RefreshRateConfigsTest.cpp
@@ -31,16 +31,24 @@
namespace scheduler {
using RefreshRate = RefreshRateConfigs::RefreshRate;
+using LayerVoteType = RefreshRateConfigs::LayerVoteType;
+using LayerRequirement = RefreshRateConfigs::LayerRequirement;
class RefreshRateConfigsTest : public testing::Test {
protected:
static inline const HwcConfigIndexType HWC_CONFIG_ID_60 = HwcConfigIndexType(0);
- static inline const HwcConfigIndexType HWC_CONFIG_ID_90 = HwcConfigIndexType(1);
+ static inline const HwcConfigIndexType HWC_CONFIG_ID_72 = HwcConfigIndexType(1);
+ static inline const HwcConfigIndexType HWC_CONFIG_ID_90 = HwcConfigIndexType(2);
+ static inline const HwcConfigIndexType HWC_CONFIG_ID_120 = HwcConfigIndexType(3);
+ static inline const HwcConfigIndexType HWC_CONFIG_ID_30 = HwcConfigIndexType(4);
static inline const HwcConfigGroupType HWC_GROUP_ID_0 = HwcConfigGroupType(0);
static inline const HwcConfigGroupType HWC_GROUP_ID_1 = HwcConfigGroupType(1);
- static constexpr int64_t VSYNC_60 = 16666667;
+ static constexpr auto VSYNC_30 = static_cast<int64_t>(1e9f / 30);
+ static constexpr auto VSYNC_60 = static_cast<int64_t>(1e9f / 60);
+ static constexpr auto VSYNC_72 = static_cast<int64_t>(1e9f / 72);
+ static constexpr auto VSYNC_90 = static_cast<int64_t>(1e9f / 90);
+ static constexpr auto VSYNC_120 = static_cast<int64_t>(1e9f / 120);
static constexpr int64_t VSYNC_60_POINT_4 = 16666665;
- static constexpr int64_t VSYNC_90 = 11111111;
RefreshRateConfigsTest();
~RefreshRateConfigsTest();
@@ -212,31 +220,476 @@
RefreshRate expected60Config = {HWC_CONFIG_ID_60, VSYNC_60, HWC_GROUP_ID_0, "60fps", 60};
RefreshRate expected90Config = {HWC_CONFIG_ID_90, VSYNC_90, HWC_GROUP_ID_0, "90fps", 90};
- ASSERT_EQ(expected90Config, refreshRateConfigs->getRefreshRateForContent(90.0f));
- ASSERT_EQ(expected60Config, refreshRateConfigs->getRefreshRateForContent(60.0f));
- ASSERT_EQ(expected90Config, refreshRateConfigs->getRefreshRateForContent(45.0f));
- ASSERT_EQ(expected60Config, refreshRateConfigs->getRefreshRateForContent(30.0f));
- ASSERT_EQ(expected60Config, refreshRateConfigs->getRefreshRateForContent(24.0f));
+ const auto makeLayerRequirements = [](float refreshRate) -> std::vector<LayerRequirement> {
+ return {{"testLayer", LayerVoteType::Heuristic, refreshRate, 1.0f}};
+ };
+
+ EXPECT_EQ(expected90Config,
+ refreshRateConfigs->getRefreshRateForContent(makeLayerRequirements(90.0f)));
+ EXPECT_EQ(expected60Config,
+ refreshRateConfigs->getRefreshRateForContent(makeLayerRequirements(60.0f)));
+ EXPECT_EQ(expected90Config,
+ refreshRateConfigs->getRefreshRateForContent(makeLayerRequirements(45.0f)));
+ EXPECT_EQ(expected60Config,
+ refreshRateConfigs->getRefreshRateForContent(makeLayerRequirements(30.0f)));
+ EXPECT_EQ(expected60Config,
+ refreshRateConfigs->getRefreshRateForContent(makeLayerRequirements(24.0f)));
ASSERT_GE(refreshRateConfigs->setPolicy(HWC_CONFIG_ID_60, 60, 60, nullptr), 0);
- ASSERT_EQ(expected60Config, refreshRateConfigs->getRefreshRateForContent(90.0f));
- ASSERT_EQ(expected60Config, refreshRateConfigs->getRefreshRateForContent(60.0f));
- ASSERT_EQ(expected60Config, refreshRateConfigs->getRefreshRateForContent(45.0f));
- ASSERT_EQ(expected60Config, refreshRateConfigs->getRefreshRateForContent(30.0f));
- ASSERT_EQ(expected60Config, refreshRateConfigs->getRefreshRateForContent(24.0f));
+ EXPECT_EQ(expected60Config,
+ refreshRateConfigs->getRefreshRateForContent(makeLayerRequirements(90.0f)));
+ EXPECT_EQ(expected60Config,
+ refreshRateConfigs->getRefreshRateForContent(makeLayerRequirements(60.0f)));
+ EXPECT_EQ(expected60Config,
+ refreshRateConfigs->getRefreshRateForContent(makeLayerRequirements(45.0f)));
+ EXPECT_EQ(expected60Config,
+ refreshRateConfigs->getRefreshRateForContent(makeLayerRequirements(30.0f)));
+ EXPECT_EQ(expected60Config,
+ refreshRateConfigs->getRefreshRateForContent(makeLayerRequirements(24.0f)));
ASSERT_GE(refreshRateConfigs->setPolicy(HWC_CONFIG_ID_90, 90, 90, nullptr), 0);
- ASSERT_EQ(expected90Config, refreshRateConfigs->getRefreshRateForContent(90.0f));
- ASSERT_EQ(expected90Config, refreshRateConfigs->getRefreshRateForContent(60.0f));
- ASSERT_EQ(expected90Config, refreshRateConfigs->getRefreshRateForContent(45.0f));
- ASSERT_EQ(expected90Config, refreshRateConfigs->getRefreshRateForContent(30.0f));
- ASSERT_EQ(expected90Config, refreshRateConfigs->getRefreshRateForContent(24.0f));
+ EXPECT_EQ(expected90Config,
+ refreshRateConfigs->getRefreshRateForContent(makeLayerRequirements(90.0f)));
+ EXPECT_EQ(expected90Config,
+ refreshRateConfigs->getRefreshRateForContent(makeLayerRequirements(60.0f)));
+ EXPECT_EQ(expected90Config,
+ refreshRateConfigs->getRefreshRateForContent(makeLayerRequirements(45.0f)));
+ EXPECT_EQ(expected90Config,
+ refreshRateConfigs->getRefreshRateForContent(makeLayerRequirements(30.0f)));
+ EXPECT_EQ(expected90Config,
+ refreshRateConfigs->getRefreshRateForContent(makeLayerRequirements(24.0f)));
ASSERT_GE(refreshRateConfigs->setPolicy(HWC_CONFIG_ID_60, 0, 120, nullptr), 0);
- ASSERT_EQ(expected90Config, refreshRateConfigs->getRefreshRateForContent(90.0f));
- ASSERT_EQ(expected60Config, refreshRateConfigs->getRefreshRateForContent(60.0f));
- ASSERT_EQ(expected90Config, refreshRateConfigs->getRefreshRateForContent(45.0f));
- ASSERT_EQ(expected60Config, refreshRateConfigs->getRefreshRateForContent(30.0f));
- ASSERT_EQ(expected60Config, refreshRateConfigs->getRefreshRateForContent(24.0f));
+ EXPECT_EQ(expected90Config,
+ refreshRateConfigs->getRefreshRateForContent(makeLayerRequirements(90.0f)));
+ EXPECT_EQ(expected60Config,
+ refreshRateConfigs->getRefreshRateForContent(makeLayerRequirements(60.0f)));
+ EXPECT_EQ(expected90Config,
+ refreshRateConfigs->getRefreshRateForContent(makeLayerRequirements(45.0f)));
+ EXPECT_EQ(expected60Config,
+ refreshRateConfigs->getRefreshRateForContent(makeLayerRequirements(30.0f)));
+ EXPECT_EQ(expected60Config,
+ refreshRateConfigs->getRefreshRateForContent(makeLayerRequirements(24.0f)));
+}
+
+TEST_F(RefreshRateConfigsTest, twoDeviceConfigs_getRefreshRateForContentV2_60_90) {
+ std::vector<RefreshRateConfigs::InputConfig> configs{
+ {{HWC_CONFIG_ID_60, HWC_GROUP_ID_0, VSYNC_60},
+ {HWC_CONFIG_ID_90, HWC_GROUP_ID_0, VSYNC_90}}};
+ auto refreshRateConfigs =
+ std::make_unique<RefreshRateConfigs>(/*refreshRateSwitching=*/true, configs,
+ /*currentConfigId=*/HWC_CONFIG_ID_60);
+
+ ASSERT_TRUE(refreshRateConfigs->refreshRateSwitchingSupported());
+
+ RefreshRate expected60Config = {HWC_CONFIG_ID_60, VSYNC_60, HWC_GROUP_ID_0, "60fps", 60};
+ RefreshRate expected90Config = {HWC_CONFIG_ID_90, VSYNC_90, HWC_GROUP_ID_0, "90fps", 90};
+
+ auto layers = std::vector<LayerRequirement>{LayerRequirement{.weight = 1.0f}};
+ auto& lr = layers[0];
+
+ lr.vote = LayerVoteType::Min;
+ EXPECT_EQ(expected60Config, refreshRateConfigs->getRefreshRateForContentV2(layers));
+
+ lr.vote = LayerVoteType::Max;
+ EXPECT_EQ(expected90Config, refreshRateConfigs->getRefreshRateForContentV2(layers));
+
+ lr.desiredRefreshRate = 90.0f;
+ lr.vote = LayerVoteType::Heuristic;
+ EXPECT_EQ(expected90Config, refreshRateConfigs->getRefreshRateForContentV2(layers));
+
+ lr.desiredRefreshRate = 60.0f;
+ EXPECT_EQ(expected60Config, refreshRateConfigs->getRefreshRateForContentV2(layers));
+
+ lr.desiredRefreshRate = 45.0f;
+ EXPECT_EQ(expected90Config, refreshRateConfigs->getRefreshRateForContentV2(layers));
+
+ lr.desiredRefreshRate = 30.0f;
+ EXPECT_EQ(expected60Config, refreshRateConfigs->getRefreshRateForContentV2(layers));
+
+ lr.desiredRefreshRate = 24.0f;
+ EXPECT_EQ(expected60Config, refreshRateConfigs->getRefreshRateForContentV2(layers));
+
+ ASSERT_GE(refreshRateConfigs->setPolicy(HWC_CONFIG_ID_60, 60, 60, nullptr), 0);
+
+ lr.vote = LayerVoteType::Min;
+ EXPECT_EQ(expected60Config, refreshRateConfigs->getRefreshRateForContentV2(layers));
+
+ lr.vote = LayerVoteType::Max;
+ EXPECT_EQ(expected60Config, refreshRateConfigs->getRefreshRateForContentV2(layers));
+
+ lr.desiredRefreshRate = 90.0f;
+ lr.vote = LayerVoteType::Heuristic;
+ EXPECT_EQ(expected60Config, refreshRateConfigs->getRefreshRateForContentV2(layers));
+
+ lr.desiredRefreshRate = 60.0f;
+ EXPECT_EQ(expected60Config, refreshRateConfigs->getRefreshRateForContentV2(layers));
+
+ lr.desiredRefreshRate = 45.0f;
+ EXPECT_EQ(expected60Config, refreshRateConfigs->getRefreshRateForContentV2(layers));
+
+ lr.desiredRefreshRate = 30.0f;
+ EXPECT_EQ(expected60Config, refreshRateConfigs->getRefreshRateForContentV2(layers));
+
+ lr.desiredRefreshRate = 24.0f;
+ EXPECT_EQ(expected60Config, refreshRateConfigs->getRefreshRateForContentV2(layers));
+
+ ASSERT_GE(refreshRateConfigs->setPolicy(HWC_CONFIG_ID_90, 90, 90, nullptr), 0);
+
+ lr.vote = LayerVoteType::Min;
+ EXPECT_EQ(expected90Config, refreshRateConfigs->getRefreshRateForContentV2(layers));
+
+ lr.vote = LayerVoteType::Max;
+ EXPECT_EQ(expected90Config, refreshRateConfigs->getRefreshRateForContentV2(layers));
+
+ lr.desiredRefreshRate = 90.0f;
+ lr.vote = LayerVoteType::Heuristic;
+ EXPECT_EQ(expected90Config, refreshRateConfigs->getRefreshRateForContentV2(layers));
+
+ lr.desiredRefreshRate = 60.0f;
+ EXPECT_EQ(expected90Config, refreshRateConfigs->getRefreshRateForContentV2(layers));
+
+ lr.desiredRefreshRate = 45.0f;
+ EXPECT_EQ(expected90Config, refreshRateConfigs->getRefreshRateForContentV2(layers));
+
+ lr.desiredRefreshRate = 30.0f;
+ EXPECT_EQ(expected90Config, refreshRateConfigs->getRefreshRateForContentV2(layers));
+
+ lr.desiredRefreshRate = 24.0f;
+ EXPECT_EQ(expected90Config, refreshRateConfigs->getRefreshRateForContentV2(layers));
+
+ ASSERT_GE(refreshRateConfigs->setPolicy(HWC_CONFIG_ID_60, 0, 120, nullptr), 0);
+ lr.vote = LayerVoteType::Min;
+ EXPECT_EQ(expected60Config, refreshRateConfigs->getRefreshRateForContentV2(layers));
+
+ lr.vote = LayerVoteType::Max;
+ EXPECT_EQ(expected90Config, refreshRateConfigs->getRefreshRateForContentV2(layers));
+
+ lr.desiredRefreshRate = 90.0f;
+ lr.vote = LayerVoteType::Heuristic;
+ EXPECT_EQ(expected90Config, refreshRateConfigs->getRefreshRateForContentV2(layers));
+
+ lr.desiredRefreshRate = 60.0f;
+ EXPECT_EQ(expected60Config, refreshRateConfigs->getRefreshRateForContentV2(layers));
+
+ lr.desiredRefreshRate = 45.0f;
+ EXPECT_EQ(expected90Config, refreshRateConfigs->getRefreshRateForContentV2(layers));
+
+ lr.desiredRefreshRate = 30.0f;
+ EXPECT_EQ(expected60Config, refreshRateConfigs->getRefreshRateForContentV2(layers));
+
+ lr.desiredRefreshRate = 24.0f;
+ EXPECT_EQ(expected60Config, refreshRateConfigs->getRefreshRateForContentV2(layers));
+}
+
+TEST_F(RefreshRateConfigsTest, twoDeviceConfigs_getRefreshRateForContentV2_60_72_90) {
+ std::vector<RefreshRateConfigs::InputConfig> configs{
+ {{HWC_CONFIG_ID_60, HWC_GROUP_ID_0, VSYNC_60},
+ {HWC_CONFIG_ID_72, HWC_GROUP_ID_0, VSYNC_72},
+ {HWC_CONFIG_ID_90, HWC_GROUP_ID_0, VSYNC_90}}};
+ auto refreshRateConfigs =
+ std::make_unique<RefreshRateConfigs>(/*refreshRateSwitching=*/true, configs,
+ /*currentConfigId=*/HWC_CONFIG_ID_60);
+
+ ASSERT_TRUE(refreshRateConfigs->refreshRateSwitchingSupported());
+
+ RefreshRate expected60Config = {HWC_CONFIG_ID_60, VSYNC_60, HWC_GROUP_ID_0, "60fps", 60};
+ RefreshRate expected72Config = {HWC_CONFIG_ID_72, VSYNC_72, HWC_GROUP_ID_0, "72fps", 70};
+ RefreshRate expected90Config = {HWC_CONFIG_ID_90, VSYNC_90, HWC_GROUP_ID_0, "90fps", 90};
+
+ auto layers = std::vector<LayerRequirement>{LayerRequirement{.weight = 1.0f}};
+ auto& lr = layers[0];
+
+ lr.vote = LayerVoteType::Min;
+ EXPECT_EQ(expected60Config, refreshRateConfigs->getRefreshRateForContentV2(layers));
+
+ lr.vote = LayerVoteType::Max;
+ EXPECT_EQ(expected90Config, refreshRateConfigs->getRefreshRateForContentV2(layers));
+
+ lr.desiredRefreshRate = 90.0f;
+ lr.vote = LayerVoteType::Heuristic;
+ EXPECT_EQ(expected90Config, refreshRateConfigs->getRefreshRateForContentV2(layers));
+
+ lr.desiredRefreshRate = 60.0f;
+ EXPECT_EQ(expected60Config, refreshRateConfigs->getRefreshRateForContentV2(layers));
+
+ lr.desiredRefreshRate = 45.0f;
+ EXPECT_EQ(expected90Config, refreshRateConfigs->getRefreshRateForContentV2(layers));
+
+ lr.desiredRefreshRate = 30.0f;
+ EXPECT_EQ(expected60Config, refreshRateConfigs->getRefreshRateForContentV2(layers));
+
+ lr.desiredRefreshRate = 24.0f;
+ EXPECT_EQ(expected72Config, refreshRateConfigs->getRefreshRateForContentV2(layers));
+}
+
+TEST_F(RefreshRateConfigsTest, twoDeviceConfigs_getRefreshRateForContentV2_30_60_72_90_120) {
+ std::vector<RefreshRateConfigs::InputConfig> configs{
+ {{HWC_CONFIG_ID_30, HWC_GROUP_ID_0, VSYNC_30},
+ {HWC_CONFIG_ID_60, HWC_GROUP_ID_0, VSYNC_60},
+ {HWC_CONFIG_ID_72, HWC_GROUP_ID_0, VSYNC_72},
+ {HWC_CONFIG_ID_90, HWC_GROUP_ID_0, VSYNC_90},
+ {HWC_CONFIG_ID_120, HWC_GROUP_ID_0, VSYNC_120}}};
+ auto refreshRateConfigs =
+ std::make_unique<RefreshRateConfigs>(/*refreshRateSwitching=*/true, configs,
+ /*currentConfigId=*/HWC_CONFIG_ID_60);
+
+ ASSERT_TRUE(refreshRateConfigs->refreshRateSwitchingSupported());
+
+ RefreshRate expected30Config = {HWC_CONFIG_ID_30, VSYNC_30, HWC_GROUP_ID_0, "30fps", 30};
+ RefreshRate expected60Config = {HWC_CONFIG_ID_60, VSYNC_60, HWC_GROUP_ID_0, "60fps", 60};
+ RefreshRate expected72Config = {HWC_CONFIG_ID_72, VSYNC_72, HWC_GROUP_ID_0, "72fps", 70};
+ RefreshRate expected90Config = {HWC_CONFIG_ID_90, VSYNC_90, HWC_GROUP_ID_0, "90fps", 90};
+ RefreshRate expected120Config = {HWC_CONFIG_ID_120, VSYNC_120, HWC_GROUP_ID_0, "120fps", 120};
+
+ auto layers = std::vector<LayerRequirement>{LayerRequirement{.weight = 1.0f},
+ LayerRequirement{.weight = 1.0f}};
+ auto& lr1 = layers[0];
+ auto& lr2 = layers[1];
+
+ lr1.desiredRefreshRate = 24.0f;
+ lr1.vote = LayerVoteType::Heuristic;
+ lr2.desiredRefreshRate = 60.0f;
+ lr2.vote = LayerVoteType::Heuristic;
+ EXPECT_EQ(expected120Config, refreshRateConfigs->getRefreshRateForContentV2(layers));
+
+ lr1.desiredRefreshRate = 24.0f;
+ lr1.vote = LayerVoteType::Heuristic;
+ lr2.desiredRefreshRate = 48.0f;
+ lr2.vote = LayerVoteType::Heuristic;
+ EXPECT_EQ(expected72Config, refreshRateConfigs->getRefreshRateForContentV2(layers));
+
+ lr1.desiredRefreshRate = 24.0f;
+ lr1.vote = LayerVoteType::Heuristic;
+ lr2.desiredRefreshRate = 48.0f;
+ lr2.vote = LayerVoteType::Heuristic;
+ EXPECT_EQ(expected72Config, refreshRateConfigs->getRefreshRateForContentV2(layers));
+}
+
+TEST_F(RefreshRateConfigsTest, twoDeviceConfigs_getRefreshRateForContentV2_30_60) {
+ std::vector<RefreshRateConfigs::InputConfig> configs{
+ {{HWC_CONFIG_ID_60, HWC_GROUP_ID_0, VSYNC_60},
+ {HWC_CONFIG_ID_30, HWC_GROUP_ID_0, VSYNC_30}}};
+ auto refreshRateConfigs =
+ std::make_unique<RefreshRateConfigs>(/*refreshRateSwitching=*/true, configs,
+ /*currentConfigId=*/HWC_CONFIG_ID_60);
+
+ ASSERT_TRUE(refreshRateConfigs->refreshRateSwitchingSupported());
+
+ RefreshRate expected60Config = {HWC_CONFIG_ID_60, VSYNC_60, HWC_GROUP_ID_0, "60fps", 60};
+ RefreshRate expected30Config = {HWC_CONFIG_ID_30, VSYNC_30, HWC_GROUP_ID_0, "30fps", 30};
+
+ auto layers = std::vector<LayerRequirement>{LayerRequirement{.weight = 1.0f}};
+ auto& lr = layers[0];
+
+ lr.vote = LayerVoteType::Min;
+ EXPECT_EQ(expected30Config, refreshRateConfigs->getRefreshRateForContentV2(layers));
+
+ lr.vote = LayerVoteType::Max;
+ EXPECT_EQ(expected60Config, refreshRateConfigs->getRefreshRateForContentV2(layers));
+
+ lr.desiredRefreshRate = 90.0f;
+ lr.vote = LayerVoteType::Heuristic;
+ EXPECT_EQ(expected60Config, refreshRateConfigs->getRefreshRateForContentV2(layers));
+
+ lr.desiredRefreshRate = 60.0f;
+ EXPECT_EQ(expected60Config, refreshRateConfigs->getRefreshRateForContentV2(layers));
+
+ lr.desiredRefreshRate = 45.0f;
+ EXPECT_EQ(expected60Config, refreshRateConfigs->getRefreshRateForContentV2(layers));
+
+ lr.desiredRefreshRate = 30.0f;
+ EXPECT_EQ(expected30Config, refreshRateConfigs->getRefreshRateForContentV2(layers));
+
+ lr.desiredRefreshRate = 24.0f;
+ EXPECT_EQ(expected60Config, refreshRateConfigs->getRefreshRateForContentV2(layers));
+}
+
+TEST_F(RefreshRateConfigsTest, twoDeviceConfigs_getRefreshRateForContentV2_30_60_72_90) {
+ std::vector<RefreshRateConfigs::InputConfig> configs{
+ {{HWC_CONFIG_ID_30, HWC_GROUP_ID_0, VSYNC_30},
+ {HWC_CONFIG_ID_60, HWC_GROUP_ID_0, VSYNC_60},
+ {HWC_CONFIG_ID_72, HWC_GROUP_ID_0, VSYNC_72},
+ {HWC_CONFIG_ID_90, HWC_GROUP_ID_0, VSYNC_90}}};
+ auto refreshRateConfigs =
+ std::make_unique<RefreshRateConfigs>(/*refreshRateSwitching=*/true, configs,
+ /*currentConfigId=*/HWC_CONFIG_ID_60);
+
+ ASSERT_TRUE(refreshRateConfigs->refreshRateSwitchingSupported());
+
+ RefreshRate expected30Config = {HWC_CONFIG_ID_30, VSYNC_30, HWC_GROUP_ID_0, "30fps", 30};
+ RefreshRate expected60Config = {HWC_CONFIG_ID_60, VSYNC_60, HWC_GROUP_ID_0, "60fps", 60};
+ RefreshRate expected72Config = {HWC_CONFIG_ID_72, VSYNC_72, HWC_GROUP_ID_0, "72fps", 70};
+ RefreshRate expected90Config = {HWC_CONFIG_ID_90, VSYNC_90, HWC_GROUP_ID_0, "90fps", 90};
+
+ auto layers = std::vector<LayerRequirement>{LayerRequirement{.weight = 1.0f}};
+ auto& lr = layers[0];
+
+ lr.vote = LayerVoteType::Min;
+ EXPECT_EQ(expected30Config, refreshRateConfigs->getRefreshRateForContentV2(layers));
+
+ lr.vote = LayerVoteType::Max;
+ EXPECT_EQ(expected90Config, refreshRateConfigs->getRefreshRateForContentV2(layers));
+
+ lr.desiredRefreshRate = 90.0f;
+ lr.vote = LayerVoteType::Heuristic;
+ EXPECT_EQ(expected90Config, refreshRateConfigs->getRefreshRateForContentV2(layers));
+
+ lr.desiredRefreshRate = 60.0f;
+ EXPECT_EQ(expected60Config, refreshRateConfigs->getRefreshRateForContentV2(layers));
+
+ lr.desiredRefreshRate = 45.0f;
+ EXPECT_EQ(expected90Config, refreshRateConfigs->getRefreshRateForContentV2(layers));
+
+ lr.desiredRefreshRate = 30.0f;
+ EXPECT_EQ(expected30Config, refreshRateConfigs->getRefreshRateForContentV2(layers));
+
+ lr.desiredRefreshRate = 24.0f;
+ EXPECT_EQ(expected72Config, refreshRateConfigs->getRefreshRateForContentV2(layers));
+}
+
+TEST_F(RefreshRateConfigsTest, twoDeviceConfigs_getRefreshRateForContentV2_PriorityTest) {
+ std::vector<RefreshRateConfigs::InputConfig> configs{
+ {{HWC_CONFIG_ID_30, HWC_GROUP_ID_0, VSYNC_30},
+ {HWC_CONFIG_ID_60, HWC_GROUP_ID_0, VSYNC_60},
+ {HWC_CONFIG_ID_90, HWC_GROUP_ID_0, VSYNC_90}}};
+ auto refreshRateConfigs =
+ std::make_unique<RefreshRateConfigs>(/*refreshRateSwitching=*/true, configs,
+ /*currentConfigId=*/HWC_CONFIG_ID_60);
+
+ ASSERT_TRUE(refreshRateConfigs->refreshRateSwitchingSupported());
+
+ RefreshRate expected30Config = {HWC_CONFIG_ID_30, VSYNC_30, HWC_GROUP_ID_0, "30fps", 30};
+ RefreshRate expected60Config = {HWC_CONFIG_ID_60, VSYNC_60, HWC_GROUP_ID_0, "60fps", 60};
+ RefreshRate expected90Config = {HWC_CONFIG_ID_90, VSYNC_90, HWC_GROUP_ID_0, "90fps", 90};
+
+ auto layers = std::vector<LayerRequirement>{LayerRequirement{.weight = 1.0f},
+ LayerRequirement{.weight = 1.0f}};
+ auto& lr1 = layers[0];
+ auto& lr2 = layers[1];
+
+ lr1.vote = LayerVoteType::Min;
+ lr2.vote = LayerVoteType::Max;
+ EXPECT_EQ(expected90Config, refreshRateConfigs->getRefreshRateForContentV2(layers));
+
+ lr1.vote = LayerVoteType::Min;
+ lr2.vote = LayerVoteType::Heuristic;
+ lr2.desiredRefreshRate = 24.0f;
+ EXPECT_EQ(expected60Config, refreshRateConfigs->getRefreshRateForContentV2(layers));
+
+ lr1.vote = LayerVoteType::Min;
+ lr2.vote = LayerVoteType::Explicit;
+ lr2.desiredRefreshRate = 24.0f;
+ EXPECT_EQ(expected60Config, refreshRateConfigs->getRefreshRateForContentV2(layers));
+
+ lr1.vote = LayerVoteType::Max;
+ lr2.vote = LayerVoteType::Heuristic;
+ lr2.desiredRefreshRate = 60.0f;
+ EXPECT_EQ(expected90Config, refreshRateConfigs->getRefreshRateForContentV2(layers));
+
+ lr1.vote = LayerVoteType::Max;
+ lr2.vote = LayerVoteType::Explicit;
+ lr2.desiredRefreshRate = 60.0f;
+ EXPECT_EQ(expected60Config, refreshRateConfigs->getRefreshRateForContentV2(layers));
+
+ lr1.vote = LayerVoteType::Heuristic;
+ lr1.desiredRefreshRate = 15.0f;
+ lr2.vote = LayerVoteType::Heuristic;
+ lr2.desiredRefreshRate = 45.0f;
+ EXPECT_EQ(expected90Config, refreshRateConfigs->getRefreshRateForContentV2(layers));
+
+ lr1.vote = LayerVoteType::Heuristic;
+ lr1.desiredRefreshRate = 30.0f;
+ lr2.vote = LayerVoteType::Explicit;
+ lr2.desiredRefreshRate = 45.0f;
+ EXPECT_EQ(expected90Config, refreshRateConfigs->getRefreshRateForContentV2(layers));
+}
+
+TEST_F(RefreshRateConfigsTest, twoDeviceConfigs_getRefreshRateForContentV2_24FpsVideo) {
+ std::vector<RefreshRateConfigs::InputConfig> configs{
+ {{HWC_CONFIG_ID_60, HWC_GROUP_ID_0, VSYNC_60},
+ {HWC_CONFIG_ID_90, HWC_GROUP_ID_0, VSYNC_90}}};
+ auto refreshRateConfigs =
+ std::make_unique<RefreshRateConfigs>(/*refreshRateSwitching=*/true, configs,
+ /*currentConfigId=*/HWC_CONFIG_ID_60);
+
+ ASSERT_TRUE(refreshRateConfigs->refreshRateSwitchingSupported());
+
+ RefreshRate expected30Config = {HWC_CONFIG_ID_30, VSYNC_30, HWC_GROUP_ID_0, "30fps", 30};
+ RefreshRate expected60Config = {HWC_CONFIG_ID_60, VSYNC_60, HWC_GROUP_ID_0, "60fps", 60};
+ RefreshRate expected90Config = {HWC_CONFIG_ID_90, VSYNC_90, HWC_GROUP_ID_0, "90fps", 90};
+
+ auto layers = std::vector<LayerRequirement>{LayerRequirement{.weight = 1.0f}};
+ auto& lr = layers[0];
+
+ lr.vote = LayerVoteType::Explicit;
+ for (float fps = 23.0f; fps < 25.0f; fps += 0.1f) {
+ lr.desiredRefreshRate = fps;
+ const auto& refreshRate = refreshRateConfigs->getRefreshRateForContentV2(layers);
+ printf("%.2fHz chooses %s\n", fps, refreshRate.name.c_str());
+ EXPECT_EQ(expected60Config, refreshRate);
+ }
+}
+
+TEST_F(RefreshRateConfigsTest, twoDeviceConfigs_getRefreshRateForContent_Explicit) {
+ std::vector<RefreshRateConfigs::InputConfig> configs{
+ {{HWC_CONFIG_ID_60, HWC_GROUP_ID_0, VSYNC_60},
+ {HWC_CONFIG_ID_90, HWC_GROUP_ID_0, VSYNC_90}}};
+ auto refreshRateConfigs =
+ std::make_unique<RefreshRateConfigs>(/*refreshRateSwitching=*/true, configs,
+ /*currentConfigId=*/HWC_CONFIG_ID_60);
+
+ ASSERT_TRUE(refreshRateConfigs->refreshRateSwitchingSupported());
+
+ RefreshRate expected60Config = {HWC_CONFIG_ID_60, VSYNC_60, HWC_GROUP_ID_0, "60fps", 60};
+ RefreshRate expected90Config = {HWC_CONFIG_ID_90, VSYNC_90, HWC_GROUP_ID_0, "90fps", 90};
+
+ auto layers = std::vector<LayerRequirement>{LayerRequirement{.weight = 1.0f},
+ LayerRequirement{.weight = 1.0f}};
+ auto& lr1 = layers[0];
+ auto& lr2 = layers[1];
+
+ lr1.vote = LayerVoteType::Heuristic;
+ lr1.desiredRefreshRate = 60.0f;
+ lr2.vote = LayerVoteType::Explicit;
+ lr2.desiredRefreshRate = 90.0f;
+ EXPECT_EQ(expected90Config, refreshRateConfigs->getRefreshRateForContent(layers));
+
+ lr1.vote = LayerVoteType::Heuristic;
+ lr1.desiredRefreshRate = 90.0f;
+ lr2.vote = LayerVoteType::Explicit;
+ lr2.desiredRefreshRate = 60.0f;
+ EXPECT_EQ(expected60Config, refreshRateConfigs->getRefreshRateForContent(layers));
+}
+
+TEST_F(RefreshRateConfigsTest, twoDeviceConfigs_getRefreshRateForContentV2_Explicit) {
+ std::vector<RefreshRateConfigs::InputConfig> configs{
+ {{HWC_CONFIG_ID_60, HWC_GROUP_ID_0, VSYNC_60},
+ {HWC_CONFIG_ID_90, HWC_GROUP_ID_0, VSYNC_90}}};
+ auto refreshRateConfigs =
+ std::make_unique<RefreshRateConfigs>(/*refreshRateSwitching=*/true, configs,
+ /*currentConfigId=*/HWC_CONFIG_ID_60);
+
+ ASSERT_TRUE(refreshRateConfigs->refreshRateSwitchingSupported());
+
+ RefreshRate expected60Config = {HWC_CONFIG_ID_60, VSYNC_60, HWC_GROUP_ID_0, "60fps", 60};
+ RefreshRate expected90Config = {HWC_CONFIG_ID_90, VSYNC_90, HWC_GROUP_ID_0, "90fps", 90};
+
+ auto layers = std::vector<LayerRequirement>{LayerRequirement{.weight = 1.0f},
+ LayerRequirement{.weight = 1.0f}};
+ auto& lr1 = layers[0];
+ auto& lr2 = layers[1];
+
+ lr1.vote = LayerVoteType::Heuristic;
+ lr1.desiredRefreshRate = 60.0f;
+ lr2.vote = LayerVoteType::Explicit;
+ lr2.desiredRefreshRate = 90.0f;
+ EXPECT_EQ(expected90Config, refreshRateConfigs->getRefreshRateForContentV2(layers));
+
+ lr1.vote = LayerVoteType::Heuristic;
+ lr1.desiredRefreshRate = 90.0f;
+ lr2.vote = LayerVoteType::Explicit;
+ lr2.desiredRefreshRate = 60.0f;
+ EXPECT_EQ(expected60Config, refreshRateConfigs->getRefreshRateForContentV2(layers));
}
TEST_F(RefreshRateConfigsTest, testInPolicy) {
@@ -249,6 +702,32 @@
ASSERT_FALSE(expectedDefaultConfig.inPolicy(50.0f, 59.998f));
}
+TEST_F(RefreshRateConfigsTest, twoDeviceConfigs_getRefreshRateForContentV2_75HzContent) {
+ std::vector<RefreshRateConfigs::InputConfig> configs{
+ {{HWC_CONFIG_ID_60, HWC_GROUP_ID_0, VSYNC_60},
+ {HWC_CONFIG_ID_90, HWC_GROUP_ID_0, VSYNC_90}}};
+ auto refreshRateConfigs =
+ std::make_unique<RefreshRateConfigs>(/*refreshRateSwitching=*/true, configs,
+ /*currentConfigId=*/HWC_CONFIG_ID_60);
+
+ ASSERT_TRUE(refreshRateConfigs->refreshRateSwitchingSupported());
+
+ RefreshRate expected30Config = {HWC_CONFIG_ID_30, VSYNC_30, HWC_GROUP_ID_0, "30fps", 30};
+ RefreshRate expected60Config = {HWC_CONFIG_ID_60, VSYNC_60, HWC_GROUP_ID_0, "60fps", 60};
+ RefreshRate expected90Config = {HWC_CONFIG_ID_90, VSYNC_90, HWC_GROUP_ID_0, "90fps", 90};
+
+ auto layers = std::vector<LayerRequirement>{LayerRequirement{.weight = 1.0f}};
+ auto& lr = layers[0];
+
+ lr.vote = LayerVoteType::Explicit;
+ for (float fps = 75.0f; fps < 100.0f; fps += 0.1f) {
+ lr.desiredRefreshRate = fps;
+ const auto& refreshRate = refreshRateConfigs->getRefreshRateForContentV2(layers);
+ printf("%.2fHz chooses %s\n", fps, refreshRate.name.c_str());
+ EXPECT_EQ(expected90Config, refreshRate);
+ }
+}
+
} // namespace
} // namespace scheduler
} // namespace android
diff --git a/services/surfaceflinger/tests/unittests/SchedulerTest.cpp b/services/surfaceflinger/tests/unittests/SchedulerTest.cpp
index b1ecf4d..82a00ee 100644
--- a/services/surfaceflinger/tests/unittests/SchedulerTest.cpp
+++ b/services/surfaceflinger/tests/unittests/SchedulerTest.cpp
@@ -76,7 +76,7 @@
scheduler::RefreshRateConfigs>(/*refreshRateSwitching=*/false, configs,
/*currentConfig=*/HwcConfigIndexType(0));
- mScheduler = std::make_unique<TestableScheduler>(*mRefreshRateConfigs);
+ mScheduler = std::make_unique<TestableScheduler>(*mRefreshRateConfigs, false);
auto eventThread = std::make_unique<mock::EventThread>();
mEventThread = eventThread.get();
diff --git a/services/surfaceflinger/tests/unittests/TestableScheduler.h b/services/surfaceflinger/tests/unittests/TestableScheduler.h
index a67c24c..52da34b 100644
--- a/services/surfaceflinger/tests/unittests/TestableScheduler.h
+++ b/services/surfaceflinger/tests/unittests/TestableScheduler.h
@@ -28,16 +28,25 @@
class TestableScheduler : public Scheduler, private ISchedulerCallback {
public:
- explicit TestableScheduler(const scheduler::RefreshRateConfigs& configs)
- : Scheduler([](bool) {}, configs, *this) {
- mLayerHistory = std::make_unique<scheduler::impl::LayerHistory>();
+ TestableScheduler(const scheduler::RefreshRateConfigs& configs, bool useContentDetectionV2)
+ : Scheduler([](bool) {}, configs, *this, useContentDetectionV2) {
+ if (mUseContentDetectionV2) {
+ mLayerHistory = std::make_unique<scheduler::impl::LayerHistoryV2>();
+ } else {
+ mLayerHistory = std::make_unique<scheduler::impl::LayerHistory>();
+ }
}
TestableScheduler(std::unique_ptr<DispSync> primaryDispSync,
std::unique_ptr<EventControlThread> eventControlThread,
- const scheduler::RefreshRateConfigs& configs)
- : Scheduler(std::move(primaryDispSync), std::move(eventControlThread), configs, *this) {
- mLayerHistory = std::make_unique<scheduler::impl::LayerHistory>();
+ const scheduler::RefreshRateConfigs& configs, bool useContentDetectionV2)
+ : Scheduler(std::move(primaryDispSync), std::move(eventControlThread), configs, *this,
+ useContentDetectionV2) {
+ if (mUseContentDetectionV2) {
+ mLayerHistory = std::make_unique<scheduler::impl::LayerHistoryV2>();
+ } else {
+ mLayerHistory = std::make_unique<scheduler::impl::LayerHistory>();
+ }
}
// Used to inject mock event thread.
@@ -46,7 +55,13 @@
}
size_t layerHistorySize() const NO_THREAD_SAFETY_ANALYSIS {
- return static_cast<scheduler::impl::LayerHistory*>(mLayerHistory.get())->mLayerInfos.size();
+ if (mUseContentDetectionV2) {
+ return static_cast<scheduler::impl::LayerHistoryV2*>(mLayerHistory.get())
+ ->mLayerInfos.size();
+ } else {
+ return static_cast<scheduler::impl::LayerHistory*>(mLayerHistory.get())
+ ->mLayerInfos.size();
+ }
}
/* ------------------------------------------------------------------------
@@ -60,6 +75,9 @@
auto mutableLayerHistory() {
return static_cast<scheduler::impl::LayerHistory*>(mLayerHistory.get());
}
+ auto mutableLayerHistoryV2() {
+ return static_cast<scheduler::impl::LayerHistoryV2*>(mLayerHistory.get());
+ }
~TestableScheduler() {
// All these pointer and container clears help ensure that GMock does
diff --git a/services/surfaceflinger/tests/unittests/TestableSurfaceFlinger.h b/services/surfaceflinger/tests/unittests/TestableSurfaceFlinger.h
index 8ddb872..2491533 100644
--- a/services/surfaceflinger/tests/unittests/TestableSurfaceFlinger.h
+++ b/services/surfaceflinger/tests/unittests/TestableSurfaceFlinger.h
@@ -198,7 +198,8 @@
void setupScheduler(std::unique_ptr<DispSync> primaryDispSync,
std::unique_ptr<EventControlThread> eventControlThread,
std::unique_ptr<EventThread> appEventThread,
- std::unique_ptr<EventThread> sfEventThread) {
+ std::unique_ptr<EventThread> sfEventThread,
+ bool useContentDetectionV2 = false) {
std::vector<scheduler::RefreshRateConfigs::InputConfig> configs{
{{HwcConfigIndexType(0), HwcConfigGroupType(0), 16666667}}};
mFlinger->mRefreshRateConfigs = std::make_unique<
@@ -213,7 +214,7 @@
mScheduler =
new TestableScheduler(std::move(primaryDispSync), std::move(eventControlThread),
- *mFlinger->mRefreshRateConfigs);
+ *mFlinger->mRefreshRateConfigs, useContentDetectionV2);
mFlinger->mAppConnectionHandle = mScheduler->createConnection(std::move(appEventThread));
mFlinger->mSfConnectionHandle = mScheduler->createConnection(std::move(sfEventThread));
@@ -443,7 +444,7 @@
static constexpr int32_t DEFAULT_REFRESH_RATE = 16'666'666;
static constexpr int32_t DEFAULT_CONFIG_GROUP = 7;
static constexpr int32_t DEFAULT_DPI = 320;
- static constexpr int32_t DEFAULT_ACTIVE_CONFIG = 0;
+ static constexpr hwc2_config_t DEFAULT_ACTIVE_CONFIG = 0;
static constexpr int32_t DEFAULT_POWER_MODE = 2;
FakeHwcDisplayInjector(DisplayId displayId, HWC2::DisplayType hwcDisplayType,
@@ -465,7 +466,7 @@
return *this;
}
- auto& setRefreshRate(uint32_t refreshRate) {
+ auto& setRefreshRate(int32_t refreshRate) {
mRefreshRate = refreshRate;
return *this;
}
@@ -480,7 +481,7 @@
return *this;
}
- auto& setActiveConfig(int32_t config) {
+ auto& setActiveConfig(hwc2_config_t config) {
mActiveConfig = config;
return *this;
}
@@ -513,7 +514,7 @@
config.setDpiX(mDpiX);
config.setDpiY(mDpiY);
config.setConfigGroup(mConfigGroup);
- display->mutableConfigs().emplace(mActiveConfig, config.build());
+ display->mutableConfigs().emplace(static_cast<int32_t>(mActiveConfig), config.build());
display->mutableIsConnected() = true;
display->setPowerMode(static_cast<HWC2::PowerMode>(mPowerMode));
@@ -534,11 +535,11 @@
hwc2_display_t mHwcDisplayId = DEFAULT_HWC_DISPLAY_ID;
int32_t mWidth = DEFAULT_WIDTH;
int32_t mHeight = DEFAULT_HEIGHT;
- uint32_t mRefreshRate = DEFAULT_REFRESH_RATE;
+ int32_t mRefreshRate = DEFAULT_REFRESH_RATE;
int32_t mDpiX = DEFAULT_DPI;
int32_t mConfigGroup = DEFAULT_CONFIG_GROUP;
int32_t mDpiY = DEFAULT_DPI;
- int32_t mActiveConfig = DEFAULT_ACTIVE_CONFIG;
+ hwc2_config_t mActiveConfig = DEFAULT_ACTIVE_CONFIG;
int32_t mPowerMode = DEFAULT_POWER_MODE;
const std::unordered_set<HWC2::Capability>* mCapabilities = nullptr;
};
diff --git a/services/surfaceflinger/tests/unittests/TimeStatsTest.cpp b/services/surfaceflinger/tests/unittests/TimeStatsTest.cpp
index 68e6697..f65af77 100644
--- a/services/surfaceflinger/tests/unittests/TimeStatsTest.cpp
+++ b/services/surfaceflinger/tests/unittests/TimeStatsTest.cpp
@@ -258,22 +258,25 @@
ASSERT_FALSE(mTimeStats->isEnabled());
}
-TEST_F(TimeStatsTest, enabledAfterBoot) {
+TEST_F(TimeStatsTest, registersCallbacksAfterBoot) {
mTimeStats->onBootFinished();
- ASSERT_TRUE(mTimeStats->isEnabled());
+ EXPECT_THAT(mDelegate->mAtomTags,
+ UnorderedElementsAre(android::util::SURFACEFLINGER_STATS_GLOBAL_INFO,
+ android::util::SURFACEFLINGER_STATS_LAYER_INFO));
+}
+
+TEST_F(TimeStatsTest, unregistersCallbacksOnDestruction) {
+ EXPECT_CALL(*mDelegate,
+ unregisterStatsPullAtomCallback(android::util::SURFACEFLINGER_STATS_GLOBAL_INFO));
+ EXPECT_CALL(*mDelegate,
+ unregisterStatsPullAtomCallback(android::util::SURFACEFLINGER_STATS_LAYER_INFO));
+ mTimeStats.reset();
}
TEST_F(TimeStatsTest, canEnableAndDisableTimeStats) {
EXPECT_TRUE(inputCommand(InputCommand::ENABLE, FMT_STRING).empty());
ASSERT_TRUE(mTimeStats->isEnabled());
- EXPECT_THAT(mDelegate->mAtomTags,
- UnorderedElementsAre(android::util::SURFACEFLINGER_STATS_GLOBAL_INFO,
- android::util::SURFACEFLINGER_STATS_LAYER_INFO));
- EXPECT_CALL(*mDelegate,
- unregisterStatsPullAtomCallback(android::util::SURFACEFLINGER_STATS_GLOBAL_INFO));
- EXPECT_CALL(*mDelegate,
- unregisterStatsPullAtomCallback(android::util::SURFACEFLINGER_STATS_LAYER_INFO));
EXPECT_TRUE(inputCommand(InputCommand::DISABLE, FMT_STRING).empty());
ASSERT_FALSE(mTimeStats->isEnabled());
}
diff --git a/services/surfaceflinger/tests/unittests/VSyncDispatchRealtimeTest.cpp b/services/surfaceflinger/tests/unittests/VSyncDispatchRealtimeTest.cpp
index acf852d..caac61d 100644
--- a/services/surfaceflinger/tests/unittests/VSyncDispatchRealtimeTest.cpp
+++ b/services/surfaceflinger/tests/unittests/VSyncDispatchRealtimeTest.cpp
@@ -37,7 +37,7 @@
public:
FixedRateIdealStubTracker() : mPeriod{toNs(3ms)} {}
- void addVsyncTimestamp(nsecs_t) final {}
+ bool addVsyncTimestamp(nsecs_t) final { return true; }
nsecs_t nextAnticipatedVSyncTimeFrom(nsecs_t timePoint) const final {
auto const floor = timePoint % mPeriod;
@@ -60,7 +60,7 @@
public:
VRRStubTracker(nsecs_t period) : mPeriod{period} {}
- void addVsyncTimestamp(nsecs_t) final {}
+ bool addVsyncTimestamp(nsecs_t) final { return true; }
nsecs_t nextAnticipatedVSyncTimeFrom(nsecs_t time_point) const final {
std::lock_guard<decltype(mMutex)> lk(mMutex);
diff --git a/services/surfaceflinger/tests/unittests/VSyncDispatchTimerQueueTest.cpp b/services/surfaceflinger/tests/unittests/VSyncDispatchTimerQueueTest.cpp
index 70c9225..3ab38e4 100644
--- a/services/surfaceflinger/tests/unittests/VSyncDispatchTimerQueueTest.cpp
+++ b/services/surfaceflinger/tests/unittests/VSyncDispatchTimerQueueTest.cpp
@@ -39,9 +39,10 @@
MockVSyncTracker(nsecs_t period) : mPeriod{period} {
ON_CALL(*this, nextAnticipatedVSyncTimeFrom(_))
.WillByDefault(Invoke(this, &MockVSyncTracker::nextVSyncTime));
+ ON_CALL(*this, addVsyncTimestamp(_)).WillByDefault(Return(true));
}
- MOCK_METHOD1(addVsyncTimestamp, void(nsecs_t));
+ MOCK_METHOD1(addVsyncTimestamp, bool(nsecs_t));
MOCK_CONST_METHOD1(nextAnticipatedVSyncTimeFrom, nsecs_t(nsecs_t));
MOCK_CONST_METHOD0(currentPeriod, nsecs_t());
MOCK_METHOD1(setPeriod, void(nsecs_t));
diff --git a/services/surfaceflinger/tests/unittests/VSyncReactorTest.cpp b/services/surfaceflinger/tests/unittests/VSyncReactorTest.cpp
index ce1fafe..1de72b9 100644
--- a/services/surfaceflinger/tests/unittests/VSyncReactorTest.cpp
+++ b/services/surfaceflinger/tests/unittests/VSyncReactorTest.cpp
@@ -35,7 +35,8 @@
class MockVSyncTracker : public VSyncTracker {
public:
- MOCK_METHOD1(addVsyncTimestamp, void(nsecs_t));
+ MockVSyncTracker() { ON_CALL(*this, addVsyncTimestamp(_)).WillByDefault(Return(true)); }
+ MOCK_METHOD1(addVsyncTimestamp, bool(nsecs_t));
MOCK_CONST_METHOD1(nextAnticipatedVSyncTimeFrom, nsecs_t(nsecs_t));
MOCK_CONST_METHOD0(currentPeriod, nsecs_t());
MOCK_METHOD1(setPeriod, void(nsecs_t));
@@ -46,7 +47,9 @@
public:
VSyncTrackerWrapper(std::shared_ptr<VSyncTracker> const& tracker) : mTracker(tracker) {}
- void addVsyncTimestamp(nsecs_t timestamp) final { mTracker->addVsyncTimestamp(timestamp); }
+ bool addVsyncTimestamp(nsecs_t timestamp) final {
+ return mTracker->addVsyncTimestamp(timestamp);
+ }
nsecs_t nextAnticipatedVSyncTimeFrom(nsecs_t timePoint) const final {
return mTracker->nextAnticipatedVSyncTimeFrom(timePoint);
}
@@ -239,6 +242,22 @@
EXPECT_FALSE(mReactor.addPresentFence(generateSignalledFenceWithTime(mDummyTime)));
}
+TEST_F(VSyncReactorTest, ignoresProperlyAfterAPeriodConfirmation) {
+ bool periodFlushed = true;
+ EXPECT_CALL(*mMockTracker, addVsyncTimestamp(_)).Times(2);
+ mReactor.setIgnorePresentFences(true);
+
+ nsecs_t const newPeriod = 5000;
+ mReactor.setPeriod(newPeriod);
+
+ EXPECT_TRUE(mReactor.addResyncSample(0, &periodFlushed));
+ EXPECT_FALSE(periodFlushed);
+ EXPECT_FALSE(mReactor.addResyncSample(newPeriod, &periodFlushed));
+ EXPECT_TRUE(periodFlushed);
+
+ EXPECT_TRUE(mReactor.addPresentFence(generateSignalledFenceWithTime(0)));
+}
+
TEST_F(VSyncReactorTest, queriesTrackerForNextRefreshNow) {
nsecs_t const fakeTimestamp = 4839;
EXPECT_CALL(*mMockTracker, currentPeriod()).Times(0);
@@ -330,6 +349,35 @@
EXPECT_TRUE(periodFlushed);
}
+TEST_F(VSyncReactorTest, reportedBadTimestampFromPredictorWillReactivateHwVSync) {
+ EXPECT_CALL(*mMockTracker, addVsyncTimestamp(_))
+ .WillOnce(Return(false))
+ .WillOnce(Return(true))
+ .WillOnce(Return(true));
+ EXPECT_TRUE(mReactor.addPresentFence(generateSignalledFenceWithTime(0)));
+ EXPECT_TRUE(mReactor.addPresentFence(generateSignalledFenceWithTime(0)));
+
+ nsecs_t skewyPeriod = period >> 1;
+ bool periodFlushed = false;
+ nsecs_t sampleTime = 0;
+ EXPECT_TRUE(mReactor.addResyncSample(sampleTime += skewyPeriod, &periodFlushed));
+ EXPECT_FALSE(periodFlushed);
+ EXPECT_FALSE(mReactor.addResyncSample(sampleTime += period, &periodFlushed));
+ EXPECT_FALSE(periodFlushed);
+}
+
+TEST_F(VSyncReactorTest, reportedBadTimestampFromPredictorWillReactivateHwVSyncPendingFence) {
+ EXPECT_CALL(*mMockTracker, addVsyncTimestamp(_))
+ .Times(2)
+ .WillOnce(Return(false))
+ .WillOnce(Return(true));
+
+ auto fence = generatePendingFence();
+ EXPECT_FALSE(mReactor.addPresentFence(fence));
+ signalFenceWithTime(fence, period >> 1);
+ EXPECT_TRUE(mReactor.addPresentFence(generateSignalledFenceWithTime(0)));
+}
+
TEST_F(VSyncReactorTest, presentFenceAdditionDoesNotInterruptConfirmationProcess) {
nsecs_t const newPeriod = 5000;
mReactor.setPeriod(newPeriod);
diff --git a/services/surfaceflinger/tests/unittests/libsurfaceflinger_unittest.filter b/services/surfaceflinger/tests/unittests/libsurfaceflinger_unittest.filter
new file mode 100644
index 0000000..8e9c3cf
--- /dev/null
+++ b/services/surfaceflinger/tests/unittests/libsurfaceflinger_unittest.filter
@@ -0,0 +1,6 @@
+{
+ // TODO(b/148517641): re-enable once this test is fixed
+ "presubmit": {
+ "filter": "*:-FrameTracerTest.*"
+ }
+}
diff --git a/services/surfaceflinger/tests/unittests/mock/MockLayer.h b/services/surfaceflinger/tests/unittests/mock/MockLayer.h
index 1fd0e61..494e73d 100644
--- a/services/surfaceflinger/tests/unittests/mock/MockLayer.h
+++ b/services/surfaceflinger/tests/unittests/mock/MockLayer.h
@@ -31,6 +31,7 @@
MOCK_METHOD0(getFrameSelectionPriority, int32_t());
MOCK_CONST_METHOD0(isVisible, bool());
MOCK_METHOD0(createClone, sp<Layer>());
+ MOCK_CONST_METHOD0(getFrameRate, std::optional<float>());
};
} // namespace android::mock
diff --git a/vulkan/libvulkan/driver.cpp b/vulkan/libvulkan/driver.cpp
index 90a73e2..a7ec4ae 100644
--- a/vulkan/libvulkan/driver.cpp
+++ b/vulkan/libvulkan/driver.cpp
@@ -164,6 +164,11 @@
"ro.board.platform",
}};
+// LoadDriver returns:
+// * 0 when succeed, or
+// * -ENOENT when fail to open binary libraries, or
+// * -EINVAL when fail to find HAL_MODULE_INFO_SYM_AS_STR or
+// HWVULKAN_HARDWARE_MODULE_ID in the library.
int LoadDriver(android_namespace_t* library_namespace,
const hwvulkan_module_t** module) {
ATRACE_CALL();
@@ -221,7 +226,13 @@
return -ENOENT;
android::GraphicsEnv::getInstance().setDriverToLoad(
android::GpuStatsInfo::Driver::VULKAN_UPDATED);
- return LoadDriver(ns, module);
+ int result = LoadDriver(ns, module);
+ if (result != 0) {
+ LOG_ALWAYS_FATAL(
+ "couldn't find an updated Vulkan implementation from %s",
+ android::GraphicsEnv::getInstance().getDriverPath().c_str());
+ }
+ return result;
}
bool Hal::Open() {