Merge "[SurfaceFlinger] Allow force a supported color mode."
diff --git a/cmds/installd/dexopt.cpp b/cmds/installd/dexopt.cpp
index a639951..852aa79 100644
--- a/cmds/installd/dexopt.cpp
+++ b/cmds/installd/dexopt.cpp
@@ -1958,6 +1958,11 @@
/* child -- drop privileges before continuing */
drop_capabilities(uid);
+ // Clear BOOTCLASSPATH.
+ // Let dex2oat use the BCP from boot image, excluding updatable BCP
+ // modules for AOT to avoid app recompilation after their upgrades.
+ unsetenv("BOOTCLASSPATH");
+
SetDex2OatScheduling(boot_complete);
if (flock(out_oat_fd.get(), LOCK_EX | LOCK_NB) != 0) {
PLOG(ERROR) << "flock(" << out_oat_path << ") failed";
diff --git a/cmds/installd/tests/installd_service_test.cpp b/cmds/installd/tests/installd_service_test.cpp
index cc24ab3..48b07c4 100644
--- a/cmds/installd/tests/installd_service_test.cpp
+++ b/cmds/installd/tests/installd_service_test.cpp
@@ -21,6 +21,7 @@
#include <stdlib.h>
#include <string.h>
#include <sys/statvfs.h>
+#include <sys/stat.h>
#include <sys/xattr.h>
#include <android-base/file.h>
@@ -70,27 +71,28 @@
static void mkdir(const char* path, uid_t owner, gid_t group, mode_t mode) {
const std::string fullPath = get_full_path(path);
- ::mkdir(fullPath.c_str(), mode);
- ::chown(fullPath.c_str(), owner, group);
- ::chmod(fullPath.c_str(), mode);
+ EXPECT_EQ(::mkdir(fullPath.c_str(), mode), 0);
+ EXPECT_EQ(::chown(fullPath.c_str(), owner, group), 0);
+ EXPECT_EQ(::chmod(fullPath.c_str(), mode), 0);
}
static void touch(const char* path, uid_t owner, gid_t group, mode_t mode) {
int fd = ::open(get_full_path(path).c_str(), O_RDWR | O_CREAT, mode);
- ::fchown(fd, owner, group);
- ::fchmod(fd, mode);
- ::close(fd);
+ EXPECT_NE(fd, -1);
+ EXPECT_EQ(::fchown(fd, owner, group), 0);
+ EXPECT_EQ(::fchmod(fd, mode), 0);
+ EXPECT_EQ(::close(fd), 0);
}
static int stat_gid(const char* path) {
struct stat buf;
- ::stat(get_full_path(path).c_str(), &buf);
+ EXPECT_EQ(::stat(get_full_path(path).c_str(), &buf), 0);
return buf.st_gid;
}
static int stat_mode(const char* path) {
struct stat buf;
- ::stat(get_full_path(path).c_str(), &buf);
+ EXPECT_EQ(::stat(get_full_path(path).c_str(), &buf), 0);
return buf.st_mode & (S_IRWXU | S_IRWXG | S_IRWXO | S_ISGID);
}
diff --git a/libs/binder/ndk/Android.bp b/libs/binder/ndk/Android.bp
index 68011bb..a96c9a0 100644
--- a/libs/binder/ndk/Android.bp
+++ b/libs/binder/ndk/Android.bp
@@ -16,7 +16,7 @@
cc_library {
name: "libbinder_ndk",
- vendor_available: false,
+ vendor_available: true,
export_include_dirs: [
"include_ndk",
@@ -50,6 +50,13 @@
"libandroid_runtime",
],
+ header_libs: [
+ "jni_headers",
+ ],
+ export_header_lib_headers: [
+ "jni_headers",
+ ],
+
version_script: "libbinder_ndk.map.txt",
stubs: {
symbol_file: "libbinder_ndk.map.txt",
diff --git a/libs/graphicsenv/GraphicsEnv.cpp b/libs/graphicsenv/GraphicsEnv.cpp
index c20d54b..75fe2d3 100644
--- a/libs/graphicsenv/GraphicsEnv.cpp
+++ b/libs/graphicsenv/GraphicsEnv.cpp
@@ -156,9 +156,9 @@
mDriverPath = path;
}
-void GraphicsEnv::setGpuStats(const std::string driverPackageName,
- const std::string driverVersionName, const uint64_t driverVersionCode,
- const std::string appPackageName) {
+void GraphicsEnv::setGpuStats(const std::string& driverPackageName,
+ const std::string& driverVersionName,
+ const uint64_t driverVersionCode, const std::string& appPackageName) {
ATRACE_CALL();
ALOGV("setGpuStats:\n"
@@ -316,28 +316,28 @@
return false;
}
- return mUseAngle;
+ return (mUseAngle == YES) ? true : false;
}
void GraphicsEnv::updateUseAngle() {
- mUseAngle = false;
+ mUseAngle = NO;
const char* ANGLE_PREFER_ANGLE = "angle";
const char* ANGLE_PREFER_NATIVE = "native";
if (mAngleDeveloperOptIn == ANGLE_PREFER_ANGLE) {
ALOGV("User set \"Developer Options\" to force the use of ANGLE");
- mUseAngle = true;
+ mUseAngle = YES;
} else if (mAngleDeveloperOptIn == ANGLE_PREFER_NATIVE) {
ALOGV("User set \"Developer Options\" to force the use of Native");
- mUseAngle = false;
+ mUseAngle = NO;
} else {
// The "Developer Options" value wasn't set to force the use of ANGLE. Need to temporarily
// load ANGLE and call the updatable opt-in/out logic:
void* featureSo = loadLibrary("feature_support");
if (featureSo) {
ALOGV("loaded ANGLE's opt-in/out logic from namespace");
- mUseAngle = checkAngleRules(featureSo);
+ mUseAngle = checkAngleRules(featureSo) ? YES : NO;
dlclose(featureSo);
featureSo = nullptr;
} else {
@@ -349,6 +349,13 @@
void GraphicsEnv::setAngleInfo(const std::string path, const std::string appName,
const std::string developerOptIn, const int rulesFd,
const long rulesOffset, const long rulesLength) {
+ if (mUseAngle != UNKNOWN) {
+ // We've already figured out an answer for this app, so just return.
+ ALOGV("Already evaluated the rules file for '%s': use ANGLE = %s", appName.c_str(),
+ (mUseAngle == YES) ? "true" : "false");
+ return;
+ }
+
ALOGV("setting ANGLE path to '%s'", path.c_str());
mAnglePath = path;
ALOGV("setting ANGLE app name to '%s'", appName.c_str());
diff --git a/libs/graphicsenv/IGpuService.cpp b/libs/graphicsenv/IGpuService.cpp
index 98a6395..762a27b 100644
--- a/libs/graphicsenv/IGpuService.cpp
+++ b/libs/graphicsenv/IGpuService.cpp
@@ -27,9 +27,9 @@
public:
explicit BpGpuService(const sp<IBinder>& impl) : BpInterface<IGpuService>(impl) {}
- virtual void setGpuStats(const std::string driverPackageName,
- const std::string driverVersionName, const uint64_t driverVersionCode,
- const std::string appPackageName) {
+ virtual void setGpuStats(const std::string& driverPackageName,
+ const std::string& driverVersionName, const uint64_t driverVersionCode,
+ const std::string& appPackageName) {
Parcel data, reply;
data.writeInterfaceToken(IGpuService::getInterfaceDescriptor());
diff --git a/libs/graphicsenv/include/graphicsenv/GraphicsEnv.h b/libs/graphicsenv/include/graphicsenv/GraphicsEnv.h
index d4edfa0..c4482b7 100644
--- a/libs/graphicsenv/include/graphicsenv/GraphicsEnv.h
+++ b/libs/graphicsenv/include/graphicsenv/GraphicsEnv.h
@@ -47,8 +47,8 @@
// /data/app/com.example.driver/base.apk!/lib/arm64-v8a
void setDriverPath(const std::string path);
android_namespace_t* getDriverNamespace();
- void setGpuStats(const std::string driverPackageName, const std::string driverVersionName,
- const uint64_t versionCode, const std::string appPackageName);
+ void setGpuStats(const std::string& driverPackageName, const std::string& driverVersionName,
+ const uint64_t versionCode, const std::string& appPackageName);
void sendGpuStats();
bool shouldUseAngle(std::string appName);
@@ -74,6 +74,8 @@
const std::string& getDebugLayersGLES();
private:
+ enum UseAngle { UNKNOWN, YES, NO };
+
void* loadLibrary(std::string name);
bool checkAngleRules(void* so);
void updateUseAngle();
@@ -85,7 +87,7 @@
std::string mAngleAppName;
std::string mAngleDeveloperOptIn;
std::vector<char> mRulesBuffer;
- bool mUseAngle;
+ UseAngle mUseAngle = UNKNOWN;
std::string mDebugLayers;
std::string mDebugLayersGLES;
std::string mLayerPaths;
diff --git a/libs/graphicsenv/include/graphicsenv/IGpuService.h b/libs/graphicsenv/include/graphicsenv/IGpuService.h
index c080c53..1e74d60 100644
--- a/libs/graphicsenv/include/graphicsenv/IGpuService.h
+++ b/libs/graphicsenv/include/graphicsenv/IGpuService.h
@@ -32,9 +32,9 @@
DECLARE_META_INTERFACE(GpuService);
// set GPU stats from GraphicsEnvironment.
- virtual void setGpuStats(const std::string driverPackageName,
- const std::string driverVersionName, const uint64_t driverVersionCode,
- const std::string appPackageName) = 0;
+ virtual void setGpuStats(const std::string& driverPackageName,
+ const std::string& driverVersionName, const uint64_t driverVersionCode,
+ const std::string& appPackageName) = 0;
};
class BnGpuService : public BnInterface<IGpuService> {
diff --git a/libs/gui/Android.bp b/libs/gui/Android.bp
index f40eb6c..0510492 100644
--- a/libs/gui/Android.bp
+++ b/libs/gui/Android.bp
@@ -104,6 +104,7 @@
"IGraphicBufferConsumer.cpp",
"IGraphicBufferProducer.cpp",
"IProducerListener.cpp",
+ "IRegionSamplingListener.cpp",
"ISurfaceComposer.cpp",
"ISurfaceComposerClient.cpp",
"ITransactionCompletedListener.cpp",
diff --git a/libs/gui/IRegionSamplingListener.cpp b/libs/gui/IRegionSamplingListener.cpp
new file mode 100644
index 0000000..40cbfce
--- /dev/null
+++ b/libs/gui/IRegionSamplingListener.cpp
@@ -0,0 +1,64 @@
+/*
+ * Copyright 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#define LOG_TAG "IRegionSamplingListener"
+//#define LOG_NDEBUG 0
+
+#include <gui/IRegionSamplingListener.h>
+
+namespace android {
+
+namespace { // Anonymous
+
+enum class Tag : uint32_t {
+ ON_SAMPLE_COLLECTED = IBinder::FIRST_CALL_TRANSACTION,
+ LAST = ON_SAMPLE_COLLECTED,
+};
+
+} // Anonymous namespace
+
+class BpRegionSamplingListener : public SafeBpInterface<IRegionSamplingListener> {
+public:
+ explicit BpRegionSamplingListener(const sp<IBinder>& impl)
+ : SafeBpInterface<IRegionSamplingListener>(impl, "BpRegionSamplingListener") {}
+
+ ~BpRegionSamplingListener() override;
+
+ void onSampleCollected(float medianLuma) override {
+ callRemoteAsync<decltype(
+ &IRegionSamplingListener::onSampleCollected)>(Tag::ON_SAMPLE_COLLECTED, medianLuma);
+ }
+};
+
+// Out-of-line virtual method definitions to trigger vtable emission in this translation unit (see
+// clang warning -Wweak-vtables)
+BpRegionSamplingListener::~BpRegionSamplingListener() = default;
+
+IMPLEMENT_META_INTERFACE(RegionSamplingListener, "android.gui.IRegionSamplingListener");
+
+status_t BnRegionSamplingListener::onTransact(uint32_t code, const Parcel& data, Parcel* reply,
+ uint32_t flags) {
+ if (code < IBinder::FIRST_CALL_TRANSACTION || code > static_cast<uint32_t>(Tag::LAST)) {
+ return BBinder::onTransact(code, data, reply, flags);
+ }
+ auto tag = static_cast<Tag>(code);
+ switch (tag) {
+ case Tag::ON_SAMPLE_COLLECTED:
+ return callLocalAsync(data, reply, &IRegionSamplingListener::onSampleCollected);
+ }
+}
+
+} // namespace android
diff --git a/libs/gui/ISurfaceComposer.cpp b/libs/gui/ISurfaceComposer.cpp
index fc9185d..f77eeb2 100644
--- a/libs/gui/ISurfaceComposer.cpp
+++ b/libs/gui/ISurfaceComposer.cpp
@@ -26,6 +26,7 @@
#include <gui/IDisplayEventConnection.h>
#include <gui/IGraphicBufferProducer.h>
+#include <gui/IRegionSamplingListener.h>
#include <gui/ISurfaceComposer.h>
#include <gui/ISurfaceComposerClient.h>
#include <gui/LayerDebugInfo.h>
@@ -754,6 +755,57 @@
error = reply.readBool(outIsWideColorDisplay);
return error;
}
+
+ virtual status_t addRegionSamplingListener(const Rect& samplingArea,
+ const sp<IBinder>& stopLayerHandle,
+ const sp<IRegionSamplingListener>& listener) {
+ Parcel data, reply;
+ status_t error = data.writeInterfaceToken(ISurfaceComposer::getInterfaceDescriptor());
+ if (error != NO_ERROR) {
+ ALOGE("addRegionSamplingListener: Failed to write interface token");
+ return error;
+ }
+ error = data.write(samplingArea);
+ if (error != NO_ERROR) {
+ ALOGE("addRegionSamplingListener: Failed to write sampling area");
+ return error;
+ }
+ error = data.writeStrongBinder(stopLayerHandle);
+ if (error != NO_ERROR) {
+ ALOGE("addRegionSamplingListener: Failed to write stop layer handle");
+ return error;
+ }
+ error = data.writeStrongBinder(IInterface::asBinder(listener));
+ if (error != NO_ERROR) {
+ ALOGE("addRegionSamplingListener: Failed to write listener");
+ return error;
+ }
+ error = remote()->transact(BnSurfaceComposer::ADD_REGION_SAMPLING_LISTENER, data, &reply);
+ if (error != NO_ERROR) {
+ ALOGE("addRegionSamplingListener: Failed to transact");
+ }
+ return error;
+ }
+
+ virtual status_t removeRegionSamplingListener(const sp<IRegionSamplingListener>& listener) {
+ Parcel data, reply;
+ status_t error = data.writeInterfaceToken(ISurfaceComposer::getInterfaceDescriptor());
+ if (error != NO_ERROR) {
+ ALOGE("addRegionSamplingListener: Failed to write interface token");
+ return error;
+ }
+ error = data.writeStrongBinder(IInterface::asBinder(listener));
+ if (error != NO_ERROR) {
+ ALOGE("addRegionSamplingListener: Failed to write listener");
+ return error;
+ }
+ error = remote()->transact(BnSurfaceComposer::REMOVE_REGION_SAMPLING_LISTENER, data,
+ &reply);
+ if (error != NO_ERROR) {
+ ALOGE("addRegionSamplingListener: Failed to transact");
+ }
+ return error;
+ }
};
// Out-of-line virtual method definition to trigger vtable emission in this
@@ -1233,6 +1285,38 @@
CHECK_INTERFACE(ISurfaceComposer, data, reply);
return reply->writeUint64Vector(getPhysicalDisplayIds());
}
+ case ADD_REGION_SAMPLING_LISTENER: {
+ CHECK_INTERFACE(ISurfaceComposer, data, reply);
+ Rect samplingArea;
+ status_t result = data.read(samplingArea);
+ if (result != NO_ERROR) {
+ ALOGE("addRegionSamplingListener: Failed to read sampling area");
+ return result;
+ }
+ sp<IBinder> stopLayerHandle;
+ result = data.readNullableStrongBinder(&stopLayerHandle);
+ if (result != NO_ERROR) {
+ ALOGE("addRegionSamplingListener: Failed to read stop layer handle");
+ return result;
+ }
+ sp<IRegionSamplingListener> listener;
+ result = data.readNullableStrongBinder(&listener);
+ if (result != NO_ERROR) {
+ ALOGE("addRegionSamplingListener: Failed to read listener");
+ return result;
+ }
+ return addRegionSamplingListener(samplingArea, stopLayerHandle, listener);
+ }
+ case REMOVE_REGION_SAMPLING_LISTENER: {
+ CHECK_INTERFACE(ISurfaceComposer, data, reply);
+ sp<IRegionSamplingListener> listener;
+ status_t result = data.readNullableStrongBinder(&listener);
+ if (result != NO_ERROR) {
+ ALOGE("removeRegionSamplingListener: Failed to read listener");
+ return result;
+ }
+ return removeRegionSamplingListener(listener);
+ }
default: {
return BBinder::onTransact(code, data, reply, flags);
}
diff --git a/libs/gui/include/gui/IRegionSamplingListener.h b/libs/gui/include/gui/IRegionSamplingListener.h
new file mode 100644
index 0000000..1803d9a
--- /dev/null
+++ b/libs/gui/include/gui/IRegionSamplingListener.h
@@ -0,0 +1,43 @@
+/*
+ * Copyright 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include <cstdint>
+#include <vector>
+
+#include <binder/IInterface.h>
+#include <binder/SafeInterface.h>
+
+namespace android {
+
+class IRegionSamplingListener : public IInterface {
+public:
+ DECLARE_META_INTERFACE(RegionSamplingListener)
+
+ virtual void onSampleCollected(float medianLuma) = 0;
+};
+
+class BnRegionSamplingListener : public SafeBnInterface<IRegionSamplingListener> {
+public:
+ BnRegionSamplingListener()
+ : SafeBnInterface<IRegionSamplingListener>("BnRegionSamplingListener") {}
+
+ status_t onTransact(uint32_t code, const Parcel& data, Parcel* reply,
+ uint32_t flags = 0) override;
+};
+
+} // namespace android
diff --git a/libs/gui/include/gui/ISurfaceComposer.h b/libs/gui/include/gui/ISurfaceComposer.h
index a2db7bc..1a0b6bb 100644
--- a/libs/gui/include/gui/ISurfaceComposer.h
+++ b/libs/gui/include/gui/ISurfaceComposer.h
@@ -50,6 +50,7 @@
class IDisplayEventConnection;
class IGraphicBufferProducer;
class ISurfaceComposerClient;
+class IRegionSamplingListener;
class Rect;
enum class FrameEvent;
@@ -338,6 +339,26 @@
*/
virtual status_t isWideColorDisplay(const sp<IBinder>& token,
bool* outIsWideColorDisplay) const = 0;
+
+ /* Registers a listener to stream median luma updates from SurfaceFlinger.
+ *
+ * The sampling area is bounded by both samplingArea and the given stopLayerHandle
+ * (i.e., only layers behind the stop layer will be captured and sampled).
+ *
+ * Multiple listeners may be provided so long as they have independent listeners.
+ * If multiple listeners are provided, the effective sampling region for each listener will
+ * be bounded by whichever stop layer has a lower Z value.
+ *
+ * Requires the same permissions as captureLayers and captureScreen.
+ */
+ virtual status_t addRegionSamplingListener(const Rect& samplingArea,
+ const sp<IBinder>& stopLayerHandle,
+ const sp<IRegionSamplingListener>& listener) = 0;
+
+ /*
+ * Removes a listener that was streaming median luma updates from SurfaceFlinger.
+ */
+ virtual status_t removeRegionSamplingListener(const sp<IRegionSamplingListener>& listener) = 0;
};
// ----------------------------------------------------------------------------
@@ -383,6 +404,8 @@
IS_WIDE_COLOR_DISPLAY,
GET_DISPLAY_NATIVE_PRIMARIES,
GET_PHYSICAL_DISPLAY_IDS,
+ ADD_REGION_SAMPLING_LISTENER,
+ REMOVE_REGION_SAMPLING_LISTENER,
// Always append new enum to the end.
};
diff --git a/libs/gui/tests/Surface_test.cpp b/libs/gui/tests/Surface_test.cpp
index bec9299..f127853 100644
--- a/libs/gui/tests/Surface_test.cpp
+++ b/libs/gui/tests/Surface_test.cpp
@@ -669,6 +669,16 @@
status_t isWideColorDisplay(const sp<IBinder>&, bool*) const override { return NO_ERROR; }
+ status_t addRegionSamplingListener(const Rect& /*samplingArea*/,
+ const sp<IBinder>& /*stopLayerHandle*/,
+ const sp<IRegionSamplingListener>& /*listener*/) override {
+ return NO_ERROR;
+ }
+ status_t removeRegionSamplingListener(
+ const sp<IRegionSamplingListener>& /*listener*/) override {
+ return NO_ERROR;
+ }
+
protected:
IBinder* onAsBinder() override { return nullptr; }
diff --git a/libs/nativewindow/AHardwareBuffer.cpp b/libs/nativewindow/AHardwareBuffer.cpp
index 8ef4896..994e953 100644
--- a/libs/nativewindow/AHardwareBuffer.cpp
+++ b/libs/nativewindow/AHardwareBuffer.cpp
@@ -122,6 +122,54 @@
return gbuffer->lockAsync(usage, usage, bounds, outVirtualAddress, fence);
}
+int AHardwareBuffer_lockPlanes(AHardwareBuffer* buffer, uint64_t usage,
+ int32_t fence, const ARect* rect, AHardwareBuffer_Planes* outPlanes) {
+ if (!buffer || !outPlanes) return BAD_VALUE;
+
+ if (usage & ~(AHARDWAREBUFFER_USAGE_CPU_READ_MASK |
+ AHARDWAREBUFFER_USAGE_CPU_WRITE_MASK)) {
+ ALOGE("Invalid usage flags passed to AHardwareBuffer_lock; only "
+ " AHARDWAREBUFFER_USAGE_CPU_* flags are allowed");
+ return BAD_VALUE;
+ }
+
+ usage = AHardwareBuffer_convertToGrallocUsageBits(usage);
+ GraphicBuffer* gBuffer = AHardwareBuffer_to_GraphicBuffer(buffer);
+ Rect bounds;
+ if (!rect) {
+ bounds.set(Rect(gBuffer->getWidth(), gBuffer->getHeight()));
+ } else {
+ bounds.set(Rect(rect->left, rect->top, rect->right, rect->bottom));
+ }
+ int format = AHardwareBuffer_convertFromPixelFormat(uint32_t(gBuffer->getPixelFormat()));
+ memset(outPlanes->planes, 0, sizeof(outPlanes->planes));
+ if (AHardwareBuffer_formatIsYuv(format)) {
+ android_ycbcr yuvData;
+ int result = gBuffer->lockAsyncYCbCr(usage, bounds, &yuvData, fence);
+ if (result == 0) {
+ outPlanes->planeCount = 3;
+ outPlanes->planes[0].data = yuvData.y;
+ outPlanes->planes[0].pixelStride = 1;
+ outPlanes->planes[0].rowStride = yuvData.ystride;
+ outPlanes->planes[1].data = yuvData.cb;
+ outPlanes->planes[1].pixelStride = yuvData.chroma_step;
+ outPlanes->planes[1].rowStride = yuvData.cstride;
+ outPlanes->planes[2].data = yuvData.cr;
+ outPlanes->planes[2].pixelStride = yuvData.chroma_step;
+ outPlanes->planes[2].rowStride = yuvData.cstride;
+ } else {
+ outPlanes->planeCount = 0;
+ }
+ return result;
+ } else {
+ const uint32_t pixelStride = AHardwareBuffer_bytesPerPixel(format);
+ outPlanes->planeCount = 1;
+ outPlanes->planes[0].pixelStride = pixelStride;
+ outPlanes->planes[0].rowStride = gBuffer->getStride() * pixelStride;
+ return gBuffer->lockAsync(usage, usage, bounds, &outPlanes->planes[0].data, fence);
+ }
+}
+
int AHardwareBuffer_unlock(AHardwareBuffer* buffer, int32_t* fence) {
if (!buffer) return BAD_VALUE;
@@ -375,6 +423,19 @@
ALOGE_IF(log, "AHARDWAREBUFFER_FORMAT_BLOB cannot be encoded as video");
return false;
}
+ } else if (AHardwareBuffer_formatIsYuv(desc->format)) {
+ if (desc->layers != 1) {
+ ALOGE_IF(log, "Layers must be 1 for YUV formats.");
+ return false;
+ }
+ const uint64_t yuvInvalidGpuMask =
+ AHARDWAREBUFFER_USAGE_GPU_MIPMAP_COMPLETE |
+ AHARDWAREBUFFER_USAGE_GPU_CUBE_MAP;
+ if (desc->usage & yuvInvalidGpuMask) {
+ ALOGE_IF(log, "Invalid usage flags specified for YUV format; "
+ "mip-mapping and cube-mapping are not allowed.");
+ return false;
+ }
} else {
if (desc->usage & AHARDWAREBUFFER_USAGE_SENSOR_DIRECT_DATA) {
ALOGE_IF(log, "AHARDWAREBUFFER_USAGE_SENSOR_DIRECT_DATA requires AHARDWAREBUFFER_FORMAT_BLOB");
@@ -474,6 +535,7 @@
case AHARDWAREBUFFER_FORMAT_D32_FLOAT:
case AHARDWAREBUFFER_FORMAT_D32_FLOAT_S8_UINT:
case AHARDWAREBUFFER_FORMAT_S8_UINT:
+ case AHARDWAREBUFFER_FORMAT_Y8Cb8Cr8_420:
// VNDK formats only -- unfortunately we can't differentiate from where we're called
case AHARDWAREBUFFER_FORMAT_B8G8R8A8_UNORM:
case AHARDWAREBUFFER_FORMAT_YV12:
@@ -484,7 +546,6 @@
case AHARDWAREBUFFER_FORMAT_RAW12:
case AHARDWAREBUFFER_FORMAT_RAW_OPAQUE:
case AHARDWAREBUFFER_FORMAT_IMPLEMENTATION_DEFINED:
- case AHARDWAREBUFFER_FORMAT_Y8Cb8Cr8_420:
case AHARDWAREBUFFER_FORMAT_YCbCr_422_SP:
case AHARDWAREBUFFER_FORMAT_YCrCb_420_SP:
case AHARDWAREBUFFER_FORMAT_YCbCr_422_I:
@@ -495,6 +556,40 @@
}
}
+bool AHardwareBuffer_formatIsYuv(uint32_t format) {
+ switch (format) {
+ case AHARDWAREBUFFER_FORMAT_Y8Cb8Cr8_420:
+ case AHARDWAREBUFFER_FORMAT_YV12:
+ case AHARDWAREBUFFER_FORMAT_Y8:
+ case AHARDWAREBUFFER_FORMAT_Y16:
+ case AHARDWAREBUFFER_FORMAT_YCbCr_422_SP:
+ case AHARDWAREBUFFER_FORMAT_YCrCb_420_SP:
+ case AHARDWAREBUFFER_FORMAT_YCbCr_422_I:
+ return true;
+ default:
+ return false;
+ }
+}
+
+uint32_t AHardwareBuffer_bytesPerPixel(uint32_t format) {
+ switch (format) {
+ case AHARDWAREBUFFER_FORMAT_R5G6B5_UNORM:
+ case AHARDWAREBUFFER_FORMAT_D16_UNORM:
+ return 2;
+ case AHARDWAREBUFFER_FORMAT_R8G8B8_UNORM:
+ case AHARDWAREBUFFER_FORMAT_D24_UNORM:
+ return 3;
+ case AHARDWAREBUFFER_FORMAT_R8G8B8A8_UNORM:
+ case AHARDWAREBUFFER_FORMAT_R8G8B8X8_UNORM:
+ case AHARDWAREBUFFER_FORMAT_D32_FLOAT:
+ case AHARDWAREBUFFER_FORMAT_R10G10B10A2_UNORM:
+ case AHARDWAREBUFFER_FORMAT_D24_UNORM_S8_UINT:
+ return 4;
+ default:
+ return 0;
+ }
+}
+
uint32_t AHardwareBuffer_convertFromPixelFormat(uint32_t hal_format) {
return hal_format;
}
diff --git a/libs/nativewindow/include-private/private/android/AHardwareBufferHelpers.h b/libs/nativewindow/include-private/private/android/AHardwareBufferHelpers.h
index bf688f8..ddfd1d1 100644
--- a/libs/nativewindow/include-private/private/android/AHardwareBufferHelpers.h
+++ b/libs/nativewindow/include-private/private/android/AHardwareBufferHelpers.h
@@ -40,6 +40,12 @@
// whether this AHardwareBuffer format is valid
bool AHardwareBuffer_isValidPixelFormat(uint32_t ahardwarebuffer_format);
+// whether this is a YUV type format
+bool AHardwareBuffer_formatIsYuv(uint32_t format);
+
+// number of bytes per pixel or 0 if unknown or multi-planar
+uint32_t AHardwareBuffer_bytesPerPixel(uint32_t format);
+
// convert AHardwareBuffer format to HAL format (note: this is a no-op)
uint32_t AHardwareBuffer_convertFromPixelFormat(uint32_t format);
diff --git a/libs/nativewindow/include/android/hardware_buffer.h b/libs/nativewindow/include/android/hardware_buffer.h
index 2796c75..02c7c1b 100644
--- a/libs/nativewindow/include/android/hardware_buffer.h
+++ b/libs/nativewindow/include/android/hardware_buffer.h
@@ -150,6 +150,14 @@
* OpenGL ES: GL_STENCIL_INDEX8
*/
AHARDWAREBUFFER_FORMAT_S8_UINT = 0x35,
+
+ /**
+ * YUV 420 888 format.
+ * Must have an even width and height. Can be accessed in OpenGL
+ * shaders through an external sampler. Does not support mip-maps
+ * cube-maps or multi-layered textures.
+ */
+ AHARDWAREBUFFER_FORMAT_Y8Cb8Cr8_420 = 0x23,
};
/**
@@ -302,6 +310,24 @@
} AHardwareBuffer_Desc;
/**
+ * Holds data for a single image plane.
+ */
+typedef struct AHardwareBuffer_Plane {
+ void* data; ///< Points to first byte in plane
+ uint32_t pixelStride; ///< Distance in bytes from the color channel of one pixel to the next
+ uint32_t rowStride; ///< Distance in bytes from the first value of one row of the image to
+ /// the first value of the next row.
+} AHardwareBuffer_Plane;
+
+/**
+ * Holds all image planes that contain the pixel data.
+ */
+typedef struct AHardwareBuffer_Planes {
+ uint32_t planeCount; ///< Number of distinct planes
+ AHardwareBuffer_Plane planes[4]; ///< Array of image planes
+} AHardwareBuffer_Planes;
+
+/**
* Opaque handle for a native hardware buffer.
*/
typedef struct AHardwareBuffer AHardwareBuffer;
@@ -323,7 +349,7 @@
AHardwareBuffer** outBuffer) __INTRODUCED_IN(26);
/**
* Acquire a reference on the given AHardwareBuffer object.
- *
+ *
* This prevents the object from being deleted until the last reference
* is removed.
*/
@@ -396,6 +422,34 @@
int32_t fence, const ARect* rect, void** outVirtualAddress) __INTRODUCED_IN(26);
/**
+ * Lock a potentially multi-planar AHardwareBuffer for direct CPU access.
+ *
+ * This function is similar to AHardwareBuffer_lock, but can lock multi-planar
+ * formats. The locked planes are returned in the \a outPlanes argument. Note,
+ * that multi-planar should not be confused with multi-layer images, which this
+ * locking function does not support.
+ *
+ * YUV formats are always represented by three separate planes of data, one for
+ * each color plane. The order of planes in the array is guaranteed such that
+ * plane #0 is always Y, plane #1 is always U (Cb), and plane #2 is always V
+ * (Cr). All other formats are represented by a single plane.
+ *
+ * Additional information always accompanies the buffers, describing the row
+ * stride and the pixel stride for each plane.
+ *
+ * In case the buffer cannot be locked, \a outPlanes will contain zero planes.
+ *
+ * See the AHardwareBuffer_lock documentation for all other locking semantics.
+ *
+ * \return 0 on success. -EINVAL if \a buffer is NULL, the usage flags
+ * are not a combination of AHARDWAREBUFFER_USAGE_CPU_*, or the buffer
+ * has more than one layer. Error number if the lock fails for any other
+ * reason.
+ */
+int AHardwareBuffer_lockPlanes(AHardwareBuffer* buffer, uint64_t usage,
+ int32_t fence, const ARect* rect, AHardwareBuffer_Planes* outPlanes) __INTRODUCED_IN(29);
+
+/**
* Unlock the AHardwareBuffer from direct CPU access.
*
* Must be called after all changes to the buffer are completed by the
diff --git a/libs/nativewindow/include/vndk/hardware_buffer.h b/libs/nativewindow/include/vndk/hardware_buffer.h
index 6c9ec34..3392d7f 100644
--- a/libs/nativewindow/include/vndk/hardware_buffer.h
+++ b/libs/nativewindow/include/vndk/hardware_buffer.h
@@ -73,8 +73,6 @@
AHARDWAREBUFFER_FORMAT_RAW_OPAQUE = 0x24,
/* same as HAL_PIXEL_FORMAT_IMPLEMENTATION_DEFINED */
AHARDWAREBUFFER_FORMAT_IMPLEMENTATION_DEFINED = 0x22,
- /* same as HAL_PIXEL_FORMAT_YCBCR_420_888 */
- AHARDWAREBUFFER_FORMAT_Y8Cb8Cr8_420 = 0x23,
/* same as HAL_PIXEL_FORMAT_YCBCR_422_SP */
AHARDWAREBUFFER_FORMAT_YCbCr_422_SP = 0x10,
/* same as HAL_PIXEL_FORMAT_YCRCB_420_SP */
diff --git a/libs/ui/BufferHubBuffer.cpp b/libs/ui/BufferHubBuffer.cpp
index 7693fcb..604665b 100644
--- a/libs/ui/BufferHubBuffer.cpp
+++ b/libs/ui/BufferHubBuffer.cpp
@@ -47,8 +47,8 @@
return buffer->isValid() ? std::move(buffer) : nullptr;
}
-std::unique_ptr<BufferHubBuffer> BufferHubBuffer::import(const native_handle_t* token) {
- if (token == nullptr) {
+std::unique_ptr<BufferHubBuffer> BufferHubBuffer::import(const sp<NativeHandle>& token) {
+ if (token == nullptr || token.get() == nullptr) {
ALOGE("%s: token cannot be nullptr!", __FUNCTION__);
return nullptr;
}
@@ -79,10 +79,10 @@
sp<IBufferClient> client;
BufferTraits bufferTraits;
IBufferHub::allocateBuffer_cb alloc_cb = [&](const auto& status, const auto& outClient,
- const auto& traits) {
+ const auto& outTraits) {
ret = status;
client = std::move(outClient);
- bufferTraits = std::move(traits);
+ bufferTraits = std::move(outTraits);
};
if (!bufferhub->allocateBuffer(desc, static_cast<uint32_t>(userMetadataSize), alloc_cb)
@@ -105,7 +105,7 @@
mBufferClient = std::move(client);
}
-BufferHubBuffer::BufferHubBuffer(const native_handle_t* token) {
+BufferHubBuffer::BufferHubBuffer(const sp<NativeHandle>& token) {
sp<IBufferHub> bufferhub = IBufferHub::getService();
if (bufferhub.get() == nullptr) {
ALOGE("%s: BufferHub service not found!", __FUNCTION__);
@@ -116,15 +116,15 @@
sp<IBufferClient> client;
BufferTraits bufferTraits;
IBufferHub::importBuffer_cb import_cb = [&](const auto& status, const auto& outClient,
- const auto& traits) {
+ const auto& outTraits) {
ret = status;
client = std::move(outClient);
- bufferTraits = std::move(traits);
+ bufferTraits = std::move(outTraits);
};
// hidl_handle(native_handle_t*) simply creates a raw pointer reference withouth ownership
// transfer.
- if (!bufferhub->importBuffer(hidl_handle(token), import_cb).isOk()) {
+ if (!bufferhub->importBuffer(hidl_handle(token.get()->handle()), import_cb).isOk()) {
ALOGE("%s: importBuffer transaction failed!", __FUNCTION__);
return;
} else if (ret != BufferHubStatus::NO_ERROR) {
@@ -209,10 +209,10 @@
}
// Populate shortcuts to the atomics in metadata.
- auto metadata_header = mMetadata.metadataHeader();
- mBufferState = &metadata_header->buffer_state;
- mFenceState = &metadata_header->fence_state;
- mActiveClientsBitMask = &metadata_header->active_clients_bit_mask;
+ auto metadataHeader = mMetadata.metadataHeader();
+ mBufferState = &metadataHeader->buffer_state;
+ mFenceState = &metadataHeader->fence_state;
+ mActiveClientsBitMask = &metadataHeader->active_clients_bit_mask;
// The C++ standard recommends (but does not require) that lock-free atomic operations are
// also address-free, that is, suitable for communication between processes using shared
// memory.
@@ -328,7 +328,7 @@
mEventFd.get() >= 0 && mMetadata.isValid() && mBufferClient != nullptr;
}
-native_handle_t* BufferHubBuffer::duplicate() {
+sp<NativeHandle> BufferHubBuffer::duplicate() {
if (mBufferClient == nullptr) {
ALOGE("%s: missing BufferClient!", __FUNCTION__);
return nullptr;
@@ -352,7 +352,7 @@
return nullptr;
}
- return native_handle_clone(token.getNativeHandle());
+ return NativeHandle::create(native_handle_clone(token.getNativeHandle()), /*ownsHandle=*/true);
}
} // namespace android
diff --git a/libs/ui/include/ui/BufferHubBuffer.h b/libs/ui/include/ui/BufferHubBuffer.h
index eac8c84..5c09032 100644
--- a/libs/ui/include/ui/BufferHubBuffer.h
+++ b/libs/ui/include/ui/BufferHubBuffer.h
@@ -23,6 +23,7 @@
#include <ui/BufferHubDefs.h>
#include <ui/BufferHubEventFd.h>
#include <ui/BufferHubMetadata.h>
+#include <utils/NativeHandle.h>
namespace android {
@@ -33,10 +34,8 @@
uint32_t layerCount, uint32_t format,
uint64_t usage, size_t userMetadataSize);
- // Imports the given token to a BufferHubBuffer. Not taking ownership of the token. Caller
- // should close and destroy the token after calling this function regardless of output.
- // TODO(b/122543147): use a movable wrapper for token
- static std::unique_ptr<BufferHubBuffer> import(const native_handle_t* token);
+ // Imports the given token to a BufferHubBuffer. Not taking ownership of the token.
+ static std::unique_ptr<BufferHubBuffer> import(const sp<NativeHandle>& token);
BufferHubBuffer(const BufferHubBuffer&) = delete;
void operator=(const BufferHubBuffer&) = delete;
@@ -100,17 +99,15 @@
// Creates a token that stands for this BufferHubBuffer client and could be used for Import to
// create another BufferHubBuffer. The new BufferHubBuffer will share the same underlying
- // gralloc buffer and ashmem region for metadata. Note that the caller owns the token and
- // should free it after use.
+ // gralloc buffer and ashmem region for metadata. Not taking ownership of the token.
// Returns a valid token on success, nullptr on failure.
- // TODO(b/122543147): use a movable wrapper for token
- native_handle_t* duplicate();
+ sp<NativeHandle> duplicate();
private:
BufferHubBuffer(uint32_t width, uint32_t height, uint32_t layerCount, uint32_t format,
uint64_t usage, size_t userMetadataSize);
- BufferHubBuffer(const native_handle_t* token);
+ BufferHubBuffer(const sp<NativeHandle>& token);
int initWithBufferTraits(const frameworks::bufferhub::V1_0::BufferTraits& bufferTraits);
diff --git a/libs/ui/tests/BufferHubBuffer_test.cpp b/libs/ui/tests/BufferHubBuffer_test.cpp
index 3087a90..634bce1 100644
--- a/libs/ui/tests/BufferHubBuffer_test.cpp
+++ b/libs/ui/tests/BufferHubBuffer_test.cpp
@@ -86,13 +86,10 @@
b1ClientMask = b1->clientStateMask();
ASSERT_NE(b1ClientMask, 0U);
- native_handle_t* token = b1->duplicate();
+ sp<NativeHandle> token = b1->duplicate();
ASSERT_THAT(token, NotNull());
- // TODO(b/122543147): use a movalbe wrapper for token
b2 = BufferHubBuffer::import(token);
- native_handle_close(token);
- native_handle_delete(token);
ASSERT_THAT(b2, NotNull());
b2ClientMask = b2->clientStateMask();
@@ -137,16 +134,14 @@
ASSERT_THAT(b1, NotNull());
EXPECT_TRUE(b1->isValid());
- native_handle_t* token = b1->duplicate();
- EXPECT_TRUE(token);
+ sp<NativeHandle> token = b1->duplicate();
+ ASSERT_THAT(token, NotNull());
// The detached buffer should still be valid.
EXPECT_TRUE(b1->isConnected());
EXPECT_TRUE(b1->isValid());
std::unique_ptr<BufferHubBuffer> b2 = BufferHubBuffer::import(token);
- native_handle_close(token);
- native_handle_delete(token);
ASSERT_THAT(b2, NotNull());
EXPECT_TRUE(b2->isValid());
@@ -197,16 +192,13 @@
ASSERT_THAT(b1, NotNull());
EXPECT_TRUE(b1->isValid());
- native_handle_t* token = b1->duplicate();
- EXPECT_TRUE(token);
+ sp<NativeHandle> token = b1->duplicate();
+ ASSERT_THAT(token, NotNull());
// Explicitly destroy b1. Backend buffer should be freed and token becomes invalid
b1.reset();
- // TODO(b/122543147): use a movalbe wrapper for token
std::unique_ptr<BufferHubBuffer> b2 = BufferHubBuffer::import(token);
- native_handle_close(token);
- native_handle_delete(token);
// Import should fail with INVALID_TOKEN
EXPECT_THAT(b2, IsNull());
@@ -222,7 +214,7 @@
native_handle_t* token = native_handle_create(/*numFds=*/0, /*numInts=*/1);
token->data[0] = 0;
- auto b1 = BufferHubBuffer::import(token);
+ auto b1 = BufferHubBuffer::import(NativeHandle::create(token, /*ownHandle=*/true));
native_handle_delete(token);
EXPECT_THAT(b1, IsNull());
@@ -425,13 +417,10 @@
// Create a consumer of the buffer and test if the consumer can acquire the
// buffer if producer posts.
- // TODO(b/122543147): use a movalbe wrapper for token
- native_handle_t* token = b1->duplicate();
- ASSERT_TRUE(token);
+ sp<NativeHandle> token = b1->duplicate();
+ ASSERT_THAT(token, NotNull());
std::unique_ptr<BufferHubBuffer> b2 = BufferHubBuffer::import(token);
- native_handle_close(token);
- native_handle_delete(token);
ASSERT_THAT(b2, NotNull());
ASSERT_NE(b1->clientStateMask(), b2->clientStateMask());
@@ -450,13 +439,10 @@
// Create a consumer of the buffer and test if the consumer can acquire the
// buffer if producer posts.
- // TODO(b/122543147): use a movalbe wrapper for token
- native_handle_t* token = b1->duplicate();
- ASSERT_TRUE(token);
+ sp<NativeHandle> token = b1->duplicate();
+ ASSERT_THAT(token, NotNull());
std::unique_ptr<BufferHubBuffer> b2 = BufferHubBuffer::import(token);
- native_handle_close(token);
- native_handle_delete(token);
ASSERT_THAT(b2, NotNull());
ASSERT_NE(b1->clientStateMask(), b2->clientStateMask());
diff --git a/services/bufferhub/BufferHubService.cpp b/services/bufferhub/BufferHubService.cpp
index 90ac1c2..ade08e7 100644
--- a/services/bufferhub/BufferHubService.cpp
+++ b/services/bufferhub/BufferHubService.cpp
@@ -73,7 +73,13 @@
buildBufferInfo(bufferInfoStorage, node->id(), node->addNewActiveClientsBitToMask(),
node->userMetadataSize(), node->metadata().ashmemFd(),
node->eventFd().get());
- BufferTraits bufferTraits = {/*bufferDesc=*/description,
+ // During the gralloc allocation carried out by BufferNode, gralloc allocator will populate the
+ // fields of its HardwareBufferDescription (i.e. strides) according to the actual
+ // gralloc implementation. We need to read those fields back and send them to the client via
+ // BufferTraits.
+ HardwareBufferDescription allocatedBufferDesc;
+ memcpy(&allocatedBufferDesc, &node->bufferDesc(), sizeof(AHardwareBuffer_Desc));
+ BufferTraits bufferTraits = {/*bufferDesc=*/allocatedBufferDesc,
/*bufferHandle=*/hidl_handle(node->bufferHandle()),
/*bufferInfo=*/std::move(bufferInfo)};
diff --git a/services/bufferhub/BufferNode.cpp b/services/bufferhub/BufferNode.cpp
index 4f877b2..1efb27e 100644
--- a/services/bufferhub/BufferNode.cpp
+++ b/services/bufferhub/BufferNode.cpp
@@ -28,17 +28,17 @@
}
// Allocates a new BufferNode.
-BufferNode::BufferNode(uint32_t width, uint32_t height, uint32_t layer_count, uint32_t format,
- uint64_t usage, size_t user_metadata_size, int id)
+BufferNode::BufferNode(uint32_t width, uint32_t height, uint32_t layerCount, uint32_t format,
+ uint64_t usage, size_t userMetadataSize, int id)
: mId(id) {
- uint32_t out_stride = 0;
+ uint32_t outStride = 0;
// graphicBufferId is not used in GraphicBufferAllocator::allocate
// TODO(b/112338294) After move to the service folder, stop using the
// hardcoded service name "bufferhub".
- int ret = GraphicBufferAllocator::get().allocate(width, height, format, layer_count, usage,
+ int ret = GraphicBufferAllocator::get().allocate(width, height, format, layerCount, usage,
const_cast<const native_handle_t**>(
&mBufferHandle),
- &out_stride,
+ &outStride,
/*graphicBufferId=*/0,
/*requestor=*/"bufferhub");
@@ -49,12 +49,12 @@
mBufferDesc.width = width;
mBufferDesc.height = height;
- mBufferDesc.layers = layer_count;
+ mBufferDesc.layers = layerCount;
mBufferDesc.format = format;
mBufferDesc.usage = usage;
- mBufferDesc.stride = out_stride;
+ mBufferDesc.stride = outStride;
- mMetadata = BufferHubMetadata::create(user_metadata_size);
+ mMetadata = BufferHubMetadata::create(userMetadataSize);
if (!mMetadata.isValid()) {
ALOGE("%s: Failed to allocate metadata.", __FUNCTION__);
return;
@@ -83,23 +83,23 @@
uint32_t BufferNode::addNewActiveClientsBitToMask() {
uint32_t currentActiveClientsBitMask = getActiveClientsBitMask();
- uint32_t client_state_mask = 0U;
+ uint32_t clientStateMask = 0U;
uint32_t updatedActiveClientsBitMask = 0U;
do {
- client_state_mask =
+ clientStateMask =
BufferHubDefs::findNextAvailableClientStateMask(currentActiveClientsBitMask);
- if (client_state_mask == 0U) {
+ if (clientStateMask == 0U) {
ALOGE("%s: reached the maximum number of channels per buffer node: %d.", __FUNCTION__,
BufferHubDefs::kMaxNumberOfClients);
errno = E2BIG;
return 0U;
}
- updatedActiveClientsBitMask = currentActiveClientsBitMask | client_state_mask;
+ updatedActiveClientsBitMask = currentActiveClientsBitMask | clientStateMask;
} while (!(mActiveClientsBitMask->compare_exchange_weak(currentActiveClientsBitMask,
updatedActiveClientsBitMask,
std::memory_order_acq_rel,
std::memory_order_acquire)));
- return client_state_mask;
+ return clientStateMask;
}
void BufferNode::removeClientsBitFromMask(const uint32_t& value) {
diff --git a/services/bufferhub/include/bufferhub/BufferClient.h b/services/bufferhub/include/bufferhub/BufferClient.h
index 66ed4bd..644b403 100644
--- a/services/bufferhub/include/bufferhub/BufferClient.h
+++ b/services/bufferhub/include/bufferhub/BufferClient.h
@@ -72,4 +72,4 @@
} // namespace frameworks
} // namespace android
-#endif
\ No newline at end of file
+#endif
diff --git a/services/bufferhub/include/bufferhub/BufferNode.h b/services/bufferhub/include/bufferhub/BufferNode.h
index 04970fd..62a8d63 100644
--- a/services/bufferhub/include/bufferhub/BufferNode.h
+++ b/services/bufferhub/include/bufferhub/BufferNode.h
@@ -16,8 +16,8 @@
class BufferNode {
public:
// Allocates a new BufferNode.
- BufferNode(uint32_t width, uint32_t height, uint32_t layer_count, uint32_t format,
- uint64_t usage, size_t user_metadata_size, int id = -1);
+ BufferNode(uint32_t width, uint32_t height, uint32_t layerCount, uint32_t format,
+ uint64_t usage, size_t userMetadataSize, int id = -1);
~BufferNode();
diff --git a/services/bufferhub/tests/BufferNode_test.cpp b/services/bufferhub/tests/BufferNode_test.cpp
index b9f1c81..2dfd4fc 100644
--- a/services/bufferhub/tests/BufferNode_test.cpp
+++ b/services/bufferhub/tests/BufferNode_test.cpp
@@ -26,79 +26,78 @@
class BufferNodeTest : public ::testing::Test {
protected:
void SetUp() override {
- buffer_node =
+ mBufferNode =
new BufferNode(kWidth, kHeight, kLayerCount, kFormat, kUsage, kUserMetadataSize);
- ASSERT_TRUE(buffer_node->isValid());
+ ASSERT_TRUE(mBufferNode->isValid());
}
void TearDown() override {
- if (buffer_node != nullptr) {
- delete buffer_node;
+ if (mBufferNode != nullptr) {
+ delete mBufferNode;
}
}
- BufferNode* buffer_node = nullptr;
+ BufferNode* mBufferNode = nullptr;
};
TEST_F(BufferNodeTest, TestCreateBufferNode) {
- EXPECT_EQ(buffer_node->userMetadataSize(), kUserMetadataSize);
+ EXPECT_EQ(mBufferNode->userMetadataSize(), kUserMetadataSize);
// Test the handle just allocated is good (i.e. able to be imported)
GraphicBufferMapper& mapper = GraphicBufferMapper::get();
const native_handle_t* outHandle;
status_t ret =
- mapper.importBuffer(buffer_node->bufferHandle(), buffer_node->bufferDesc().width,
- buffer_node->bufferDesc().height, buffer_node->bufferDesc().layers,
- buffer_node->bufferDesc().format, buffer_node->bufferDesc().usage,
- buffer_node->bufferDesc().stride, &outHandle);
+ mapper.importBuffer(mBufferNode->bufferHandle(), mBufferNode->bufferDesc().width,
+ mBufferNode->bufferDesc().height, mBufferNode->bufferDesc().layers,
+ mBufferNode->bufferDesc().format, mBufferNode->bufferDesc().usage,
+ mBufferNode->bufferDesc().stride, &outHandle);
EXPECT_EQ(ret, OK);
EXPECT_THAT(outHandle, NotNull());
}
TEST_F(BufferNodeTest, TestaddNewActiveClientsBitToMask_twoNewClients) {
- uint32_t new_client_state_mask_1 = buffer_node->addNewActiveClientsBitToMask();
- EXPECT_EQ(buffer_node->getActiveClientsBitMask(), new_client_state_mask_1);
+ uint32_t newClientStateMask1 = mBufferNode->addNewActiveClientsBitToMask();
+ EXPECT_EQ(mBufferNode->getActiveClientsBitMask(), newClientStateMask1);
// Request and add a new client_state_mask again.
// Active clients bit mask should be the union of the two new
// client_state_masks.
- uint32_t new_client_state_mask_2 = buffer_node->addNewActiveClientsBitToMask();
- EXPECT_EQ(buffer_node->getActiveClientsBitMask(),
- new_client_state_mask_1 | new_client_state_mask_2);
+ uint32_t newClientStateMask2 = mBufferNode->addNewActiveClientsBitToMask();
+ EXPECT_EQ(mBufferNode->getActiveClientsBitMask(), newClientStateMask1 | newClientStateMask2);
}
TEST_F(BufferNodeTest, TestaddNewActiveClientsBitToMask_32NewClients) {
- uint32_t new_client_state_mask = 0U;
- uint32_t current_mask = 0U;
- uint32_t expected_mask = 0U;
+ uint32_t newClientStateMask = 0U;
+ uint32_t currentMask = 0U;
+ uint32_t expectedMask = 0U;
for (int i = 0; i < BufferHubDefs::kMaxNumberOfClients; ++i) {
- new_client_state_mask = buffer_node->addNewActiveClientsBitToMask();
- EXPECT_NE(new_client_state_mask, 0U);
- EXPECT_FALSE(new_client_state_mask & current_mask);
- expected_mask = current_mask | new_client_state_mask;
- current_mask = buffer_node->getActiveClientsBitMask();
- EXPECT_EQ(current_mask, expected_mask);
+ newClientStateMask = mBufferNode->addNewActiveClientsBitToMask();
+ EXPECT_NE(newClientStateMask, 0U);
+ EXPECT_FALSE(newClientStateMask & currentMask);
+ expectedMask = currentMask | newClientStateMask;
+ currentMask = mBufferNode->getActiveClientsBitMask();
+ EXPECT_EQ(currentMask, expectedMask);
}
// Method should fail upon requesting for more than maximum allowable clients.
- new_client_state_mask = buffer_node->addNewActiveClientsBitToMask();
- EXPECT_EQ(new_client_state_mask, 0U);
+ newClientStateMask = mBufferNode->addNewActiveClientsBitToMask();
+ EXPECT_EQ(newClientStateMask, 0U);
EXPECT_EQ(errno, E2BIG);
}
TEST_F(BufferNodeTest, TestRemoveActiveClientsBitFromMask) {
- buffer_node->addNewActiveClientsBitToMask();
- uint32_t current_mask = buffer_node->getActiveClientsBitMask();
- uint32_t new_client_state_mask = buffer_node->addNewActiveClientsBitToMask();
- EXPECT_NE(buffer_node->getActiveClientsBitMask(), current_mask);
+ mBufferNode->addNewActiveClientsBitToMask();
+ uint32_t currentMask = mBufferNode->getActiveClientsBitMask();
+ uint32_t newClientStateMask = mBufferNode->addNewActiveClientsBitToMask();
+ EXPECT_NE(mBufferNode->getActiveClientsBitMask(), currentMask);
- buffer_node->removeClientsBitFromMask(new_client_state_mask);
- EXPECT_EQ(buffer_node->getActiveClientsBitMask(), current_mask);
+ mBufferNode->removeClientsBitFromMask(newClientStateMask);
+ EXPECT_EQ(mBufferNode->getActiveClientsBitMask(), currentMask);
// Remove the test_mask again to the active client bit mask should not modify
// the value of active clients bit mask.
- buffer_node->removeClientsBitFromMask(new_client_state_mask);
- EXPECT_EQ(buffer_node->getActiveClientsBitMask(), current_mask);
+ mBufferNode->removeClientsBitFromMask(newClientStateMask);
+ EXPECT_EQ(mBufferNode->getActiveClientsBitMask(), currentMask);
}
} // namespace
diff --git a/services/gpuservice/GpuService.cpp b/services/gpuservice/GpuService.cpp
index 9906dea..68c185c 100644
--- a/services/gpuservice/GpuService.cpp
+++ b/services/gpuservice/GpuService.cpp
@@ -37,9 +37,9 @@
GpuService::GpuService() = default;
-void GpuService::setGpuStats(const std::string driverPackageName,
- const std::string driverVersionName, const uint64_t driverVersionCode,
- const std::string appPackageName) {
+void GpuService::setGpuStats(const std::string& driverPackageName,
+ const std::string& driverVersionName, const uint64_t driverVersionCode,
+ const std::string& appPackageName) {
ATRACE_CALL();
std::lock_guard<std::mutex> lock(mStateLock);
diff --git a/services/gpuservice/GpuService.h b/services/gpuservice/GpuService.h
index edfd364..5304fa1 100644
--- a/services/gpuservice/GpuService.h
+++ b/services/gpuservice/GpuService.h
@@ -37,8 +37,8 @@
private:
// IGpuService interface
- void setGpuStats(const std::string driverPackageName, const std::string driverVersionName,
- const uint64_t driverVersionCode, const std::string appPackageName);
+ void setGpuStats(const std::string& driverPackageName, const std::string& driverVersionName,
+ const uint64_t driverVersionCode, const std::string& appPackageName);
// GpuStats access must be protected by mStateLock
std::mutex mStateLock;
diff --git a/services/inputflinger/InputClassifier.cpp b/services/inputflinger/InputClassifier.cpp
index 09a004c..3905ed0 100644
--- a/services/inputflinger/InputClassifier.cpp
+++ b/services/inputflinger/InputClassifier.cpp
@@ -705,13 +705,12 @@
}
void InputClassifier::notifyMotion(const NotifyMotionArgs* args) {
+ NotifyMotionArgs copyArgs = NotifyMotionArgs(*args);
if (mMotionClassifier && isTouchEvent(*args)) {
// We only cover touch events, for now.
- NotifyMotionArgs newArgs = NotifyMotionArgs(*args);
- newArgs.classification = mMotionClassifier->classify(newArgs);
- args = &newArgs;
+ copyArgs.classification = mMotionClassifier->classify(copyArgs);
}
- mListener->notifyMotion(args);
+ mListener->notifyMotion(©Args);
}
void InputClassifier::notifySwitch(const NotifySwitchArgs* args) {
diff --git a/services/surfaceflinger/BufferLayer.cpp b/services/surfaceflinger/BufferLayer.cpp
index 63748bf..89eee6b 100644
--- a/services/surfaceflinger/BufferLayer.cpp
+++ b/services/surfaceflinger/BufferLayer.cpp
@@ -206,7 +206,7 @@
memcpy(textureMatrix, texTransform.asArray(), sizeof(textureMatrix));
}
- const Rect win{computeBounds()};
+ const Rect win{getBounds()};
const float bufferWidth = getBufferSize(s).getWidth();
const float bufferHeight = getBufferSize(s).getHeight();
@@ -654,6 +654,38 @@
return mCompositionLayer;
}
+FloatRect BufferLayer::computeSourceBounds(const FloatRect& parentBounds) const {
+ const State& s(getDrawingState());
+
+ // If we have a sideband stream, or we are scaling the buffer then return the layer size since
+ // we cannot determine the buffer size.
+ if ((s.sidebandStream != nullptr) ||
+ (getEffectiveScalingMode() != NATIVE_WINDOW_SCALING_MODE_FREEZE)) {
+ return FloatRect(0, 0, getActiveWidth(s), getActiveHeight(s));
+ }
+
+ if (mActiveBuffer == nullptr) {
+ return parentBounds;
+ }
+
+ uint32_t bufWidth = mActiveBuffer->getWidth();
+ uint32_t bufHeight = mActiveBuffer->getHeight();
+
+ // Undo any transformations on the buffer and return the result.
+ if (mCurrentTransform & ui::Transform::ROT_90) {
+ std::swap(bufWidth, bufHeight);
+ }
+
+ if (getTransformToDisplayInverse()) {
+ uint32_t invTransform = DisplayDevice::getPrimaryDisplayOrientationTransform();
+ if (invTransform & ui::Transform::ROT_90) {
+ std::swap(bufWidth, bufHeight);
+ }
+ }
+
+ return FloatRect(0, 0, bufWidth, bufHeight);
+}
+
} // namespace android
#if defined(__gl_h_)
diff --git a/services/surfaceflinger/BufferLayer.h b/services/surfaceflinger/BufferLayer.h
index c118b78..0f8a350 100644
--- a/services/surfaceflinger/BufferLayer.h
+++ b/services/surfaceflinger/BufferLayer.h
@@ -191,6 +191,8 @@
Rect getBufferSize(const State& s) const override;
std::shared_ptr<compositionengine::Layer> mCompositionLayer;
+
+ FloatRect computeSourceBounds(const FloatRect& parentBounds) const override;
};
} // namespace android
diff --git a/services/surfaceflinger/BufferQueueLayer.cpp b/services/surfaceflinger/BufferQueueLayer.cpp
index 3915757..0313c32 100644
--- a/services/surfaceflinger/BufferQueueLayer.cpp
+++ b/services/surfaceflinger/BufferQueueLayer.cpp
@@ -232,7 +232,7 @@
getTransformToDisplayInverse(), mFreezeGeometryUpdates);
const nsecs_t expectedPresentTime = mFlinger->mUseScheduler
- ? mFlinger->mScheduler->mPrimaryDispSync->expectedPresentTime()
+ ? mFlinger->mScheduler->expectedPresentTime()
: mFlinger->mPrimaryDispSync->expectedPresentTime();
// updateTexImage() below might drop the some buffers at the head of the queue if there is a
diff --git a/services/surfaceflinger/BufferStateLayer.cpp b/services/surfaceflinger/BufferStateLayer.cpp
index b0506fe..2d6da76 100644
--- a/services/surfaceflinger/BufferStateLayer.cpp
+++ b/services/surfaceflinger/BufferStateLayer.cpp
@@ -314,7 +314,7 @@
// if the display frame is not defined, use the parent bounds as the buffer size.
const auto& p = mDrawingParent.promote();
if (p != nullptr) {
- Rect parentBounds = Rect(p->computeBounds(Region()));
+ Rect parentBounds = Rect(p->getBounds(Region()));
if (!parentBounds.isEmpty()) {
return parentBounds;
}
@@ -326,6 +326,18 @@
}
return Rect::INVALID_RECT;
}
+
+FloatRect BufferStateLayer::computeSourceBounds(const FloatRect& parentBounds) const {
+ const State& s(getDrawingState());
+ // for buffer state layers we use the display frame size as the buffer size.
+ if (getActiveWidth(s) < UINT32_MAX && getActiveHeight(s) < UINT32_MAX) {
+ return FloatRect(0, 0, getActiveWidth(s), getActiveHeight(s));
+ }
+
+ // if the display frame is not defined, use the parent bounds as the buffer size.
+ return parentBounds;
+}
+
// -----------------------------------------------------------------------
// -----------------------------------------------------------------------
diff --git a/services/surfaceflinger/BufferStateLayer.h b/services/surfaceflinger/BufferStateLayer.h
index ef287b9..3320c5b 100644
--- a/services/surfaceflinger/BufferStateLayer.h
+++ b/services/surfaceflinger/BufferStateLayer.h
@@ -88,6 +88,7 @@
uint64_t /*frameNumber*/) override {}
Rect getBufferSize(const State& s) const override;
+ FloatRect computeSourceBounds(const FloatRect& parentBounds) const override;
// -----------------------------------------------------------------------
// -----------------------------------------------------------------------
diff --git a/services/surfaceflinger/Layer.cpp b/services/surfaceflinger/Layer.cpp
index 46e587d..51e1f00 100644
--- a/services/surfaceflinger/Layer.cpp
+++ b/services/surfaceflinger/Layer.cpp
@@ -294,80 +294,83 @@
return Region(Rect{win}).subtract(exclude).getBounds().toFloatRect();
}
-Rect Layer::computeScreenBounds(bool reduceTransparentRegion) const {
- const State& s(getDrawingState());
- Region transparentRegion = reduceTransparentRegion ? getActiveTransparentRegion(s) : Region();
- FloatRect bounds = computeBounds(transparentRegion);
+Rect Layer::getScreenBounds(bool reduceTransparentRegion) const {
+ if (!reduceTransparentRegion) {
+ return Rect{mScreenBounds};
+ }
+
+ FloatRect bounds = getBounds();
ui::Transform t = getTransform();
// Transform to screen space.
bounds = t.transform(bounds);
return Rect{bounds};
}
-FloatRect Layer::computeBounds() const {
+FloatRect Layer::getBounds() const {
const State& s(getDrawingState());
- return computeBounds(getActiveTransparentRegion(s));
+ return getBounds(getActiveTransparentRegion(s));
}
-FloatRect Layer::computeBounds(const Region& activeTransparentRegion) const {
- const State& s(getDrawingState());
- Rect bounds = getCroppedBufferSize(s);
- FloatRect floatBounds = bounds.toFloatRect();
- if (bounds.isValid()) {
- // Layer has bounds. Pass in our bounds as a special case. Then pass on to our parents so
- // that they can clip it.
- floatBounds = cropChildBounds(floatBounds);
- } else {
- // Layer does not have bounds, so we fill to our parent bounds. This is done by getting our
- // parent bounds and inverting the transform to get the maximum bounds we can have that
- // will fit within our parent bounds.
- const auto& p = mDrawingParent.promote();
- if (p != nullptr) {
- ui::Transform t = s.active_legacy.transform;
- // When calculating the parent bounds for purposes of clipping, we don't need to
- // constrain the parent to its transparent region. The transparent region is an
- // optimization based on the buffer contents of the layer, but does not affect the
- // space allocated to it by policy, and thus children should be allowed to extend into
- // the parent's transparent region.
- // One of the main uses is a parent window with a child sitting behind the parent
- // window, marked by a transparent region. When computing the parent bounds from the
- // parent's perspective we pass in the transparent region to reduce buffer allocation
- // size. When computing the parent bounds from the child's perspective, we pass in an
- // empty transparent region in order to extend into the the parent bounds.
- floatBounds = p->computeBounds(Region());
- // Transform back to layer space.
- floatBounds = t.inverse().transform(floatBounds);
- }
- }
-
+FloatRect Layer::getBounds(const Region& activeTransparentRegion) const {
// Subtract the transparent region and snap to the bounds.
- return reduce(floatBounds, activeTransparentRegion);
+ return reduce(mBounds, activeTransparentRegion);
}
-FloatRect Layer::cropChildBounds(const FloatRect& childBounds) const {
+ui::Transform Layer::getTransformWithScale() const {
+ // If the layer is not using NATIVE_WINDOW_SCALING_MODE_FREEZE (e.g.
+ // it isFixedSize) then there may be additional scaling not accounted
+ // for in the transform. We need to mirror this scaling to child surfaces
+ // or we will break the contract where WM can treat child surfaces as
+ // pixels in the parent surface.
+ if (!isFixedSize() || !getBE().compositionInfo.mBuffer) {
+ return mEffectiveTransform;
+ }
+
+ int bufferWidth;
+ int bufferHeight;
+ if ((mCurrentTransform & NATIVE_WINDOW_TRANSFORM_ROT_90) == 0) {
+ bufferWidth = getBE().compositionInfo.mBuffer->getWidth();
+ bufferHeight = getBE().compositionInfo.mBuffer->getHeight();
+ } else {
+ bufferHeight = getBE().compositionInfo.mBuffer->getWidth();
+ bufferWidth = getBE().compositionInfo.mBuffer->getHeight();
+ }
+ float sx = getActiveWidth(getDrawingState()) / static_cast<float>(bufferWidth);
+ float sy = getActiveHeight(getDrawingState()) / static_cast<float>(bufferHeight);
+ ui::Transform extraParentScaling;
+ extraParentScaling.set(sx, 0, 0, sy);
+ return mEffectiveTransform * extraParentScaling;
+}
+
+void Layer::computeBounds(FloatRect parentBounds, ui::Transform parentTransform) {
const State& s(getDrawingState());
- Rect bounds = getCroppedBufferSize(s);
- FloatRect croppedBounds = childBounds;
- // If the layer has bounds, then crop the passed in child bounds and pass
- // it to our parents so they can crop it as well. If the layer has no bounds,
- // then pass on the child bounds.
- if (bounds.isValid()) {
- croppedBounds = croppedBounds.intersect(bounds.toFloatRect());
- }
+ // Calculate effective layer transform
+ mEffectiveTransform = parentTransform * getActiveTransform(s);
- const auto& p = mDrawingParent.promote();
- if (p != nullptr) {
- // Transform to parent space and allow parent layer to crop the
- // child bounds as well.
- ui::Transform t = s.active_legacy.transform;
- croppedBounds = t.transform(croppedBounds);
- croppedBounds = p->cropChildBounds(croppedBounds);
- croppedBounds = t.inverse().transform(croppedBounds);
+ // Transform parent bounds to layer space
+ parentBounds = getActiveTransform(s).inverse().transform(parentBounds);
+
+ // Calculate display frame
+ mSourceBounds = computeSourceBounds(parentBounds);
+
+ // Calculate bounds by croping diplay frame with layer crop and parent bounds
+ FloatRect bounds = mSourceBounds;
+ const Rect layerCrop = getCrop(s);
+ if (!layerCrop.isEmpty()) {
+ bounds = mSourceBounds.intersect(layerCrop.toFloatRect());
}
- return croppedBounds;
+ bounds = bounds.intersect(parentBounds);
+
+ mBounds = bounds;
+ mScreenBounds = mEffectiveTransform.transform(mBounds);
+ for (const sp<Layer>& child : mDrawingChildren) {
+ child->computeBounds(mBounds, getTransformWithScale());
+ }
}
+
+
Rect Layer::getCroppedBufferSize(const State& s) const {
Rect size = getBufferSize(s);
Rect crop = getCrop(s);
@@ -389,7 +392,7 @@
// if there are no window scaling involved, this operation will map to full
// pixels in the buffer.
- FloatRect activeCropFloat = computeBounds();
+ FloatRect activeCropFloat = getBounds();
ui::Transform t = getTransform();
// Transform to screen space.
activeCropFloat = t.transform(activeCropFloat);
@@ -550,9 +553,9 @@
Rect(activeCrop.right, activeCrop.top, bufferSize.getWidth(), activeCrop.bottom));
}
- // computeBounds returns a FloatRect to provide more accuracy during the
+ // getBounds returns a FloatRect to provide more accuracy during the
// transformation. We then round upon constructing 'frame'.
- Rect frame{t.transform(computeBounds(activeTransparentRegion))};
+ Rect frame{t.transform(getBounds(activeTransparentRegion))};
if (!frame.intersect(display->getViewport(), &frame)) {
frame.clear();
}
@@ -721,7 +724,7 @@
bool Layer::prepareClientLayer(const RenderArea& /*renderArea*/, const Region& /*clip*/,
bool useIdentityTransform, Region& /*clearRegion*/,
renderengine::LayerSettings& layer) {
- FloatRect bounds = computeBounds();
+ FloatRect bounds = getBounds();
half alpha = getAlpha();
layer.geometry.boundaries = bounds;
if (useIdentityTransform) {
@@ -841,7 +844,7 @@
renderengine::Mesh& mesh,
bool useIdentityTransform) const {
const ui::Transform renderAreaTransform(renderArea.getTransform());
- FloatRect win = computeBounds();
+ FloatRect win = getBounds();
vec2 lt = vec2(win.left, win.top);
vec2 lb = vec2(win.left, win.bottom);
@@ -1680,6 +1683,7 @@
void Layer::setChildrenDrawingParent(const sp<Layer>& newParent) {
for (const sp<Layer>& child : mDrawingChildren) {
child->mDrawingParent = newParent;
+ child->computeBounds(newParent->mBounds, newParent->getTransformWithScale());
}
}
@@ -2007,34 +2011,7 @@
}
ui::Transform Layer::getTransform() const {
- ui::Transform t;
- const auto& p = mDrawingParent.promote();
- if (p != nullptr) {
- t = p->getTransform();
-
- // If the parent is not using NATIVE_WINDOW_SCALING_MODE_FREEZE (e.g.
- // it isFixedSize) then there may be additional scaling not accounted
- // for in the transform. We need to mirror this scaling in child surfaces
- // or we will break the contract where WM can treat child surfaces as
- // pixels in the parent surface.
- if (p->isFixedSize() && p->getBE().compositionInfo.mBuffer != nullptr) {
- int bufferWidth;
- int bufferHeight;
- if ((p->mCurrentTransform & NATIVE_WINDOW_TRANSFORM_ROT_90) == 0) {
- bufferWidth = p->getBE().compositionInfo.mBuffer->getWidth();
- bufferHeight = p->getBE().compositionInfo.mBuffer->getHeight();
- } else {
- bufferHeight = p->getBE().compositionInfo.mBuffer->getWidth();
- bufferWidth = p->getBE().compositionInfo.mBuffer->getHeight();
- }
- float sx = p->getActiveWidth(p->getDrawingState()) / static_cast<float>(bufferWidth);
- float sy = p->getActiveHeight(p->getDrawingState()) / static_cast<float>(bufferHeight);
- ui::Transform extraParentScaling;
- extraParentScaling.set(sx, 0, 0, sy);
- t = t * extraParentScaling;
- }
- }
- return t * getActiveTransform(getDrawingState());
+ return mEffectiveTransform;
}
half Layer::getAlpha() const {
@@ -2066,7 +2043,7 @@
}
}
const float radius = getDrawingState().cornerRadius;
- return radius > 0 ? RoundedCornerState(computeBounds(), radius) : RoundedCornerState();
+ return radius > 0 ? RoundedCornerState(getBounds(), radius) : RoundedCornerState();
}
void Layer::commitChildList() {
@@ -2181,6 +2158,10 @@
for (const auto& entry : state.metadata.mMap) {
(*protoMap)[entry.first] = std::string(entry.second.cbegin(), entry.second.cend());
}
+ LayerProtoHelper::writeToProto(mEffectiveTransform, layerInfo->mutable_effective_transform());
+ LayerProtoHelper::writeToProto(mSourceBounds, layerInfo->mutable_source_bounds());
+ LayerProtoHelper::writeToProto(mScreenBounds, layerInfo->mutable_screen_bounds());
+ LayerProtoHelper::writeToProto(mBounds, layerInfo->mutable_bounds());
}
void Layer::writeToProto(LayerProto* layerInfo, DisplayId displayId) {
diff --git a/services/surfaceflinger/Layer.h b/services/surfaceflinger/Layer.h
index 8b6d4c7..9241e71 100644
--- a/services/surfaceflinger/Layer.h
+++ b/services/surfaceflinger/Layer.h
@@ -349,8 +349,15 @@
void computeGeometry(const RenderArea& renderArea, renderengine::Mesh& mesh,
bool useIdentityTransform) const;
- FloatRect computeBounds(const Region& activeTransparentRegion) const;
- FloatRect computeBounds() const;
+ FloatRect getBounds(const Region& activeTransparentRegion) const;
+ FloatRect getBounds() const;
+
+ // Compute bounds for the layer and cache the results.
+ void computeBounds(FloatRect parentBounds, ui::Transform parentTransform);
+
+ // Get effective layer transform, taking into account all its parent transform with any
+ // scaling if the parent scaling more is not NATIVE_WINDOW_SCALING_MODE_FREEZE.
+ ui::Transform getTransformWithScale() const;
int32_t getSequence() const { return sequence; }
@@ -643,7 +650,7 @@
ssize_t removeChild(const sp<Layer>& layer);
sp<Layer> getParent() const { return mCurrentParent.promote(); }
bool hasParent() const { return getParent() != nullptr; }
- Rect computeScreenBounds(bool reduceTransparentRegion = true) const;
+ Rect getScreenBounds(bool reduceTransparentRegion = true) const;
bool setChildLayer(const sp<Layer>& childLayer, int32_t z);
bool setChildRelativeLayer(const sp<Layer>& childLayer,
const sp<IBinder>& relativeToHandle, int32_t relativeZ);
@@ -660,6 +667,15 @@
*/
virtual Rect getBufferSize(const Layer::State&) const { return Rect::INVALID_RECT; }
+ /**
+ * Returns the source bounds. If the bounds are not defined, it is inferred from the
+ * buffer size. Failing that, the bounds are determined from the passed in parent bounds.
+ * For the root layer, this is the display viewport size.
+ */
+ virtual FloatRect computeSourceBounds(const FloatRect& parentBounds) const {
+ return parentBounds;
+ }
+
protected:
// constant
sp<SurfaceFlinger> mFlinger;
@@ -879,19 +895,27 @@
LayerVector makeChildrenTraversalList(LayerVector::StateSet stateSet,
const std::vector<Layer*>& layersInTree);
/**
- * Retuns the child bounds in layer space cropped to its bounds as well all its parent bounds.
- * The cropped bounds must be transformed back from parent layer space to child layer space by
- * applying the inverse of the child's transformation.
- */
- FloatRect cropChildBounds(const FloatRect& childBounds) 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;
+
+ // Cached properties computed from drawing state
+ // Effective transform taking into account parent transforms and any parent scaling.
+ ui::Transform mEffectiveTransform;
+
+ // Bounds of the layer before any transformation is applied and before it has been cropped
+ // by its parents.
+ FloatRect mSourceBounds;
+
+ // Bounds of the layer in layer space. This is the mSourceBounds cropped by its layer crop and
+ // its parent bounds.
+ FloatRect mBounds;
+
+ // Layer bounds in screen space.
+ FloatRect mScreenBounds;
};
} // namespace android
diff --git a/services/surfaceflinger/Scheduler/IdleTimer.cpp b/services/surfaceflinger/Scheduler/IdleTimer.cpp
index e975e65..b28b1aa 100644
--- a/services/surfaceflinger/Scheduler/IdleTimer.cpp
+++ b/services/surfaceflinger/Scheduler/IdleTimer.cpp
@@ -22,8 +22,9 @@
namespace android {
namespace scheduler {
-IdleTimer::IdleTimer(const Interval& interval, const TimeoutCallback& timeoutCallback)
- : mInterval(interval), mTimeoutCallback(timeoutCallback) {}
+IdleTimer::IdleTimer(const Interval& interval, const ResetCallback& resetCallback,
+ const TimeoutCallback& timeoutCallback)
+ : mInterval(interval), mResetCallback(resetCallback), mTimeoutCallback(timeoutCallback) {}
IdleTimer::~IdleTimer() {
stop();
@@ -49,11 +50,34 @@
}
void IdleTimer::loop() {
- std::lock_guard<std::mutex> lock(mMutex);
- while (mState != TimerState::STOPPED) {
- if (mState == TimerState::IDLE) {
- mCondition.wait(mMutex);
- } else if (mState == TimerState::RESET) {
+ while (true) {
+ bool triggerReset = false;
+ bool triggerTimeout = false;
+ {
+ std::lock_guard<std::mutex> lock(mMutex);
+ if (mState == TimerState::STOPPED) {
+ break;
+ }
+
+ if (mState == TimerState::IDLE) {
+ mCondition.wait(mMutex);
+ continue;
+ }
+
+ if (mState == TimerState::RESET) {
+ triggerReset = true;
+ }
+ }
+ if (triggerReset && mResetCallback) {
+ mResetCallback();
+ }
+
+ { // lock the mutex again. someone might have called stop meanwhile
+ std::lock_guard<std::mutex> lock(mMutex);
+ if (mState == TimerState::STOPPED) {
+ break;
+ }
+
auto triggerTime = std::chrono::steady_clock::now() + mInterval;
mState = TimerState::WAITING;
while (mState == TimerState::WAITING) {
@@ -62,14 +86,14 @@
if (waitTime > zero) mCondition.wait_for(mMutex, waitTime);
if (mState == TimerState::WAITING &&
(triggerTime - std::chrono::steady_clock::now()) <= zero) {
- if (mTimeoutCallback) {
- mTimeoutCallback();
- }
-
+ triggerTimeout = true;
mState = TimerState::IDLE;
}
}
}
+ if (triggerTimeout && mTimeoutCallback) {
+ mTimeoutCallback();
+ }
}
} // namespace scheduler
diff --git a/services/surfaceflinger/Scheduler/IdleTimer.h b/services/surfaceflinger/Scheduler/IdleTimer.h
index aee3fa3..19f1267 100644
--- a/services/surfaceflinger/Scheduler/IdleTimer.h
+++ b/services/surfaceflinger/Scheduler/IdleTimer.h
@@ -32,9 +32,11 @@
class IdleTimer {
public:
using Interval = std::chrono::milliseconds;
+ using ResetCallback = std::function<void()>;
using TimeoutCallback = std::function<void()>;
- IdleTimer(const Interval& interval, const TimeoutCallback& timeoutCallback);
+ IdleTimer(const Interval& interval, const ResetCallback& resetCallback,
+ const TimeoutCallback& timeoutCallback);
~IdleTimer();
void start();
@@ -62,6 +64,9 @@
// Interval after which timer expires.
const Interval mInterval;
+ // Callback that happens when timer resets.
+ const ResetCallback mResetCallback;
+
// Callback that happens when timer expires.
const TimeoutCallback mTimeoutCallback;
};
diff --git a/services/surfaceflinger/Scheduler/PhaseOffsets.cpp b/services/surfaceflinger/Scheduler/PhaseOffsets.cpp
index a0a4455..ab1f460 100644
--- a/services/surfaceflinger/Scheduler/PhaseOffsets.cpp
+++ b/services/surfaceflinger/Scheduler/PhaseOffsets.cpp
@@ -59,10 +59,10 @@
const int highFpsEarlyGlAppOffsetNs = atoi(value);
// TODO(b/122905996): Define these in device.mk.
- property_get("debug.sf.high_fps_late_app_phase_offset_ns", value, "1000000");
+ property_get("debug.sf.high_fps_late_app_phase_offset_ns", value, "2000000");
const int highFpsLateAppOffsetNs = atoi(value);
- property_get("debug.sf.high_fps_late_sf_phase_offset_ns", value, "8000000");
+ property_get("debug.sf.high_fps_late_sf_phase_offset_ns", value, "1000000");
const int highFpsLateSfOffsetNs = atoi(value);
mDefaultRefreshRateOffsets.early = {earlySfOffsetNs != -1 ? earlySfOffsetNs
@@ -83,7 +83,7 @@
: highFpsLateAppOffsetNs,
highFpsEarlyGlAppOffsetNs != -1 ? highFpsEarlyGlAppOffsetNs
: highFpsLateSfOffsetNs};
- mHighRefreshRateOffsets.late = {highFpsLateAppOffsetNs, highFpsLateSfOffsetNs};
+ mHighRefreshRateOffsets.late = {highFpsLateSfOffsetNs, highFpsLateAppOffsetNs};
}
PhaseOffsets::Offsets PhaseOffsets::getCurrentOffsets() const {
diff --git a/services/surfaceflinger/Scheduler/Scheduler.cpp b/services/surfaceflinger/Scheduler/Scheduler.cpp
index 2f581d2..af439f7 100644
--- a/services/surfaceflinger/Scheduler/Scheduler.cpp
+++ b/services/surfaceflinger/Scheduler/Scheduler.cpp
@@ -69,12 +69,13 @@
mEventControlThread = std::make_unique<impl::EventControlThread>(function);
char value[PROPERTY_VALUE_MAX];
- property_get("debug.sf.set_idle_timer_ms", value, "30");
+ property_get("debug.sf.set_idle_timer_ms", value, "0");
mSetIdleTimerMs = atoi(value);
if (mSetIdleTimerMs > 0) {
mIdleTimer =
std::make_unique<scheduler::IdleTimer>(std::chrono::milliseconds(mSetIdleTimerMs),
+ [this] { resetTimerCallback(); },
[this] { expiredTimerCallback(); });
mIdleTimer->start();
}
@@ -87,7 +88,6 @@
sp<Scheduler::ConnectionHandle> Scheduler::createConnection(
const char* connectionName, int64_t phaseOffsetNs, ResyncCallback resyncCallback,
- ResetIdleTimerCallback resetIdleTimerCallback,
impl::EventThread::InterceptVSyncsCallback interceptCallback) {
const int64_t id = sNextId++;
ALOGV("Creating a connection handle with ID: %" PRId64 "\n", id);
@@ -97,8 +97,7 @@
std::move(interceptCallback));
auto eventThreadConnection =
- createConnectionInternal(eventThread.get(), std::move(resyncCallback),
- std::move(resetIdleTimerCallback));
+ createConnectionInternal(eventThread.get(), std::move(resyncCallback));
mConnections.emplace(id,
std::make_unique<Connection>(new ConnectionHandle(id),
eventThreadConnection,
@@ -115,26 +114,17 @@
std::move(interceptCallback), connectionName);
}
-sp<EventThreadConnection> Scheduler::createConnectionInternal(
- EventThread* eventThread, ResyncCallback&& resyncCallback,
- ResetIdleTimerCallback&& resetIdleTimerCallback) {
+sp<EventThreadConnection> Scheduler::createConnectionInternal(EventThread* eventThread,
+ ResyncCallback&& resyncCallback) {
return eventThread->createEventConnection(std::move(resyncCallback),
- [this,
- resetIdleTimerCallback =
- std::move(resetIdleTimerCallback)] {
- resetIdleTimer();
- if (resetIdleTimerCallback) {
- resetIdleTimerCallback();
- }
- });
+ [this] { resetIdleTimer(); });
}
sp<IDisplayEventConnection> Scheduler::createDisplayEventConnection(
- const sp<Scheduler::ConnectionHandle>& handle, ResyncCallback resyncCallback,
- ResetIdleTimerCallback resetIdleTimerCallback) {
+ const sp<Scheduler::ConnectionHandle>& handle, ResyncCallback resyncCallback) {
RETURN_VALUE_IF_INVALID(nullptr);
return createConnectionInternal(mConnections[handle->id]->thread.get(),
- std::move(resyncCallback), std::move(resetIdleTimerCallback));
+ std::move(resyncCallback));
}
EventThread* Scheduler::getEventThread(const sp<Scheduler::ConnectionHandle>& handle) {
@@ -200,9 +190,15 @@
}
void Scheduler::setVsyncPeriod(const nsecs_t period) {
+ std::lock_guard<std::mutex> lock(mHWVsyncLock);
mPrimaryDispSync->reset();
mPrimaryDispSync->setPeriod(period);
- enableHardwareVsync();
+
+ if (!mPrimaryHWVsyncEnabled) {
+ mPrimaryDispSync->beginResync();
+ mEventControlThread->setVsyncEnabled(true);
+ mPrimaryHWVsyncEnabled = true;
+ }
}
void Scheduler::addResyncSample(const nsecs_t timestamp) {
@@ -238,6 +234,19 @@
mHWVsyncAvailable = makeAvailable;
}
+bool Scheduler::getHWSyncAvailable() {
+ std::lock_guard<std::mutex> lock(mHWVsyncLock);
+ return mHWVsyncAvailable;
+}
+
+nsecs_t Scheduler::expectedPresentTime() {
+ return mPrimaryDispSync->expectedPresentTime();
+}
+
+void Scheduler::dumpPrimaryDispSync(std::string& result) const {
+ mPrimaryDispSync->dump(result);
+}
+
void Scheduler::addFramePresentTimeForLayer(const nsecs_t framePresentTime, bool isAutoTimestamp,
const std::string layerName) {
// This is V1 logic. It calculates the average FPS based on the timestamp frequency
@@ -259,6 +268,11 @@
mExpiredTimerCallback = expiredTimerCallback;
}
+void Scheduler::setResetIdleTimerCallback(const ResetIdleTimerCallback& resetTimerCallback) {
+ std::lock_guard<std::mutex> lock(mCallbackLock);
+ mResetTimerCallback = resetTimerCallback;
+}
+
void Scheduler::updateFrameSkipping(const int64_t skipCount) {
ATRACE_INT("FrameSkipCount", skipCount);
if (mSkipCount != skipCount) {
@@ -347,6 +361,13 @@
void Scheduler::resetIdleTimer() {
if (mIdleTimer) {
mIdleTimer->reset();
+ }
+}
+
+void Scheduler::resetTimerCallback() {
+ std::lock_guard<std::mutex> lock(mCallbackLock);
+ if (mResetTimerCallback) {
+ mResetTimerCallback();
ATRACE_INT("ExpiredIdleTimer", 0);
}
}
diff --git a/services/surfaceflinger/Scheduler/Scheduler.h b/services/surfaceflinger/Scheduler/Scheduler.h
index 4abf027..d628e40 100644
--- a/services/surfaceflinger/Scheduler/Scheduler.h
+++ b/services/surfaceflinger/Scheduler/Scheduler.h
@@ -37,6 +37,7 @@
class Scheduler {
public:
using ExpiredIdleTimerCallback = std::function<void()>;
+ using ResetIdleTimerCallback = std::function<void()>;
// Enum to indicate whether to start the transaction early, or at vsync time.
enum class TransactionStart { EARLY, NORMAL };
@@ -72,12 +73,11 @@
/** Creates an EventThread connection. */
sp<ConnectionHandle> createConnection(const char* connectionName, int64_t phaseOffsetNs,
- ResyncCallback, ResetIdleTimerCallback,
+ ResyncCallback,
impl::EventThread::InterceptVSyncsCallback);
sp<IDisplayEventConnection> createDisplayEventConnection(const sp<ConnectionHandle>& handle,
- ResyncCallback,
- ResetIdleTimerCallback);
+ ResyncCallback);
// Getter methods.
EventThread* getEventThread(const sp<ConnectionHandle>& handle);
@@ -109,6 +109,9 @@
void addPresentFence(const std::shared_ptr<FenceTime>& fenceTime);
void setIgnorePresentFences(bool ignore);
void makeHWSyncAvailable(bool makeAvailable);
+ // returns HWSyncAvailable flag to SF would enable HW vsync based on this
+ bool getHWSyncAvailable();
+ nsecs_t expectedPresentTime();
// Adds the present time for given layer to the history of present times.
void addFramePresentTimeForLayer(const nsecs_t framePresentTime, bool isAutoTimestamp,
const std::string layerName);
@@ -116,9 +119,14 @@
void incrementFrameCounter();
// Callback that gets invoked once the idle timer expires.
void setExpiredIdleTimerCallback(const ExpiredIdleTimerCallback& expiredTimerCallback);
+ // Callback that gets invoked once the idle timer is reset.
+ void setResetIdleTimerCallback(const ResetIdleTimerCallback& resetTimerCallback);
// Returns relevant information about Scheduler for dumpsys purposes.
std::string doDump();
+ // calls DispSync::dump() on primary disp sync
+ void dumpPrimaryDispSync(std::string& result) const;
+
protected:
virtual std::unique_ptr<EventThread> makeEventThread(
const char* connectionName, DispSync* dispSync, int64_t phaseOffsetNs,
@@ -126,8 +134,7 @@
private:
// Creates a connection on the given EventThread and forwards the given callbacks.
- sp<EventThreadConnection> createConnectionInternal(EventThread*, ResyncCallback&&,
- ResetIdleTimerCallback&&);
+ sp<EventThreadConnection> createConnectionInternal(EventThread*, ResyncCallback&&);
nsecs_t calculateAverage() const;
void updateFrameSkipping(const int64_t skipCount);
@@ -139,13 +146,11 @@
void determineTimestampAverage(bool isAutoTimestamp, const nsecs_t framePresentTime);
// Function that resets the idle timer.
void resetIdleTimer();
+ // Function that is called when the timer resets.
+ void resetTimerCallback();
// Function that is called when the timer expires.
void expiredTimerCallback();
- // TODO(b/113612090): Instead of letting BufferQueueLayer to access mDispSync directly, it
- // should make request to Scheduler to compute next refresh.
- friend class BufferQueueLayer;
-
// If fences from sync Framework are supported.
const bool mHasSyncFramework;
@@ -186,6 +191,7 @@
std::mutex mCallbackLock;
ExpiredIdleTimerCallback mExpiredTimerCallback GUARDED_BY(mCallbackLock);
+ ExpiredIdleTimerCallback mResetTimerCallback GUARDED_BY(mCallbackLock);
};
} // namespace android
diff --git a/services/surfaceflinger/Scheduler/VSyncModulator.h b/services/surfaceflinger/Scheduler/VSyncModulator.h
index d7ec733..0bf3ceb 100644
--- a/services/surfaceflinger/Scheduler/VSyncModulator.h
+++ b/services/surfaceflinger/Scheduler/VSyncModulator.h
@@ -124,7 +124,7 @@
changed = true;
}
if (desired.app != current.app) {
- if (mSfConnectionHandle != nullptr) {
+ if (mAppConnectionHandle != nullptr) {
mScheduler->setPhaseOffset(mAppConnectionHandle, desired.app);
} else {
mAppEventThread->setPhaseOffset(desired.app);
diff --git a/services/surfaceflinger/SurfaceFlinger.cpp b/services/surfaceflinger/SurfaceFlinger.cpp
index 954ecda..c98220d 100644
--- a/services/surfaceflinger/SurfaceFlinger.cpp
+++ b/services/surfaceflinger/SurfaceFlinger.cpp
@@ -351,10 +351,6 @@
}
ALOGV("Primary Display Orientation is set to %2d.", SurfaceFlinger::primaryDisplayOrientation);
- mPrimaryDispSync =
- getFactory().createDispSync("PrimaryDispSync", SurfaceFlinger::hasSyncFramework,
- SurfaceFlinger::dispSyncPresentTimeOffset);
-
auto surfaceFlingerConfigsServiceV1_2 = V1_2::ISurfaceFlingerConfigs::getService();
if (surfaceFlingerConfigsServiceV1_2) {
surfaceFlingerConfigsServiceV1_2->getDisplayNativePrimaries(
@@ -400,6 +396,12 @@
property_get("debug.sf.use_scheduler", value, "0");
mUseScheduler = atoi(value);
+ if (!mUseScheduler) {
+ mPrimaryDispSync =
+ getFactory().createDispSync("PrimaryDispSync", SurfaceFlinger::hasSyncFramework,
+ SurfaceFlinger::dispSyncPresentTimeOffset);
+ }
+
const auto [early, gl, late] = mPhaseOffsets->getCurrentOffsets();
mVsyncModulator.setPhaseOffsets(early, gl, late);
@@ -633,17 +635,13 @@
mPhaseOffsets->setRefreshRateType(
scheduler::RefreshRateConfigs::RefreshRateType::PERFORMANCE);
- auto resetIdleTimerCallback =
- std::bind(&SurfaceFlinger::setRefreshRateTo, this, RefreshRateType::PERFORMANCE);
-
mAppConnectionHandle =
- mScheduler->createConnection("appConnection", mPhaseOffsets->getCurrentAppOffset(),
- resyncCallback, resetIdleTimerCallback,
+ mScheduler->createConnection("app", mPhaseOffsets->getCurrentAppOffset(),
+ resyncCallback,
impl::EventThread::InterceptVSyncsCallback());
mSfConnectionHandle =
- mScheduler->createConnection("sfConnection", mPhaseOffsets->getCurrentSfOffset(),
- resyncCallback, resetIdleTimerCallback,
- [this](nsecs_t timestamp) {
+ mScheduler->createConnection("sf", mPhaseOffsets->getCurrentSfOffset(),
+ resyncCallback, [this](nsecs_t timestamp) {
mInterceptor->saveVSyncEvent(timestamp);
});
@@ -719,8 +717,10 @@
}
}
- mEventControlThread = getFactory().createEventControlThread(
- [this](bool enabled) { setPrimaryVsyncEnabled(enabled); });
+ if (!mUseScheduler) {
+ mEventControlThread = getFactory().createEventControlThread(
+ [this](bool enabled) { setPrimaryVsyncEnabled(enabled); });
+ }
// initialize our drawing state
mDrawingState = mCurrentState;
@@ -745,6 +745,10 @@
Mutex::Autolock lock(mStateLock);
setRefreshRateTo(RefreshRateType::DEFAULT);
});
+ mScheduler->setResetIdleTimerCallback([this] {
+ Mutex::Autolock lock(mStateLock);
+ setRefreshRateTo(RefreshRateType::PERFORMANCE);
+ });
mRefreshRateStats =
std::make_unique<scheduler::RefreshRateStats>(getHwComposer().getConfigs(
@@ -825,14 +829,12 @@
return NO_ERROR;
}
-status_t SurfaceFlinger::getDisplayConfigs(const sp<IBinder>& displayToken,
- Vector<DisplayInfo>* configs) {
+status_t SurfaceFlinger::getDisplayConfigsLocked(const sp<IBinder>& displayToken,
+ Vector<DisplayInfo>* configs) {
if (!displayToken || !configs) {
return BAD_VALUE;
}
- ConditionalLock lock(mStateLock, std::this_thread::get_id() != mMainThreadId);
-
const auto displayId = getPhysicalDisplayIdLocked(displayToken);
if (!displayId) {
return NAME_NOT_FOUND;
@@ -960,22 +962,19 @@
return display->getActiveConfig();
}
-status_t SurfaceFlinger::setActiveConfig(const sp<IBinder>& displayToken, int mode) {
- ATRACE_NAME("setActiveConfigSync");
- postMessageSync(new LambdaMessage(
- [&]() NO_THREAD_SAFETY_ANALYSIS { setActiveConfigInternal(displayToken, mode); }));
- return NO_ERROR;
-}
+void SurfaceFlinger::setDesiredActiveConfig(const sp<IBinder>& displayToken, int mode) {
+ ATRACE_CALL();
-void SurfaceFlinger::setActiveConfigInternal(const sp<IBinder>& displayToken, int mode) {
Vector<DisplayInfo> configs;
- getDisplayConfigs(displayToken, &configs);
+ // Lock is acquired by setRefreshRateTo.
+ getDisplayConfigsLocked(displayToken, &configs);
if (mode < 0 || mode >= static_cast<int>(configs.size())) {
ALOGE("Attempt to set active config %d for display with %zu configs", mode, configs.size());
return;
}
- const auto display = getDisplayDevice(displayToken);
+ // Lock is acquired by setRefreshRateTo.
+ const auto display = getDisplayDeviceLocked(displayToken);
if (!display) {
ALOGE("Attempt to set active config %d for invalid display token %p", mode,
displayToken.get());
@@ -991,23 +990,95 @@
return;
}
- int currentMode = display->getActiveConfig();
- if (mode == currentMode) {
- // Don't update config if we are already running in the desired mode.
+ // Don't check against the current mode yet. Worst case we set the desired
+ // config twice.
+ {
+ std::lock_guard<std::mutex> lock(mActiveConfigLock);
+ mDesiredActiveConfig = ActiveConfigInfo{mode, displayToken};
+ }
+ // This will trigger HWC refresh without resetting the idle timer.
+ repaintEverythingForHWC();
+}
+
+status_t SurfaceFlinger::setActiveConfig(const sp<IBinder>& displayToken, int mode) {
+ ATRACE_CALL();
+ postMessageSync(new LambdaMessage(
+ [&]() NO_THREAD_SAFETY_ANALYSIS { setDesiredActiveConfig(displayToken, mode); }));
+ return NO_ERROR;
+}
+
+void SurfaceFlinger::setActiveConfigInHWC() {
+ ATRACE_CALL();
+
+ const auto display = getDisplayDevice(mUpcomingActiveConfig.displayToken);
+ if (!display) {
return;
}
- if (mUseScheduler) {
- mRefreshRateStats->setConfigMode(mode);
- }
-
const auto displayId = display->getId();
LOG_ALWAYS_FATAL_IF(!displayId);
- display->setActiveConfig(mode);
- getHwComposer().setActiveConfig(*displayId, mode);
+ ATRACE_INT("ActiveConfigModeHWC", mUpcomingActiveConfig.configId);
+ getHwComposer().setActiveConfig(*displayId, mUpcomingActiveConfig.configId);
+ mSetActiveConfigState = SetActiveConfigState::NOTIFIED_HWC;
+ ATRACE_INT("SetActiveConfigState", mSetActiveConfigState);
+}
- ATRACE_INT("ActiveConfigMode", mode);
+void SurfaceFlinger::setActiveConfigInternal() {
+ ATRACE_CALL();
+
+ std::lock_guard<std::mutex> lock(mActiveConfigLock);
+ if (mUseScheduler) {
+ mRefreshRateStats->setConfigMode(mUpcomingActiveConfig.configId);
+ }
+
+ const auto display = getDisplayDeviceLocked(mUpcomingActiveConfig.displayToken);
+ display->setActiveConfig(mUpcomingActiveConfig.configId);
+
+ mSetActiveConfigState = SetActiveConfigState::NONE;
+ ATRACE_INT("SetActiveConfigState", mSetActiveConfigState);
+
resyncToHardwareVsync(true, getVsyncPeriod());
+ ATRACE_INT("ActiveConfigMode", mUpcomingActiveConfig.configId);
+}
+
+bool SurfaceFlinger::updateSetActiveConfigStateMachine() NO_THREAD_SAFETY_ANALYSIS {
+ // Store the local variable to release the lock.
+ ActiveConfigInfo desiredActiveConfig;
+ {
+ std::lock_guard<std::mutex> lock(mActiveConfigLock);
+ desiredActiveConfig = mDesiredActiveConfig;
+ }
+
+ const auto display = getDisplayDevice(desiredActiveConfig.displayToken);
+ if (display) {
+ if (mSetActiveConfigState == SetActiveConfigState::NONE &&
+ display->getActiveConfig() != desiredActiveConfig.configId) {
+ // Step 1) Desired active config was set, it is different than the
+ // config currently in use. Notify HWC.
+ mUpcomingActiveConfig = desiredActiveConfig;
+ setActiveConfigInHWC();
+ } else if (mSetActiveConfigState == SetActiveConfigState::NOTIFIED_HWC) {
+ // Step 2) HWC was notified and we received refresh from it.
+ mSetActiveConfigState = SetActiveConfigState::REFRESH_RECEIVED;
+ ATRACE_INT("SetActiveConfigState", mSetActiveConfigState);
+ repaintEverythingForHWC();
+ // We do not want to give another frame to HWC while we are transitioning.
+ return false;
+ } else if (mSetActiveConfigState == SetActiveConfigState::REFRESH_RECEIVED &&
+ !(mPreviousPresentFence != Fence::NO_FENCE &&
+ (mPreviousPresentFence->getStatus() == Fence::Status::Unsignaled))) {
+ // Step 3) We received the present fence from the HWC, so we assume it
+ // successfully updated the config, hence we update SF.
+ setActiveConfigInternal();
+ // If the config changed again while we were transitioning, restart the
+ // process.
+ if (desiredActiveConfig != mUpcomingActiveConfig) {
+ mUpcomingActiveConfig = desiredActiveConfig;
+ setActiveConfigInHWC(); // sets the state to NOTIFY_HWC
+ }
+ }
+ }
+ return true;
}
status_t SurfaceFlinger::getDisplayColorModes(const sp<IBinder>& displayToken,
@@ -1256,6 +1327,17 @@
return NO_ERROR;
}
+status_t SurfaceFlinger::addRegionSamplingListener(
+ const Rect& /*samplingArea*/, const sp<IBinder>& /*stopLayerHandle*/,
+ const sp<IRegionSamplingListener>& /*listener*/) {
+ return NO_ERROR;
+}
+
+status_t SurfaceFlinger::removeRegionSamplingListener(
+ const sp<IRegionSamplingListener>& /*listener*/) {
+ return NO_ERROR;
+}
+
// ----------------------------------------------------------------------------
sp<IDisplayEventConnection> SurfaceFlinger::createDisplayEventConnection(
@@ -1266,16 +1348,10 @@
});
if (mUseScheduler) {
- auto resetIdleTimerCallback = [this] {
- Mutex::Autolock lock(mStateLock);
- setRefreshRateTo(RefreshRateType::PERFORMANCE);
- };
-
const auto& handle = vsyncSource == eVsyncSourceSurfaceFlinger ? mSfConnectionHandle
: mAppConnectionHandle;
- return mScheduler->createDisplayEventConnection(handle, std::move(resyncCallback),
- std::move(resetIdleTimerCallback));
+ return mScheduler->createDisplayEventConnection(handle, std::move(resyncCallback));
} else {
if (vsyncSource == eVsyncSourceSurfaceFlinger) {
return mSFEventThread->createEventConnection(resyncCallback, ResetIdleTimerCallback());
@@ -1335,6 +1411,7 @@
}
void SurfaceFlinger::enableHardwareVsync() {
+ assert(!mUseScheduler);
Mutex::Autolock _l(mHWVsyncLock);
if (!mPrimaryHWVsyncEnabled && mHWVsyncAvailable) {
mPrimaryDispSync->beginResync();
@@ -1345,17 +1422,23 @@
void SurfaceFlinger::resyncToHardwareVsync(bool makeAvailable, nsecs_t period) {
Mutex::Autolock _l(mHWVsyncLock);
-
- if (makeAvailable) {
- mHWVsyncAvailable = true;
- // TODO(b/113612090): This is silly, but necessary evil until we turn on the flag for good.
- if (mUseScheduler) {
+ // TODO(b/113612090): This is silly, but necessary evil until we turn on the flag for good.
+ if (mUseScheduler) {
+ if (makeAvailable) {
mScheduler->makeHWSyncAvailable(true);
+ } else if (!mScheduler->getHWSyncAvailable()) {
+ // Hardware vsync is not currently available, so abort the resync
+ // attempt for now
+ return;
}
- } else if (!mHWVsyncAvailable) {
- // Hardware vsync is not currently available, so abort the resync
- // attempt for now
- return;
+ } else {
+ if (makeAvailable) {
+ mHWVsyncAvailable = true;
+ } else if (!mHWVsyncAvailable) {
+ // Hardware vsync is not currently available, so abort the resync
+ // attempt for now
+ return;
+ }
}
if (period <= 0) {
@@ -1377,6 +1460,7 @@
}
void SurfaceFlinger::disableHardwareVsync(bool makeUnavailable) {
+ assert(!mUseScheduler);
Mutex::Autolock _l(mHWVsyncLock);
if (mPrimaryHWVsyncEnabled) {
mEventControlThread->setVsyncEnabled(false);
@@ -1485,8 +1569,7 @@
// TODO(b/113612090): There should be a better way at determining which config
// has the right refresh rate.
if (std::abs(fps - newFps) <= 1) {
- setActiveConfigInternal(getInternalDisplayTokenLocked(), i);
- ATRACE_INT("FPS", newFps);
+ setDesiredActiveConfig(getInternalDisplayTokenLocked(), i);
}
}
}
@@ -1630,14 +1713,16 @@
ATRACE_CALL();
switch (what) {
case MessageQueue::INVALIDATE: {
+ if (!updateSetActiveConfigStateMachine()) {
+ break;
+ }
+
if (mUseScheduler) {
// This call is made each time SF wakes up and creates a new frame.
mScheduler->incrementFrameCounter();
}
- bool frameMissed = !mHadClientComposition &&
- mPreviousPresentFence != Fence::NO_FENCE &&
- (mPreviousPresentFence->getSignalTime() ==
- Fence::SIGNAL_TIME_PENDING);
+ bool frameMissed = !mHadClientComposition && mPreviousPresentFence != Fence::NO_FENCE &&
+ (mPreviousPresentFence->getStatus() == Fence::Status::Unsignaled);
mFrameMissedCount += frameMissed;
ATRACE_INT("FrameMissed", static_cast<int>(frameMissed));
if (frameMissed) {
@@ -1755,9 +1840,13 @@
ATRACE_CALL();
bool refreshNeeded = handlePageFlip();
+ if (mVisibleRegionsDirty) {
+ computeLayerBounds();
+ }
+
for (auto& layer : mLayersPendingRefresh) {
Region visibleReg;
- visibleReg.set(layer->computeScreenBounds());
+ visibleReg.set(layer->getScreenBounds());
invalidateLayerStack(layer, visibleReg);
}
mLayersPendingRefresh.clear();
@@ -2236,6 +2325,21 @@
}
#pragma clang optimize on // b/119477596
+void SurfaceFlinger::computeLayerBounds() {
+ for (const auto& pair : mDisplays) {
+ const auto& displayDevice = pair.second;
+ const auto display = displayDevice->getCompositionDisplay();
+ for (const auto& layer : mDrawingState.layersSortedByZ) {
+ // only consider the layers on the given layer stack
+ if (!display->belongsInOutput(layer->getLayerStack(), layer->getPrimaryDisplayOnly())) {
+ continue;
+ }
+
+ layer->computeBounds(displayDevice->getViewport().toFloatRect(), ui::Transform());
+ }
+ }
+}
+
void SurfaceFlinger::rebuildLayerStacks() {
ATRACE_CALL();
ALOGV("rebuildLayerStacks");
@@ -2987,11 +3091,8 @@
mDrawingState.traverseInZOrder([&](Layer* layer) {
if (mLayersPendingRemoval.indexOf(layer) >= 0) {
// this layer is not visible anymore
- // TODO: we could traverse the tree from front to back and
- // compute the actual visible region
- // TODO: we could cache the transformed region
Region visibleReg;
- visibleReg.set(layer->computeScreenBounds());
+ visibleReg.set(layer->getScreenBounds());
invalidateLayerStack(layer, visibleReg);
}
});
@@ -3055,7 +3156,12 @@
void SurfaceFlinger::latchAndReleaseBuffer(const sp<Layer>& layer) {
if (layer->hasReadyFrame()) {
- const nsecs_t expectedPresentTime = mPrimaryDispSync->expectedPresentTime();
+ nsecs_t expectedPresentTime;
+ if (mUseScheduler) {
+ expectedPresentTime = mScheduler->expectedPresentTime();
+ } else {
+ expectedPresentTime = mPrimaryDispSync->expectedPresentTime();
+ }
if (layer->shouldPresentNow(expectedPresentTime)) {
bool ignored = false;
layer->latchBuffer(ignored, systemTime(), Fence::NO_FENCE);
@@ -3155,7 +3261,7 @@
// handle hidden surfaces by setting the visible region to empty
if (CC_LIKELY(layer->isVisible())) {
const bool translucent = !layer->isOpaque(s);
- Rect bounds(layer->computeScreenBounds());
+ Rect bounds(layer->getScreenBounds());
visibleRegion.set(bounds);
ui::Transform tr = layer->getTransform();
@@ -3287,7 +3393,12 @@
mDrawingState.traverseInZOrder([&](Layer* layer) {
if (layer->hasReadyFrame()) {
frameQueued = true;
- const nsecs_t expectedPresentTime = mPrimaryDispSync->expectedPresentTime();
+ nsecs_t expectedPresentTime;
+ if (mUseScheduler) {
+ expectedPresentTime = mScheduler->expectedPresentTime();
+ } else {
+ expectedPresentTime = mPrimaryDispSync->expectedPresentTime();
+ }
if (layer->shouldPresentNow(expectedPresentTime)) {
mLayersWithQueuedFrames.push_back(layer);
} else {
@@ -3628,7 +3739,12 @@
bool SurfaceFlinger::transactionIsReadyToBeApplied(int64_t desiredPresentTime,
const Vector<ComposerState>& states) {
- const nsecs_t expectedPresentTime = mPrimaryDispSync->expectedPresentTime();
+ nsecs_t expectedPresentTime;
+ if (mUseScheduler) {
+ expectedPresentTime = mScheduler->expectedPresentTime();
+ } else {
+ expectedPresentTime = mPrimaryDispSync->expectedPresentTime();
+ }
// Do not present if the desiredPresentTime has not passed unless it is more than one second
// in the future. We ignore timestamps more than 1 second in the future for stability reasons.
if (desiredPresentTime >= 0 && desiredPresentTime >= expectedPresentTime &&
@@ -4449,7 +4565,13 @@
{"--clear-layer-stats"s, dumper([this](std::string&) { mLayerStats.clear(); })},
{"--disable-layer-stats"s, dumper([this](std::string&) { mLayerStats.disable(); })},
{"--display-id"s, dumper(&SurfaceFlinger::dumpDisplayIdentificationData)},
- {"--dispsync"s, dumper([this](std::string& s) { mPrimaryDispSync->dump(s); })},
+ {"--dispsync"s, dumper([this](std::string& s) {
+ if (mUseScheduler) {
+ mScheduler->dumpPrimaryDispSync(s);
+ } else {
+ mPrimaryDispSync->dump(s);
+ }
+ })},
{"--dump-layer-stats"s, dumper([this](std::string& s) { mLayerStats.dump(s); })},
{"--enable-layer-stats"s, dumper([this](std::string&) { mLayerStats.enable(); })},
{"--frame-composition"s, dumper(&SurfaceFlinger::dumpFrameCompositionInfo)},
@@ -5030,7 +5152,9 @@
return OK;
}
case CAPTURE_LAYERS:
- case CAPTURE_SCREEN: {
+ case CAPTURE_SCREEN:
+ case ADD_REGION_SAMPLING_LISTENER:
+ case REMOVE_REGION_SAMPLING_LISTENER: {
// codes that require permission check
IPCThreadState* ipc = IPCThreadState::self();
const int pid = ipc->getCallingPid();
@@ -5452,11 +5576,14 @@
const sp<Layer>& oldParent;
const sp<Layer>& newParent;
- ReparentForDrawing(const sp<Layer>& oldParent, const sp<Layer>& newParent)
+ ReparentForDrawing(const sp<Layer>& oldParent, const sp<Layer>& newParent,
+ const Rect& drawingBounds)
: oldParent(oldParent), newParent(newParent) {
+ // Compute and cache the bounds for the new parent layer.
+ newParent->computeBounds(drawingBounds.toFloatRect(), ui::Transform());
oldParent->setChildrenDrawingParent(newParent);
}
- ~ReparentForDrawing() { oldParent->setChildrenDrawingParent(oldParent); }
+ ~ReparentForDrawing() { newParent->setChildrenDrawingParent(oldParent); }
};
void render(std::function<void()> drawLayers) override {
@@ -5474,7 +5601,7 @@
LayerCreationArgs(mFlinger, nullptr, String8("Screenshot Parent"),
bounds.getWidth(), bounds.getHeight(), 0));
- ReparentForDrawing reparent(mLayer, screenshotParentLayer);
+ ReparentForDrawing reparent(mLayer, screenshotParentLayer, sourceCrop);
drawLayers();
}
}
diff --git a/services/surfaceflinger/SurfaceFlinger.h b/services/surfaceflinger/SurfaceFlinger.h
index 10ec5ee..a0f83a2 100644
--- a/services/surfaceflinger/SurfaceFlinger.h
+++ b/services/surfaceflinger/SurfaceFlinger.h
@@ -437,7 +437,12 @@
const Rect& sourceCrop, float frameScale, bool childrenOnly) override;
status_t getDisplayStats(const sp<IBinder>& displayToken, DisplayStatInfo* stats) override;
status_t getDisplayConfigs(const sp<IBinder>& displayToken,
- Vector<DisplayInfo>* configs) override;
+ Vector<DisplayInfo>* configs) override {
+ Mutex::Autolock _l(mStateLock);
+ return getDisplayConfigsLocked(displayToken, configs);
+ }
+ status_t getDisplayConfigsLocked(const sp<IBinder>& displayToken, Vector<DisplayInfo>* configs)
+ REQUIRES(mStateLock);
int getActiveConfig(const sp<IBinder>& displayToken) override;
status_t getDisplayColorModes(const sp<IBinder>& displayToken,
Vector<ui::ColorMode>* configs) override;
@@ -471,7 +476,9 @@
status_t getProtectedContentSupport(bool* outSupported) const override;
status_t isWideColorDisplay(const sp<IBinder>& displayToken,
bool* outIsWideColorDisplay) const override;
-
+ status_t addRegionSamplingListener(const Rect& samplingArea, const sp<IBinder>& stopLayerHandle,
+ const sp<IRegionSamplingListener>& listener) override;
+ status_t removeRegionSamplingListener(const sp<IRegionSamplingListener>& listener) override;
/* ------------------------------------------------------------------------
* DeathRecipient interface
*/
@@ -503,8 +510,18 @@
// called on the main thread in response to initializeDisplays()
void onInitializeDisplays() REQUIRES(mStateLock);
- // called on the main thread in response to setActiveConfig()
- void setActiveConfigInternal(const sp<IBinder>& displayToken, int mode) REQUIRES(mStateLock);
+ // Sets the desired active config bit. It obtains the lock, and sets mDesiredActiveConfig.
+ void setDesiredActiveConfig(const sp<IBinder>& displayToken, int mode) REQUIRES(mStateLock);
+ // Calls setActiveConfig in HWC.
+ void setActiveConfigInHWC();
+ // Once HWC has returned the present fence, this sets the active config and a new refresh
+ // rate in SF. It also triggers HWC vsync.
+ void setActiveConfigInternal() REQUIRES(mStateLock);
+ // Active config is updated on INVALIDATE call in a state machine-like manner. When the
+ // desired config was set, HWC needs to update the pannel on the next refresh, and when
+ // we receive the fence back, we know that the process was complete. It returns whether
+ // the invalidate process should continue.
+ bool updateSetActiveConfigStateMachine();
// called on the main thread in response to setPowerMode()
void setPowerModeInternal(const sp<DisplayDevice>& display, int mode) REQUIRES(mStateLock);
@@ -591,6 +608,9 @@
const sp<Layer>& parent,
bool addToCurrentState);
+ // Traverse through all the layers and compute and cache its bounds.
+ void computeLayerBounds();
+
/* ------------------------------------------------------------------------
* Boot animation, on/off animations and screen capture
*/
@@ -1093,6 +1113,34 @@
sp<Scheduler::ConnectionHandle> mSfConnectionHandle;
std::unique_ptr<scheduler::RefreshRateStats> mRefreshRateStats;
+ // The following structs are used for configuring active config state at a desired time,
+ // which is once per vsync at invalidate time.
+ enum SetActiveConfigState {
+ NONE, // not in progress
+ NOTIFIED_HWC, // call to HWC has been made
+ REFRESH_RECEIVED // onRefresh was received from HWC
+ };
+ std::atomic<SetActiveConfigState> mSetActiveConfigState = SetActiveConfigState::NONE;
+
+ struct ActiveConfigInfo {
+ int configId;
+ sp<IBinder> displayToken;
+
+ bool operator!=(const ActiveConfigInfo& other) const {
+ if (configId != other.configId) {
+ return true;
+ }
+ return (displayToken != other.displayToken);
+ }
+ };
+ std::mutex mActiveConfigLock;
+ // This bit is set once we start setting the config. We read from this bit during the
+ // process. If at the end, this bit is different than mDesiredActiveConfig, we restart
+ // the process.
+ ActiveConfigInfo mUpcomingActiveConfig; // Always read and written on the main thread.
+ // This bit can be set at any point in time when the system wants the new config.
+ ActiveConfigInfo mDesiredActiveConfig GUARDED_BY(mActiveConfigLock);
+
/* ------------------------------------------------------------------------ */
sp<IInputFlinger> mInputFlinger;
diff --git a/services/surfaceflinger/layerproto/layers.proto b/services/surfaceflinger/layerproto/layers.proto
index 4c756d9..72cbfac 100644
--- a/services/surfaceflinger/layerproto/layers.proto
+++ b/services/surfaceflinger/layerproto/layers.proto
@@ -91,6 +91,11 @@
optional float corner_radius = 41;
// Metadata map. May be empty.
map<int32, bytes> metadata = 42;
+
+ optional TransformProto effective_transform = 43;
+ optional FloatRectProto source_bounds = 44;
+ optional FloatRectProto bounds = 45;
+ optional FloatRectProto screen_bounds = 46;
}
message PositionProto {
diff --git a/services/surfaceflinger/tests/unittests/CompositionTest.cpp b/services/surfaceflinger/tests/unittests/CompositionTest.cpp
index ee79c18..3fb8708 100644
--- a/services/surfaceflinger/tests/unittests/CompositionTest.cpp
+++ b/services/surfaceflinger/tests/unittests/CompositionTest.cpp
@@ -746,7 +746,7 @@
layerDrawingState.active.h = 100;
layerDrawingState.color = half4(LayerProperties::COLOR[0], LayerProperties::COLOR[1],
LayerProperties::COLOR[2], LayerProperties::COLOR[3]);
-
+ layer->computeBounds(FloatRect(0, 0, 100, 100), ui::Transform());
layer->setVisibleRegion(Region(Rect(0, 0, 100, 100)));
return layer;
diff --git a/services/surfaceflinger/tests/unittests/IdleTimerTest.cpp b/services/surfaceflinger/tests/unittests/IdleTimerTest.cpp
index dc63260..5e82225 100644
--- a/services/surfaceflinger/tests/unittests/IdleTimerTest.cpp
+++ b/services/surfaceflinger/tests/unittests/IdleTimerTest.cpp
@@ -13,7 +13,6 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-
#undef LOG_TAG
#define LOG_TAG "SchedulerUnittests"
@@ -44,6 +43,7 @@
// Note that there can be false-negatives if the callback happens later.
static constexpr auto waitTimeForUnexpected3msCallback = 6ms;
+ AsyncCallRecorder<void (*)()> mResetTimerCallback;
AsyncCallRecorder<void (*)()> mExpiredTimerCallback;
std::unique_ptr<IdleTimer> mIdleTimer;
@@ -56,31 +56,39 @@
namespace {
TEST_F(IdleTimerTest, createAndDestroyTest) {
- mIdleTimer = std::make_unique<scheduler::IdleTimer>(3ms, [] {});
+ mIdleTimer = std::make_unique<scheduler::IdleTimer>(3ms, [] {}, [] {});
}
TEST_F(IdleTimerTest, startStopTest) {
- mIdleTimer = std::make_unique<scheduler::IdleTimer>(30ms, mExpiredTimerCallback.getInvocable());
+ mIdleTimer = std::make_unique<scheduler::IdleTimer>(30ms, mResetTimerCallback.getInvocable(),
+ mExpiredTimerCallback.getInvocable());
auto startTime = std::chrono::steady_clock::now();
mIdleTimer->start();
+ EXPECT_TRUE(mResetTimerCallback.waitForCall().has_value());
// The idle timer fires after 30ms, so there should be no callback within
- // 25ms (waiting for a ballback for the full 30ms would be problematic).
+ // 25ms (waiting for a callback for the full 30ms would be problematic).
bool callbackCalled = mExpiredTimerCallback.waitForCall(25ms).has_value();
// Under ideal conditions there should be no event. But occasionally
// it is possible that the wait just prior takes more than 30ms, and
// a callback is observed. We check the elapsed time since before the IdleTimer
// thread was started as a sanity check to not have a flakey test.
EXPECT_FALSE(callbackCalled && std::chrono::steady_clock::now() - startTime < 30ms);
+
+ std::this_thread::sleep_for(std::chrono::milliseconds(25));
+ EXPECT_FALSE(mResetTimerCallback.waitForCall().has_value());
mIdleTimer->stop();
}
TEST_F(IdleTimerTest, resetTest) {
- mIdleTimer = std::make_unique<scheduler::IdleTimer>(30ms, mExpiredTimerCallback.getInvocable());
+ mIdleTimer = std::make_unique<scheduler::IdleTimer>(20ms, mResetTimerCallback.getInvocable(),
+ mExpiredTimerCallback.getInvocable());
mIdleTimer->start();
+ EXPECT_TRUE(mResetTimerCallback.waitForCall().has_value());
// Observe any event that happens in about 25ms. We don't care if one was
// observed or not.
mExpiredTimerCallback.waitForCall(25ms).has_value();
mIdleTimer->reset();
+ EXPECT_TRUE(mResetTimerCallback.waitForCall().has_value());
// There may have been a race with the reset. Clear any callbacks we
// received right afterwards.
clearPendingCallbacks();
@@ -93,60 +101,110 @@
mIdleTimer->stop();
// Final quick check that no more callback were observed.
EXPECT_FALSE(mExpiredTimerCallback.waitForCall(0ms).has_value());
+ EXPECT_FALSE(mResetTimerCallback.waitForCall(0ms).has_value());
}
-TEST_F(IdleTimerTest, startNotCalledTest) {
- mIdleTimer = std::make_unique<scheduler::IdleTimer>(3ms, mExpiredTimerCallback.getInvocable());
- // The start hasn't happened, so the callback does not happen.
+TEST_F(IdleTimerTest, resetBackToBackTest) {
+ mIdleTimer = std::make_unique<scheduler::IdleTimer>(20ms, mResetTimerCallback.getInvocable(),
+ mExpiredTimerCallback.getInvocable());
+ mIdleTimer->start();
+ EXPECT_TRUE(mResetTimerCallback.waitForCall().has_value());
+
+ mIdleTimer->reset();
+ EXPECT_TRUE(mResetTimerCallback.waitForCall().has_value());
EXPECT_FALSE(mExpiredTimerCallback.waitForCall(waitTimeForUnexpected3msCallback).has_value());
+
+ mIdleTimer->reset();
+ EXPECT_TRUE(mResetTimerCallback.waitForCall().has_value());
+ EXPECT_FALSE(mExpiredTimerCallback.waitForCall(waitTimeForUnexpected3msCallback).has_value());
+
+ mIdleTimer->reset();
+ EXPECT_TRUE(mResetTimerCallback.waitForCall().has_value());
+ EXPECT_FALSE(mExpiredTimerCallback.waitForCall(waitTimeForUnexpected3msCallback).has_value());
+
+ mIdleTimer->reset();
+ EXPECT_TRUE(mResetTimerCallback.waitForCall().has_value());
+ EXPECT_FALSE(mExpiredTimerCallback.waitForCall(waitTimeForUnexpected3msCallback).has_value());
+
+ // A single callback should be generated after 30ms
+ EXPECT_TRUE(
+ mExpiredTimerCallback.waitForCall(waitTimeForExpected3msCallback + 30ms).has_value());
mIdleTimer->stop();
// Final quick check that no more callback were observed.
EXPECT_FALSE(mExpiredTimerCallback.waitForCall(0ms).has_value());
+ EXPECT_FALSE(mResetTimerCallback.waitForCall(0ms).has_value());
+}
+
+TEST_F(IdleTimerTest, startNotCalledTest) {
+ mIdleTimer = std::make_unique<scheduler::IdleTimer>(3ms, mResetTimerCallback.getInvocable(),
+ mExpiredTimerCallback.getInvocable());
+ // The start hasn't happened, so the callback does not happen.
+ EXPECT_FALSE(mExpiredTimerCallback.waitForCall(waitTimeForUnexpected3msCallback).has_value());
+ EXPECT_FALSE(mResetTimerCallback.waitForCall(1us).has_value());
+ mIdleTimer->stop();
+ // Final quick check that no more callback were observed.
+ EXPECT_FALSE(mExpiredTimerCallback.waitForCall(0ms).has_value());
+ EXPECT_FALSE(mResetTimerCallback.waitForCall(0ms).has_value());
}
TEST_F(IdleTimerTest, idleTimerIdlesTest) {
- mIdleTimer = std::make_unique<scheduler::IdleTimer>(3ms, mExpiredTimerCallback.getInvocable());
+ mIdleTimer = std::make_unique<scheduler::IdleTimer>(3ms, mResetTimerCallback.getInvocable(),
+ mExpiredTimerCallback.getInvocable());
mIdleTimer->start();
+ EXPECT_TRUE(mResetTimerCallback.waitForCall().has_value());
+
// A callback should be generated after 3ms
EXPECT_TRUE(mExpiredTimerCallback.waitForCall(waitTimeForExpected3msCallback).has_value());
// After one event, it should be idle, and not generate another.
EXPECT_FALSE(mExpiredTimerCallback.waitForCall(waitTimeForUnexpected3msCallback).has_value());
// Once reset, it should generate another
mIdleTimer->reset();
+ EXPECT_TRUE(mResetTimerCallback.waitForCall(1ms).has_value());
EXPECT_TRUE(mExpiredTimerCallback.waitForCall(waitTimeForExpected3msCallback).has_value());
mIdleTimer->stop();
// Final quick check that no more callback were observed.
EXPECT_FALSE(mExpiredTimerCallback.waitForCall(0ms).has_value());
+ EXPECT_FALSE(mResetTimerCallback.waitForCall(0ms).has_value());
}
TEST_F(IdleTimerTest, timeoutCallbackExecutionTest) {
- mIdleTimer = std::make_unique<scheduler::IdleTimer>(3ms, mExpiredTimerCallback.getInvocable());
-
+ mIdleTimer = std::make_unique<scheduler::IdleTimer>(3ms, mResetTimerCallback.getInvocable(),
+ mExpiredTimerCallback.getInvocable());
mIdleTimer->start();
+ EXPECT_TRUE(mResetTimerCallback.waitForCall(1us).has_value());
EXPECT_TRUE(mExpiredTimerCallback.waitForCall(waitTimeForExpected3msCallback).has_value());
mIdleTimer->stop();
}
TEST_F(IdleTimerTest, noCallbacksAfterStopAndResetTest) {
- mIdleTimer = std::make_unique<scheduler::IdleTimer>(3ms, mExpiredTimerCallback.getInvocable());
+ mIdleTimer = std::make_unique<scheduler::IdleTimer>(3ms, mResetTimerCallback.getInvocable(),
+ mExpiredTimerCallback.getInvocable());
mIdleTimer->start();
+ EXPECT_TRUE(mResetTimerCallback.waitForCall(1ms).has_value());
EXPECT_TRUE(mExpiredTimerCallback.waitForCall(waitTimeForExpected3msCallback).has_value());
+
mIdleTimer->stop();
clearPendingCallbacks();
mIdleTimer->reset();
EXPECT_FALSE(mExpiredTimerCallback.waitForCall(waitTimeForUnexpected3msCallback).has_value());
+ EXPECT_FALSE(mResetTimerCallback.waitForCall(1ms).has_value());
}
TEST_F(IdleTimerTest, noCallbacksAfterStopTest) {
- mIdleTimer = std::make_unique<scheduler::IdleTimer>(3ms, mExpiredTimerCallback.getInvocable());
+ mIdleTimer = std::make_unique<scheduler::IdleTimer>(3ms, mResetTimerCallback.getInvocable(),
+ mExpiredTimerCallback.getInvocable());
mIdleTimer->start();
+ EXPECT_TRUE(mResetTimerCallback.waitForCall(1ms).has_value());
+
mIdleTimer->stop();
clearPendingCallbacks();
mIdleTimer->reset();
+
// No more idle events should be observed
EXPECT_FALSE(mExpiredTimerCallback.waitForCall(waitTimeForUnexpected3msCallback).has_value());
+ EXPECT_FALSE(mResetTimerCallback.waitForCall(1ms).has_value());
}
} // namespace
} // namespace scheduler
-} // namespace android
\ No newline at end of file
+} // namespace android
diff --git a/services/surfaceflinger/tests/unittests/SchedulerTest.cpp b/services/surfaceflinger/tests/unittests/SchedulerTest.cpp
index 26b6d0c..ec76538 100644
--- a/services/surfaceflinger/tests/unittests/SchedulerTest.cpp
+++ b/services/surfaceflinger/tests/unittests/SchedulerTest.cpp
@@ -82,7 +82,6 @@
.WillRepeatedly(Return(mEventThreadConnection));
mConnectionHandle = mScheduler->createConnection("appConnection", 16, ResyncCallback(),
- ResetIdleTimerCallback(),
impl::EventThread::InterceptVSyncsCallback());
EXPECT_TRUE(mConnectionHandle != nullptr);
}
@@ -103,8 +102,7 @@
// exceptions, just gracefully continues.
sp<IDisplayEventConnection> returnedValue;
ASSERT_NO_FATAL_FAILURE(
- returnedValue = mScheduler->createDisplayEventConnection(nullptr, ResyncCallback(),
- ResetIdleTimerCallback()));
+ returnedValue = mScheduler->createDisplayEventConnection(nullptr, ResyncCallback()));
EXPECT_TRUE(returnedValue == nullptr);
EXPECT_TRUE(mScheduler->getEventThread(nullptr) == nullptr);
EXPECT_TRUE(mScheduler->getEventConnection(nullptr) == nullptr);
@@ -125,8 +123,7 @@
sp<IDisplayEventConnection> returnedValue;
ASSERT_NO_FATAL_FAILURE(
returnedValue =
- mScheduler->createDisplayEventConnection(connectionHandle, ResyncCallback(),
- ResetIdleTimerCallback()));
+ mScheduler->createDisplayEventConnection(connectionHandle, ResyncCallback()));
EXPECT_TRUE(returnedValue == nullptr);
EXPECT_TRUE(mScheduler->getEventThread(connectionHandle) == nullptr);
EXPECT_TRUE(mScheduler->getEventConnection(connectionHandle) == nullptr);
@@ -155,8 +152,7 @@
sp<IDisplayEventConnection> returnedValue;
ASSERT_NO_FATAL_FAILURE(
returnedValue =
- mScheduler->createDisplayEventConnection(mConnectionHandle, ResyncCallback(),
- ResetIdleTimerCallback()));
+ mScheduler->createDisplayEventConnection(mConnectionHandle, ResyncCallback()));
EXPECT_TRUE(returnedValue != nullptr);
ASSERT_EQ(returnedValue, mEventThreadConnection);