Merge "SF: VSyncReactor add hwvsync ingestion functions"
diff --git a/TEST_MAPPING b/TEST_MAPPING
new file mode 100644
index 0000000..8173c89
--- /dev/null
+++ b/TEST_MAPPING
@@ -0,0 +1,63 @@
+{
+ "presubmit": [
+ {
+ "name": "SurfaceFlinger_test",
+ "options": [
+ {
+ "include-filter": "*CredentialsTest.*"
+ },
+ {
+ "include-filter": "*SurfaceFlingerStress.*"
+ },
+ {
+ "include-filter": "*SurfaceInterceptorTest.*"
+ },
+ {
+ "include-filter": "*LayerTransactionTest.*"
+ },
+ {
+ "include-filter": "*LayerTypeTransactionTest.*"
+ },
+ {
+ "include-filter": "*LayerUpdateTest.*"
+ },
+ {
+ "include-filter": "*GeometryLatchingTest.*"
+ },
+ {
+ "include-filter": "*CropLatchingTest.*"
+ },
+ {
+ "include-filter": "*ChildLayerTest.*"
+ },
+ {
+ "include-filter": "*ScreenCaptureTest.*"
+ },
+ {
+ "include-filter": "*ScreenCaptureChildOnlyTest.*"
+ },
+ {
+ "include-filter": "*DereferenceSurfaceControlTest.*"
+ },
+ {
+ "include-filter": "*BoundlessLayerTest.*"
+ },
+ {
+ "include-filter": "*MultiDisplayLayerBoundsTest.*"
+ },
+ {
+ "include-filter": "*InvalidHandleTest.*"
+ },
+ {
+ "include-filter": "*VirtualDisplayTest.*"
+ },
+ {
+ "include-filter": "*RelativeZTest.*"
+ }
+ ]
+ },
+ {
+ "name": "libsurfaceflinger_unittest"
+ }
+ ]
+}
diff --git a/cmds/installd/InstalldNativeService.cpp b/cmds/installd/InstalldNativeService.cpp
index e6e232c..95957a0 100644
--- a/cmds/installd/InstalldNativeService.cpp
+++ b/cmds/installd/InstalldNativeService.cpp
@@ -97,6 +97,7 @@
static constexpr int kVerityPageSize = 4096;
static constexpr size_t kSha256Size = 32;
static constexpr const char* kPropApkVerityMode = "ro.apk_verity.mode";
+static constexpr const char* kFuseProp = "persist.sys.fuse";
namespace {
@@ -588,12 +589,21 @@
std::lock_guard<std::recursive_mutex> lock(mMountsLock);
for (const auto& n : mStorageMounts) {
auto extPath = n.second;
- if (n.first.compare(0, 14, "/mnt/media_rw/") != 0) {
- extPath += StringPrintf("/%d", userId);
- } else if (userId != 0) {
- // TODO: support devices mounted under secondary users
- continue;
+
+ if (android::base::GetBoolProperty(kFuseProp, false)) {
+ std::regex re("^\\/mnt\\/pass_through\\/[0-9]+\\/emulated");
+ if (std::regex_match(extPath, re)) {
+ extPath += "/" + std::to_string(userId);
+ }
+ } else {
+ if (n.first.compare(0, 14, "/mnt/media_rw/") != 0) {
+ extPath += StringPrintf("/%d", userId);
+ } else if (userId != 0) {
+ // TODO: support devices mounted under secondary users
+ continue;
+ }
}
+
if (flags & FLAG_CLEAR_CACHE_ONLY) {
// Clear only cached data from shared storage
auto path = StringPrintf("%s/Android/data/%s/cache", extPath.c_str(), pkgname);
@@ -684,16 +694,26 @@
std::lock_guard<std::recursive_mutex> lock(mMountsLock);
for (const auto& n : mStorageMounts) {
auto extPath = n.second;
- if (n.first.compare(0, 14, "/mnt/media_rw/") != 0) {
- extPath += StringPrintf("/%d", userId);
- } else if (userId != 0) {
- // TODO: support devices mounted under secondary users
- continue;
+
+ if (android::base::GetBoolProperty(kFuseProp, false)) {
+ std::regex re("^\\/mnt\\/pass_through\\/[0-9]+\\/emulated");
+ if (std::regex_match(extPath, re)) {
+ extPath += "/" + std::to_string(userId);
+ }
+ } else {
+ if (n.first.compare(0, 14, "/mnt/media_rw/") != 0) {
+ extPath += StringPrintf("/%d", userId);
+ } else if (userId != 0) {
+ // TODO: support devices mounted under secondary users
+ continue;
+ }
}
+
auto path = StringPrintf("%s/Android/data/%s", extPath.c_str(), pkgname);
if (delete_dir_contents_and_dir(path, true) != 0) {
res = error("Failed to delete contents of " + path);
}
+
path = StringPrintf("%s/Android/media/%s", extPath.c_str(), pkgname);
if (delete_dir_contents_and_dir(path, true) != 0) {
res = error("Failed to delete contents of " + path);
@@ -2574,12 +2594,19 @@
std::getline(in, target, ' ');
std::getline(in, ignored);
+ if (android::base::GetBoolProperty(kFuseProp, false)) {
+ if (target.compare(0, 17, "/mnt/pass_through") == 0) {
+ LOG(DEBUG) << "Found storage mount " << source << " at " << target;
+ mStorageMounts[source] = target;
+ }
+ } else {
#if !BYPASS_SDCARDFS
- if (target.compare(0, 21, "/mnt/runtime/default/") == 0) {
- LOG(DEBUG) << "Found storage mount " << source << " at " << target;
- mStorageMounts[source] = target;
- }
+ if (target.compare(0, 21, "/mnt/runtime/default/") == 0) {
+ LOG(DEBUG) << "Found storage mount " << source << " at " << target;
+ mStorageMounts[source] = target;
+ }
#endif
+ }
}
return ok();
}
diff --git a/include/binder b/include/binder
deleted file mode 120000
index 35a022a..0000000
--- a/include/binder
+++ /dev/null
@@ -1 +0,0 @@
-../libs/binder/include/binder/
\ No newline at end of file
diff --git a/include/binder/ActivityManager.h b/include/binder/ActivityManager.h
new file mode 120000
index 0000000..018f7a5
--- /dev/null
+++ b/include/binder/ActivityManager.h
@@ -0,0 +1 @@
+../../libs/binder/include/binder/ActivityManager.h
\ No newline at end of file
diff --git a/include/binder/AppOpsManager.h b/include/binder/AppOpsManager.h
new file mode 120000
index 0000000..4658269
--- /dev/null
+++ b/include/binder/AppOpsManager.h
@@ -0,0 +1 @@
+../../libs/binder/include/binder/AppOpsManager.h
\ No newline at end of file
diff --git a/include/binder/Binder.h b/include/binder/Binder.h
new file mode 120000
index 0000000..0fc6db7
--- /dev/null
+++ b/include/binder/Binder.h
@@ -0,0 +1 @@
+../../libs/binder/include/binder/Binder.h
\ No newline at end of file
diff --git a/include/binder/BinderService.h b/include/binder/BinderService.h
new file mode 120000
index 0000000..370b260
--- /dev/null
+++ b/include/binder/BinderService.h
@@ -0,0 +1 @@
+../../libs/binder/include/binder/BinderService.h
\ No newline at end of file
diff --git a/include/binder/BpBinder.h b/include/binder/BpBinder.h
new file mode 120000
index 0000000..bc1f3d5
--- /dev/null
+++ b/include/binder/BpBinder.h
@@ -0,0 +1 @@
+../../libs/binder/include/binder/BpBinder.h
\ No newline at end of file
diff --git a/include/binder/BufferedTextOutput.h b/include/binder/BufferedTextOutput.h
new file mode 120000
index 0000000..fcad4fa
--- /dev/null
+++ b/include/binder/BufferedTextOutput.h
@@ -0,0 +1 @@
+../../libs/binder/include/binder/BufferedTextOutput.h
\ No newline at end of file
diff --git a/include/binder/Debug.h b/include/binder/Debug.h
new file mode 120000
index 0000000..d76a7f1
--- /dev/null
+++ b/include/binder/Debug.h
@@ -0,0 +1 @@
+../../libs/binder/include/binder/Debug.h
\ No newline at end of file
diff --git a/include/binder/IActivityManager.h b/include/binder/IActivityManager.h
new file mode 120000
index 0000000..4a868e0
--- /dev/null
+++ b/include/binder/IActivityManager.h
@@ -0,0 +1 @@
+../../libs/binder/include/binder/IActivityManager.h
\ No newline at end of file
diff --git a/include/binder/IAppOpsCallback.h b/include/binder/IAppOpsCallback.h
new file mode 120000
index 0000000..d587a0c
--- /dev/null
+++ b/include/binder/IAppOpsCallback.h
@@ -0,0 +1 @@
+../../libs/binder/include/binder/IAppOpsCallback.h
\ No newline at end of file
diff --git a/include/binder/IAppOpsService.h b/include/binder/IAppOpsService.h
new file mode 120000
index 0000000..9e1c15a
--- /dev/null
+++ b/include/binder/IAppOpsService.h
@@ -0,0 +1 @@
+../../libs/binder/include/binder/IAppOpsService.h
\ No newline at end of file
diff --git a/include/binder/IBatteryStats.h b/include/binder/IBatteryStats.h
new file mode 120000
index 0000000..689b540
--- /dev/null
+++ b/include/binder/IBatteryStats.h
@@ -0,0 +1 @@
+../../libs/binder/include/binder/IBatteryStats.h
\ No newline at end of file
diff --git a/include/binder/IBinder.h b/include/binder/IBinder.h
new file mode 120000
index 0000000..93a6219
--- /dev/null
+++ b/include/binder/IBinder.h
@@ -0,0 +1 @@
+../../libs/binder/include/binder/IBinder.h
\ No newline at end of file
diff --git a/include/binder/IInterface.h b/include/binder/IInterface.h
new file mode 120000
index 0000000..8579878
--- /dev/null
+++ b/include/binder/IInterface.h
@@ -0,0 +1 @@
+../../libs/binder/include/binder/IInterface.h
\ No newline at end of file
diff --git a/include/binder/IMediaResourceMonitor.h b/include/binder/IMediaResourceMonitor.h
new file mode 120000
index 0000000..d23a4da
--- /dev/null
+++ b/include/binder/IMediaResourceMonitor.h
@@ -0,0 +1 @@
+../../libs/binder/include/binder/IMediaResourceMonitor.h
\ No newline at end of file
diff --git a/include/binder/IMemory.h b/include/binder/IMemory.h
new file mode 120000
index 0000000..5171c08
--- /dev/null
+++ b/include/binder/IMemory.h
@@ -0,0 +1 @@
+../../libs/binder/include/binder/IMemory.h
\ No newline at end of file
diff --git a/include/binder/IPCThreadState.h b/include/binder/IPCThreadState.h
new file mode 120000
index 0000000..ecd4f81
--- /dev/null
+++ b/include/binder/IPCThreadState.h
@@ -0,0 +1 @@
+../../libs/binder/include/binder/IPCThreadState.h
\ No newline at end of file
diff --git a/include/binder/IPermissionController.h b/include/binder/IPermissionController.h
new file mode 120000
index 0000000..6f33c58
--- /dev/null
+++ b/include/binder/IPermissionController.h
@@ -0,0 +1 @@
+../../libs/binder/include/binder/IPermissionController.h
\ No newline at end of file
diff --git a/include/binder/IProcessInfoService.h b/include/binder/IProcessInfoService.h
new file mode 120000
index 0000000..be0933f
--- /dev/null
+++ b/include/binder/IProcessInfoService.h
@@ -0,0 +1 @@
+../../libs/binder/include/binder/IProcessInfoService.h
\ No newline at end of file
diff --git a/include/binder/IResultReceiver.h b/include/binder/IResultReceiver.h
new file mode 120000
index 0000000..b10d19a
--- /dev/null
+++ b/include/binder/IResultReceiver.h
@@ -0,0 +1 @@
+../../libs/binder/include/binder/IResultReceiver.h
\ No newline at end of file
diff --git a/include/binder/IServiceManager.h b/include/binder/IServiceManager.h
new file mode 120000
index 0000000..33d18cc
--- /dev/null
+++ b/include/binder/IServiceManager.h
@@ -0,0 +1 @@
+../../libs/binder/include/binder/IServiceManager.h
\ No newline at end of file
diff --git a/include/binder/IShellCallback.h b/include/binder/IShellCallback.h
new file mode 120000
index 0000000..8931195
--- /dev/null
+++ b/include/binder/IShellCallback.h
@@ -0,0 +1 @@
+../../libs/binder/include/binder/IShellCallback.h
\ No newline at end of file
diff --git a/include/binder/IUidObserver.h b/include/binder/IUidObserver.h
new file mode 120000
index 0000000..1382897
--- /dev/null
+++ b/include/binder/IUidObserver.h
@@ -0,0 +1 @@
+../../libs/binder/include/binder/IUidObserver.h
\ No newline at end of file
diff --git a/include/binder/IpPrefix.h b/include/binder/IpPrefix.h
new file mode 120000
index 0000000..655c774
--- /dev/null
+++ b/include/binder/IpPrefix.h
@@ -0,0 +1 @@
+../../libs/binder/include/binder/IpPrefix.h
\ No newline at end of file
diff --git a/include/binder/MemoryBase.h b/include/binder/MemoryBase.h
new file mode 120000
index 0000000..3fd3e99
--- /dev/null
+++ b/include/binder/MemoryBase.h
@@ -0,0 +1 @@
+../../libs/binder/include/binder/MemoryBase.h
\ No newline at end of file
diff --git a/include/binder/MemoryDealer.h b/include/binder/MemoryDealer.h
new file mode 120000
index 0000000..71881fb
--- /dev/null
+++ b/include/binder/MemoryDealer.h
@@ -0,0 +1 @@
+../../libs/binder/include/binder/MemoryDealer.h
\ No newline at end of file
diff --git a/include/binder/MemoryHeapBase.h b/include/binder/MemoryHeapBase.h
new file mode 120000
index 0000000..8fb51cc
--- /dev/null
+++ b/include/binder/MemoryHeapBase.h
@@ -0,0 +1 @@
+../../libs/binder/include/binder/MemoryHeapBase.h
\ No newline at end of file
diff --git a/include/binder/Parcel.h b/include/binder/Parcel.h
new file mode 120000
index 0000000..23492be
--- /dev/null
+++ b/include/binder/Parcel.h
@@ -0,0 +1 @@
+../../libs/binder/include/binder/Parcel.h
\ No newline at end of file
diff --git a/include/binder/ParcelFileDescriptor.h b/include/binder/ParcelFileDescriptor.h
new file mode 120000
index 0000000..777bd49
--- /dev/null
+++ b/include/binder/ParcelFileDescriptor.h
@@ -0,0 +1 @@
+../../libs/binder/include/binder/ParcelFileDescriptor.h
\ No newline at end of file
diff --git a/include/binder/Parcelable.h b/include/binder/Parcelable.h
new file mode 120000
index 0000000..438e223
--- /dev/null
+++ b/include/binder/Parcelable.h
@@ -0,0 +1 @@
+../../libs/binder/include/binder/Parcelable.h
\ No newline at end of file
diff --git a/include/binder/PermissionCache.h b/include/binder/PermissionCache.h
new file mode 120000
index 0000000..e910c12
--- /dev/null
+++ b/include/binder/PermissionCache.h
@@ -0,0 +1 @@
+../../libs/binder/include/binder/PermissionCache.h
\ No newline at end of file
diff --git a/include/binder/PermissionController.h b/include/binder/PermissionController.h
new file mode 120000
index 0000000..b6e1928
--- /dev/null
+++ b/include/binder/PermissionController.h
@@ -0,0 +1 @@
+../../libs/binder/include/binder/PermissionController.h
\ No newline at end of file
diff --git a/include/binder/PersistableBundle.h b/include/binder/PersistableBundle.h
new file mode 120000
index 0000000..785f2b4
--- /dev/null
+++ b/include/binder/PersistableBundle.h
@@ -0,0 +1 @@
+../../libs/binder/include/binder/PersistableBundle.h
\ No newline at end of file
diff --git a/include/binder/ProcessInfoService.h b/include/binder/ProcessInfoService.h
new file mode 120000
index 0000000..e67eb19
--- /dev/null
+++ b/include/binder/ProcessInfoService.h
@@ -0,0 +1 @@
+../../libs/binder/include/binder/ProcessInfoService.h
\ No newline at end of file
diff --git a/include/binder/ProcessState.h b/include/binder/ProcessState.h
new file mode 120000
index 0000000..4cbe7a5
--- /dev/null
+++ b/include/binder/ProcessState.h
@@ -0,0 +1 @@
+../../libs/binder/include/binder/ProcessState.h
\ No newline at end of file
diff --git a/include/binder/SafeInterface.h b/include/binder/SafeInterface.h
new file mode 120000
index 0000000..7cefe94
--- /dev/null
+++ b/include/binder/SafeInterface.h
@@ -0,0 +1 @@
+../../libs/binder/include/binder/SafeInterface.h
\ No newline at end of file
diff --git a/include/binder/Stability.h b/include/binder/Stability.h
new file mode 120000
index 0000000..9b431d2
--- /dev/null
+++ b/include/binder/Stability.h
@@ -0,0 +1 @@
+../../libs/binder/include/binder/Stability.h
\ No newline at end of file
diff --git a/include/binder/Status.h b/include/binder/Status.h
new file mode 120000
index 0000000..ccb994e
--- /dev/null
+++ b/include/binder/Status.h
@@ -0,0 +1 @@
+../../libs/binder/include/binder/Status.h
\ No newline at end of file
diff --git a/include/binder/TextOutput.h b/include/binder/TextOutput.h
new file mode 120000
index 0000000..2abd209
--- /dev/null
+++ b/include/binder/TextOutput.h
@@ -0,0 +1 @@
+../../libs/binder/include/binder/TextOutput.h
\ No newline at end of file
diff --git a/libs/dumputils/dump_utils.cpp b/libs/dumputils/dump_utils.cpp
index 56b94c1..0477801 100644
--- a/libs/dumputils/dump_utils.cpp
+++ b/libs/dumputils/dump_utils.cpp
@@ -16,7 +16,9 @@
#include <set>
#include <android-base/file.h>
+#include <android-base/properties.h>
#include <android-base/stringprintf.h>
+#include <android-base/strings.h>
#include <android/hidl/manager/1.0/IServiceManager.h>
#include <dumputils/dump_utils.h>
#include <log/log.h>
@@ -68,13 +70,34 @@
NULL,
};
-bool should_dump_hal_interface(const char* interface) {
+/* list of extra hal interfaces to dump containing process during native dumps */
+// This is filled when dumpstate is called.
+static std::set<const std::string> extra_hal_interfaces_to_dump;
+
+static void read_extra_hals_to_dump_from_property() {
+ // extra hals to dump are already filled
+ if (extra_hal_interfaces_to_dump.size() > 0) {
+ return;
+ }
+ std::string value = android::base::GetProperty("ro.dump.hals.extra", "");
+ std::vector<std::string> tokens = android::base::Split(value, ",");
+ for (const auto &token : tokens) {
+ std::string trimmed_token = android::base::Trim(token);
+ if (trimmed_token.length() == 0) {
+ continue;
+ }
+ extra_hal_interfaces_to_dump.insert(trimmed_token);
+ }
+}
+
+// check if interface is included in either default hal list or extra hal list
+bool should_dump_hal_interface(const std::string& interface) {
for (const char** i = hal_interfaces_to_dump; *i; i++) {
- if (!strcmp(*i, interface)) {
+ if (interface == *i) {
return true;
}
}
- return false;
+ return extra_hal_interfaces_to_dump.find(interface) != extra_hal_interfaces_to_dump.end();
}
bool should_dump_native_traces(const char* path) {
@@ -94,13 +117,15 @@
sp<IServiceManager> manager = IServiceManager::getService();
std::set<int> pids;
+ read_extra_hals_to_dump_from_property();
+
Return<void> ret = manager->debugDump([&](auto& hals) {
for (const auto &info : hals) {
if (info.pid == static_cast<int>(IServiceManager::PidConstant::NO_PID)) {
continue;
}
- if (!should_dump_hal_interface(info.interfaceName.c_str())) {
+ if (!should_dump_hal_interface(info.interfaceName)) {
continue;
}
diff --git a/libs/gui/BLASTBufferQueue.cpp b/libs/gui/BLASTBufferQueue.cpp
index 06a5f06..29ea84e 100644
--- a/libs/gui/BLASTBufferQueue.cpp
+++ b/libs/gui/BLASTBufferQueue.cpp
@@ -19,6 +19,7 @@
#include <gui/BLASTBufferQueue.h>
#include <gui/BufferItemConsumer.h>
+#include <gui/GLConsumer.h>
#include <chrono>
@@ -131,13 +132,20 @@
t->addTransactionCompletedCallback(transactionCallbackThunk, static_cast<void*>(this));
t->setFrame(mSurfaceControl, {0, 0, (int32_t)buffer->getWidth(), (int32_t)buffer->getHeight()});
- t->setCrop(mSurfaceControl, {0, 0, (int32_t)buffer->getWidth(), (int32_t)buffer->getHeight()});
+ t->setCrop(mSurfaceControl, computeCrop(mLastSubmittedBufferItem));
if (applyTransaction) {
t->apply();
}
}
+Rect BLASTBufferQueue::computeCrop(const BufferItem& item) {
+ if (item.mScalingMode == NATIVE_WINDOW_SCALING_MODE_SCALE_CROP) {
+ return GLConsumer::scaleDownCrop(item.mCrop, mWidth, mHeight);
+ }
+ return item.mCrop;
+}
+
void BLASTBufferQueue::onFrameAvailable(const BufferItem& item) {
std::lock_guard _lock{mMutex};
diff --git a/libs/gui/include/gui/BLASTBufferQueue.h b/libs/gui/include/gui/BLASTBufferQueue.h
index f1758a2..dd0b470 100644
--- a/libs/gui/include/gui/BLASTBufferQueue.h
+++ b/libs/gui/include/gui/BLASTBufferQueue.h
@@ -62,6 +62,7 @@
BLASTBufferQueue(const BLASTBufferQueue& rhs);
void processNextBufferLocked() REQUIRES(mMutex);
+ Rect computeCrop(const BufferItem& item);
sp<SurfaceControl> mSurfaceControl;
diff --git a/libs/gui/tests/BLASTBufferQueue_test.cpp b/libs/gui/tests/BLASTBufferQueue_test.cpp
index 6ecdae5..ae6c5cf 100644
--- a/libs/gui/tests/BLASTBufferQueue_test.cpp
+++ b/libs/gui/tests/BLASTBufferQueue_test.cpp
@@ -131,10 +131,10 @@
producer = igbProducer;
}
- void fillBuffer(uint32_t* bufData, uint32_t width, uint32_t height, uint32_t stride, uint8_t r,
- uint8_t g, uint8_t b) {
- for (uint32_t row = 0; row < height; row++) {
- for (uint32_t col = 0; col < width; col++) {
+ void fillBuffer(uint32_t* bufData, Rect rect, uint32_t stride, uint8_t r, uint8_t g,
+ uint8_t b) {
+ for (uint32_t row = rect.top; row < rect.bottom; row++) {
+ for (uint32_t col = rect.left; col < rect.right; col++) {
uint8_t* pixel = (uint8_t*)(bufData + (row * stride) + col);
*pixel = r;
*(pixel + 1) = g;
@@ -144,7 +144,7 @@
}
}
- void checkScreenCapture(uint8_t r, uint8_t g, uint8_t b) {
+ void checkScreenCapture(uint8_t r, uint8_t g, uint8_t b, Rect region) {
const auto width = mScreenCaptureBuf->getWidth();
const auto height = mScreenCaptureBuf->getHeight();
const auto stride = mScreenCaptureBuf->getStride();
@@ -156,9 +156,16 @@
for (uint32_t row = 0; row < height; row++) {
for (uint32_t col = 0; col < width; col++) {
uint8_t* pixel = (uint8_t*)(bufData + (row * stride) + col);
- EXPECT_EQ(r, *(pixel));
- EXPECT_EQ(g, *(pixel + 1));
- EXPECT_EQ(b, *(pixel + 2));
+ if (row >= region.top && row < region.bottom && col >= region.left &&
+ col < region.right) {
+ EXPECT_EQ(r, *(pixel));
+ EXPECT_EQ(g, *(pixel + 1));
+ EXPECT_EQ(b, *(pixel + 2));
+ } else {
+ EXPECT_EQ(0, *(pixel));
+ EXPECT_EQ(0, *(pixel + 1));
+ EXPECT_EQ(0, *(pixel + 2));
+ }
}
}
mScreenCaptureBuf->unlock();
@@ -225,7 +232,7 @@
uint32_t* bufData;
buf->lock(static_cast<uint32_t>(GraphicBuffer::USAGE_SW_WRITE_OFTEN),
reinterpret_cast<void**>(&bufData));
- fillBuffer(bufData, buf->getWidth(), buf->getHeight(), buf->getStride(), r, g, b);
+ fillBuffer(bufData, Rect(buf->getWidth(), buf->getHeight()), buf->getStride(), r, g, b);
buf->unlock();
IGraphicBufferProducer::QueueBufferOutput qbOutput;
@@ -236,7 +243,7 @@
igbProducer->queueBuffer(slot, input, &qbOutput);
ASSERT_NE(ui::Transform::orientation_flags::ROT_INVALID, qbOutput.transformHint);
- sleep(1);
+ adapter.waitForCallbacks();
// capture screen and verify that it is red
bool capturedSecureLayers;
@@ -245,7 +252,8 @@
ui::Dataspace::V0_SRGB, ui::PixelFormat::RGBA_8888, Rect(),
mDisplayWidth, mDisplayHeight,
/*useIdentityTransform*/ false));
- ASSERT_NO_FATAL_FAILURE(checkScreenCapture(r, g, b));
+ ASSERT_NO_FATAL_FAILURE(
+ checkScreenCapture(r, g, b, {0, 0, (int32_t)mDisplayWidth, (int32_t)mDisplayHeight}));
}
TEST_F(BLASTBufferQueueTest, TripleBuffering) {
@@ -286,4 +294,110 @@
}
adapter.waitForCallbacks();
}
+
+TEST_F(BLASTBufferQueueTest, SetCrop_Item) {
+ uint8_t r = 255;
+ uint8_t g = 0;
+ uint8_t b = 0;
+
+ 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));
+
+ uint32_t* bufData;
+ buf->lock(static_cast<uint32_t>(GraphicBuffer::USAGE_SW_WRITE_OFTEN),
+ reinterpret_cast<void**>(&bufData));
+ fillBuffer(bufData, Rect(buf->getWidth(), buf->getHeight() / 2), buf->getStride(), r, g, b);
+ buf->unlock();
+
+ IGraphicBufferProducer::QueueBufferOutput qbOutput;
+ IGraphicBufferProducer::QueueBufferInput input(systemTime(), false, HAL_DATASPACE_UNKNOWN,
+ Rect(mDisplayWidth, mDisplayHeight / 2),
+ NATIVE_WINDOW_SCALING_MODE_FREEZE, 0,
+ Fence::NO_FENCE);
+ igbProducer->queueBuffer(slot, input, &qbOutput);
+ ASSERT_NE(ui::Transform::orientation_flags::ROT_INVALID, qbOutput.transformHint);
+
+ adapter.waitForCallbacks();
+ // capture screen and verify that it is red
+ bool capturedSecureLayers;
+ ASSERT_EQ(NO_ERROR,
+ mComposer->captureScreen(mDisplayToken, &mScreenCaptureBuf, capturedSecureLayers,
+ ui::Dataspace::V0_SRGB, ui::PixelFormat::RGBA_8888, Rect(),
+ mDisplayWidth, mDisplayHeight,
+ /*useIdentityTransform*/ false));
+ ASSERT_NO_FATAL_FAILURE(
+ checkScreenCapture(r, g, b, {0, 0, (int32_t)mDisplayWidth, (int32_t)mDisplayHeight}));
+}
+
+TEST_F(BLASTBufferQueueTest, SetCrop_ScalingModeScaleCrop) {
+ uint8_t r = 255;
+ uint8_t g = 0;
+ uint8_t b = 0;
+
+ int32_t bufferSideLength =
+ (mDisplayWidth < mDisplayHeight) ? mDisplayWidth / 2 : mDisplayHeight / 2;
+ int32_t finalCropSideLength = bufferSideLength / 2;
+
+ auto bg = mClient->createSurface(String8("BGTest"), 0, 0, PIXEL_FORMAT_RGBA_8888,
+ ISurfaceComposerClient::eFXSurfaceColor);
+ ASSERT_NE(nullptr, bg.get());
+ Transaction t;
+ t.setLayerStack(bg, 0)
+ .setCrop_legacy(bg, Rect(0, 0, mDisplayWidth, mDisplayHeight))
+ .setColor(bg, half3{0, 0, 0})
+ .setLayer(bg, 0)
+ .apply();
+
+ BLASTBufferQueueHelper adapter(mSurfaceControl, bufferSideLength, bufferSideLength);
+ sp<IGraphicBufferProducer> igbProducer;
+ setUpProducer(adapter, igbProducer);
+ int slot;
+ sp<Fence> fence;
+ sp<GraphicBuffer> buf;
+ auto ret = igbProducer->dequeueBuffer(&slot, &fence, bufferSideLength, bufferSideLength,
+ 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));
+
+ uint32_t* bufData;
+ buf->lock(static_cast<uint32_t>(GraphicBuffer::USAGE_SW_WRITE_OFTEN),
+ reinterpret_cast<void**>(&bufData));
+ fillBuffer(bufData, Rect(buf->getWidth(), buf->getHeight()), buf->getStride(), 0, 0, 0);
+ fillBuffer(bufData,
+ Rect(finalCropSideLength / 2, 0, buf->getWidth() - finalCropSideLength / 2,
+ buf->getHeight()),
+ buf->getStride(), r, g, b);
+ buf->unlock();
+
+ IGraphicBufferProducer::QueueBufferOutput qbOutput;
+ IGraphicBufferProducer::QueueBufferInput input(systemTime(), false, HAL_DATASPACE_UNKNOWN,
+ Rect(bufferSideLength, finalCropSideLength),
+ NATIVE_WINDOW_SCALING_MODE_SCALE_CROP, 0,
+ Fence::NO_FENCE);
+ igbProducer->queueBuffer(slot, input, &qbOutput);
+ ASSERT_NE(ui::Transform::orientation_flags::ROT_INVALID, qbOutput.transformHint);
+
+ adapter.waitForCallbacks();
+ // capture screen and verify that it is red
+ bool capturedSecureLayers;
+ ASSERT_EQ(NO_ERROR,
+ mComposer->captureScreen(mDisplayToken, &mScreenCaptureBuf, capturedSecureLayers,
+ ui::Dataspace::V0_SRGB, ui::PixelFormat::RGBA_8888, Rect(),
+ mDisplayWidth, mDisplayHeight,
+ /*useIdentityTransform*/ false));
+ ASSERT_NO_FATAL_FAILURE(
+ checkScreenCapture(r, g, b,
+ {0, 0, (int32_t)bufferSideLength, (int32_t)bufferSideLength}));
+}
+
} // namespace android
diff --git a/services/inputflinger/dispatcher/InputDispatcher.cpp b/services/inputflinger/dispatcher/InputDispatcher.cpp
index 9c0e08e..b9bec44 100644
--- a/services/inputflinger/dispatcher/InputDispatcher.cpp
+++ b/services/inputflinger/dispatcher/InputDispatcher.cpp
@@ -298,6 +298,12 @@
if (runCommandsLockedInterruptible()) {
nextWakeupTime = LONG_LONG_MIN;
}
+
+ // We are about to enter an infinitely long sleep, because we have no commands or
+ // pending or queued events
+ if (nextWakeupTime == LONG_LONG_MAX) {
+ mDispatcherEnteredIdle.notify_all();
+ }
} // release lock
// Wait for callback or timeout or wake. (make sure we round up, not down)
@@ -4582,4 +4588,22 @@
mDispatcherIsAlive.wait(_l);
}
+/**
+ * Wake up the dispatcher and wait until it processes all events and commands.
+ * The notification of mDispatcherEnteredIdle is guaranteed to happen after wake(), so
+ * this method can be safely called from any thread, as long as you've ensured that
+ * the work you are interested in completing has already been queued.
+ */
+bool InputDispatcher::waitForIdle() {
+ /**
+ * Timeout should represent the longest possible time that a device might spend processing
+ * events and commands.
+ */
+ constexpr std::chrono::duration TIMEOUT = 100ms;
+ std::unique_lock lock(mLock);
+ mLooper->wake();
+ std::cv_status result = mDispatcherEnteredIdle.wait_for(lock, TIMEOUT);
+ return result == std::cv_status::no_timeout;
+}
+
} // namespace android::inputdispatcher
diff --git a/services/inputflinger/dispatcher/InputDispatcher.h b/services/inputflinger/dispatcher/InputDispatcher.h
index 5d7d812..38f8674 100644
--- a/services/inputflinger/dispatcher/InputDispatcher.h
+++ b/services/inputflinger/dispatcher/InputDispatcher.h
@@ -81,6 +81,7 @@
virtual void dump(std::string& dump) override;
virtual void monitor() override;
+ virtual bool waitForIdle() override;
virtual void dispatchOnce() override;
@@ -129,6 +130,7 @@
std::mutex mLock;
std::condition_variable mDispatcherIsAlive;
+ std::condition_variable mDispatcherEnteredIdle;
sp<Looper> mLooper;
diff --git a/services/inputflinger/dispatcher/include/InputDispatcherInterface.h b/services/inputflinger/dispatcher/include/InputDispatcherInterface.h
index 3082738..db9fba6 100644
--- a/services/inputflinger/dispatcher/include/InputDispatcherInterface.h
+++ b/services/inputflinger/dispatcher/include/InputDispatcherInterface.h
@@ -63,6 +63,14 @@
/* Called by the heatbeat to ensures that the dispatcher has not deadlocked. */
virtual void monitor() = 0;
+ /**
+ * Wait until dispatcher is idle. That means, there are no further events to be processed,
+ * and all of the policy callbacks have been completed.
+ * Return true if the dispatcher is idle.
+ * Return false if the timeout waiting for the dispatcher to become idle has expired.
+ */
+ virtual bool waitForIdle() = 0;
+
/* Runs a single iteration of the dispatch loop.
* Nominally processes one queued event, a timeout, or a response from an input consumer.
*
diff --git a/services/inputflinger/tests/InputDispatcher_test.cpp b/services/inputflinger/tests/InputDispatcher_test.cpp
index 4f28261..c8d39cf 100644
--- a/services/inputflinger/tests/InputDispatcher_test.cpp
+++ b/services/inputflinger/tests/InputDispatcher_test.cpp
@@ -51,7 +51,6 @@
public:
FakeInputDispatcherPolicy() {
- mOnPointerDownToken.clear();
}
void assertFilterInputEventWasCalled(const NotifyKeyArgs& args) {
@@ -66,17 +65,41 @@
void assertFilterInputEventWasNotCalled() { ASSERT_EQ(nullptr, mFilteredEvent); }
+ void assertNotifyConfigurationChangedWasCalled(nsecs_t when) {
+ ASSERT_TRUE(mConfigurationChangedTime)
+ << "Timed out waiting for configuration changed call";
+ ASSERT_EQ(*mConfigurationChangedTime, when);
+ mConfigurationChangedTime = std::nullopt;
+ }
+
+ void assertNotifySwitchWasCalled(const NotifySwitchArgs& args) {
+ ASSERT_TRUE(mLastNotifySwitch);
+ // We do not check sequenceNum because it is not exposed to the policy
+ EXPECT_EQ(args.eventTime, mLastNotifySwitch->eventTime);
+ EXPECT_EQ(args.policyFlags, mLastNotifySwitch->policyFlags);
+ EXPECT_EQ(args.switchValues, mLastNotifySwitch->switchValues);
+ EXPECT_EQ(args.switchMask, mLastNotifySwitch->switchMask);
+ mLastNotifySwitch = std::nullopt;
+ }
+
void assertOnPointerDownEquals(const sp<IBinder>& touchedToken) {
- ASSERT_EQ(mOnPointerDownToken, touchedToken)
- << "Expected token from onPointerDownOutsideFocus was not matched";
- reset();
+ ASSERT_EQ(touchedToken, mOnPointerDownToken);
+ mOnPointerDownToken.clear();
+ }
+
+ void assertOnPointerDownWasNotCalled() {
+ ASSERT_TRUE(mOnPointerDownToken == nullptr)
+ << "Expected onPointerDownOutsideFocus to not have been called";
}
private:
std::unique_ptr<InputEvent> mFilteredEvent;
+ std::optional<nsecs_t> mConfigurationChangedTime;
sp<IBinder> mOnPointerDownToken;
+ std::optional<NotifySwitchArgs> mLastNotifySwitch;
- virtual void notifyConfigurationChanged(nsecs_t) {
+ virtual void notifyConfigurationChanged(nsecs_t when) override {
+ mConfigurationChangedTime = when;
}
virtual nsecs_t notifyANR(const sp<InputApplicationHandle>&,
@@ -128,7 +151,13 @@
return false;
}
- virtual void notifySwitch(nsecs_t, uint32_t, uint32_t, uint32_t) {
+ virtual void notifySwitch(nsecs_t when, uint32_t switchValues, uint32_t switchMask,
+ uint32_t policyFlags) override {
+ /** We simply reconstruct NotifySwitchArgs in policy because InputDispatcher is
+ * essentially a passthrough for notifySwitch.
+ */
+ mLastNotifySwitch =
+ NotifySwitchArgs(1 /*sequenceNum*/, when, policyFlags, switchValues, switchMask);
}
virtual void pokeUserActivity(nsecs_t, int32_t) {
@@ -163,8 +192,6 @@
mFilteredEvent = nullptr;
}
-
- void reset() { mOnPointerDownToken.clear(); }
};
@@ -351,9 +378,30 @@
<< "Should reject motion events with duplicate pointer ids.";
}
+/* Test InputDispatcher for notifyConfigurationChanged and notifySwitch events */
+
+TEST_F(InputDispatcherTest, NotifyConfigurationChanged_CallsPolicy) {
+ constexpr nsecs_t eventTime = 20;
+ NotifyConfigurationChangedArgs args(10 /*sequenceNum*/, eventTime);
+ mDispatcher->notifyConfigurationChanged(&args);
+ ASSERT_TRUE(mDispatcher->waitForIdle());
+
+ mFakePolicy->assertNotifyConfigurationChangedWasCalled(eventTime);
+}
+
+TEST_F(InputDispatcherTest, NotifySwitch_CallsPolicy) {
+ NotifySwitchArgs args(10 /*sequenceNum*/, 20 /*eventTime*/, 0 /*policyFlags*/,
+ 1 /*switchValues*/, 2 /*switchMask*/);
+ mDispatcher->notifySwitch(&args);
+
+ // InputDispatcher adds POLICY_FLAG_TRUSTED because the event went through InputListener
+ args.policyFlags |= POLICY_FLAG_TRUSTED;
+ mFakePolicy->assertNotifySwitchWasCalled(args);
+}
+
// --- InputDispatcherTest SetInputWindowTest ---
-static const int32_t INJECT_EVENT_TIMEOUT = 500;
-static const int32_t DISPATCHING_TIMEOUT = 100;
+static constexpr int32_t INJECT_EVENT_TIMEOUT = 500;
+static constexpr int32_t DISPATCHING_TIMEOUT = 100;
class FakeApplicationHandle : public InputApplicationHandle {
public:
@@ -536,9 +584,7 @@
InputWindowHandle::releaseChannel();
}
protected:
- virtual bool handled() {
- return true;
- }
+ virtual bool handled() override { return true; }
bool mFocused;
Rect mFrame;
@@ -761,6 +807,51 @@
windowRight->assertNoEvents();
}
+TEST_F(InputDispatcherTest, NotifyDeviceReset_CancelsKeyStream) {
+ sp<FakeApplicationHandle> application = new FakeApplicationHandle();
+ sp<FakeWindowHandle> window =
+ new FakeWindowHandle(application, mDispatcher, "Fake Window", ADISPLAY_ID_DEFAULT);
+ window->setFocus();
+
+ mDispatcher->setInputWindows({window}, ADISPLAY_ID_DEFAULT);
+
+ NotifyKeyArgs keyArgs = generateKeyArgs(AKEY_EVENT_ACTION_DOWN, ADISPLAY_ID_DEFAULT);
+ mDispatcher->notifyKey(&keyArgs);
+
+ // Window should receive key down event.
+ window->consumeKeyDown(ADISPLAY_ID_DEFAULT);
+
+ // When device reset happens, that key stream should be terminated with FLAG_CANCELED
+ // on the app side.
+ NotifyDeviceResetArgs args(10 /*sequenceNum*/, 20 /*eventTime*/, DEVICE_ID);
+ mDispatcher->notifyDeviceReset(&args);
+ window->consumeEvent(AINPUT_EVENT_TYPE_KEY, AKEY_EVENT_ACTION_UP, ADISPLAY_ID_DEFAULT,
+ AKEY_EVENT_FLAG_CANCELED);
+}
+
+TEST_F(InputDispatcherTest, NotifyDeviceReset_CancelsMotionStream) {
+ sp<FakeApplicationHandle> application = new FakeApplicationHandle();
+ sp<FakeWindowHandle> window =
+ new FakeWindowHandle(application, mDispatcher, "Fake Window", ADISPLAY_ID_DEFAULT);
+
+ mDispatcher->setInputWindows({window}, ADISPLAY_ID_DEFAULT);
+
+ NotifyMotionArgs motionArgs =
+ generateMotionArgs(AMOTION_EVENT_ACTION_DOWN, AINPUT_SOURCE_TOUCHSCREEN,
+ ADISPLAY_ID_DEFAULT);
+ mDispatcher->notifyMotion(&motionArgs);
+
+ // Window should receive motion down event.
+ window->consumeMotionDown(ADISPLAY_ID_DEFAULT);
+
+ // When device reset happens, that motion stream should be terminated with ACTION_CANCEL
+ // on the app side.
+ NotifyDeviceResetArgs args(10 /*sequenceNum*/, 20 /*eventTime*/, DEVICE_ID);
+ mDispatcher->notifyDeviceReset(&args);
+ window->consumeEvent(AINPUT_EVENT_TYPE_MOTION, AMOTION_EVENT_ACTION_CANCEL, ADISPLAY_ID_DEFAULT,
+ 0 /*expectedFlags*/);
+}
+
/* Test InputDispatcher for MultiDisplay */
class InputDispatcherFocusOnTwoDisplaysTest : public InputDispatcherTest {
public:
@@ -924,7 +1015,7 @@
motionArgs = generateMotionArgs(
AMOTION_EVENT_ACTION_UP, AINPUT_SOURCE_TOUCHSCREEN, displayId);
mDispatcher->notifyMotion(&motionArgs);
-
+ ASSERT_TRUE(mDispatcher->waitForIdle());
if (expectToBeFiltered) {
mFakePolicy->assertFilterInputEventWasCalled(motionArgs);
} else {
@@ -939,6 +1030,7 @@
mDispatcher->notifyKey(&keyArgs);
keyArgs = generateKeyArgs(AKEY_EVENT_ACTION_UP);
mDispatcher->notifyKey(&keyArgs);
+ ASSERT_TRUE(mDispatcher->waitForIdle());
if (expectToBeFiltered) {
mFakePolicy->assertFilterInputEventWasCalled(keyArgs);
@@ -1029,9 +1121,8 @@
ASSERT_EQ(INPUT_EVENT_INJECTION_SUCCEEDED, injectMotionDown(mDispatcher,
AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT, 20, 20))
<< "Inject motion event should return INPUT_EVENT_INJECTION_SUCCEEDED";
- // Call monitor to wait for the command queue to get flushed.
- mDispatcher->monitor();
+ ASSERT_TRUE(mDispatcher->waitForIdle());
mFakePolicy->assertOnPointerDownEquals(mUnfocusedWindow->getToken());
}
@@ -1042,10 +1133,9 @@
ASSERT_EQ(INPUT_EVENT_INJECTION_SUCCEEDED, injectMotionDown(mDispatcher,
AINPUT_SOURCE_TRACKBALL, ADISPLAY_ID_DEFAULT, 20, 20))
<< "Inject motion event should return INPUT_EVENT_INJECTION_SUCCEEDED";
- // Call monitor to wait for the command queue to get flushed.
- mDispatcher->monitor();
- mFakePolicy->assertOnPointerDownEquals(nullptr);
+ ASSERT_TRUE(mDispatcher->waitForIdle());
+ mFakePolicy->assertOnPointerDownWasNotCalled();
}
// Have two windows, one with focus. Inject KeyEvent with action DOWN on the window that doesn't
@@ -1053,10 +1143,9 @@
TEST_F(InputDispatcherOnPointerDownOutsideFocus, OnPointerDownOutsideFocus_NonMotionFailure) {
ASSERT_EQ(INPUT_EVENT_INJECTION_SUCCEEDED, injectKeyDown(mDispatcher, ADISPLAY_ID_DEFAULT))
<< "Inject key event should return INPUT_EVENT_INJECTION_SUCCEEDED";
- // Call monitor to wait for the command queue to get flushed.
- mDispatcher->monitor();
- mFakePolicy->assertOnPointerDownEquals(nullptr);
+ ASSERT_TRUE(mDispatcher->waitForIdle());
+ mFakePolicy->assertOnPointerDownWasNotCalled();
}
// Have two windows, one with focus. Inject MotionEvent with source TOUCHSCREEN and action
@@ -1068,10 +1157,9 @@
injectMotionDown(mDispatcher, AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT,
mFocusedWindowTouchPoint, mFocusedWindowTouchPoint))
<< "Inject motion event should return INPUT_EVENT_INJECTION_SUCCEEDED";
- // Call monitor to wait for the command queue to get flushed.
- mDispatcher->monitor();
- mFakePolicy->assertOnPointerDownEquals(nullptr);
+ ASSERT_TRUE(mDispatcher->waitForIdle());
+ mFakePolicy->assertOnPointerDownWasNotCalled();
}
} // namespace android::inputdispatcher
diff --git a/services/surfaceflinger/BufferQueueLayer.cpp b/services/surfaceflinger/BufferQueueLayer.cpp
index d51d34b..543b0c4 100644
--- a/services/surfaceflinger/BufferQueueLayer.cpp
+++ b/services/surfaceflinger/BufferQueueLayer.cpp
@@ -35,6 +35,7 @@
BufferQueueLayer::BufferQueueLayer(const LayerCreationArgs& args) : BufferLayer(args) {}
BufferQueueLayer::~BufferQueueLayer() {
+ mContentsChangedListener->abandon();
mConsumer->abandon();
}
@@ -480,7 +481,9 @@
mFlinger->getFactory().createBufferLayerConsumer(consumer, mFlinger->getRenderEngine(),
mTextureName, this);
mConsumer->setConsumerUsageBits(getEffectiveUsage(0));
- mConsumer->setContentsChangedListener(this);
+
+ mContentsChangedListener = new ContentsChangedListener(this);
+ mConsumer->setContentsChangedListener(mContentsChangedListener);
mConsumer->setName(String8(mName.data(), mName.size()));
// BufferQueueCore::mMaxDequeuedBufferCount is default to 1
@@ -552,4 +555,57 @@
return layer;
}
+// -----------------------------------------------------------------------
+// Interface implementation for BufferLayerConsumer::ContentsChangedListener
+// -----------------------------------------------------------------------
+
+void BufferQueueLayer::ContentsChangedListener::onFrameAvailable(const BufferItem& item) {
+ Mutex::Autolock lock(mMutex);
+ if (mBufferQueueLayer != nullptr) {
+ mBufferQueueLayer->onFrameAvailable(item);
+ }
+}
+
+void BufferQueueLayer::ContentsChangedListener::onFrameReplaced(const BufferItem& item) {
+ Mutex::Autolock lock(mMutex);
+ if (mBufferQueueLayer != nullptr) {
+ mBufferQueueLayer->onFrameReplaced(item);
+ }
+}
+
+void BufferQueueLayer::ContentsChangedListener::onSidebandStreamChanged() {
+ Mutex::Autolock lock(mMutex);
+ if (mBufferQueueLayer != nullptr) {
+ mBufferQueueLayer->onSidebandStreamChanged();
+ }
+}
+
+void BufferQueueLayer::ContentsChangedListener::onFrameDequeued(const uint64_t bufferId) {
+ Mutex::Autolock lock(mMutex);
+ if (mBufferQueueLayer != nullptr) {
+ mBufferQueueLayer->onFrameDequeued(bufferId);
+ }
+}
+
+void BufferQueueLayer::ContentsChangedListener::onFrameDetached(const uint64_t bufferId) {
+ Mutex::Autolock lock(mMutex);
+ if (mBufferQueueLayer != nullptr) {
+ mBufferQueueLayer->onFrameDetached(bufferId);
+ }
+}
+
+void BufferQueueLayer::ContentsChangedListener::onFrameCancelled(const uint64_t bufferId) {
+ Mutex::Autolock lock(mMutex);
+ if (mBufferQueueLayer != nullptr) {
+ mBufferQueueLayer->onFrameCancelled(bufferId);
+ }
+}
+
+void BufferQueueLayer::ContentsChangedListener::abandon() {
+ Mutex::Autolock lock(mMutex);
+ mBufferQueueLayer = nullptr;
+}
+
+// -----------------------------------------------------------------------
+
} // namespace android
diff --git a/services/surfaceflinger/BufferQueueLayer.h b/services/surfaceflinger/BufferQueueLayer.h
index 1b1fccd..b040556 100644
--- a/services/surfaceflinger/BufferQueueLayer.h
+++ b/services/surfaceflinger/BufferQueueLayer.h
@@ -29,7 +29,7 @@
* This also implements onFrameAvailable(), which notifies SurfaceFlinger
* that new data has arrived.
*/
-class BufferQueueLayer : public BufferLayer, public BufferLayerConsumer::ContentsChangedListener {
+class BufferQueueLayer : public BufferLayer {
public:
// Only call while mStateLock is held
explicit BufferQueueLayer(const LayerCreationArgs&);
@@ -84,18 +84,37 @@
void latchPerFrameState(compositionengine::LayerFECompositionState&) const override;
sp<Layer> createClone() override;
- // -----------------------------------------------------------------------
- // Interface implementation for BufferLayerConsumer::ContentsChangedListener
- // -----------------------------------------------------------------------
+ void onFrameAvailable(const BufferItem& item);
+ void onFrameReplaced(const BufferItem& item);
+ void onSidebandStreamChanged();
+ void onFrameDequeued(const uint64_t bufferId);
+ void onFrameDetached(const uint64_t bufferId);
+ void onFrameCancelled(const uint64_t bufferId);
+
protected:
void gatherBufferInfo() override;
- void onFrameAvailable(const BufferItem& item) override;
- void onFrameReplaced(const BufferItem& item) override;
- void onSidebandStreamChanged() override;
- void onFrameDequeued(const uint64_t bufferId) override;
- void onFrameDetached(const uint64_t bufferId) override;
- void onFrameCancelled(const uint64_t bufferId) override;
+ // -----------------------------------------------------------------------
+ // Interface implementation for BufferLayerConsumer::ContentsChangedListener
+ // -----------------------------------------------------------------------
+ class ContentsChangedListener : public BufferLayerConsumer::ContentsChangedListener {
+ public:
+ ContentsChangedListener(BufferQueueLayer* bufferQueueLayer)
+ : mBufferQueueLayer(bufferQueueLayer) {}
+ void abandon();
+
+ protected:
+ void onFrameAvailable(const BufferItem& item) override;
+ void onFrameReplaced(const BufferItem& item) override;
+ void onSidebandStreamChanged() override;
+ void onFrameDequeued(const uint64_t bufferId) override;
+ void onFrameDetached(const uint64_t bufferId) override;
+ void onFrameCancelled(const uint64_t bufferId) override;
+
+ private:
+ BufferQueueLayer* mBufferQueueLayer = nullptr;
+ Mutex mMutex;
+ };
// -----------------------------------------------------------------------
public:
@@ -130,6 +149,8 @@
// thread-safe
std::atomic<int32_t> mQueuedFrames{0};
std::atomic<bool> mSidebandStreamChanged{false};
+
+ sp<ContentsChangedListener> mContentsChangedListener;
};
} // namespace android
diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/LayerFE.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/LayerFE.h
index e585769..0a70165 100644
--- a/services/surfaceflinger/CompositionEngine/include/compositionengine/LayerFE.h
+++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/LayerFE.h
@@ -93,6 +93,13 @@
virtual std::optional<renderengine::LayerSettings> prepareClientComposition(
ClientCompositionTargetSettings&) = 0;
+ // Returns the LayerSettings used to draw shadows around a layer. It is passed
+ // to RenderEngine::drawLayers. Returns nullopt_t if the layer does not render
+ // shadows.
+ virtual std::optional<renderengine::LayerSettings> prepareShadowClientComposition(
+ const renderengine::LayerSettings& layerSettings, const Rect& displayViewport,
+ ui::Dataspace outputDataspace) = 0;
+
// Called after the layer is displayed to update the presentation fence
virtual void onLayerDisplayed(const sp<Fence>&) = 0;
diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/Output.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/Output.h
index 5f42ea1..076fdad 100644
--- a/services/surfaceflinger/CompositionEngine/include/compositionengine/Output.h
+++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/Output.h
@@ -272,7 +272,7 @@
virtual bool getSkipColorTransform() const = 0;
virtual FrameFences presentAndGetFrameFences() = 0;
virtual std::vector<renderengine::LayerSettings> generateClientCompositionRequests(
- bool supportsProtectedContent, Region& clearRegion) = 0;
+ bool supportsProtectedContent, Region& clearRegion, ui::Dataspace outputDataspace) = 0;
virtual void appendRegionFlashRequests(
const Region& flashRegion,
std::vector<renderengine::LayerSettings>& clientCompositionLayers) = 0;
diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/Output.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/Output.h
index a2342ae..159e928 100644
--- a/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/Output.h
+++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/Output.h
@@ -97,7 +97,8 @@
bool getSkipColorTransform() const override;
compositionengine::Output::FrameFences presentAndGetFrameFences() override;
std::vector<renderengine::LayerSettings> generateClientCompositionRequests(
- bool supportsProtectedContent, Region& clearRegion) override;
+ bool supportsProtectedContent, Region& clearRegion,
+ ui::Dataspace outputDataspace) override;
void appendRegionFlashRequests(const Region&,
std::vector<renderengine::LayerSettings>&) override;
void setExpensiveRenderingExpected(bool enabled) override;
diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/mock/LayerFE.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/mock/LayerFE.h
index 3eada3c..739490f 100644
--- a/services/surfaceflinger/CompositionEngine/include/compositionengine/mock/LayerFE.h
+++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/mock/LayerFE.h
@@ -38,6 +38,10 @@
MOCK_METHOD1(prepareClientComposition,
std::optional<renderengine::LayerSettings>(
compositionengine::LayerFE::ClientCompositionTargetSettings&));
+ MOCK_METHOD3(prepareShadowClientComposition,
+ std::optional<renderengine::LayerSettings>(const renderengine::LayerSettings&,
+ const Rect&, ui::Dataspace));
+
MOCK_METHOD1(onLayerDisplayed, void(const sp<Fence>&));
MOCK_CONST_METHOD0(getDebugName, const char*());
diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/mock/Output.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/mock/Output.h
index 02e68fc..7f5bd06 100644
--- a/services/surfaceflinger/CompositionEngine/include/compositionengine/mock/Output.h
+++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/mock/Output.h
@@ -106,8 +106,8 @@
MOCK_METHOD0(postFramebuffer, void());
MOCK_METHOD0(presentAndGetFrameFences, compositionengine::Output::FrameFences());
- MOCK_METHOD2(generateClientCompositionRequests,
- std::vector<renderengine::LayerSettings>(bool, Region&));
+ MOCK_METHOD3(generateClientCompositionRequests,
+ std::vector<renderengine::LayerSettings>(bool, Region&, ui::Dataspace));
MOCK_METHOD2(appendRegionFlashRequests,
void(const Region&, std::vector<renderengine::LayerSettings>&));
MOCK_METHOD1(setExpensiveRenderingExpected, void(bool));
diff --git a/services/surfaceflinger/CompositionEngine/src/Output.cpp b/services/surfaceflinger/CompositionEngine/src/Output.cpp
index 6877f8b..9cc0540 100644
--- a/services/surfaceflinger/CompositionEngine/src/Output.cpp
+++ b/services/surfaceflinger/CompositionEngine/src/Output.cpp
@@ -799,7 +799,8 @@
// Generate the client composition requests for the layers on this output.
std::vector<renderengine::LayerSettings> clientCompositionLayers =
generateClientCompositionRequests(supportsProtectedContent,
- clientCompositionDisplay.clearRegion);
+ clientCompositionDisplay.clearRegion,
+ clientCompositionDisplay.outputDataspace);
appendRegionFlashRequests(debugRegion, clientCompositionLayers);
// If we the display is secure, protected content support is enabled, and at
@@ -850,7 +851,7 @@
}
std::vector<renderengine::LayerSettings> Output::generateClientCompositionRequests(
- bool supportsProtectedContent, Region& clearRegion) {
+ bool supportsProtectedContent, Region& clearRegion, ui::Dataspace outputDataspace) {
std::vector<renderengine::LayerSettings> clientCompositionLayers;
ALOGV("Rendering client layers");
@@ -902,6 +903,13 @@
layerSettings.source.solidColor = half3(0.0, 0.0, 0.0);
layerSettings.alpha = half(0.0);
layerSettings.disableBlending = true;
+ } else {
+ std::optional<renderengine::LayerSettings> shadowLayer =
+ layerFE.prepareShadowClientComposition(*result, outputState.viewport,
+ outputDataspace);
+ if (shadowLayer) {
+ clientCompositionLayers.push_back(*shadowLayer);
+ }
}
layer->editState().clientCompositionTimestamp = systemTime();
diff --git a/services/surfaceflinger/CompositionEngine/tests/OutputTest.cpp b/services/surfaceflinger/CompositionEngine/tests/OutputTest.cpp
index 110760c..9e79062 100644
--- a/services/surfaceflinger/CompositionEngine/tests/OutputTest.cpp
+++ b/services/surfaceflinger/CompositionEngine/tests/OutputTest.cpp
@@ -2279,8 +2279,8 @@
// Sets up the helper functions called by the function under test to use
// mock implementations.
MOCK_CONST_METHOD0(getSkipColorTransform, bool());
- MOCK_METHOD2(generateClientCompositionRequests,
- std::vector<renderengine::LayerSettings>(bool, Region&));
+ MOCK_METHOD3(generateClientCompositionRequests,
+ std::vector<renderengine::LayerSettings>(bool, Region&, ui::Dataspace));
MOCK_METHOD2(appendRegionFlashRequests,
void(const Region&, std::vector<renderengine::LayerSettings>&));
MOCK_METHOD1(setExpensiveRenderingExpected, void(bool));
@@ -2358,7 +2358,7 @@
EXPECT_CALL(*mRenderSurface, dequeueBuffer(_)).WillOnce(Return(mOutputBuffer));
EXPECT_CALL(mOutput, getSkipColorTransform()).WillOnce(Return(false));
- EXPECT_CALL(mOutput, generateClientCompositionRequests(false, _)).Times(1);
+ EXPECT_CALL(mOutput, generateClientCompositionRequests(false, _, _)).Times(1);
EXPECT_CALL(mOutput, appendRegionFlashRequests(RegionEq(kDebugRegion), _)).Times(1);
EXPECT_CALL(mOutput, setExpensiveRenderingExpected(true)).Times(1);
EXPECT_CALL(mOutput, setExpensiveRenderingExpected(false)).Times(1);
@@ -2375,9 +2375,10 @@
struct OutputPartialMock : public OutputPartialMockBase {
// compositionengine::Output overrides
std::vector<renderengine::LayerSettings> generateClientCompositionRequests(
- bool supportsProtectedContent, Region& clearRegion) override {
+ bool supportsProtectedContent, Region& clearRegion,
+ ui::Dataspace dataspace) override {
return impl::Output::generateClientCompositionRequests(supportsProtectedContent,
- clearRegion);
+ clearRegion, dataspace);
}
};
@@ -2436,6 +2437,8 @@
EXPECT_CALL(leftOutputLayer, needsFiltering()).WillRepeatedly(Return(false));
EXPECT_CALL(leftLayer, getFEState()).WillRepeatedly(ReturnRef(leftLayerFEState));
EXPECT_CALL(leftLayerFE, prepareClientComposition(_)).WillOnce(Return(leftLayerRESettings));
+ EXPECT_CALL(leftLayerFE, prepareShadowClientComposition(_, _, _))
+ .WillOnce(Return(std::optional<renderengine::LayerSettings>()));
EXPECT_CALL(leftOutputLayer, editState()).WillRepeatedly(ReturnRef(leftOutputLayerState));
EXPECT_CALL(rightOutputLayer, getState()).WillRepeatedly(ReturnRef(rightOutputLayerState));
@@ -2445,6 +2448,8 @@
EXPECT_CALL(rightOutputLayer, needsFiltering()).WillRepeatedly(Return(false));
EXPECT_CALL(rightLayer, getFEState()).WillRepeatedly(ReturnRef(rightLayerFEState));
EXPECT_CALL(rightLayerFE, prepareClientComposition(_)).WillOnce(Return(rightLayerRESettings));
+ EXPECT_CALL(rightLayerFE, prepareShadowClientComposition(_, _, _))
+ .WillOnce(Return(std::optional<renderengine::LayerSettings>()));
EXPECT_CALL(rightOutputLayer, editState()).WillRepeatedly(ReturnRef(rightOutputLayerState));
EXPECT_CALL(mOutput, getOutputLayerCount()).WillRepeatedly(Return(2u));
@@ -2468,8 +2473,8 @@
constexpr bool supportsProtectedContent = false;
Region clearRegion;
- auto requests =
- mOutput.generateClientCompositionRequests(supportsProtectedContent, clearRegion);
+ auto requests = mOutput.generateClientCompositionRequests(supportsProtectedContent, clearRegion,
+ mOutput.getState().targetDataspace);
ASSERT_EQ(2u, requests.size());
EXPECT_EQ(leftLayerColor, requests[0].source.solidColor);
@@ -2518,8 +2523,8 @@
constexpr bool supportsProtectedContent = false;
Region clearRegion;
- auto requests =
- mOutput.generateClientCompositionRequests(supportsProtectedContent, clearRegion);
+ auto requests = mOutput.generateClientCompositionRequests(supportsProtectedContent, clearRegion,
+ mOutput.getState().targetDataspace);
EXPECT_EQ(0u, requests.size());
}
@@ -2596,8 +2601,8 @@
constexpr bool supportsProtectedContent = false;
Region clearRegion;
- auto requests =
- mOutput.generateClientCompositionRequests(supportsProtectedContent, clearRegion);
+ auto requests = mOutput.generateClientCompositionRequests(supportsProtectedContent, clearRegion,
+ mOutput.getState().targetDataspace);
const half3 clearColor{0.f, 0.f, 0.f};
diff --git a/services/surfaceflinger/DisplayDevice.h b/services/surfaceflinger/DisplayDevice.h
index ce4e1e6..74f47ff 100644
--- a/services/surfaceflinger/DisplayDevice.h
+++ b/services/surfaceflinger/DisplayDevice.h
@@ -246,6 +246,7 @@
uint32_t reqHeight, ui::Dataspace reqDataSpace,
ui::Transform::orientation_flags rotation, bool allowSecureLayers = true)
: RenderArea(reqWidth, reqHeight, CaptureFill::OPAQUE, reqDataSpace,
+ device->getViewport(),
getDisplayRotation(rotation, device->getInstallOrientation())),
mDevice(device),
mSourceCrop(sourceCrop),
diff --git a/services/surfaceflinger/Layer.cpp b/services/surfaceflinger/Layer.cpp
index 3d00352..35fc4be 100644
--- a/services/surfaceflinger/Layer.cpp
+++ b/services/surfaceflinger/Layer.cpp
@@ -571,6 +571,53 @@
return layerSettings;
}
+std::optional<renderengine::LayerSettings> Layer::prepareShadowClientComposition(
+ const renderengine::LayerSettings& casterLayerSettings, const Rect& displayViewport,
+ ui::Dataspace outputDataspace) {
+ renderengine::ShadowSettings shadow = getShadowSettings(displayViewport);
+ if (shadow.length <= 0.f) {
+ return {};
+ }
+
+ const float casterAlpha = casterLayerSettings.alpha;
+ const bool casterIsOpaque = ((casterLayerSettings.source.buffer.buffer != nullptr) &&
+ casterLayerSettings.source.buffer.isOpaque);
+
+ renderengine::LayerSettings shadowLayer = casterLayerSettings;
+ shadowLayer.shadow = shadow;
+
+ // If the casting layer is translucent, we need to fill in the shadow underneath the layer.
+ // Otherwise the generated shadow will only be shown around the casting layer.
+ shadowLayer.shadow.casterIsTranslucent = !casterIsOpaque || (casterAlpha < 1.0f);
+ shadowLayer.shadow.ambientColor *= casterAlpha;
+ shadowLayer.shadow.spotColor *= casterAlpha;
+ shadowLayer.sourceDataspace = outputDataspace;
+ shadowLayer.source.buffer.buffer = nullptr;
+
+ if (shadowLayer.shadow.ambientColor.a <= 0.f && shadowLayer.shadow.spotColor.a <= 0.f) {
+ return {};
+ }
+
+ float casterCornerRadius = shadowLayer.geometry.roundedCornersRadius;
+ const FloatRect& cornerRadiusCropRect = casterLayerSettings.geometry.roundedCornersCrop;
+ const FloatRect& casterRect = shadowLayer.geometry.boundaries;
+
+ // crop used to set the corner radius may be larger than the content rect. Adjust the corner
+ // radius accordingly.
+ if (casterCornerRadius > 0.f) {
+ float cropRectOffset = std::max(std::abs(cornerRadiusCropRect.top - casterRect.top),
+ std::abs(cornerRadiusCropRect.left - casterRect.left));
+ if (cropRectOffset > casterCornerRadius) {
+ casterCornerRadius = 0;
+ } else {
+ casterCornerRadius -= cropRectOffset;
+ }
+ shadowLayer.geometry.roundedCornersRadius = casterCornerRadius;
+ }
+
+ return shadowLayer;
+}
+
Hwc2::IComposerClient::Composition Layer::getCompositionType(
const sp<const DisplayDevice>& display) const {
const auto outputLayer = findOutputLayerForDisplay(display);
diff --git a/services/surfaceflinger/Layer.h b/services/surfaceflinger/Layer.h
index 3c92c22..843d3ae 100644
--- a/services/surfaceflinger/Layer.h
+++ b/services/surfaceflinger/Layer.h
@@ -510,6 +510,9 @@
void latchCursorCompositionState(compositionengine::LayerFECompositionState&) const override;
std::optional<renderengine::LayerSettings> prepareClientComposition(
compositionengine::LayerFE::ClientCompositionTargetSettings&) override;
+ std::optional<renderengine::LayerSettings> prepareShadowClientComposition(
+ const renderengine::LayerSettings& layerSettings, const Rect& displayViewport,
+ ui::Dataspace outputDataspace) override;
void onLayerDisplayed(const sp<Fence>& releaseFence) override;
const char* getDebugName() const override;
@@ -713,6 +716,14 @@
Region debugGetVisibleRegionOnDefaultDisplay() const;
+ /**
+ * Returns the cropped buffer size or the layer crop if the layer has no buffer. Return
+ * INVALID_RECT if the layer has no buffer and no crop.
+ * A layer with an invalid buffer size and no crop is considered to be boundless. The layer
+ * bounds are constrained by its parent bounds.
+ */
+ Rect getCroppedBufferSize(const Layer::State& s) const;
+
protected:
// constant
sp<SurfaceFlinger> mFlinger;
@@ -921,13 +932,6 @@
const LayerVector::Visitor& visitor);
LayerVector makeChildrenTraversalList(LayerVector::StateSet stateSet,
const std::vector<Layer*>& layersInTree);
- /**
- * Returns the cropped buffer size or the layer crop if the layer has no buffer. Return
- * INVALID_RECT if the layer has no buffer and no crop.
- * A layer with an invalid buffer size and no crop is considered to be boundless. The layer
- * bounds are constrained by its parent bounds.
- */
- Rect getCroppedBufferSize(const Layer::State& s) const;
// Cached properties computed from drawing state
// Effective transform taking into account parent transforms and any parent scaling.
diff --git a/services/surfaceflinger/RenderArea.h b/services/surfaceflinger/RenderArea.h
index edc6442..532572f 100644
--- a/services/surfaceflinger/RenderArea.h
+++ b/services/surfaceflinger/RenderArea.h
@@ -22,13 +22,14 @@
static float getCaptureFillValue(CaptureFill captureFill);
RenderArea(uint32_t reqWidth, uint32_t reqHeight, CaptureFill captureFill,
- ui::Dataspace reqDataSpace,
+ ui::Dataspace reqDataSpace, const Rect& displayViewport,
ui::Transform::orientation_flags rotation = ui::Transform::ROT_0)
: mReqWidth(reqWidth),
mReqHeight(reqHeight),
mReqDataSpace(reqDataSpace),
mCaptureFill(captureFill),
- mRotationFlags(rotation) {}
+ mRotationFlags(rotation),
+ mDisplayViewport(displayViewport) {}
virtual ~RenderArea() = default;
@@ -80,12 +81,16 @@
virtual const sp<const DisplayDevice> getDisplayDevice() const = 0;
+ // Returns the source display viewport.
+ const Rect& getDisplayViewport() const { return mDisplayViewport; }
+
private:
const uint32_t mReqWidth;
const uint32_t mReqHeight;
const ui::Dataspace mReqDataSpace;
const CaptureFill mCaptureFill;
const ui::Transform::orientation_flags mRotationFlags;
+ const Rect mDisplayViewport;
};
} // namespace android
diff --git a/services/surfaceflinger/SurfaceFlinger.cpp b/services/surfaceflinger/SurfaceFlinger.cpp
index 8cd7223..12206e5 100644
--- a/services/surfaceflinger/SurfaceFlinger.cpp
+++ b/services/surfaceflinger/SurfaceFlinger.cpp
@@ -4930,8 +4930,12 @@
}
// Couldn't find display by displayId. Try to get display by layerStack since virtual displays
// may not have a displayId.
+ return getDisplayByLayerStack(displayOrLayerStack);
+}
+
+const sp<DisplayDevice> SurfaceFlinger::getDisplayByLayerStack(uint64_t layerStack) {
for (const auto& [token, display] : mDisplays) {
- if (display->getLayerStack() == displayOrLayerStack) {
+ if (display->getLayerStack() == layerStack) {
return display;
}
}
@@ -4987,8 +4991,8 @@
public:
LayerRenderArea(SurfaceFlinger* flinger, const sp<Layer>& layer, const Rect crop,
int32_t reqWidth, int32_t reqHeight, Dataspace reqDataSpace,
- bool childrenOnly)
- : RenderArea(reqWidth, reqHeight, CaptureFill::CLEAR, reqDataSpace),
+ bool childrenOnly, const Rect& displayViewport)
+ : RenderArea(reqWidth, reqHeight, CaptureFill::CLEAR, reqDataSpace, displayViewport),
mLayer(layer),
mCrop(crop),
mNeedsFiltering(false),
@@ -5072,7 +5076,7 @@
sp<Layer> parent;
Rect crop(sourceCrop);
std::unordered_set<sp<Layer>, ISurfaceComposer::SpHash<Layer>> excludeLayers;
-
+ Rect displayViewport;
{
Mutex::Autolock _l(mStateLock);
@@ -5089,14 +5093,21 @@
return PERMISSION_DENIED;
}
+ Rect parentSourceBounds = parent->getCroppedBufferSize(parent->getCurrentState());
if (sourceCrop.width() <= 0) {
crop.left = 0;
- crop.right = parent->getBufferSize(parent->getCurrentState()).getWidth();
+ crop.right = parentSourceBounds.getWidth();
}
if (sourceCrop.height() <= 0) {
crop.top = 0;
- crop.bottom = parent->getBufferSize(parent->getCurrentState()).getHeight();
+ crop.bottom = parentSourceBounds.getHeight();
+ }
+
+ if (crop.isEmpty() || frameScale <= 0.0f) {
+ // Error out if the layer has no source bounds (i.e. they are boundless) and a source
+ // crop was not specified, or an invalid frame scale was provided.
+ return BAD_VALUE;
}
reqWidth = crop.width() * frameScale;
reqHeight = crop.height() * frameScale;
@@ -5110,6 +5121,13 @@
return NAME_NOT_FOUND;
}
}
+
+ auto display = getDisplayByLayerStack(parent->getLayerStack());
+ if (!display) {
+ return BAD_VALUE;
+ }
+
+ displayViewport = display->getViewport();
} // mStateLock
// really small crop or frameScale
@@ -5120,7 +5138,8 @@
reqHeight = 1;
}
- LayerRenderArea renderArea(this, parent, crop, reqWidth, reqHeight, reqDataspace, childrenOnly);
+ LayerRenderArea renderArea(this, parent, crop, reqWidth, reqHeight, reqDataspace, childrenOnly,
+ displayViewport);
auto traverseLayers = [parent, childrenOnly,
&excludeLayers](const LayerVector::Visitor& visitor) {
parent->traverseChildrenInZOrder(LayerVector::StateSet::Drawing, [&](Layer* layer) {
@@ -5246,6 +5265,7 @@
const auto rotation = renderArea.getRotationFlags();
const auto transform = renderArea.getTransform();
const auto sourceCrop = renderArea.getSourceCrop();
+ const auto& displayViewport = renderArea.getDisplayViewport();
renderengine::DisplaySettings clientCompositionDisplay;
std::vector<renderengine::LayerSettings> clientCompositionLayers;
@@ -5330,6 +5350,12 @@
};
auto result = layer->prepareClientComposition(targetSettings);
if (result) {
+ std::optional<renderengine::LayerSettings> shadowLayer =
+ layer->prepareShadowClientComposition(*result, displayViewport,
+ clientCompositionDisplay.outputDataspace);
+ if (shadowLayer) {
+ clientCompositionLayers.push_back(*shadowLayer);
+ }
clientCompositionLayers.push_back(*result);
}
});
diff --git a/services/surfaceflinger/SurfaceFlinger.h b/services/surfaceflinger/SurfaceFlinger.h
index 5f1580b..7144608 100644
--- a/services/surfaceflinger/SurfaceFlinger.h
+++ b/services/surfaceflinger/SurfaceFlinger.h
@@ -688,6 +688,7 @@
const sp<GraphicBuffer>& buffer, bool useIdentityTransform,
bool& outCapturedSecureLayers);
const sp<DisplayDevice> getDisplayByIdOrLayerStack(uint64_t displayOrLayerStack);
+ const sp<DisplayDevice> getDisplayByLayerStack(uint64_t layerStack);
status_t captureScreenImplLocked(const RenderArea& renderArea,
TraverseLayersFunction traverseLayers,
ANativeWindowBuffer* buffer, bool useIdentityTransform,
diff --git a/services/surfaceflinger/tests/Credentials_test.cpp b/services/surfaceflinger/tests/Credentials_test.cpp
index b667a74..b1bb7fd 100644
--- a/services/surfaceflinger/tests/Credentials_test.cpp
+++ b/services/surfaceflinger/tests/Credentials_test.cpp
@@ -274,7 +274,7 @@
sp<GraphicBuffer> outBuffer;
return ScreenshotClient::captureLayers(mBGSurfaceControl->getHandle(),
ui::Dataspace::V0_SRGB, ui::PixelFormat::RGBA_8888,
- Rect(), FRAME_SCALE, &outBuffer);
+ Rect(0, 0, 1, 1), FRAME_SCALE, &outBuffer);
};
ASSERT_NO_FATAL_FAILURE(checkWithPrivileges<status_t>(condition, NO_ERROR, PERMISSION_DENIED));
}
diff --git a/services/surfaceflinger/tests/LayerUpdate_test.cpp b/services/surfaceflinger/tests/LayerUpdate_test.cpp
index 73f563d..0ad122b 100644
--- a/services/surfaceflinger/tests/LayerUpdate_test.cpp
+++ b/services/surfaceflinger/tests/LayerUpdate_test.cpp
@@ -1443,6 +1443,61 @@
mCapture->expectColor(Rect(0, 0, 9, 9), {100, 100, 100, 255});
}
+TEST_F(ScreenCaptureTest, CaptureBoundlessLayerWithSourceCrop) {
+ sp<SurfaceControl> child = createColorLayer("Child layer", Color::RED, mFGSurfaceControl.get());
+ SurfaceComposerClient::Transaction().show(child).apply(true);
+
+ sp<ISurfaceComposer> sf(ComposerService::getComposerService());
+ sp<GraphicBuffer> outBuffer;
+ Rect sourceCrop(0, 0, 10, 10);
+ ASSERT_EQ(NO_ERROR, sf->captureLayers(child->getHandle(), &outBuffer, sourceCrop));
+ ScreenCapture sc(outBuffer);
+
+ sc.expectColor(Rect(0, 0, 9, 9), Color::RED);
+}
+
+TEST_F(ScreenCaptureTest, CaptureBoundedLayerWithoutSourceCrop) {
+ sp<SurfaceControl> child = createColorLayer("Child layer", Color::RED, mFGSurfaceControl.get());
+ Rect layerCrop(0, 0, 10, 10);
+ SurfaceComposerClient::Transaction().setCrop_legacy(child, layerCrop).show(child).apply(true);
+
+ sp<ISurfaceComposer> sf(ComposerService::getComposerService());
+ sp<GraphicBuffer> outBuffer;
+ Rect sourceCrop = Rect();
+ ASSERT_EQ(NO_ERROR, sf->captureLayers(child->getHandle(), &outBuffer, sourceCrop));
+ ScreenCapture sc(outBuffer);
+
+ sc.expectColor(Rect(0, 0, 9, 9), Color::RED);
+}
+
+TEST_F(ScreenCaptureTest, CaptureBoundlessLayerWithoutSourceCropFails) {
+ sp<SurfaceControl> child = createColorLayer("Child layer", Color::RED, mFGSurfaceControl.get());
+ SurfaceComposerClient::Transaction().show(child).apply(true);
+
+ sp<ISurfaceComposer> sf(ComposerService::getComposerService());
+ sp<GraphicBuffer> outBuffer;
+ Rect sourceCrop = Rect();
+
+ ASSERT_EQ(BAD_VALUE, sf->captureLayers(child->getHandle(), &outBuffer, sourceCrop));
+}
+
+TEST_F(ScreenCaptureTest, CaptureBufferLayerWithoutBufferFails) {
+ sp<SurfaceControl> child = createSurface(mClient, "Child surface", 10, 10,
+ PIXEL_FORMAT_RGBA_8888, 0, mFGSurfaceControl.get());
+ SurfaceComposerClient::Transaction().show(child).apply(true);
+
+ sp<ISurfaceComposer> sf(ComposerService::getComposerService());
+ sp<GraphicBuffer> outBuffer;
+ Rect sourceCrop = Rect();
+ ASSERT_EQ(BAD_VALUE, sf->captureLayers(child->getHandle(), &outBuffer, sourceCrop));
+
+ TransactionUtils::fillSurfaceRGBA8(child, Color::RED);
+ SurfaceComposerClient::Transaction().apply(true);
+ ASSERT_EQ(NO_ERROR, sf->captureLayers(child->getHandle(), &outBuffer, sourceCrop));
+ ScreenCapture sc(outBuffer);
+ sc.expectColor(Rect(0, 0, 9, 9), Color::RED);
+}
+
// In the following tests we verify successful skipping of a parent layer,
// so we use the same verification logic and only change how we mutate
// the parent layer to verify that various properties are ignored.
diff --git a/services/surfaceflinger/tests/utils/TransactionUtils.h b/services/surfaceflinger/tests/utils/TransactionUtils.h
index 22df255..2c63da0 100644
--- a/services/surfaceflinger/tests/utils/TransactionUtils.h
+++ b/services/surfaceflinger/tests/utils/TransactionUtils.h
@@ -136,6 +136,11 @@
}
}
+ static void fillSurfaceRGBA8(const sp<SurfaceControl>& sc, const Color& color,
+ bool unlock = true) {
+ fillSurfaceRGBA8(sc, color.r, color.g, color.b, unlock);
+ }
+
// Fill an RGBA_8888 formatted surface with a single color.
static void fillSurfaceRGBA8(const sp<SurfaceControl>& sc, uint8_t r, uint8_t g, uint8_t b,
bool unlock = true) {