Merge "[Lut shader] in Adaptive mode, GPU path outputs gamma 2p2 encoding for HDR layer." into main
diff --git a/METADATA b/METADATA
index d97975c..86892cd 100644
--- a/METADATA
+++ b/METADATA
@@ -1,3 +1,3 @@
-third_party {
+ third_party {
license_type: NOTICE
}
diff --git a/cmds/servicemanager/Access.cpp b/cmds/servicemanager/Access.cpp
index 8098724..6e2abf6 100644
--- a/cmds/servicemanager/Access.cpp
+++ b/cmds/servicemanager/Access.cpp
@@ -34,7 +34,9 @@
#ifdef __ANDROID__
static std::string getPidcon(pid_t pid) {
- android_errorWriteLog(0x534e4554, "121035042");
+ CHECK_EQ(nullptr, IPCThreadState::self()->getServingStackPointer())
+ << "Did not get context from PID " << pid
+ << ". We should always get contexts from other processes.";
char* lookup = nullptr;
if (getpidcon(pid, &lookup) < 0) {
diff --git a/include/android/looper.h b/include/android/looper.h
index 8cf1396..409db84 100644
--- a/include/android/looper.h
+++ b/include/android/looper.h
@@ -106,7 +106,7 @@
/**
* Result from ALooper_pollOnce() and ALooper_pollAll():
* An error occurred. The poll may also have been explicitly woken by
- * ALooper_wake(()).
+ * ALooper_wake().
*/
ALOOPER_POLL_ERROR = -4,
};
@@ -224,11 +224,11 @@
* hidden and callers should migrate to ALooper_pollOnce. Binary compatibility
* is preserved to support already-compiled apps.
*
- * \bug ALooper_pollAll will not wake in response to ALooper_wake calls if it
+ * \bug ALooper_pollAll() will not wake in response to ALooper_wake() calls if it
* also handles another event at the same time.
*
- * \deprecated Calls to ALooper_pollAll should be replaced with
- * ALooper_pollOnce. If you call ALooper_pollOnce in a loop, you *must* treat
+ * \deprecated Calls to ALooper_pollAll() should be replaced with
+ * ALooper_pollOnce(). If you call ALooper_pollOnce() in a loop, you *must* treat
* all return values as if they also indicate ALOOPER_POLL_WAKE.
*/
int ALooper_pollAll(int timeoutMillis, int* outFd, int* outEvents, void** outData)
@@ -242,7 +242,7 @@
* This method can be called on any thread.
* This method returns immediately.
*
- * \bug ALooper_pollAll will not reliably wake in response to ALooper_wake.
+ * \bug ALooper_pollAll() will not reliably wake in response to ALooper_wake().
*/
void ALooper_wake(ALooper* looper);
diff --git a/include/android/performance_hint.h b/include/android/performance_hint.h
index 2b4a5f5..9ea6549 100644
--- a/include/android/performance_hint.h
+++ b/include/android/performance_hint.h
@@ -478,8 +478,13 @@
/**
* Return the APerformanceHintSession wrapped by a Java PerformanceHintManager.Session object.
*
- * The Java session maintains ownership over the wrapped native session, so it cannot be
- * closed using {@link APerformanceHint_closeSession}.
+ * The Java session maintains ownership over the wrapped native session, so it cannot be closed
+ * using {@link APerformanceHint_closeSession}. The return value is valid until the Java object
+ * containing this value dies.
+ *
+ * The returned pointer is intended to be used by JNI calls to access native performance APIs using
+ * a Java hint session wrapper, and then immediately discarded. Using the pointer after the death of
+ * the Java container results in undefined behavior.
*
* @param env The Java environment where the PerformanceHintManager.Session lives.
* @param sessionObj The Java Session to unwrap.
diff --git a/include/android/system_health.h b/include/android/system_health.h
index 69620df..352eb72 100644
--- a/include/android/system_health.h
+++ b/include/android/system_health.h
@@ -81,10 +81,10 @@
*
* @return A new instance of ACpuHeadroomParams.
*/
-ACpuHeadroomParams *_Nonnull ACpuHeadroomParams_create()
+ACpuHeadroomParams* _Nonnull ACpuHeadroomParams_create(void)
__INTRODUCED_IN(36);
-enum ACpuHeadroomCalculationType {
+typedef enum ACpuHeadroomCalculationType : int32_t {
/**
* Use the minimum headroom value within the calculation window.
* Introduced in API level 36.
@@ -95,10 +95,9 @@
* Introduced in API level 36.
*/
ACPU_HEADROOM_CALCULATION_TYPE_AVERAGE = 1,
-};
-typedef enum ACpuHeadroomCalculationType ACpuHeadroomCalculationType;
+} ACpuHeadroomCalculationType;
-enum AGpuHeadroomCalculationType {
+typedef enum AGpuHeadroomCalculationType : int32_t {
/**
* Use the minimum headroom value within the calculation window.
* Introduced in API level 36.
@@ -109,8 +108,7 @@
* Introduced in API level 36.
*/
AGPU_HEADROOM_CALCULATION_TYPE_AVERAGE = 1,
-};
-typedef enum AGpuHeadroomCalculationType AGpuHeadroomCalculationType;
+} AGpuHeadroomCalculationType;
/**
* Sets the headroom calculation window size in ACpuHeadroomParams.
@@ -124,7 +122,7 @@
* {@link #ACpuHeadroomParams_getCalculationWindowMillis} if not set. The device
* will try to use the closest feasible window size to this param.
*/
-void ACpuHeadroomParams_setCalculationWindowMillis(ACpuHeadroomParams *_Nonnull params,
+void ACpuHeadroomParams_setCalculationWindowMillis(ACpuHeadroomParams* _Nonnull params,
int windowMillis)
__INTRODUCED_IN(36);
@@ -136,7 +134,7 @@
* @param params The params to be set.
* @return This will return the default value chosen by the device if the params is not set.
*/
-int ACpuHeadroomParams_getCalculationWindowMillis(ACpuHeadroomParams *_Nonnull params)
+int ACpuHeadroomParams_getCalculationWindowMillis(ACpuHeadroomParams* _Nonnull params)
__INTRODUCED_IN(36);
/**
@@ -151,7 +149,7 @@
* {@link #AGpuHeadroomParams_getCalculationWindowMillis} if not set. The device
* will try to use the closest feasible window size to this param.
*/
-void AGpuHeadroomParams_setCalculationWindowMillis(AGpuHeadroomParams *_Nonnull params,
+void AGpuHeadroomParams_setCalculationWindowMillis(AGpuHeadroomParams* _Nonnull params,
int windowMillis)
__INTRODUCED_IN(36);
@@ -163,7 +161,7 @@
* @param params The params to be set.
* @return This will return the default value chosen by the device if the params is not set.
*/
-int AGpuHeadroomParams_getCalculationWindowMillis(AGpuHeadroomParams *_Nonnull params)
+int AGpuHeadroomParams_getCalculationWindowMillis(AGpuHeadroomParams* _Nonnull params)
__INTRODUCED_IN(36);
/**
@@ -174,7 +172,7 @@
* @param params The params to be set.
* @param calculationType The headroom calculation type.
*/
-void ACpuHeadroomParams_setCalculationType(ACpuHeadroomParams *_Nonnull params,
+void ACpuHeadroomParams_setCalculationType(ACpuHeadroomParams* _Nonnull params,
ACpuHeadroomCalculationType calculationType)
__INTRODUCED_IN(36);
@@ -187,7 +185,7 @@
* @return The headroom calculation type.
*/
ACpuHeadroomCalculationType
-ACpuHeadroomParams_getCalculationType(ACpuHeadroomParams *_Nonnull params)
+ACpuHeadroomParams_getCalculationType(ACpuHeadroomParams* _Nonnull params)
__INTRODUCED_IN(36);
/**
@@ -198,7 +196,7 @@
* @param params The params to be set.
* @param calculationType The headroom calculation type.
*/
-void AGpuHeadroomParams_setCalculationType(AGpuHeadroomParams *_Nonnull params,
+void AGpuHeadroomParams_setCalculationType(AGpuHeadroomParams* _Nonnull params,
AGpuHeadroomCalculationType calculationType)
__INTRODUCED_IN(36);
@@ -211,7 +209,7 @@
* @return The headroom calculation type.
*/
AGpuHeadroomCalculationType
-AGpuHeadroomParams_getCalculationType(AGpuHeadroomParams *_Nonnull params)
+AGpuHeadroomParams_getCalculationType(AGpuHeadroomParams* _Nonnull params)
__INTRODUCED_IN(36);
/**
@@ -223,7 +221,7 @@
* @param tids Non-null array of TIDs, maximum 5.
* @param tidsSize The size of the tids array.
*/
-void ACpuHeadroomParams_setTids(ACpuHeadroomParams *_Nonnull params, const int *_Nonnull tids,
+void ACpuHeadroomParams_setTids(ACpuHeadroomParams* _Nonnull params, const int* _Nonnull tids,
int tidsSize)
__INTRODUCED_IN(36);
@@ -238,7 +236,7 @@
*
* @return A new instance of AGpuHeadroomParams.
*/
-AGpuHeadroomParams *_Nonnull AGpuHeadroomParams_create()
+AGpuHeadroomParams* _Nonnull AGpuHeadroomParams_create(void)
__INTRODUCED_IN(36);
/**
@@ -248,7 +246,7 @@
*
* @param params The params to be deleted.
*/
-void ACpuHeadroomParams_destroy(ACpuHeadroomParams *_Nonnull params)
+void ACpuHeadroomParams_destroy(ACpuHeadroomParams* _Nonnull params)
__INTRODUCED_IN(36);
/**
@@ -258,7 +256,7 @@
*
* @param params The params to be deleted.
*/
-void AGpuHeadroomParams_destroy(AGpuHeadroomParams *_Nonnull params)
+void AGpuHeadroomParams_destroy(AGpuHeadroomParams* _Nonnull params)
__INTRODUCED_IN(36);
/**
@@ -282,8 +280,8 @@
* EPERM if the TIDs do not belong to the same process.
* ENOTSUP if API or requested params is unsupported.
*/
-int ASystemHealth_getCpuHeadroom(const ACpuHeadroomParams *_Nullable params,
- float *_Nonnull outHeadroom)
+int ASystemHealth_getCpuHeadroom(const ACpuHeadroomParams* _Nullable params,
+ float* _Nonnull outHeadroom)
__INTRODUCED_IN(36);
/**
@@ -306,8 +304,8 @@
* EPIPE if failed to get the GPU headroom.
* ENOTSUP if API or requested params is unsupported.
*/
-int ASystemHealth_getGpuHeadroom(const AGpuHeadroomParams *_Nullable params,
- float *_Nonnull outHeadroom)
+int ASystemHealth_getGpuHeadroom(const AGpuHeadroomParams* _Nullable params,
+ float* _Nonnull outHeadroom)
__INTRODUCED_IN(36);
/**
@@ -323,7 +321,7 @@
* EPIPE if failed to get the minimum polling interval.
* ENOTSUP if API is unsupported.
*/
-int ASystemHealth_getCpuHeadroomMinIntervalMillis(int64_t *_Nonnull outMinIntervalMillis)
+int ASystemHealth_getCpuHeadroomMinIntervalMillis(int64_t* _Nonnull outMinIntervalMillis)
__INTRODUCED_IN(36);
/**
@@ -339,7 +337,7 @@
* EPIPE if failed to get the minimum polling interval.
* ENOTSUP if API is unsupported.
*/
-int ASystemHealth_getGpuHeadroomMinIntervalMillis(int64_t *_Nonnull outMinIntervalMillis)
+int ASystemHealth_getGpuHeadroomMinIntervalMillis(int64_t* _Nonnull outMinIntervalMillis)
__INTRODUCED_IN(36);
#ifdef __cplusplus
diff --git a/libs/binder/IPCThreadState.cpp b/libs/binder/IPCThreadState.cpp
index 6698d0c..623e7b9 100644
--- a/libs/binder/IPCThreadState.cpp
+++ b/libs/binder/IPCThreadState.cpp
@@ -775,6 +775,7 @@
{
LOG_THREADPOOL("**** THREAD %p (PID %d) IS JOINING THE THREAD POOL\n", (void*)pthread_self(),
getpid());
+ mProcess->checkExpectingThreadPoolStart();
mProcess->mCurrentThreads++;
mOut.writeInt32(isMain ? BC_ENTER_LOOPER : BC_REGISTER_LOOPER);
diff --git a/libs/binder/Parcel.cpp b/libs/binder/Parcel.cpp
index a5f416f..0b7cd81 100644
--- a/libs/binder/Parcel.cpp
+++ b/libs/binder/Parcel.cpp
@@ -156,7 +156,7 @@
#ifdef BINDER_WITH_KERNEL_IPC
static void acquire_object(const sp<ProcessState>& proc, const flat_binder_object& obj,
- const void* who) {
+ const void* who, bool tagFds) {
switch (obj.hdr.type) {
case BINDER_TYPE_BINDER:
if (obj.binder) {
@@ -173,7 +173,7 @@
return;
}
case BINDER_TYPE_FD: {
- if (obj.cookie != 0) { // owned
+ if (tagFds && obj.cookie != 0) { // owned
FdTag(obj.handle, nullptr, who);
}
return;
@@ -611,7 +611,7 @@
}
}
- acquire_object(proc, *flat, this);
+ acquire_object(proc, *flat, this, true /*tagFds*/);
}
}
#else
@@ -1797,13 +1797,22 @@
// Need to write meta-data?
if (nullMetaData || val.binder != 0) {
kernelFields->mObjects[kernelFields->mObjectsSize] = mDataPos;
- acquire_object(ProcessState::self(), val, this);
+ acquire_object(ProcessState::self(), val, this, true /*tagFds*/);
kernelFields->mObjectsSize++;
}
return finishWrite(sizeof(flat_binder_object));
}
+ if (mOwner) {
+ // continueWrite does have the logic to convert this from an
+ // owned to an unowned Parcel. However, this is pretty inefficient,
+ // and it's really strange to need to do so, so prefer to avoid
+ // these paths than try to support them.
+ ALOGE("writing objects not supported on owned Parcels");
+ return PERMISSION_DENIED;
+ }
+
if (!enoughData) {
const status_t err = growData(sizeof(val));
if (err != NO_ERROR) return err;
@@ -2719,6 +2728,65 @@
return 0;
}
+static void do_nothing_release_func(const uint8_t* data, size_t dataSize,
+ const binder_size_t* objects, size_t objectsCount) {
+ (void)data;
+ (void)dataSize;
+ (void)objects;
+ (void)objectsCount;
+}
+static void delete_data_release_func(const uint8_t* data, size_t dataSize,
+ const binder_size_t* objects, size_t objectsCount) {
+ delete[] data;
+ (void)dataSize;
+ (void)objects;
+ (void)objectsCount;
+}
+
+void Parcel::makeDangerousViewOf(Parcel* p) {
+ if (p->isForRpc()) {
+ // warning: this must match the logic in rpcSetDataReference
+ auto* rf = p->maybeRpcFields();
+ LOG_ALWAYS_FATAL_IF(rf == nullptr);
+ std::vector<std::variant<binder::unique_fd, binder::borrowed_fd>> fds;
+ if (rf->mFds) {
+ fds.reserve(rf->mFds->size());
+ for (const auto& fd : *rf->mFds) {
+ fds.push_back(binder::borrowed_fd(toRawFd(fd)));
+ }
+ }
+ status_t result =
+ rpcSetDataReference(rf->mSession, p->mData, p->mDataSize,
+ rf->mObjectPositions.data(), rf->mObjectPositions.size(),
+ std::move(fds), do_nothing_release_func);
+ LOG_ALWAYS_FATAL_IF(result != OK, "Failed: %s", statusToString(result).c_str());
+ } else {
+#ifdef BINDER_WITH_KERNEL_IPC
+ // warning: this must match the logic in ipcSetDataReference
+ auto* kf = p->maybeKernelFields();
+ LOG_ALWAYS_FATAL_IF(kf == nullptr);
+
+ // Ownership of FDs is passed to the Parcel from kernel binder. This should be refactored
+ // to move this ownership out of Parcel and into release_func. However, today, Parcel
+ // always assums it can own and close FDs today. So, for purposes of testing consistency,
+ // , create new FDs it can own.
+
+ uint8_t* newData = new uint8_t[p->mDataSize]; // deleted by delete_data_release_func
+ memcpy(newData, p->mData, p->mDataSize);
+ for (size_t i = 0; i < kf->mObjectsSize; i++) {
+ flat_binder_object* flat =
+ reinterpret_cast<flat_binder_object*>(newData + kf->mObjects[i]);
+ if (flat->hdr.type == BINDER_TYPE_FD) {
+ flat->handle = fcntl(flat->handle, F_DUPFD_CLOEXEC, 0);
+ }
+ }
+
+ ipcSetDataReference(newData, p->mDataSize, kf->mObjects, kf->mObjectsSize,
+ delete_data_release_func);
+#endif // BINDER_WITH_KERNEL_IPC
+ }
+}
+
void Parcel::ipcSetDataReference(const uint8_t* data, size_t dataSize, const binder_size_t* objects,
size_t objectsCount, release_func relFunc) {
// this code uses 'mOwner == nullptr' to understand whether it owns memory
@@ -2729,6 +2797,7 @@
auto* kernelFields = maybeKernelFields();
LOG_ALWAYS_FATAL_IF(kernelFields == nullptr); // guaranteed by freeData.
+ // must match makeDangerousViewOf
mData = const_cast<uint8_t*>(data);
mDataSize = mDataCapacity = dataSize;
kernelFields->mObjects = const_cast<binder_size_t*>(objects);
@@ -2807,6 +2876,7 @@
auto* rpcFields = maybeRpcFields();
LOG_ALWAYS_FATAL_IF(rpcFields == nullptr); // guaranteed by markForRpc.
+ // must match makeDangerousViewOf
mData = const_cast<uint8_t*>(data);
mDataSize = mDataCapacity = dataSize;
mOwner = relFunc;
@@ -2874,15 +2944,17 @@
#endif // BINDER_WITH_KERNEL_IPC
}
-void Parcel::acquireObjects()
-{
+void Parcel::reacquireObjects(size_t objectsSize) {
auto* kernelFields = maybeKernelFields();
if (kernelFields == nullptr) {
return;
}
#ifdef BINDER_WITH_KERNEL_IPC
- size_t i = kernelFields->mObjectsSize;
+ LOG_ALWAYS_FATAL_IF(objectsSize > kernelFields->mObjectsSize,
+ "Object size %zu out of range of %zu", objectsSize,
+ kernelFields->mObjectsSize);
+ size_t i = objectsSize;
if (i == 0) {
return;
}
@@ -2892,8 +2964,10 @@
while (i > 0) {
i--;
const flat_binder_object* flat = reinterpret_cast<flat_binder_object*>(data + objects[i]);
- acquire_object(proc, *flat, this);
+ acquire_object(proc, *flat, this, false /*tagFds*/); // they are already tagged
}
+#else
+ (void) objectsSize;
#endif // BINDER_WITH_KERNEL_IPC
}
@@ -3110,12 +3184,8 @@
return NO_MEMORY;
}
- // Little hack to only acquire references on objects
- // we will be keeping.
- size_t oldObjectsSize = kernelFields->mObjectsSize;
- kernelFields->mObjectsSize = objectsSize;
- acquireObjects();
- kernelFields->mObjectsSize = oldObjectsSize;
+ // only acquire references on objects we are keeping
+ reacquireObjects(objectsSize);
}
if (rpcFields) {
if (status_t status = truncateRpcObjects(objectsSize); status != OK) {
diff --git a/libs/binder/ProcessState.cpp b/libs/binder/ProcessState.cpp
index 5e7f151..0e1e9b4 100644
--- a/libs/binder/ProcessState.cpp
+++ b/libs/binder/ProcessState.cpp
@@ -501,6 +501,21 @@
return mThreadPoolStarted;
}
+void ProcessState::checkExpectingThreadPoolStart() const {
+ if (mThreadPoolStarted) return;
+
+ // this is also racey, but you should setup the threadpool in the main thread. If that is an
+ // issue, we can check if we are the process leader, but haven't seen the issue in practice.
+ size_t requestedThreads = mMaxThreads.load();
+
+ // if it's manually set to the default, we do ignore it here...
+ if (requestedThreads == DEFAULT_MAX_BINDER_THREADS) return;
+ if (requestedThreads == 0) return;
+
+ ALOGW("Thread pool configuration of size %zu requested, but startThreadPool was not called.",
+ requestedThreads);
+}
+
#define DRIVER_FEATURES_PATH "/dev/binderfs/features/"
bool ProcessState::isDriverFeatureEnabled(const DriverFeature feature) {
// Use static variable to cache the results.
diff --git a/libs/binder/binder_module.h b/libs/binder/binder_module.h
index 65cdcd7..b3a2d9e 100644
--- a/libs/binder/binder_module.h
+++ b/libs/binder/binder_module.h
@@ -32,34 +32,4 @@
#include <linux/android/binder.h>
#include <sys/ioctl.h>
-struct binder_frozen_state_info {
- binder_uintptr_t cookie;
- __u32 is_frozen;
-};
-
-#ifndef BR_FROZEN_BINDER
-// Temporary definition of BR_FROZEN_BINDER until UAPI binder.h includes it.
-#define BR_FROZEN_BINDER _IOR('r', 21, struct binder_frozen_state_info)
-#endif // BR_FROZEN_BINDER
-
-#ifndef BR_CLEAR_FREEZE_NOTIFICATION_DONE
-// Temporary definition of BR_CLEAR_FREEZE_NOTIFICATION_DONE until UAPI binder.h includes it.
-#define BR_CLEAR_FREEZE_NOTIFICATION_DONE _IOR('r', 22, binder_uintptr_t)
-#endif // BR_CLEAR_FREEZE_NOTIFICATION_DONE
-
-#ifndef BC_REQUEST_FREEZE_NOTIFICATION
-// Temporary definition of BC_REQUEST_FREEZE_NOTIFICATION until UAPI binder.h includes it.
-#define BC_REQUEST_FREEZE_NOTIFICATION _IOW('c', 19, struct binder_handle_cookie)
-#endif // BC_REQUEST_FREEZE_NOTIFICATION
-
-#ifndef BC_CLEAR_FREEZE_NOTIFICATION
-// Temporary definition of BC_CLEAR_FREEZE_NOTIFICATION until UAPI binder.h includes it.
-#define BC_CLEAR_FREEZE_NOTIFICATION _IOW('c', 20, struct binder_handle_cookie)
-#endif // BC_CLEAR_FREEZE_NOTIFICATION
-
-#ifndef BC_FREEZE_NOTIFICATION_DONE
-// Temporary definition of BC_FREEZE_NOTIFICATION_DONE until UAPI binder.h includes it.
-#define BC_FREEZE_NOTIFICATION_DONE _IOW('c', 21, binder_uintptr_t)
-#endif // BC_FREEZE_NOTIFICATION_DONE
-
#endif // _BINDER_MODULE_H_
diff --git a/libs/binder/include/binder/IInterface.h b/libs/binder/include/binder/IInterface.h
index 5924a2d..cdee17c 100644
--- a/libs/binder/include/binder/IInterface.h
+++ b/libs/binder/include/binder/IInterface.h
@@ -219,7 +219,6 @@
constexpr const char* const kManualInterfaces[] = {
"android.app.IActivityManager",
"android.app.IUidObserver",
- "android.drm.IDrm",
"android.gfx.tests.ICallback",
"android.gfx.tests.IIPCTest",
"android.gfx.tests.ISafeInterfaceTest",
@@ -233,17 +232,13 @@
"android.hardware.ICameraClient",
"android.hardware.ICameraRecordingProxy",
"android.hardware.ICameraRecordingProxyListener",
- "android.hardware.ICrypto",
"android.hardware.IOMXObserver",
"android.hardware.IStreamListener",
"android.hardware.IStreamSource",
"android.media.IAudioService",
"android.media.IDataSource",
- "android.media.IDrmClient",
"android.media.IMediaCodecList",
- "android.media.IMediaDrmService",
"android.media.IMediaExtractor",
- "android.media.IMediaExtractorService",
"android.media.IMediaHTTPConnection",
"android.media.IMediaHTTPService",
"android.media.IMediaLogService",
@@ -258,9 +253,6 @@
"android.media.IMediaSource",
"android.media.IRemoteDisplay",
"android.media.IRemoteDisplayClient",
- "android.media.IResourceManagerClient",
- "android.media.IResourceManagerService",
- "android.os.IComplexTypeInterface",
"android.os.IPermissionController",
"android.os.IProcessInfoService",
"android.os.ISchedulingPolicyService",
diff --git a/libs/binder/include/binder/Parcel.h b/libs/binder/include/binder/Parcel.h
index 0c7366e..b4efa0a 100644
--- a/libs/binder/include/binder/Parcel.h
+++ b/libs/binder/include/binder/Parcel.h
@@ -649,6 +649,11 @@
LIBBINDER_EXPORTED void print(std::ostream& to, uint32_t flags = 0) const;
+ // This API is to quickly become a view of another Parcel, so that we can also
+ // test 'owner' paths quickly. It's extremely dangerous to use this API in
+ // practice, and you should never ever do it.
+ LIBBINDER_EXPORTED void makeDangerousViewOf(Parcel* p);
+
private:
// Close all file descriptors in the parcel at object positions >= newObjectsSize.
void closeFileDescriptors(size_t newObjectsSize);
@@ -664,7 +669,7 @@
void ipcSetDataReference(const uint8_t* data, size_t dataSize, const binder_size_t* objects,
size_t objectsCount, release_func relFunc);
// Takes ownership even when an error is returned.
- status_t rpcSetDataReference(
+ [[nodiscard]] status_t rpcSetDataReference(
const sp<RpcSession>& session, const uint8_t* data, size_t dataSize,
const uint32_t* objectTable, size_t objectTableSize,
std::vector<std::variant<binder::unique_fd, binder::borrowed_fd>>&& ancillaryFds,
@@ -672,7 +677,7 @@
status_t finishWrite(size_t len);
void releaseObjects();
- void acquireObjects();
+ void reacquireObjects(size_t objectSize);
status_t growData(size_t len);
// Clear the Parcel and set the capacity to `desired`.
// Doesn't reset the RPC session association.
diff --git a/libs/binder/include/binder/ProcessState.h b/libs/binder/include/binder/ProcessState.h
index 21bfd42..ced49c1 100644
--- a/libs/binder/include/binder/ProcessState.h
+++ b/libs/binder/include/binder/ProcessState.h
@@ -141,6 +141,8 @@
private:
static sp<ProcessState> init(const char* defaultDriver, bool requireDefault);
+ void checkExpectingThreadPoolStart() const;
+
static void onFork();
static void parentPostFork();
static void childPostFork();
diff --git a/libs/binder/libbinder_rpc_unstable.cpp b/libs/binder/libbinder_rpc_unstable.cpp
index a84a0c6..64b1be2 100644
--- a/libs/binder/libbinder_rpc_unstable.cpp
+++ b/libs/binder/libbinder_rpc_unstable.cpp
@@ -23,11 +23,8 @@
#ifndef __TRUSTY__
#include <cutils/sockets.h>
-#endif
-
-#ifdef __linux__
-#include <linux/vm_sockets.h>
-#endif // __linux__
+#include "vm_sockets.h"
+#endif // !__TRUSTY__
using android::OK;
using android::RpcServer;
diff --git a/libs/binder/rust/src/lib.rs b/libs/binder/rust/src/lib.rs
index 1b24b0a..e08a763 100644
--- a/libs/binder/rust/src/lib.rs
+++ b/libs/binder/rust/src/lib.rs
@@ -104,7 +104,7 @@
mod service;
#[cfg(not(any(trusty, android_ndk)))]
mod state;
-#[cfg(not(any(android_vendor, android_ndk, android_vndk)))]
+#[cfg(not(any(android_vendor, android_ndk, android_vndk, trusty)))]
mod system_only;
use binder_ndk_sys as sys;
@@ -125,7 +125,7 @@
pub use service::{get_interface, get_service};
#[cfg(not(any(trusty, android_ndk)))]
pub use state::{ProcessState, ThreadState};
-#[cfg(not(any(android_vendor, android_vndk, android_ndk)))]
+#[cfg(not(any(android_vendor, android_vndk, android_ndk, trusty)))]
pub use system_only::{delegate_accessor, Accessor, AccessorProvider, ConnectionInfo};
/// Binder result containing a [`Status`] on error.
diff --git a/libs/binder/tests/parcel_fuzzer/include_random_parcel/fuzzbinder/random_parcel.h b/libs/binder/tests/parcel_fuzzer/include_random_parcel/fuzzbinder/random_parcel.h
index 2812da7..11fcb06 100644
--- a/libs/binder/tests/parcel_fuzzer/include_random_parcel/fuzzbinder/random_parcel.h
+++ b/libs/binder/tests/parcel_fuzzer/include_random_parcel/fuzzbinder/random_parcel.h
@@ -28,6 +28,9 @@
std::function<void(Parcel* p, FuzzedDataProvider& provider)> writeHeader;
std::vector<sp<IBinder>> extraBinders;
std::vector<binder::unique_fd> extraFds;
+
+ // internal state owned by fillRandomParcel, for Parcel views
+ std::vector<std::unique_ptr<Parcel>> extraParcels;
};
/**
diff --git a/libs/binder/tests/parcel_fuzzer/main.cpp b/libs/binder/tests/parcel_fuzzer/main.cpp
index 192f9d5..d06b2d9 100644
--- a/libs/binder/tests/parcel_fuzzer/main.cpp
+++ b/libs/binder/tests/parcel_fuzzer/main.cpp
@@ -70,7 +70,7 @@
uint32_t code = provider.ConsumeIntegral<uint32_t>();
uint32_t flag = provider.ConsumeIntegral<uint32_t>();
- FUZZ_LOG() << "backend: " << backend;
+ FUZZ_LOG() << "doTransactFuzz backend: " << backend;
RandomParcelOptions options;
@@ -101,7 +101,7 @@
// since we are only using a byte to index
CHECK_LE(reads.size(), 255u) << reads.size();
- FUZZ_LOG() << "backend: " << backend;
+ FUZZ_LOG() << "doReadFuzz backend: " << backend;
FUZZ_LOG() << "input: " << HexString(p.data(), p.dataSize());
FUZZ_LOG() << "instructions: " << HexString(instructions.data(), instructions.size());
@@ -122,10 +122,15 @@
RandomParcelOptions options;
P p;
+ // small amount of initial Parcel data, since fillRandomParcel uses makeDangerousViewOf
+ std::vector<uint8_t> parcelData =
+ provider.ConsumeBytes<uint8_t>(provider.ConsumeIntegralInRange<size_t>(0, 20));
+ fillRandomParcel(&p, FuzzedDataProvider(parcelData.data(), parcelData.size()), &options);
+
// since we are only using a byte to index
CHECK_LE(reads.size() + writes.size(), 255u) << reads.size();
- FUZZ_LOG() << "backend: " << backend;
+ FUZZ_LOG() << "doReadWriteFuzz backend: " << backend;
while (provider.remaining_bytes() > 0) {
uint8_t idx = provider.ConsumeIntegralInRange<uint8_t>(0, reads.size() + writes.size() - 1);
diff --git a/libs/binder/tests/parcel_fuzzer/random_parcel.cpp b/libs/binder/tests/parcel_fuzzer/random_parcel.cpp
index 7c19614..dfd178a 100644
--- a/libs/binder/tests/parcel_fuzzer/random_parcel.cpp
+++ b/libs/binder/tests/parcel_fuzzer/random_parcel.cpp
@@ -17,6 +17,7 @@
#include <fuzzbinder/random_parcel.h>
#include <android-base/logging.h>
+#include <binder/Functional.h>
#include <binder/RpcSession.h>
#include <binder/RpcTransportRaw.h>
#include <fuzzbinder/random_binder.h>
@@ -32,10 +33,29 @@
CHECK(OK == p->write(data.data(), data.size()));
}
-void fillRandomParcel(Parcel* p, FuzzedDataProvider&& provider, RandomParcelOptions* options) {
+void fillRandomParcel(Parcel* outputParcel, FuzzedDataProvider&& provider,
+ RandomParcelOptions* options) {
CHECK_NE(options, nullptr);
- if (provider.ConsumeBool()) {
+ const uint8_t fuzzerParcelOptions = provider.ConsumeIntegral<uint8_t>();
+ const bool resultShouldBeView = fuzzerParcelOptions & 1;
+ const bool resultShouldBeRpc = fuzzerParcelOptions & 2;
+
+ Parcel* p;
+ if (resultShouldBeView) {
+ options->extraParcels.push_back(std::make_unique<Parcel>());
+ // held for duration of test, so that view will be valid
+ p = options->extraParcels[options->extraParcels.size() - 1].get();
+ } else {
+ p = outputParcel; // directly fill out the output Parcel
+ }
+ auto viewify_guard = binder::impl::make_scope_guard([&]() {
+ if (resultShouldBeView) {
+ outputParcel->makeDangerousViewOf(p);
+ }
+ });
+
+ if (resultShouldBeRpc) {
auto session = RpcSession::make(RpcTransportCtxFactoryRaw::make());
CHECK_EQ(OK, session->addNullDebuggingClient());
// Set the protocol version so that we don't crash if the session
diff --git a/libs/binder/tests/parcel_fuzzer/random_parcel_seeds.cpp b/libs/binder/tests/parcel_fuzzer/random_parcel_seeds.cpp
index 0ed8a55..3cb6289 100644
--- a/libs/binder/tests/parcel_fuzzer/random_parcel_seeds.cpp
+++ b/libs/binder/tests/parcel_fuzzer/random_parcel_seeds.cpp
@@ -281,9 +281,9 @@
// This buffer holds the bytes which will be used for fillRandomParcel API
std::vector<uint8_t> fillParcelBuffer;
- // Don't take rpc path
- uint8_t rpcBranch = 0;
- impl::writeReversedBuffer(fillParcelBuffer, rpcBranch);
+ // Use all default options.
+ uint8_t parcelOptions = 0;
+ impl::writeReversedBuffer(fillParcelBuffer, parcelOptions);
// Implicit branch on this path -> options->writeHeader(p, provider)
uint8_t writeHeaderInternal = 0;
diff --git a/libs/binder/trusty/rules.mk b/libs/binder/trusty/rules.mk
index 5e38ad0..dd9d4d1 100644
--- a/libs/binder/trusty/rules.mk
+++ b/libs/binder/trusty/rules.mk
@@ -64,14 +64,24 @@
MODULE_EXPORT_INCLUDES += \
$(LIBBINDER_DIR)/ndk/include_cpp \
+ifeq (false,$(call TOBOOL,$(USE_SYSTEM_BINDER)))
+BINDER_EXTRA_COMPILE_FLAGS := \
+ -D__ANDROID_VENDOR__ \
+ -D__ANDROID_VNDK__ \
+
+else
+BINDER_EXTRA_COMPILE_FLAGS := \
+ -DANDROID_PLATFORM \
+
+endif
+
MODULE_EXPORT_COMPILEFLAGS += \
-DBINDER_RPC_SINGLE_THREADED \
-DBINDER_ENABLE_LIBLOG_ASSERT \
-DBINDER_DISABLE_NATIVE_HANDLE \
-DBINDER_DISABLE_BLOB \
-DBINDER_NO_LIBBASE \
- -D__ANDROID_VENDOR__ \
- -D__ANDROID_VNDK__ \
+ $(BINDER_EXTRA_COMPILE_FLAGS)
# libbinder has some deprecated declarations that we want to produce warnings
# not errors
diff --git a/libs/binder/trusty/rust/binder_ndk_sys/rules.mk b/libs/binder/trusty/rust/binder_ndk_sys/rules.mk
index 2aaa061..56d711e 100644
--- a/libs/binder/trusty/rust/binder_ndk_sys/rules.mk
+++ b/libs/binder/trusty/rust/binder_ndk_sys/rules.mk
@@ -30,9 +30,14 @@
trusty/user/base/lib/trusty-sys \
MODULE_RUSTFLAGS += \
- --cfg 'android_vendor' \
--cfg 'trusty' \
+ifeq (false,$(call TOBOOL,$(USE_SYSTEM_BINDER)))
+MODULE_RUSTFLAGS += \
+ --cfg 'android_vendor' \
+
+endif
+
MODULE_BINDGEN_SRC_HEADER := $(LIBBINDER_DIR)/rust/sys/BinderBindings.hpp
# Add the flags from the flag file
diff --git a/libs/binder/trusty/rust/rules.mk b/libs/binder/trusty/rust/rules.mk
index e622b22..acd74d2 100644
--- a/libs/binder/trusty/rust/rules.mk
+++ b/libs/binder/trusty/rust/rules.mk
@@ -32,9 +32,15 @@
trusty/user/base/lib/trusty-sys \
MODULE_RUSTFLAGS += \
- --cfg 'android_vendor' \
--cfg 'trusty' \
+ifeq (false,$(call TOBOOL,$(USE_SYSTEM_BINDER)))
+MODULE_RUSTFLAGS += \
+ --cfg 'android_vendor' \
+
+endif
+
+
# Trusty does not have `ProcessState`, so there are a few
# doc links in `IBinder` that are still broken.
MODULE_RUSTFLAGS += \
diff --git a/libs/gui/BLASTBufferQueue.cpp b/libs/gui/BLASTBufferQueue.cpp
index 7aee903..38465b0 100644
--- a/libs/gui/BLASTBufferQueue.cpp
+++ b/libs/gui/BLASTBufferQueue.cpp
@@ -1032,7 +1032,7 @@
std::lock_guard _lock{mMutex};
if (mLastAcquiredFrameNumber >= frameNumber) {
// Apply the transaction since we have already acquired the desired frame.
- t->apply();
+ t->setApplyToken(mApplyToken).apply();
} else {
mPendingTransactions.emplace_back(frameNumber, *t);
// Clear the transaction so it can't be applied elsewhere.
@@ -1232,6 +1232,11 @@
return OK;
}
+ // Provide a callback for Choreographer to start buffer stuffing recovery when blocked
+ // on buffer release.
+ std::function<void()> callbackCopy = bbq->getWaitForBufferReleaseCallback();
+ if (callbackCopy) callbackCopy();
+
// BufferQueue has already checked if we have a free buffer. If there's an unread interrupt,
// we want to ignore it. This must be done before unlocking the BufferQueue lock to ensure
// we don't miss an interrupt.
@@ -1344,6 +1349,16 @@
mApplyToken = std::move(applyToken);
}
+void BLASTBufferQueue::setWaitForBufferReleaseCallback(std::function<void()> callback) {
+ std::lock_guard _lock{mWaitForBufferReleaseMutex};
+ mWaitForBufferReleaseCallback = std::move(callback);
+}
+
+std::function<void()> BLASTBufferQueue::getWaitForBufferReleaseCallback() const {
+ std::lock_guard _lock{mWaitForBufferReleaseMutex};
+ return mWaitForBufferReleaseCallback;
+}
+
#if COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(BUFFER_RELEASE_CHANNEL)
void BLASTBufferQueue::updateBufferReleaseProducer() {
diff --git a/libs/gui/BufferQueue.cpp b/libs/gui/BufferQueue.cpp
index b0f6e69..f1374e2 100644
--- a/libs/gui/BufferQueue.cpp
+++ b/libs/gui/BufferQueue.cpp
@@ -108,6 +108,15 @@
}
#endif
+#if COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(WB_UNLIMITED_SLOTS)
+void BufferQueue::ProxyConsumerListener::onSlotCountChanged(int slotCount) {
+ sp<ConsumerListener> listener(mConsumerListener.promote());
+ if (listener != nullptr) {
+ listener->onSlotCountChanged(slotCount);
+ }
+}
+#endif
+
void BufferQueue::createBufferQueue(sp<IGraphicBufferProducer>* outProducer,
sp<IGraphicBufferConsumer>* outConsumer,
bool consumerIsSurfaceFlinger) {
diff --git a/libs/gui/BufferQueueConsumer.cpp b/libs/gui/BufferQueueConsumer.cpp
index 9855b5b..f012586 100644
--- a/libs/gui/BufferQueueConsumer.cpp
+++ b/libs/gui/BufferQueueConsumer.cpp
@@ -341,9 +341,9 @@
return BAD_VALUE;
}
- if (slot < 0 || slot >= BufferQueueDefs::NUM_BUFFER_SLOTS) {
- BQ_LOGE("detachBuffer: slot index %d out of range [0, %d)",
- slot, BufferQueueDefs::NUM_BUFFER_SLOTS);
+ const int totalSlotCount = mCore->getTotalSlotCountLocked();
+ if (slot < 0 || slot >= totalSlotCount) {
+ BQ_LOGE("detachBuffer: slot index %d out of range [0, %d)", slot, totalSlotCount);
return BAD_VALUE;
} else if (!mSlots[slot].mBufferState.isAcquired()) {
BQ_LOGE("detachBuffer: slot %d is not owned by the consumer "
@@ -483,10 +483,13 @@
ATRACE_CALL();
ATRACE_BUFFER_INDEX(slot);
- if (slot < 0 || slot >= BufferQueueDefs::NUM_BUFFER_SLOTS ||
- releaseFence == nullptr) {
- BQ_LOGE("releaseBuffer: slot %d out of range or fence %p NULL", slot,
- releaseFence.get());
+ const int totalSlotCount = mCore->getTotalSlotCountLocked();
+ if (slot < 0 || slot >= totalSlotCount) {
+ BQ_LOGE("releaseBuffer: slot index %d out of range [0, %d)", slot, totalSlotCount);
+ return BAD_VALUE;
+ }
+ if (releaseFence == nullptr) {
+ BQ_LOGE("releaseBuffer: slot %d fence %p NULL", slot, releaseFence.get());
return BAD_VALUE;
}
@@ -515,6 +518,13 @@
{ // Autolock scope
std::lock_guard<std::mutex> lock(mCore->mMutex);
+ const int totalSlotCount = mCore->getTotalSlotCountLocked();
+ if (slot < 0 || slot >= totalSlotCount || releaseFence == nullptr) {
+ BQ_LOGE("releaseBuffer: slot %d out of range [0, %d) or fence %p NULL", slot,
+ totalSlotCount, releaseFence.get());
+ return BAD_VALUE;
+ }
+
// If the frame number has changed because the buffer has been reallocated,
// we can ignore this releaseBuffer for the old buffer.
// Ignore this for the shared buffer where the frame number can easily
@@ -661,6 +671,43 @@
return NO_ERROR;
}
+#if COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(WB_UNLIMITED_SLOTS)
+status_t BufferQueueConsumer::getReleasedBuffersExtended(std::vector<bool>* outSlotMask) {
+ ATRACE_CALL();
+
+ if (outSlotMask == nullptr) {
+ BQ_LOGE("getReleasedBuffersExtended: outSlotMask may not be NULL");
+ return BAD_VALUE;
+ }
+
+ std::lock_guard<std::mutex> lock(mCore->mMutex);
+
+ if (mCore->mIsAbandoned) {
+ BQ_LOGE("getReleasedBuffersExtended: BufferQueue has been abandoned");
+ return NO_INIT;
+ }
+
+ const int totalSlotCount = mCore->getTotalSlotCountLocked();
+ outSlotMask->resize(totalSlotCount);
+ for (int s = 0; s < totalSlotCount; ++s) {
+ (*outSlotMask)[s] = !mSlots[s].mAcquireCalled;
+ }
+
+ // Remove from the mask queued buffers for which acquire has been called,
+ // since the consumer will not receive their buffer addresses and so must
+ // retain their cached information
+ BufferQueueCore::Fifo::iterator current(mCore->mQueue.begin());
+ while (current != mCore->mQueue.end()) {
+ if (current->mAcquireCalled) {
+ (*outSlotMask)[current->mSlot] = false;
+ }
+ ++current;
+ }
+
+ return NO_ERROR;
+}
+#endif
+
status_t BufferQueueConsumer::setDefaultBufferSize(uint32_t width,
uint32_t height) {
ATRACE_CALL();
@@ -679,6 +726,28 @@
return NO_ERROR;
}
+#if COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(WB_UNLIMITED_SLOTS)
+status_t BufferQueueConsumer::allowUnlimitedSlots(bool allowUnlimitedSlots) {
+ ATRACE_CALL();
+ BQ_LOGV("allowUnlimitedSlots: %d", allowUnlimitedSlots);
+ std::lock_guard<std::mutex> lock(mCore->mMutex);
+
+ if (mCore->mIsAbandoned) {
+ BQ_LOGE("allowUnlimitedSlots: BufferQueue has been abandoned");
+ return NO_INIT;
+ }
+
+ if (mCore->mConnectedApi != BufferQueueCore::NO_CONNECTED_API) {
+ BQ_LOGE("allowUnlimitedSlots: BufferQueue already connected");
+ return INVALID_OPERATION;
+ }
+
+ mCore->mAllowExtendedSlotCount = allowUnlimitedSlots;
+
+ return OK;
+}
+#endif // COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(WB_UNLIMITED_SLOTS)
+
status_t BufferQueueConsumer::setMaxBufferCount(int bufferCount) {
ATRACE_CALL();
@@ -718,16 +787,23 @@
int maxAcquiredBuffers) {
ATRACE_FORMAT("%s(%d)", __func__, maxAcquiredBuffers);
- if (maxAcquiredBuffers < 1 ||
- maxAcquiredBuffers > BufferQueueCore::MAX_MAX_ACQUIRED_BUFFERS) {
- BQ_LOGE("setMaxAcquiredBufferCount: invalid count %d",
- maxAcquiredBuffers);
- return BAD_VALUE;
- }
-
sp<IConsumerListener> listener;
{ // Autolock scope
std::unique_lock<std::mutex> lock(mCore->mMutex);
+
+ // We reserve two slots in order to guarantee that the producer and
+ // consumer can run asynchronously.
+ int maxMaxAcquiredBuffers =
+#if COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(WB_UNLIMITED_SLOTS)
+ mCore->getTotalSlotCountLocked() - 2;
+#else
+ BufferQueueCore::MAX_MAX_ACQUIRED_BUFFERS;
+#endif
+ if (maxAcquiredBuffers < 1 || maxAcquiredBuffers > maxMaxAcquiredBuffers) {
+ BQ_LOGE("setMaxAcquiredBufferCount: invalid count %d", maxAcquiredBuffers);
+ return BAD_VALUE;
+ }
+
mCore->waitWhileAllocatingLocked(lock);
if (mCore->mIsAbandoned) {
diff --git a/libs/gui/BufferQueueCore.cpp b/libs/gui/BufferQueueCore.cpp
index 5a09399..6c79904 100644
--- a/libs/gui/BufferQueueCore.cpp
+++ b/libs/gui/BufferQueueCore.cpp
@@ -38,6 +38,8 @@
#include <system/window.h>
+#include <ui/BufferQueueDefs.h>
+
namespace android {
// Macros for include BufferQueueCore information in log messages
@@ -97,7 +99,11 @@
mConnectedProducerListener(),
mBufferReleasedCbEnabled(false),
mBufferAttachedCbEnabled(false),
+#if COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(WB_UNLIMITED_SLOTS)
+ mSlots(BufferQueueDefs::NUM_BUFFER_SLOTS),
+#else
mSlots(),
+#endif
mQueue(),
mFreeSlots(),
mFreeBuffers(),
@@ -111,6 +117,9 @@
mDefaultWidth(1),
mDefaultHeight(1),
mDefaultBufferDataSpace(HAL_DATASPACE_UNKNOWN),
+#if COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(WB_UNLIMITED_SLOTS)
+ mAllowExtendedSlotCount(false),
+#endif
mMaxBufferCount(BufferQueueDefs::NUM_BUFFER_SLOTS),
mMaxAcquiredBufferCount(1),
mMaxDequeuedBufferCount(1),
@@ -221,6 +230,14 @@
}
}
+int BufferQueueCore::getTotalSlotCountLocked() const {
+#if COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(WB_UNLIMITED_SLOTS)
+ return mAllowExtendedSlotCount ? mMaxBufferCount : BufferQueueDefs::NUM_BUFFER_SLOTS;
+#else
+ return BufferQueueDefs::NUM_BUFFER_SLOTS;
+#endif
+}
+
int BufferQueueCore::getMinUndequeuedBufferCountLocked() const {
// If dequeueBuffer is allowed to error out, we don't have to add an
// extra buffer.
@@ -253,6 +270,26 @@
return maxBufferCount;
}
+#if COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(WB_UNLIMITED_SLOTS)
+status_t BufferQueueCore::extendSlotCountLocked(int size) {
+ int previousSize = (int)mSlots.size();
+ if (previousSize > size) {
+ return BAD_VALUE;
+ }
+ if (previousSize == size) {
+ return NO_ERROR;
+ }
+
+ mSlots.resize(size);
+ for (int i = previousSize; i < size; i++) {
+ mUnusedSlots.push_back(i);
+ }
+
+ mMaxBufferCount = size;
+ return NO_ERROR;
+}
+#endif
+
void BufferQueueCore::clearBufferSlotLocked(int slot) {
BQ_LOGV("clearBufferSlotLocked: slot %d", slot);
@@ -383,7 +420,7 @@
void BufferQueueCore::validateConsistencyLocked() const {
static const useconds_t PAUSE_TIME = 0;
int allocatedSlots = 0;
- for (int slot = 0; slot < BufferQueueDefs::NUM_BUFFER_SLOTS; ++slot) {
+ for (int slot = 0; slot < getTotalSlotCountLocked(); ++slot) {
bool isInFreeSlots = mFreeSlots.count(slot) != 0;
bool isInFreeBuffers =
std::find(mFreeBuffers.cbegin(), mFreeBuffers.cend(), slot) !=
diff --git a/libs/gui/BufferQueueProducer.cpp b/libs/gui/BufferQueueProducer.cpp
index 2e7cef0..c241482 100644
--- a/libs/gui/BufferQueueProducer.cpp
+++ b/libs/gui/BufferQueueProducer.cpp
@@ -40,6 +40,7 @@
#include <gui/TraceUtils.h>
#include <private/gui/BufferQueueThreadState.h>
+#include <utils/Errors.h>
#include <utils/Log.h>
#include <utils/Trace.h>
@@ -108,9 +109,9 @@
return NO_INIT;
}
- if (slot < 0 || slot >= BufferQueueDefs::NUM_BUFFER_SLOTS) {
- BQ_LOGE("requestBuffer: slot index %d out of range [0, %d)",
- slot, BufferQueueDefs::NUM_BUFFER_SLOTS);
+ int maxSlot = mCore->getTotalSlotCountLocked();
+ if (slot < 0 || slot >= maxSlot) {
+ BQ_LOGE("requestBuffer: slot index %d out of range [0, %d)", slot, maxSlot);
return BAD_VALUE;
} else if (!mSlots[slot].mBufferState.isDequeued()) {
BQ_LOGE("requestBuffer: slot %d is not owned by the producer "
@@ -123,6 +124,49 @@
return NO_ERROR;
}
+#if COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(WB_UNLIMITED_SLOTS)
+status_t BufferQueueProducer::extendSlotCount(int size) {
+ ATRACE_CALL();
+
+ sp<IConsumerListener> listener;
+ {
+ std::lock_guard<std::mutex> lock(mCore->mMutex);
+ BQ_LOGV("extendSlotCount: size %d", size);
+
+ if (mCore->mIsAbandoned) {
+ BQ_LOGE("extendSlotCount: BufferQueue has been abandoned");
+ return NO_INIT;
+ }
+
+ if (!mCore->mAllowExtendedSlotCount) {
+ BQ_LOGE("extendSlotCount: Consumer did not allow unlimited slots");
+ return INVALID_OPERATION;
+ }
+
+ int maxBeforeExtension = mCore->mMaxBufferCount;
+
+ if (size == maxBeforeExtension) {
+ return NO_ERROR;
+ }
+
+ if (size < maxBeforeExtension) {
+ return BAD_VALUE;
+ }
+
+ if (status_t ret = mCore->extendSlotCountLocked(size); ret != OK) {
+ return ret;
+ }
+ listener = mCore->mConsumerListener;
+ }
+
+ if (listener) {
+ listener->onSlotCountChanged(size);
+ }
+
+ return NO_ERROR;
+}
+#endif
+
status_t BufferQueueProducer::setMaxDequeuedBufferCount(
int maxDequeuedBuffers) {
int maxBufferCount;
@@ -170,9 +214,10 @@
int bufferCount = mCore->getMinUndequeuedBufferCountLocked();
bufferCount += maxDequeuedBuffers;
- if (bufferCount > BufferQueueDefs::NUM_BUFFER_SLOTS) {
+ if (bufferCount > mCore->getTotalSlotCountLocked()) {
BQ_LOGE("setMaxDequeuedBufferCount: bufferCount %d too large "
- "(max %d)", bufferCount, BufferQueueDefs::NUM_BUFFER_SLOTS);
+ "(max %d)",
+ bufferCount, mCore->getTotalSlotCountLocked());
return BAD_VALUE;
}
@@ -756,9 +801,9 @@
return BAD_VALUE;
}
- if (slot < 0 || slot >= BufferQueueDefs::NUM_BUFFER_SLOTS) {
- BQ_LOGE("detachBuffer: slot index %d out of range [0, %d)",
- slot, BufferQueueDefs::NUM_BUFFER_SLOTS);
+ const int totalSlotCount = mCore->getTotalSlotCountLocked();
+ if (slot < 0 || slot >= totalSlotCount) {
+ BQ_LOGE("detachBuffer: slot index %d out of range [0, %d)", slot, totalSlotCount);
return BAD_VALUE;
} else if (!mSlots[slot].mBufferState.isDequeued()) {
// TODO(http://b/140581935): This message is BQ_LOGW because it
@@ -993,9 +1038,9 @@
return NO_INIT;
}
- if (slot < 0 || slot >= BufferQueueDefs::NUM_BUFFER_SLOTS) {
- BQ_LOGE("queueBuffer: slot index %d out of range [0, %d)",
- slot, BufferQueueDefs::NUM_BUFFER_SLOTS);
+ const int totalSlotCount = mCore->getTotalSlotCountLocked();
+ if (slot < 0 || slot >= totalSlotCount) {
+ BQ_LOGE("queueBuffer: slot index %d out of range [0, %d)", slot, totalSlotCount);
return BAD_VALUE;
} else if (!mSlots[slot].mBufferState.isDequeued()) {
BQ_LOGE("queueBuffer: slot %d is not owned by the producer "
@@ -1239,9 +1284,9 @@
return BAD_VALUE;
}
- if (slot < 0 || slot >= BufferQueueDefs::NUM_BUFFER_SLOTS) {
- BQ_LOGE("cancelBuffer: slot index %d out of range [0, %d)", slot,
- BufferQueueDefs::NUM_BUFFER_SLOTS);
+ const int totalSlotCount = mCore->getTotalSlotCountLocked();
+ if (slot < 0 || slot >= totalSlotCount) {
+ BQ_LOGE("cancelBuffer: slot index %d out of range [0, %d)", slot, totalSlotCount);
return BAD_VALUE;
} else if (!mSlots[slot].mBufferState.isDequeued()) {
BQ_LOGE("cancelBuffer: slot %d is not owned by the producer "
@@ -1409,6 +1454,9 @@
output->nextFrameNumber = mCore->mFrameCounter + 1;
output->bufferReplaced = false;
output->maxBufferCount = mCore->mMaxBufferCount;
+#if COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(WB_UNLIMITED_SLOTS)
+ output->isSlotExpansionAllowed = mCore->mAllowExtendedSlotCount;
+#endif
if (listener != nullptr) {
// Set up a death notification so that we can disconnect
diff --git a/libs/gui/ConsumerBase.cpp b/libs/gui/ConsumerBase.cpp
index 602bba8..504509d 100644
--- a/libs/gui/ConsumerBase.cpp
+++ b/libs/gui/ConsumerBase.cpp
@@ -37,6 +37,8 @@
#include <private/gui/ComposerService.h>
+#include <ui/BufferQueueDefs.h>
+
#include <log/log.h>
#include <utils/Log.h>
#include <utils/String8.h>
@@ -59,7 +61,11 @@
return android_atomic_inc(&globalCounter);
}
-ConsumerBase::ConsumerBase(const sp<IGraphicBufferConsumer>& bufferQueue, bool controlledByApp) :
+ConsumerBase::ConsumerBase(const sp<IGraphicBufferConsumer>& bufferQueue, bool controlledByApp)
+ :
+#if COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(WB_UNLIMITED_SLOTS)
+ mSlots(BufferQueueDefs::NUM_BUFFER_SLOTS),
+#endif
mAbandoned(false),
mConsumer(bufferQueue),
mPrevFinalReleaseFence(Fence::NO_FENCE) {
@@ -68,7 +74,12 @@
#if COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(WB_CONSUMER_BASE_OWNS_BQ)
ConsumerBase::ConsumerBase(bool controlledByApp, bool consumerIsSurfaceFlinger)
- : mAbandoned(false), mPrevFinalReleaseFence(Fence::NO_FENCE) {
+ :
+#if COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(WB_UNLIMITED_SLOTS)
+ mSlots(BufferQueueDefs::NUM_BUFFER_SLOTS),
+#endif
+ mAbandoned(false),
+ mPrevFinalReleaseFence(Fence::NO_FENCE) {
sp<IGraphicBufferProducer> producer;
BufferQueue::createBufferQueue(&producer, &mConsumer, consumerIsSurfaceFlinger);
mSurface = sp<Surface>::make(producer, controlledByApp);
@@ -77,7 +88,11 @@
ConsumerBase::ConsumerBase(const sp<IGraphicBufferProducer>& producer,
const sp<IGraphicBufferConsumer>& consumer, bool controlledByApp)
- : mAbandoned(false),
+ :
+#if COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(WB_UNLIMITED_SLOTS)
+ mSlots(BufferQueueDefs::NUM_BUFFER_SLOTS),
+#endif
+ mAbandoned(false),
mConsumer(consumer),
mSurface(sp<Surface>::make(producer, controlledByApp)),
mPrevFinalReleaseFence(Fence::NO_FENCE) {
@@ -101,9 +116,16 @@
if (err != NO_ERROR) {
CB_LOGE("ConsumerBase: error connecting to BufferQueue: %s (%d)",
strerror(-err), err);
- } else {
- mConsumer->setConsumerName(mName);
+ return;
}
+
+ mConsumer->setConsumerName(mName);
+#if COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(WB_UNLIMITED_SLOTS)
+ if (err = mConsumer->allowUnlimitedSlots(true); err != NO_ERROR) {
+ CB_LOGE("ConsumerBase: error marking as allowed to have unlimited slots: %s (%d)",
+ strerror(-err), err);
+ }
+#endif
}
ConsumerBase::~ConsumerBase() {
@@ -130,7 +152,11 @@
}
uint64_t id = buffer->getId();
- for (int i = 0; i < BufferQueueDefs::NUM_BUFFER_SLOTS; i++) {
+#if COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(WB_UNLIMITED_SLOTS)
+ for (int i = 0; i < (int)mSlots.size(); ++i) {
+#else
+ for (int i = 0; i < BufferQueue::NUM_BUFFER_SLOTS; i++) {
+#endif
auto& slot = mSlots[i];
if (slot.mGraphicBuffer && slot.mGraphicBuffer->getId() == id) {
return i;
@@ -242,6 +268,15 @@
return;
}
+#if COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(WB_UNLIMITED_SLOTS)
+ std::vector<bool> mask;
+ mConsumer->getReleasedBuffersExtended(&mask);
+ for (size_t i = 0; i < mSlots.size(); i++) {
+ if (mask[i]) {
+ freeBufferLocked(i);
+ }
+ }
+#else
uint64_t mask = 0;
mConsumer->getReleasedBuffers(&mask);
for (int i = 0; i < BufferQueue::NUM_BUFFER_SLOTS; i++) {
@@ -249,11 +284,23 @@
freeBufferLocked(i);
}
}
+#endif
}
void ConsumerBase::onSidebandStreamChanged() {
}
+#if COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(WB_UNLIMITED_SLOTS)
+void ConsumerBase::onSlotCountChanged(int slotCount) {
+ CB_LOGV("onSlotCountChanged: %d", slotCount);
+ Mutex::Autolock lock(mMutex);
+
+ if (slotCount > (int)mSlots.size()) {
+ mSlots.resize(slotCount);
+ }
+}
+#endif
+
void ConsumerBase::abandon() {
CB_LOGV("abandon");
Mutex::Autolock lock(mMutex);
@@ -270,7 +317,11 @@
CB_LOGE("abandonLocked: ConsumerBase is abandoned!");
return;
}
+#if COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(WB_UNLIMITED_SLOTS)
+ for (int i = 0; i < (int)mSlots.size(); ++i) {
+#else
for (int i =0; i < BufferQueue::NUM_BUFFER_SLOTS; i++) {
+#endif
freeBufferLocked(i);
}
// disconnect from the BufferQueue
@@ -387,6 +438,15 @@
CB_LOGE("setMaxBufferCount: ConsumerBase is abandoned!");
return NO_INIT;
}
+
+#if COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(WB_UNLIMITED_SLOTS)
+ if (status_t err = mConsumer->allowUnlimitedSlots(false); err != NO_ERROR) {
+ CB_LOGE("ConsumerBase: error marking as not allowed to have unlimited slots: %s (%d)",
+ strerror(-err), err);
+ return err;
+ }
+#endif
+
return mConsumer->setMaxBufferCount(bufferCount);
}
#endif // COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(WB_CONSUMER_BASE_OWNS_BQ)
@@ -448,6 +508,15 @@
if (err != OK) {
return err;
}
+#if COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(WB_UNLIMITED_SLOTS)
+ std::vector<bool> mask;
+ mConsumer->getReleasedBuffersExtended(&mask);
+ for (int i = 0; i < (int)mSlots.size(); i++) {
+ if (mask[i]) {
+ freeBufferLocked(i);
+ }
+ }
+#else
uint64_t mask;
mConsumer->getReleasedBuffers(&mask);
for (int i = 0; i < BufferQueue::NUM_BUFFER_SLOTS; i++) {
@@ -455,6 +524,8 @@
freeBufferLocked(i);
}
}
+#endif
+
return OK;
}
@@ -596,6 +667,9 @@
// buffer on the same slot), the buffer producer is definitely no longer
// tracking it.
if (!stillTracking(slot, graphicBuffer)) {
+ CB_LOGV("releaseBufferLocked: Not tracking, exiting without calling releaseBuffer for "
+ "slot=%d/%" PRIu64,
+ slot, mSlots[slot].mFrameNumber);
return OK;
}
@@ -615,7 +689,11 @@
bool ConsumerBase::stillTracking(int slot,
const sp<GraphicBuffer> graphicBuffer) {
+#if COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(WB_UNLIMITED_SLOTS)
+ if (slot < 0 || slot >= (int)mSlots.size()) {
+#else
if (slot < 0 || slot >= BufferQueue::NUM_BUFFER_SLOTS) {
+#endif
return false;
}
return (mSlots[slot].mGraphicBuffer != nullptr &&
diff --git a/libs/gui/Flags.cpp b/libs/gui/Flags.cpp
index 85ee2cd..ee2802f 100644
--- a/libs/gui/Flags.cpp
+++ b/libs/gui/Flags.cpp
@@ -29,6 +29,14 @@
#endif
}
+ParcelableSurfaceType surfaceToParcelableSurfaceType(const sp<Surface>& surface) {
+#if WB_LIBCAMERASERVICE_WITH_DEPENDENCIES
+ return view::Surface::fromSurface(surface);
+#else
+ return surface->getIGraphicBufferProducer();
+#endif
+}
+
sp<IGraphicBufferProducer> surfaceTypeToIGBP(const sp<SurfaceType>& surface) {
#if WB_LIBCAMERASERVICE_WITH_DEPENDENCIES
return surface->getIGraphicBufferProducer();
diff --git a/libs/gui/FrameRateUtils.cpp b/libs/gui/FrameRateUtils.cpp
index 5c4879c..1b2354e 100644
--- a/libs/gui/FrameRateUtils.cpp
+++ b/libs/gui/FrameRateUtils.cpp
@@ -42,7 +42,7 @@
if (compatibility != ANATIVEWINDOW_FRAME_RATE_COMPATIBILITY_DEFAULT &&
compatibility != ANATIVEWINDOW_FRAME_RATE_COMPATIBILITY_FIXED_SOURCE &&
- compatibility != ANATIVEWINDOW_FRAME_RATE_COMPATIBILITY_GTE &&
+ compatibility != ANATIVEWINDOW_FRAME_RATE_COMPATIBILITY_AT_LEAST &&
(!privileged ||
(compatibility != ANATIVEWINDOW_FRAME_RATE_EXACT &&
compatibility != ANATIVEWINDOW_FRAME_RATE_NO_VOTE))) {
diff --git a/libs/gui/GLConsumer.cpp b/libs/gui/GLConsumer.cpp
index f2173cd..168129b 100644
--- a/libs/gui/GLConsumer.cpp
+++ b/libs/gui/GLConsumer.cpp
@@ -119,6 +119,9 @@
mTexTarget(texTarget),
mEglDisplay(EGL_NO_DISPLAY),
mEglContext(EGL_NO_CONTEXT),
+#if COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(WB_UNLIMITED_SLOTS)
+ mEglSlots(BufferQueueDefs::NUM_BUFFER_SLOTS),
+#endif
mCurrentTexture(BufferQueue::INVALID_BUFFER_SLOT),
mAttached(true) {
GLC_LOGV("GLConsumer");
@@ -129,27 +132,29 @@
}
#endif // COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(WB_CONSUMER_BASE_OWNS_BQ)
-GLConsumer::GLConsumer(const sp<IGraphicBufferConsumer>& bq, uint32_t tex,
- uint32_t texTarget, bool useFenceSync, bool isControlledByApp) :
- ConsumerBase(bq, isControlledByApp),
- mCurrentCrop(Rect::EMPTY_RECT),
- mCurrentTransform(0),
- mCurrentScalingMode(NATIVE_WINDOW_SCALING_MODE_FREEZE),
- mCurrentFence(Fence::NO_FENCE),
- mCurrentTimestamp(0),
- mCurrentDataSpace(HAL_DATASPACE_UNKNOWN),
- mCurrentFrameNumber(0),
- mDefaultWidth(1),
- mDefaultHeight(1),
- mFilteringEnabled(true),
- mTexName(tex),
- mUseFenceSync(useFenceSync),
- mTexTarget(texTarget),
- mEglDisplay(EGL_NO_DISPLAY),
- mEglContext(EGL_NO_CONTEXT),
- mCurrentTexture(BufferQueue::INVALID_BUFFER_SLOT),
- mAttached(true)
-{
+GLConsumer::GLConsumer(const sp<IGraphicBufferConsumer>& bq, uint32_t tex, uint32_t texTarget,
+ bool useFenceSync, bool isControlledByApp)
+ : ConsumerBase(bq, isControlledByApp),
+ mCurrentCrop(Rect::EMPTY_RECT),
+ mCurrentTransform(0),
+ mCurrentScalingMode(NATIVE_WINDOW_SCALING_MODE_FREEZE),
+ mCurrentFence(Fence::NO_FENCE),
+ mCurrentTimestamp(0),
+ mCurrentDataSpace(HAL_DATASPACE_UNKNOWN),
+ mCurrentFrameNumber(0),
+ mDefaultWidth(1),
+ mDefaultHeight(1),
+ mFilteringEnabled(true),
+ mTexName(tex),
+ mUseFenceSync(useFenceSync),
+ mTexTarget(texTarget),
+ mEglDisplay(EGL_NO_DISPLAY),
+ mEglContext(EGL_NO_CONTEXT),
+#if COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(WB_UNLIMITED_SLOTS)
+ mEglSlots(BufferQueueDefs::NUM_BUFFER_SLOTS),
+#endif
+ mCurrentTexture(BufferQueue::INVALID_BUFFER_SLOT),
+ mAttached(true) {
GLC_LOGV("GLConsumer");
memcpy(mCurrentTransformMatrix, mtxIdentity.asArray(),
@@ -176,6 +181,9 @@
mTexTarget(texTarget),
mEglDisplay(EGL_NO_DISPLAY),
mEglContext(EGL_NO_CONTEXT),
+#if COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(WB_UNLIMITED_SLOTS)
+ mEglSlots(BufferQueueDefs::NUM_BUFFER_SLOTS),
+#endif
mCurrentTexture(BufferQueue::INVALID_BUFFER_SLOT),
mAttached(false) {
GLC_LOGV("GLConsumer");
@@ -204,6 +212,9 @@
mTexTarget(texTarget),
mEglDisplay(EGL_NO_DISPLAY),
mEglContext(EGL_NO_CONTEXT),
+#if COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(WB_UNLIMITED_SLOTS)
+ mEglSlots(BufferQueueDefs::NUM_BUFFER_SLOTS),
+#endif
mCurrentTexture(BufferQueue::INVALID_BUFFER_SLOT),
mAttached(false) {
GLC_LOGV("GLConsumer");
@@ -395,6 +406,17 @@
return NO_ERROR;
}
+#if COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(WB_UNLIMITED_SLOTS)
+void GLConsumer::onSlotCountChanged(int slotCount) {
+ ConsumerBase::onSlotCountChanged(slotCount);
+
+ Mutex::Autolock lock(mMutex);
+ if (slotCount > (int)mEglSlots.size()) {
+ mEglSlots.resize(slotCount);
+ }
+}
+#endif
+
status_t GLConsumer::releaseBufferLocked(int buf,
sp<GraphicBuffer> graphicBuffer,
EGLDisplay display, EGLSyncKHR eglFence) {
diff --git a/libs/gui/IConsumerListener.cpp b/libs/gui/IConsumerListener.cpp
index f3bd90c..939db59 100644
--- a/libs/gui/IConsumerListener.cpp
+++ b/libs/gui/IConsumerListener.cpp
@@ -31,7 +31,8 @@
ON_FRAME_DEQUEUED,
ON_FRAME_CANCELLED,
ON_FRAME_DETACHED,
- LAST = ON_FRAME_DETACHED,
+ ON_SLOT_COUNT_CHANGED,
+ LAST = ON_SLOT_COUNT_CHANGED,
};
} // Anonymous namespace
@@ -85,6 +86,14 @@
FrameEventHistoryDelta* /*outDelta*/) override {
LOG_ALWAYS_FATAL("IConsumerListener::addAndGetFrameTimestamps cannot be proxied");
}
+
+#if COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(WB_UNLIMITED_SLOTS)
+ void onSlotCountChanged(int slotCount) override {
+ callRemoteAsync<
+ decltype(&IConsumerListener::onSlotCountChanged)>(Tag::ON_SLOT_COUNT_CHANGED,
+ slotCount);
+ }
+#endif
};
// Out-of-line virtual method definitions to trigger vtable emission in this translation unit (see
@@ -116,6 +125,13 @@
return callLocalAsync(data, reply, &IConsumerListener::onFrameCancelled);
case Tag::ON_FRAME_DETACHED:
return callLocalAsync(data, reply, &IConsumerListener::onFrameDetached);
+ case Tag::ON_SLOT_COUNT_CHANGED: {
+#if COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(WB_UNLIMITED_SLOTS)
+ return callLocalAsync(data, reply, &IConsumerListener::onSlotCountChanged);
+#else
+ return INVALID_OPERATION;
+#endif
+ }
}
}
diff --git a/libs/gui/IGraphicBufferConsumer.cpp b/libs/gui/IGraphicBufferConsumer.cpp
index 282957b..c1b6568 100644
--- a/libs/gui/IGraphicBufferConsumer.cpp
+++ b/libs/gui/IGraphicBufferConsumer.cpp
@@ -16,6 +16,7 @@
#include <gui/IGraphicBufferConsumer.h>
+#include <com_android_graphics_libgui_flags.h>
#include <gui/BufferItem.h>
#include <gui/IConsumerListener.h>
@@ -24,6 +25,7 @@
#include <ui/Fence.h>
#include <ui/GraphicBuffer.h>
+#include <utils/Errors.h>
#include <utils/NativeHandle.h>
#include <utils/String8.h>
#include <cstdint>
@@ -53,7 +55,9 @@
GET_OCCUPANCY_HISTORY,
DISCARD_FREE_BUFFERS,
DUMP_STATE,
- LAST = DUMP_STATE,
+ ALLOW_UNLIMITED_SLOTS,
+ GET_RELEASED_BUFFERS_EXTENDED,
+ LAST = GET_RELEASED_BUFFERS_EXTENDED,
};
} // Anonymous namespace
@@ -104,11 +108,25 @@
return callRemote<Signature>(Tag::GET_RELEASED_BUFFERS, slotMask);
}
+#if COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(WB_UNLIMITED_SLOTS)
+ status_t getReleasedBuffersExtended(std::vector<bool>* slotMask) override {
+ using Signature = decltype(&IGraphicBufferConsumer::getReleasedBuffersExtended);
+ return callRemote<Signature>(Tag::GET_RELEASED_BUFFERS_EXTENDED, slotMask);
+ }
+#endif
+
status_t setDefaultBufferSize(uint32_t width, uint32_t height) override {
using Signature = decltype(&IGraphicBufferConsumer::setDefaultBufferSize);
return callRemote<Signature>(Tag::SET_DEFAULT_BUFFER_SIZE, width, height);
}
+#if COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(WB_UNLIMITED_SLOTS)
+ status_t allowUnlimitedSlots(bool allowUnlimitedSlots) override {
+ using Signature = decltype(&IGraphicBufferConsumer::allowUnlimitedSlots);
+ return callRemote<Signature>(Tag::ALLOW_UNLIMITED_SLOTS, allowUnlimitedSlots);
+ }
+#endif
+
status_t setMaxBufferCount(int bufferCount) override {
using Signature = decltype(&IGraphicBufferConsumer::setMaxBufferCount);
return callRemote<Signature>(Tag::SET_MAX_BUFFER_COUNT, bufferCount);
@@ -228,6 +246,20 @@
using Signature = status_t (IGraphicBufferConsumer::*)(const String8&, String8*) const;
return callLocal<Signature>(data, reply, &IGraphicBufferConsumer::dumpState);
}
+ case Tag::GET_RELEASED_BUFFERS_EXTENDED: {
+#if COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(WB_UNLIMITED_SLOTS)
+ return callLocal(data, reply, &IGraphicBufferConsumer::getReleasedBuffersExtended);
+#else
+ return INVALID_OPERATION;
+#endif
+ }
+ case Tag::ALLOW_UNLIMITED_SLOTS: {
+#if COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(WB_UNLIMITED_SLOTS)
+ return callLocal(data, reply, &IGraphicBufferConsumer::allowUnlimitedSlots);
+#else
+ return INVALID_OPERATION;
+#endif
+ }
}
}
diff --git a/libs/gui/IGraphicBufferProducer.cpp b/libs/gui/IGraphicBufferProducer.cpp
index 0914480..9f71eb1 100644
--- a/libs/gui/IGraphicBufferProducer.cpp
+++ b/libs/gui/IGraphicBufferProducer.cpp
@@ -81,6 +81,7 @@
GET_LAST_QUEUED_BUFFER2,
SET_FRAME_RATE,
SET_ADDITIONAL_OPTIONS,
+ SET_MAX_BUFER_COUNT_EXTENDED,
};
class BpGraphicBufferProducer : public BpInterface<IGraphicBufferProducer>
@@ -149,6 +150,20 @@
return result;
}
+#if COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(WB_UNLIMITED_SLOTS)
+ status_t extendSlotCount(int size) override {
+ Parcel data, reply;
+ data.writeInterfaceToken(IGraphicBufferProducer::getInterfaceDescriptor());
+ data.writeInt32(size);
+ status_t result = remote()->transact(SET_MAX_BUFER_COUNT_EXTENDED, data, &reply);
+ if (result != NO_ERROR) {
+ return result;
+ }
+ result = reply.readInt32();
+ return result;
+ }
+#endif
+
virtual status_t setAsyncMode(bool async) {
Parcel data, reply;
data.writeInterfaceToken(
@@ -981,6 +996,14 @@
// ----------------------------------------------------------------------
+#if COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(WB_UNLIMITED_SLOTS)
+status_t IGraphicBufferProducer::extendSlotCount(int size) {
+ // No-op for IGBP other than BufferQueue.
+ (void)size;
+ return INVALID_OPERATION;
+}
+#endif
+
status_t IGraphicBufferProducer::setLegacyBufferDrop(bool drop) {
// No-op for IGBP other than BufferQueue.
(void) drop;
@@ -1582,6 +1605,15 @@
return NO_ERROR;
}
#endif
+#if COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(WB_UNLIMITED_SLOTS)
+ case SET_MAX_BUFER_COUNT_EXTENDED: {
+ CHECK_INTERFACE(IGraphicBufferProducer, data, reply);
+ int size = data.readInt32();
+ status_t result = extendSlotCount(size);
+ reply->writeInt32(result);
+ return NO_ERROR;
+ }
+#endif
}
return BBinder::onTransact(code, data, reply, flags);
}
diff --git a/libs/gui/IGraphicBufferProducerFlattenables.cpp b/libs/gui/IGraphicBufferProducerFlattenables.cpp
index 4e92a39..8b2e2dd 100644
--- a/libs/gui/IGraphicBufferProducerFlattenables.cpp
+++ b/libs/gui/IGraphicBufferProducerFlattenables.cpp
@@ -128,7 +128,7 @@
constexpr size_t IGraphicBufferProducer::QueueBufferOutput::minFlattenedSize() {
return sizeof(width) + sizeof(height) + sizeof(transformHint) + sizeof(numPendingBuffers) +
sizeof(nextFrameNumber) + sizeof(bufferReplaced) + sizeof(maxBufferCount) +
- sizeof(result);
+ sizeof(result) + sizeof(isSlotExpansionAllowed);
}
size_t IGraphicBufferProducer::QueueBufferOutput::getFlattenedSize() const {
return minFlattenedSize() + frameTimestamps.getFlattenedSize();
@@ -152,6 +152,7 @@
FlattenableUtils::write(buffer, size, nextFrameNumber);
FlattenableUtils::write(buffer, size, bufferReplaced);
FlattenableUtils::write(buffer, size, maxBufferCount);
+ FlattenableUtils::write(buffer, size, isSlotExpansionAllowed);
status_t result = frameTimestamps.flatten(buffer, size, fds, count);
if (result != NO_ERROR) {
@@ -175,6 +176,7 @@
FlattenableUtils::read(buffer, size, nextFrameNumber);
FlattenableUtils::read(buffer, size, bufferReplaced);
FlattenableUtils::read(buffer, size, maxBufferCount);
+ FlattenableUtils::read(buffer, size, isSlotExpansionAllowed);
status_t result = frameTimestamps.unflatten(buffer, size, fds, count);
if (result != NO_ERROR) {
diff --git a/libs/gui/Surface.cpp b/libs/gui/Surface.cpp
index e41f9bb..ec23365 100644
--- a/libs/gui/Surface.cpp
+++ b/libs/gui/Surface.cpp
@@ -38,6 +38,7 @@
#include <utils/NativeHandle.h>
#include <utils/Trace.h>
+#include <ui/BufferQueueDefs.h>
#include <ui/DynamicDisplayInfo.h>
#include <ui/Fence.h>
#include <ui/GraphicBuffer.h>
@@ -98,7 +99,10 @@
: mGraphicBufferProducer(bufferProducer),
#if COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(WB_PLATFORM_API_IMPROVEMENTS)
mSurfaceDeathListener(nullptr),
-#endif // COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(WB_PLATFORM_API_IMPROVEMENTS)
+#endif
+#if COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(WB_UNLIMITED_SLOTS)
+ mSlots(NUM_BUFFER_SLOTS),
+#endif
mCrop(Rect::EMPTY_RECT),
mBufferAge(0),
mGenerationNumber(0),
@@ -192,7 +196,7 @@
status_t Surface::allowAllocation(bool allowAllocation) {
return mGraphicBufferProducer->allowAllocation(allowAllocation);
}
-#endif // COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(WB_PLATFORM_API_IMPROVEMENTS)
+#endif
status_t Surface::setGenerationNumber(uint32_t generation) {
status_t result = mGraphicBufferProducer->setGenerationNumber(generation);
@@ -658,7 +662,11 @@
return result;
}
+#if COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(WB_UNLIMITED_SLOTS)
+ if (buf < 0 || buf >= (int)mSlots.size()) {
+#else
if (buf < 0 || buf >= NUM_BUFFER_SLOTS) {
+#endif
ALOGE("dequeueBuffer: IGraphicBufferProducer returned invalid slot number %d", buf);
android_errorWriteLog(0x534e4554, "36991414"); // SafetyNet logging
return FAILED_TRANSACTION;
@@ -757,7 +765,11 @@
Mutex::Autolock lock(mMutex);
uint64_t bufferId = buffer->getId();
+#if COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(WB_UNLIMITED_SLOTS)
+ for (int slot = 0; slot < (int)mSlots.size(); ++slot) {
+#else
for (int slot = 0; slot < Surface::NUM_BUFFER_SLOTS; ++slot) {
+#endif
auto& bufferSlot = mSlots[slot];
if (bufferSlot.buffer != nullptr && bufferSlot.buffer->getId() == bufferId) {
bufferSlot.buffer = nullptr;
@@ -840,7 +852,11 @@
return output.result;
}
+#if COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(WB_UNLIMITED_SLOTS)
+ if (output.slot < 0 || output.slot >= (int)mSlots.size()) {
+#else
if (output.slot < 0 || output.slot >= NUM_BUFFER_SLOTS) {
+#endif
mGraphicBufferProducer->cancelBuffers(cancelBufferInputs, &cancelBufferOutputs);
ALOGE("%s: IGraphicBufferProducer returned invalid slot number %d",
__FUNCTION__, output.slot);
@@ -1027,7 +1043,11 @@
return BAD_VALUE;
}
+#if COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(WB_UNLIMITED_SLOTS)
+ for (int i = 0; i < (int)mSlots.size(); i++) {
+#else
for (int i = 0; i < NUM_BUFFER_SLOTS; i++) {
+#endif
if (mSlots[i].buffer != nullptr &&
mSlots[i].buffer->handle == buffer->handle) {
return i;
@@ -2094,6 +2114,9 @@
mDefaultHeight = output.height;
mNextFrameNumber = output.nextFrameNumber;
mMaxBufferCount = output.maxBufferCount;
+#if COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(WB_UNLIMITED_SLOTS)
+ mIsSlotExpansionAllowed = output.isSlotExpansionAllowed;
+#endif
// Ignore transform hint if sticky transform is set or transform to display inverse flag is
// set. Transform hint should be ignored if the client is expected to always submit buffers
@@ -2190,7 +2213,11 @@
*outFence = Fence::NO_FENCE;
}
+#if COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(WB_UNLIMITED_SLOTS)
+ for (int i = 0; i < (int)mSlots.size(); i++) {
+#else
for (int i = 0; i < NUM_BUFFER_SLOTS; i++) {
+#endif
if (mSlots[i].buffer != nullptr &&
mSlots[i].buffer->getId() == buffer->getId()) {
if (mReportRemovedBuffers) {
@@ -2292,8 +2319,35 @@
ALOGV("Surface::setMaxDequeuedBufferCount");
Mutex::Autolock lock(mMutex);
- status_t err = mGraphicBufferProducer->setMaxDequeuedBufferCount(
- maxDequeuedBuffers);
+#if COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(WB_UNLIMITED_SLOTS)
+ if (maxDequeuedBuffers > BufferQueueDefs::NUM_BUFFER_SLOTS && !mIsSlotExpansionAllowed) {
+ return BAD_VALUE;
+ }
+
+ int minUndequeuedBuffers = 0;
+ status_t err = mGraphicBufferProducer->query(NATIVE_WINDOW_MIN_UNDEQUEUED_BUFFERS,
+ &minUndequeuedBuffers);
+ if (err != OK) {
+ ALOGE("IGraphicBufferProducer::query(NATIVE_WINDOW_MIN_UNDEQUEUED_BUFFERS) returned %s",
+ strerror(-err));
+ return err;
+ }
+
+ if (maxDequeuedBuffers > (int)mSlots.size()) {
+ int newSlotCount = minUndequeuedBuffers + maxDequeuedBuffers;
+ err = mGraphicBufferProducer->extendSlotCount(newSlotCount);
+ if (err != OK) {
+ ALOGE("IGraphicBufferProducer::extendSlotCount(%d) returned %s", newSlotCount,
+ strerror(-err));
+ return err;
+ }
+
+ mSlots.resize(newSlotCount);
+ }
+ err = mGraphicBufferProducer->setMaxDequeuedBufferCount(maxDequeuedBuffers);
+#else
+ status_t err = mGraphicBufferProducer->setMaxDequeuedBufferCount(maxDequeuedBuffers);
+#endif
ALOGE_IF(err, "IGraphicBufferProducer::setMaxDequeuedBufferCount(%d) "
"returned %s", maxDequeuedBuffers, strerror(-err));
@@ -2501,7 +2555,11 @@
ALOGE("%s: %zu buffers were freed while being dequeued!",
__FUNCTION__, mDequeuedSlots.size());
}
+#if COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(WB_UNLIMITED_SLOTS)
+ for (int i = 0; i < (int)mSlots.size(); i++) {
+#else
for (int i = 0; i < NUM_BUFFER_SLOTS; i++) {
+#endif
mSlots[i].buffer = nullptr;
}
}
@@ -2510,7 +2568,11 @@
std::vector<sp<GraphicBuffer>>* outBuffers) {
ALOGV("Surface::getAndFlushBuffersFromSlots");
for (int32_t i : slots) {
+#if COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(WB_UNLIMITED_SLOTS)
+ if (i < 0 || i >= (int)mSlots.size()) {
+#else
if (i < 0 || i >= NUM_BUFFER_SLOTS) {
+#endif
ALOGE("%s: Invalid slotIndex: %d", __FUNCTION__, i);
return BAD_VALUE;
}
@@ -2670,7 +2732,11 @@
newDirtyRegion.set(bounds);
mDirtyRegion.clear();
Mutex::Autolock lock(mMutex);
- for (size_t i=0 ; i<NUM_BUFFER_SLOTS ; i++) {
+#if COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(WB_UNLIMITED_SLOTS)
+ for (int i = 0; i < (int)mSlots.size(); i++) {
+#else
+ for (int i = 0; i < NUM_BUFFER_SLOTS; i++) {
+#endif
mSlots[i].dirtyRegion.clear();
}
}
diff --git a/libs/gui/SurfaceComposerClient.cpp b/libs/gui/SurfaceComposerClient.cpp
index be88b11..cabde22 100644
--- a/libs/gui/SurfaceComposerClient.cpp
+++ b/libs/gui/SurfaceComposerClient.cpp
@@ -3293,10 +3293,17 @@
return statusTFromBinderStatus(status);
}
-status_t SurfaceComposerClient::setActivePictureListener(
+status_t SurfaceComposerClient::addActivePictureListener(
const sp<gui::IActivePictureListener>& listener) {
binder::Status status =
- ComposerServiceAIDL::getComposerService()->setActivePictureListener(listener);
+ ComposerServiceAIDL::getComposerService()->addActivePictureListener(listener);
+ return statusTFromBinderStatus(status);
+}
+
+status_t SurfaceComposerClient::removeActivePictureListener(
+ const sp<gui::IActivePictureListener>& listener) {
+ binder::Status status =
+ ComposerServiceAIDL::getComposerService()->removeActivePictureListener(listener);
return statusTFromBinderStatus(status);
}
diff --git a/libs/gui/aidl/android/gui/CaptureArgs.aidl b/libs/gui/aidl/android/gui/CaptureArgs.aidl
index 4920344..2bbed2b 100644
--- a/libs/gui/aidl/android/gui/CaptureArgs.aidl
+++ b/libs/gui/aidl/android/gui/CaptureArgs.aidl
@@ -69,10 +69,5 @@
// exact colorspace is not an appropriate intermediate result.
// Note that if the caller is requesting a specific dataspace, this hint does nothing.
boolean hintForSeamlessTransition = false;
-
- // Allows the screenshot to attach a gainmap, which allows for a per-pixel
- // transformation of the screenshot to another luminance range, typically
- // mapping an SDR base image into HDR.
- boolean attachGainmap = false;
}
diff --git a/libs/gui/aidl/android/gui/ISurfaceComposer.aidl b/libs/gui/aidl/android/gui/ISurfaceComposer.aidl
index 8c19bbb..da47ee2 100644
--- a/libs/gui/aidl/android/gui/ISurfaceComposer.aidl
+++ b/libs/gui/aidl/android/gui/ISurfaceComposer.aidl
@@ -607,8 +607,14 @@
oneway void removeJankListener(int layerId, IJankListener listener, long afterVsync);
/**
- * Sets the listener used to monitor visible content that is being processed with picture
+ * Adds a listener used to monitor visible content that is being processed with picture
* profiles.
*/
- oneway void setActivePictureListener(IActivePictureListener listener);
+ oneway void addActivePictureListener(IActivePictureListener listener);
+
+ /**
+ * Removes a listener used to monitor visible content that is being processed with picture
+ * profiles.
+ */
+ oneway void removeActivePictureListener(IActivePictureListener listener);
}
diff --git a/libs/gui/include/gui/BLASTBufferQueue.h b/libs/gui/include/gui/BLASTBufferQueue.h
index 07558aa..1bc1dd0 100644
--- a/libs/gui/include/gui/BLASTBufferQueue.h
+++ b/libs/gui/include/gui/BLASTBufferQueue.h
@@ -144,6 +144,12 @@
*/
void setTransactionHangCallback(std::function<void(const std::string&)> callback);
void setApplyToken(sp<IBinder>);
+
+ void setWaitForBufferReleaseCallback(std::function<void()> callback)
+ EXCLUDES(mWaitForBufferReleaseMutex);
+ std::function<void()> getWaitForBufferReleaseCallback() const
+ EXCLUDES(mWaitForBufferReleaseMutex);
+
virtual ~BLASTBufferQueue();
void onFirstRef() override;
@@ -186,6 +192,7 @@
sp<SurfaceControl> mSurfaceControl GUARDED_BY(mMutex);
mutable std::mutex mMutex;
+ mutable std::mutex mWaitForBufferReleaseMutex;
std::condition_variable mCallbackCV;
// BufferQueue internally allows 1 more than
@@ -324,6 +331,7 @@
std::unordered_set<uint64_t> mSyncedFrameNumbers GUARDED_BY(mMutex);
+ std::function<void()> mWaitForBufferReleaseCallback GUARDED_BY(mWaitForBufferReleaseMutex);
#if COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(BUFFER_RELEASE_CHANNEL)
// BufferReleaseChannel is used to communicate buffer releases from SurfaceFlinger to the
// client.
diff --git a/libs/gui/include/gui/BufferQueue.h b/libs/gui/include/gui/BufferQueue.h
index 0948c4d0..f1c75d3 100644
--- a/libs/gui/include/gui/BufferQueue.h
+++ b/libs/gui/include/gui/BufferQueue.h
@@ -76,6 +76,9 @@
void onSetFrameRate(float frameRate, int8_t compatibility,
int8_t changeFrameRateStrategy) override;
#endif
+#if COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(WB_UNLIMITED_SLOTS)
+ void onSlotCountChanged(int slotCount) override;
+#endif
private:
// mConsumerListener is a weak reference to the IConsumerListener. This is
// the raison d'etre of ProxyConsumerListener.
diff --git a/libs/gui/include/gui/BufferQueueConsumer.h b/libs/gui/include/gui/BufferQueueConsumer.h
index 6aa801a..e00c44e 100644
--- a/libs/gui/include/gui/BufferQueueConsumer.h
+++ b/libs/gui/include/gui/BufferQueueConsumer.h
@@ -96,11 +96,26 @@
// This should be called from the onBuffersReleased() callback.
virtual status_t getReleasedBuffers(uint64_t* outSlotMask);
+#if COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(WB_UNLIMITED_SLOTS)
+ // getReleasedBuffers sets the values pointed to by outSlotMask to the bits
+ // indicating which buffer slots have been released by the BufferQueue
+ // but have not yet been released by the consumer.
+ //
+ // This should be called from the onBuffersReleased() callback when
+ // allowUnlimitedSlots has been called.
+ virtual status_t getReleasedBuffersExtended(std::vector<bool>* outSlotMask) override;
+#endif
+
// setDefaultBufferSize is used to set the size of buffers returned by
// dequeueBuffer when a width and height of zero is requested. Default
// is 1x1.
virtual status_t setDefaultBufferSize(uint32_t width, uint32_t height);
+#if COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(WB_UNLIMITED_SLOTS)
+ // see IGraphicBufferConsumer::allowUnlimitedSlots
+ virtual status_t allowUnlimitedSlots(bool allowUnlimitedSlots) override;
+#endif
+
// see IGraphicBufferConsumer::setMaxBufferCount
virtual status_t setMaxBufferCount(int bufferCount);
diff --git a/libs/gui/include/gui/BufferQueueCore.h b/libs/gui/include/gui/BufferQueueCore.h
index 77cdf2c..7f92a46 100644
--- a/libs/gui/include/gui/BufferQueueCore.h
+++ b/libs/gui/include/gui/BufferQueueCore.h
@@ -32,10 +32,11 @@
#include <utils/Trace.h>
#include <utils/Vector.h>
-#include <list>
-#include <set>
-#include <mutex>
#include <condition_variable>
+#include <list>
+#include <mutex>
+#include <set>
+#include <vector>
#define ATRACE_BUFFER_INDEX(index) \
do { \
@@ -91,6 +92,10 @@
// Dump our state in a string
void dumpState(const String8& prefix, String8* outResult) const;
+ // getTotalSlotCountLocked returns the total number of slots in use by the
+ // buffer queue at this time.
+ int getTotalSlotCountLocked() const;
+
// getMinUndequeuedBufferCountLocked returns the minimum number of buffers
// that must remain in a state other than DEQUEUED. The async parameter
// tells whether we're in asynchronous mode.
@@ -120,6 +125,10 @@
int getMaxBufferCountLocked(bool asyncMode,
bool dequeueBufferCannotBlock, int maxBufferCount) const;
+#if COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(WB_UNLIMITED_SLOTS)
+ // This resizes mSlots to the given size, but only if it's increasing.
+ status_t extendSlotCountLocked(int size);
+#endif
// clearBufferSlotLocked frees the GraphicBuffer and sync resources for the
// given slot.
void clearBufferSlotLocked(int slot);
@@ -204,7 +213,7 @@
// mConnectedProducerListener will not trigger onBufferAttached() callback.
bool mBufferAttachedCbEnabled;
- // mSlots is an array of buffer slots that must be mirrored on the producer
+ // mSlots is a collection of buffer slots that must be mirrored on the producer
// side. This allows buffer ownership to be transferred between the producer
// and consumer without sending a GraphicBuffer over Binder. The entire
// array is initialized to NULL at construction time, and buffers are
@@ -266,8 +275,14 @@
// is specified.
android_dataspace mDefaultBufferDataSpace;
+#if COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(WB_UNLIMITED_SLOTS)
+ // mAllowExtendedSlotCount is set by the consumer to permit the producer to
+ // request an unlimited number of slots.
+ bool mAllowExtendedSlotCount;
+#endif
+
// mMaxBufferCount is the limit on the number of buffers that will be
- // allocated at one time. This limit can be set by the consumer.
+ // allocated at one time.
int mMaxBufferCount;
// mMaxAcquiredBufferCount is the number of buffers that the consumer may
diff --git a/libs/gui/include/gui/BufferQueueDefs.h b/libs/gui/include/gui/BufferQueueDefs.h
index ffafb49..42cf439 100644
--- a/libs/gui/include/gui/BufferQueueDefs.h
+++ b/libs/gui/include/gui/BufferQueueDefs.h
@@ -17,6 +17,7 @@
#ifndef ANDROID_GUI_BUFFERQUEUECOREDEFS_H
#define ANDROID_GUI_BUFFERQUEUECOREDEFS_H
+#include <com_android_graphics_libgui_flags.h>
#include <gui/BufferSlot.h>
#include <ui/BufferQueueDefs.h>
@@ -24,7 +25,11 @@
class BufferQueueCore;
namespace BufferQueueDefs {
- typedef BufferSlot SlotsType[NUM_BUFFER_SLOTS];
+#if COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(WB_UNLIMITED_SLOTS)
+ typedef std::vector<BufferSlot> SlotsType;
+#else
+ typedef BufferSlot SlotsType[NUM_BUFFER_SLOTS];
+#endif
} // namespace BufferQueueDefs
} // namespace android
diff --git a/libs/gui/include/gui/BufferQueueProducer.h b/libs/gui/include/gui/BufferQueueProducer.h
index 086ce7c..50abadb 100644
--- a/libs/gui/include/gui/BufferQueueProducer.h
+++ b/libs/gui/include/gui/BufferQueueProducer.h
@@ -47,6 +47,11 @@
// flags indicating that previously-returned buffers are no longer valid.
virtual status_t requestBuffer(int slot, sp<GraphicBuffer>* buf);
+#if COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(WB_UNLIMITED_SLOTS)
+ // see IGraphicsBufferProducer::extendSlotCount
+ virtual status_t extendSlotCount(int size) override;
+#endif
+
// see IGraphicsBufferProducer::setMaxDequeuedBufferCount
virtual status_t setMaxDequeuedBufferCount(int maxDequeuedBuffers);
diff --git a/libs/gui/include/gui/ConsumerBase.h b/libs/gui/include/gui/ConsumerBase.h
index e976aa4..5cd19c1 100644
--- a/libs/gui/include/gui/ConsumerBase.h
+++ b/libs/gui/include/gui/ConsumerBase.h
@@ -185,7 +185,9 @@
virtual void onFrameDetached(const uint64_t bufferId) override;
virtual void onBuffersReleased() override;
virtual void onSidebandStreamChanged() override;
-
+#if COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(WB_UNLIMITED_SLOTS)
+ virtual void onSlotCountChanged(int slotCount) override;
+#endif
virtual int getSlotForBufferLocked(const sp<GraphicBuffer>& buffer);
virtual status_t detachBufferLocked(int slotIndex);
@@ -284,7 +286,11 @@
// slot that has not yet been used. The buffer allocated to a slot will also
// be replaced if the requested buffer usage or geometry differs from that
// of the buffer allocated to a slot.
+#if COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(WB_UNLIMITED_SLOTS)
+ std::vector<Slot> mSlots;
+#else
Slot mSlots[BufferQueueDefs::NUM_BUFFER_SLOTS];
+#endif
// mAbandoned indicates that the BufferQueue will no longer be used to
// consume images buffers pushed to it using the IGraphicBufferProducer
diff --git a/libs/gui/include/gui/Flags.h b/libs/gui/include/gui/Flags.h
index 845bc54..446841b 100644
--- a/libs/gui/include/gui/Flags.h
+++ b/libs/gui/include/gui/Flags.h
@@ -46,6 +46,7 @@
namespace flagtools {
sp<SurfaceType> surfaceToSurfaceType(const sp<Surface>& surface);
+ParcelableSurfaceType surfaceToParcelableSurfaceType(const sp<Surface>& surface);
ParcelableSurfaceType toParcelableSurfaceType(const view::Surface& surface);
sp<IGraphicBufferProducer> surfaceTypeToIGBP(const sp<SurfaceType>& surface);
bool isSurfaceTypeValid(const sp<SurfaceType>& surface);
diff --git a/libs/gui/include/gui/GLConsumer.h b/libs/gui/include/gui/GLConsumer.h
index 8a66dc0..30cbfa2 100644
--- a/libs/gui/include/gui/GLConsumer.h
+++ b/libs/gui/include/gui/GLConsumer.h
@@ -266,6 +266,9 @@
virtual status_t acquireBufferLocked(BufferItem *item, nsecs_t presentWhen,
uint64_t maxFrameNumber = 0) override;
+#if COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(WB_UNLIMITED_SLOTS)
+ virtual void onSlotCountChanged(int slotCount) override;
+#endif
// releaseBufferLocked overrides the ConsumerBase method to update the
// mEglSlots array in addition to the ConsumerBase.
virtual status_t releaseBufferLocked(int slot, const sp<GraphicBuffer> graphicBuffer,
@@ -496,8 +499,11 @@
// slot that has not yet been used. The buffer allocated to a slot will also
// be replaced if the requested buffer usage or geometry differs from that
// of the buffer allocated to a slot.
+#if COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(WB_UNLIMITED_SLOTS)
+ std::vector<EglSlot> mEglSlots;
+#else
EglSlot mEglSlots[BufferQueueDefs::NUM_BUFFER_SLOTS];
-
+#endif
// mCurrentTexture is the buffer slot index of the buffer that is currently
// bound to the OpenGL texture. It is initialized to INVALID_BUFFER_SLOT,
// indicating that no buffer slot is currently bound to the texture. Note,
diff --git a/libs/gui/include/gui/IConsumerListener.h b/libs/gui/include/gui/IConsumerListener.h
index 51d3959..1695aae 100644
--- a/libs/gui/include/gui/IConsumerListener.h
+++ b/libs/gui/include/gui/IConsumerListener.h
@@ -98,6 +98,16 @@
virtual void onSetFrameRate(float /*frameRate*/, int8_t /*compatibility*/,
int8_t /*changeFrameRateStrategy*/) {}
#endif
+
+#if COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(WB_UNLIMITED_SLOTS)
+ // Notifies the consumer that IGraphicBufferProducer::extendSlotCount has
+ // been called and the total slot count has increased.
+ //
+ // This will only ever be called if
+ // IGraphicBufferConsumer::allowUnlimitedSlots has been called on the
+ // consumer.
+ virtual void onSlotCountChanged(int /* slotCount */) {}
+#endif
};
#ifndef NO_BINDER
diff --git a/libs/gui/include/gui/IGraphicBufferConsumer.h b/libs/gui/include/gui/IGraphicBufferConsumer.h
index 18f5488..56eb291 100644
--- a/libs/gui/include/gui/IGraphicBufferConsumer.h
+++ b/libs/gui/include/gui/IGraphicBufferConsumer.h
@@ -16,6 +16,7 @@
#pragma once
+#include <com_android_graphics_libgui_flags.h>
#include <gui/OccupancyTracker.h>
#include <binder/IInterface.h>
@@ -35,6 +36,10 @@
class GraphicBuffer;
class IConsumerListener;
class NativeHandle;
+
+/*
+ * See IGraphicBufferProducer for details on SLOT_COUNT.
+ */
#ifndef NO_BINDER
class IGraphicBufferConsumer : public IInterface {
public:
@@ -92,7 +97,7 @@
//
// Return of a value other than NO_ERROR means an error has occurred:
// * BAD_VALUE - the given slot number is invalid, either because it is out of the range
- // [0, NUM_BUFFER_SLOTS) or because the slot it refers to is not
+ // [0, SLOT_COUNT) or because the slot it refers to is not
// currently acquired.
virtual status_t detachBuffer(int slot) = 0;
@@ -173,6 +178,19 @@
// * NO_INIT - the BufferQueue has been abandoned.
virtual status_t getReleasedBuffers(uint64_t* slotMask) = 0;
+#if COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(WB_UNLIMITED_SLOTS)
+ // getReleasedBuffersExtended for each slot, sets slotMask[slot] to 1 if it
+ // corresponds to a released buffer slot. In particular, a released buffer
+ // is one that has been released by the BufferQueue but has not yet been
+ // released by the consumer.
+ //
+ // This should be called from the onBuffersReleased() callback.
+ //
+ // Return of a value other than NO_ERROR means an error has occurred:
+ // * NO_INIT - the BufferQueue has been abandoned.
+ virtual status_t getReleasedBuffersExtended(std::vector<bool>* slotMask) = 0;
+#endif
+
// setDefaultBufferSize is used to set the size of buffers returned by dequeueBuffer when a
// width and height of zero is requested. Default is 1x1.
//
@@ -180,6 +198,26 @@
// * BAD_VALUE - either w or h was zero
virtual status_t setDefaultBufferSize(uint32_t w, uint32_t h) = 0;
+#if COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(WB_UNLIMITED_SLOTS)
+ // allowUnlimitedSlots allows the producer to set the upper bound on slots.
+ //
+ // Must be called before the producer is connected. If the producer
+ // increases the slot count, an IConsumerListener::onSlotCountChanged
+ // update is sent.
+ //
+ // This can not be used with setMaxBufferCount. Calls after
+ // setMaxBufferCount will fail and calls to setMaxBufferCount after setting
+ // this to true will fail.
+ //
+ // Return of a value other than NO_ERROR means an error has occurred:
+ // * NO_INIT - the BufferQueue has been abandoned
+ // * INVALID_OPERATION - one of the following errors has occurred:
+ // * Producer has been connected
+ // * setMaxBufferCount has been called and shrunk the
+ // BufferQueue.
+ virtual status_t allowUnlimitedSlots(bool allowUnlimitedSlots) = 0;
+#endif
+
// setMaxBufferCount sets the maximum value for the number of buffers used in the BufferQueue
// (the initial default is NUM_BUFFER_SLOTS). If a call to setMaxAcquiredBufferCount (by the
// consumer), or a call to setAsyncMode or setMaxDequeuedBufferCount (by the producer), would
diff --git a/libs/gui/include/gui/IGraphicBufferProducer.h b/libs/gui/include/gui/IGraphicBufferProducer.h
index a42ddc4..7accca6 100644
--- a/libs/gui/include/gui/IGraphicBufferProducer.h
+++ b/libs/gui/include/gui/IGraphicBufferProducer.h
@@ -72,6 +72,14 @@
* dequeueBuffer() to get an empty buffer, fills it with data, then
* calls queueBuffer() to make it available to the consumer.
*
+ * BufferQueues have a size, which we'll refer to in other comments as
+ * SLOT_COUNT. Its default is 64 (NUM_BUFFER_SLOTS). It can be adjusted by
+ * the IGraphicBufferConsumer::setMaxBufferCount, or when
+ * IGraphicBufferConsumer::allowUnlimitedSlots is set to true, by
+ * IGraphicBufferProducer::extendSlotCount. The actual number of buffers in use
+ * is a function of various configurations, including whether we're in single
+ * buffer mode, the maximum dequeuable/aquirable buffers, and SLOT_COUNT.
+ *
* This class was previously called ISurfaceTexture.
*/
#ifndef NO_BINDER
@@ -106,7 +114,7 @@
// slot->buffer mapping so that it's not necessary to transfer a
// GraphicBuffer for every dequeue operation.
//
- // The slot must be in the range of [0, NUM_BUFFER_SLOTS).
+ // The slot must be in the range of [0, SLOT_COUNT).
//
// Return of a value other than NO_ERROR means an error has occurred:
// * NO_INIT - the buffer queue has been abandoned or the producer is not
@@ -116,6 +124,30 @@
// * buffer specified by the slot is not dequeued
virtual status_t requestBuffer(int slot, sp<GraphicBuffer>* buf) = 0;
+#if COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(WB_UNLIMITED_SLOTS)
+ // extendSlotCount sets the maximum slot count (SLOT_COUNT) to the given
+ // size. This feature must be enabled by the consumer to function via
+ // IGraphicBufferConsumer::allowUnlimitedSlots. This must be called before
+ // the producer connects.
+ //
+ // After calling this, any slot can be returned in the [0, size) range.
+ // Callers are responsible for the allocation of the appropriate slots
+ // array for their own buffer cache.
+ //
+ // On success, the consumer is notified (so that it can increase its own
+ // slot cache).
+ //
+ // Return of a value other than NO_ERROR means that an error has occurred:
+ // * NO_INIT - the buffer queue has been abandoned
+ // * INVALID_OPERATION - one of the following conditions has occurred:
+ // * The producer is connected already
+ // * The consumer didn't call allowUnlimitedSlots
+ // * BAD_VALUE - The value is smaller than the previous max size
+ // (initialized to 64, then whatever the last call to this
+ // was)
+ virtual status_t extendSlotCount(int size);
+#endif
+
// setMaxDequeuedBufferCount sets the maximum number of buffers that can be
// dequeued by the producer at one time. If this method succeeds, any new
// buffer slots will be both unallocated and owned by the BufferQueue object
@@ -129,7 +161,7 @@
// will result in a BAD_VALUE error.
//
// The buffer count should be at least 1 (inclusive), but at most
- // (NUM_BUFFER_SLOTS - the minimum undequeued buffer count) (exclusive). The
+ // (SLOT_COUNT - the minimum undequeued buffer count) (exclusive). The
// minimum undequeued buffer count can be obtained by calling
// query(NATIVE_WINDOW_MIN_UNDEQUEUED_BUFFERS).
//
@@ -239,8 +271,8 @@
// * NO_INIT - the buffer queue has been abandoned or the producer is not
// connected.
// * BAD_VALUE - the given slot number is invalid, either because it is
- // out of the range [0, NUM_BUFFER_SLOTS), or because the slot
- // it refers to is not currently dequeued and requested.
+ // out of the range [0, SLOT_COUNT), or because the slot it
+ // refers to is not currently dequeued and requested.
virtual status_t detachBuffer(int slot) = 0;
// detachNextBuffer is equivalent to calling dequeueBuffer, requestBuffer,
@@ -415,6 +447,7 @@
FrameEventHistoryDelta frameTimestamps;
bool bufferReplaced{false};
int maxBufferCount{BufferQueueDefs::NUM_BUFFER_SLOTS};
+ bool isSlotExpansionAllowed{false};
status_t result{NO_ERROR};
};
@@ -430,7 +463,7 @@
// below). Any other properties (zero point, etc)
// are client-dependent, and should be documented by the client.
//
- // The slot must be in the range of [0, NUM_BUFFER_SLOTS).
+ // The slot must be in the range of [0, SLOT_COUNT).
//
// Upon success, the output will be filled with meaningful values
// (refer to the documentation below).
@@ -460,7 +493,7 @@
//
// The buffer is not queued for use by the consumer.
//
- // The slot must be in the range of [0, NUM_BUFFER_SLOTS).
+ // The slot must be in the range of [0, SLOT_COUNT).
//
// The buffer will not be overwritten until the fence signals. The fence
// will usually be the one obtained from dequeueBuffer.
diff --git a/libs/gui/include/gui/LayerState.h b/libs/gui/include/gui/LayerState.h
index 1c31e46..64f191b 100644
--- a/libs/gui/include/gui/LayerState.h
+++ b/libs/gui/include/gui/LayerState.h
@@ -425,7 +425,7 @@
PictureProfileHandle pictureProfileHandle{PictureProfileHandle::NONE};
// A value indicating the significance of the layer's content to the app's desired user
- // experience. A lower priority will result in more likelihood of getting access to limited
+ // experience. A higher value will result in more likelihood of getting access to limited
// resources, such as picture processing hardware.
int32_t appContentPriority = 0;
diff --git a/libs/gui/include/gui/Surface.h b/libs/gui/include/gui/Surface.h
index 14a3513..755674d 100644
--- a/libs/gui/include/gui/Surface.h
+++ b/libs/gui/include/gui/Surface.h
@@ -558,7 +558,11 @@
// slot that has not yet been used. The buffer allocated to a slot will also
// be replaced if the requested buffer usage or geometry differs from that
// of the buffer allocated to a slot.
+#if COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(WB_UNLIMITED_SLOTS)
+ std::vector<BufferSlot> mSlots;
+#else
BufferSlot mSlots[NUM_BUFFER_SLOTS];
+#endif
// mReqWidth is the buffer width that will be requested at the next dequeue
// operation. It is initialized to 1.
@@ -732,6 +736,10 @@
std::vector<sp<GraphicBuffer>> mRemovedBuffers;
int mMaxBufferCount;
+#if COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(WB_UNLIMITED_SLOTS)
+ bool mIsSlotExpansionAllowed;
+#endif
+
sp<IProducerListener> mListenerProxy;
// Get and flush the buffers of given slots, if the buffer in the slot
diff --git a/libs/gui/include/gui/SurfaceComposerClient.h b/libs/gui/include/gui/SurfaceComposerClient.h
index 0ce0c0a..0f66c8b 100644
--- a/libs/gui/include/gui/SurfaceComposerClient.h
+++ b/libs/gui/include/gui/SurfaceComposerClient.h
@@ -298,7 +298,9 @@
static status_t removeHdrLayerInfoListener(const sp<IBinder>& displayToken,
const sp<gui::IHdrLayerInfoListener>& listener);
- static status_t setActivePictureListener(const sp<gui::IActivePictureListener>& listener);
+ static status_t addActivePictureListener(const sp<gui::IActivePictureListener>& listener);
+
+ static status_t removeActivePictureListener(const sp<gui::IActivePictureListener>& listener);
/*
* Sends a power boost to the composer. This function is asynchronous.
diff --git a/libs/gui/tests/Android.bp b/libs/gui/tests/Android.bp
index f07747f..87051a7 100644
--- a/libs/gui/tests/Android.bp
+++ b/libs/gui/tests/Android.bp
@@ -55,6 +55,7 @@
"-DCOM_ANDROID_GRAPHICS_LIBGUI_FLAGS_BQ_EXTENDEDALLOCATE=true",
"-DCOM_ANDROID_GRAPHICS_LIBGUI_FLAGS_WB_CONSUMER_BASE_OWNS_BQ=true",
"-DCOM_ANDROID_GRAPHICS_LIBGUI_FLAGS_WB_PLATFORM_API_IMPROVEMENTS=true",
+ "-DCOM_ANDROID_GRAPHICS_LIBGUI_FLAGS_WB_UNLIMITED_SLOTS=true",
],
srcs: [
diff --git a/libs/gui/tests/BufferItemConsumer_test.cpp b/libs/gui/tests/BufferItemConsumer_test.cpp
index 3b6a66e..6453885 100644
--- a/libs/gui/tests/BufferItemConsumer_test.cpp
+++ b/libs/gui/tests/BufferItemConsumer_test.cpp
@@ -22,8 +22,11 @@
#include <gui/BufferItemConsumer.h>
#include <gui/IProducerListener.h>
#include <gui/Surface.h>
+#include <ui/BufferQueueDefs.h>
#include <ui/GraphicBuffer.h>
+#include <unordered_set>
+
namespace android {
static constexpr int kWidth = 100;
@@ -57,6 +60,8 @@
};
void SetUp() override {
+ mBuffers.resize(BufferQueueDefs::NUM_BUFFER_SLOTS);
+
mBIC = new BufferItemConsumer(kUsage, kMaxLockedBuffers, true);
String8 name("BufferItemConsumer_Under_Test");
mBIC->setName(name);
@@ -137,6 +142,11 @@
ASSERT_EQ(NO_ERROR, ret);
}
+ void DetachBuffer(int slot) {
+ ALOGD("detachBuffer: slot=%d", slot);
+ status_t ret = mBIC->detachBuffer(mBuffers[slot]);
+ ASSERT_EQ(NO_ERROR, ret);
+ }
std::mutex mMutex;
int mFreedBufferCount{0};
@@ -146,7 +156,7 @@
sp<BufferFreedListener> mBFL;
sp<IGraphicBufferProducer> mProducer;
sp<IGraphicBufferConsumer> mConsumer;
- sp<GraphicBuffer> mBuffers[BufferQueueDefs::NUM_BUFFER_SLOTS];
+ std::vector<sp<GraphicBuffer>> mBuffers;
};
// Test that detaching buffer from consumer side triggers onBufferFreed.
@@ -239,4 +249,52 @@
}
#endif // COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(WB_PLATFORM_API_IMPROVEMENTS)
+#if COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(WB_UNLIMITED_SLOTS)
+TEST_F(BufferItemConsumerTest, UnlimitedSlots_AcquireReleaseAll) {
+ ASSERT_EQ(OK, mProducer->extendSlotCount(256));
+ mBuffers.resize(256);
+
+ ASSERT_EQ(OK, mProducer->setMaxDequeuedBufferCount(100));
+
+ std::unordered_set<int> slots;
+ for (int i = 0; i < 100; i++) {
+ int slot;
+ DequeueBuffer(&slot);
+ slots.insert(slot);
+ }
+ EXPECT_EQ(100u, slots.size());
+
+ for (int dequeuedSlot : slots) {
+ QueueBuffer(dequeuedSlot);
+
+ int slot;
+ AcquireBuffer(&slot);
+ ReleaseBuffer(slot);
+ }
+}
+
+TEST_F(BufferItemConsumerTest, UnlimitedSlots_AcquireDetachAll) {
+ ASSERT_EQ(OK, mProducer->extendSlotCount(256));
+ mBuffers.resize(256);
+
+ ASSERT_EQ(OK, mProducer->setMaxDequeuedBufferCount(100));
+
+ std::unordered_set<int> slots;
+ for (int i = 0; i < 100; i++) {
+ int slot;
+ DequeueBuffer(&slot);
+ slots.insert(slot);
+ }
+ EXPECT_EQ(100u, slots.size());
+
+ for (int dequeuedSlot : slots) {
+ QueueBuffer(dequeuedSlot);
+
+ int slot;
+ AcquireBuffer(&slot);
+ DetachBuffer(slot);
+ }
+}
+#endif
+
} // namespace android
diff --git a/libs/gui/tests/BufferQueue_test.cpp b/libs/gui/tests/BufferQueue_test.cpp
index 1606099..77b4ae8 100644
--- a/libs/gui/tests/BufferQueue_test.cpp
+++ b/libs/gui/tests/BufferQueue_test.cpp
@@ -20,6 +20,8 @@
#include "Constants.h"
#include "MockConsumer.h"
+#include <EGL/egl.h>
+
#include <gui/BufferItem.h>
#include <gui/BufferItemConsumer.h>
#include <gui/BufferQueue.h>
@@ -44,7 +46,9 @@
#include <gtest/gtest.h>
#include <future>
+#include <optional>
#include <thread>
+#include <unordered_map>
#include <com_android_graphics_libgui_flags.h>
@@ -1612,4 +1616,221 @@
}
}
+#if COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(WB_UNLIMITED_SLOTS)
+struct MockUnlimitedSlotConsumer : public MockConsumer {
+ virtual void onSlotCountChanged(int size) override { mSize = size; }
+
+ std::optional<int> mSize;
+};
+
+TEST_F(BufferQueueTest, UnlimitedSlots_FailsWhenNotAllowed) {
+ createBufferQueue();
+
+ sp<MockUnlimitedSlotConsumer> mc = sp<MockUnlimitedSlotConsumer>::make();
+ EXPECT_EQ(OK, mConsumer->consumerConnect(mc, false));
+
+ EXPECT_EQ(INVALID_OPERATION, mProducer->extendSlotCount(64));
+ EXPECT_EQ(INVALID_OPERATION, mProducer->extendSlotCount(32));
+ EXPECT_EQ(INVALID_OPERATION, mProducer->extendSlotCount(128));
+
+ EXPECT_EQ(std::nullopt, mc->mSize);
+}
+
+TEST_F(BufferQueueTest, UnlimitedSlots_OnlyAllowedForExtensions) {
+ createBufferQueue();
+
+ sp<MockUnlimitedSlotConsumer> consumerListener = sp<MockUnlimitedSlotConsumer>::make();
+ EXPECT_EQ(OK, mConsumer->consumerConnect(consumerListener, false));
+ EXPECT_EQ(OK, mConsumer->allowUnlimitedSlots(true));
+
+ EXPECT_EQ(BAD_VALUE, mProducer->extendSlotCount(32));
+ EXPECT_EQ(OK, mProducer->extendSlotCount(64));
+ EXPECT_EQ(OK, mProducer->extendSlotCount(128));
+ EXPECT_EQ(128, *consumerListener->mSize);
+
+ EXPECT_EQ(OK, mProducer->extendSlotCount(128));
+ EXPECT_EQ(BAD_VALUE, mProducer->extendSlotCount(127));
+}
+
+class BufferQueueUnlimitedTest : public BufferQueueTest {
+protected:
+ static constexpr auto kMaxBufferCount = 128;
+ static constexpr auto kAcquirableBufferCount = 2;
+ static constexpr auto kDequeableBufferCount = kMaxBufferCount - kAcquirableBufferCount;
+
+ virtual void SetUp() override {
+ BufferQueueTest::SetUp();
+
+ createBufferQueue();
+ setUpConsumer();
+ setUpProducer();
+ }
+
+ void setUpConsumer() {
+ EXPECT_EQ(OK, mConsumer->consumerConnect(mConsumerListener, false));
+
+#if COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(WB_UNLIMITED_SLOTS)
+ EXPECT_EQ(OK, mConsumer->allowUnlimitedSlots(true));
+#endif
+ EXPECT_EQ(OK, mConsumer->setConsumerUsageBits(GraphicBuffer::USAGE_SW_READ_OFTEN));
+ EXPECT_EQ(OK, mConsumer->setDefaultBufferSize(10, 10));
+ EXPECT_EQ(OK, mConsumer->setDefaultBufferFormat(PIXEL_FORMAT_RGBA_8888));
+ EXPECT_EQ(OK, mConsumer->setMaxAcquiredBufferCount(kAcquirableBufferCount));
+ }
+
+ void setUpProducer() {
+ EXPECT_EQ(OK, mProducer->extendSlotCount(kMaxBufferCount));
+
+ IGraphicBufferProducer::QueueBufferOutput output;
+ EXPECT_EQ(OK,
+ mProducer->connect(mProducerListener, NATIVE_WINDOW_API_CPU,
+ /*producerControlledByApp*/ true, &output));
+#if COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(WB_UNLIMITED_SLOTS)
+ ASSERT_TRUE(output.isSlotExpansionAllowed);
+#endif
+ ASSERT_EQ(OK, mProducer->setMaxDequeuedBufferCount(kDequeableBufferCount));
+ ASSERT_EQ(OK, mProducer->allowAllocation(true));
+ }
+
+ std::unordered_map<int, sp<Fence>> dequeueAll() {
+ std::unordered_map<int, sp<Fence>> slotsToFences;
+
+ for (int i = 0; i < kDequeableBufferCount; ++i) {
+ int slot;
+ sp<Fence> fence;
+ sp<GraphicBuffer> buffer;
+
+ status_t ret =
+ mProducer->dequeueBuffer(&slot, &fence, /*w*/ 0, /*h*/ 0, /*format*/ 0,
+ /*uint64_t*/ 0,
+ /*outBufferAge*/ nullptr, /*outTimestamps*/ nullptr);
+ if (ret & IGraphicBufferProducer::BUFFER_NEEDS_REALLOCATION) {
+ EXPECT_EQ(OK, mProducer->requestBuffer(slot, &buffer))
+ << "Unable to request buffer for slot " << slot;
+ }
+ EXPECT_FALSE(slotsToFences.contains(slot));
+ slotsToFences.emplace(slot, fence);
+ }
+ EXPECT_EQ(kDequeableBufferCount, (int)slotsToFences.size());
+ return slotsToFences;
+ }
+
+ sp<MockUnlimitedSlotConsumer> mConsumerListener = sp<MockUnlimitedSlotConsumer>::make();
+ sp<StubProducerListener> mProducerListener = sp<StubProducerListener>::make();
+};
+
+TEST_F(BufferQueueUnlimitedTest, ExpandOverridesConsumerMaxBuffers) {
+ createBufferQueue();
+ setUpConsumer();
+ EXPECT_EQ(OK, mConsumer->setMaxBufferCount(10));
+
+ setUpProducer();
+
+ EXPECT_EQ(kDequeableBufferCount, (int)dequeueAll().size());
+}
+
+TEST_F(BufferQueueUnlimitedTest, CanDetachAll) {
+ auto slotsToFences = dequeueAll();
+ for (auto& [slot, fence] : slotsToFences) {
+ EXPECT_EQ(OK, mProducer->detachBuffer(slot));
+ }
+}
+
+TEST_F(BufferQueueUnlimitedTest, CanCancelAll) {
+ auto slotsToFences = dequeueAll();
+ for (auto& [slot, fence] : slotsToFences) {
+ EXPECT_EQ(OK, mProducer->cancelBuffer(slot, fence));
+ }
+}
+
+TEST_F(BufferQueueUnlimitedTest, CanAcquireAndReleaseAll) {
+ auto slotsToFences = dequeueAll();
+ for (auto& [slot, fence] : slotsToFences) {
+ IGraphicBufferProducer::QueueBufferInput input;
+ input.fence = fence;
+
+ IGraphicBufferProducer::QueueBufferOutput output;
+ EXPECT_EQ(OK, mProducer->queueBuffer(slot, input, &output));
+
+ BufferItem buffer;
+ EXPECT_EQ(OK, mConsumer->acquireBuffer(&buffer, 0));
+ EXPECT_EQ(OK,
+ mConsumer->releaseBuffer(buffer.mSlot, buffer.mFrameNumber, EGL_NO_DISPLAY,
+ EGL_NO_SYNC, buffer.mFence));
+ }
+}
+
+TEST_F(BufferQueueUnlimitedTest, CanAcquireAndDetachAll) {
+ auto slotsToFences = dequeueAll();
+ for (auto& [slot, fence] : slotsToFences) {
+ IGraphicBufferProducer::QueueBufferInput input;
+ input.fence = fence;
+
+ IGraphicBufferProducer::QueueBufferOutput output;
+ EXPECT_EQ(OK, mProducer->queueBuffer(slot, input, &output));
+
+ BufferItem buffer;
+ EXPECT_EQ(OK, mConsumer->acquireBuffer(&buffer, 0));
+ EXPECT_EQ(OK, mConsumer->detachBuffer(buffer.mSlot));
+ }
+}
+
+TEST_F(BufferQueueUnlimitedTest, GetReleasedBuffersExtended) {
+ // First, acquire and release all the buffers so the consumer "knows" about
+ // them
+ auto slotsToFences = dequeueAll();
+
+ std::vector<bool> releasedSlots;
+ EXPECT_EQ(OK, mConsumer->getReleasedBuffersExtended(&releasedSlots));
+ for (auto& [slot, _] : slotsToFences) {
+ EXPECT_TRUE(releasedSlots[slot])
+ << "Slots that haven't been acquired will show up as released.";
+ }
+ for (auto& [slot, fence] : slotsToFences) {
+ IGraphicBufferProducer::QueueBufferInput input;
+ input.fence = fence;
+
+ IGraphicBufferProducer::QueueBufferOutput output;
+ EXPECT_EQ(OK, mProducer->queueBuffer(slot, input, &output));
+
+ BufferItem buffer;
+ EXPECT_EQ(OK, mConsumer->acquireBuffer(&buffer, 0));
+ EXPECT_EQ(OK,
+ mConsumer->releaseBuffer(buffer.mSlot, buffer.mFrameNumber, EGL_NO_DISPLAY,
+ EGL_NO_SYNC_KHR, buffer.mFence));
+ }
+
+ EXPECT_EQ(OK, mConsumer->getReleasedBuffersExtended(&releasedSlots));
+ for (auto& [slot, _] : slotsToFences) {
+ EXPECT_FALSE(releasedSlots[slot])
+ << "Slots that have been acquired will show up as not released.";
+ }
+
+ // Then, alternatively cancel and detach (release) buffers. Only detached
+ // buffers should be returned by getReleasedBuffersExtended
+ slotsToFences = dequeueAll();
+ std::set<int> cancelledSlots;
+ std::set<int> detachedSlots;
+ bool cancel;
+ for (auto& [slot, fence] : slotsToFences) {
+ if (cancel) {
+ EXPECT_EQ(OK, mProducer->cancelBuffer(slot, fence));
+ cancelledSlots.insert(slot);
+ } else {
+ EXPECT_EQ(OK, mProducer->detachBuffer(slot));
+ detachedSlots.insert(slot);
+ }
+ cancel = !cancel;
+ }
+
+ EXPECT_EQ(OK, mConsumer->getReleasedBuffersExtended(&releasedSlots));
+ for (int slot : detachedSlots) {
+ EXPECT_TRUE(releasedSlots[slot]) << "Slots that are detached are released.";
+ }
+ for (int slot : cancelledSlots) {
+ EXPECT_FALSE(releasedSlots[slot])
+ << "Slots that are still held in the queue are not released.";
+ }
+}
+#endif // COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(WB_UNLIMITED_SLOTS)
} // namespace android
diff --git a/libs/gui/tests/CpuConsumer_test.cpp b/libs/gui/tests/CpuConsumer_test.cpp
index f4239cb..9476930 100644
--- a/libs/gui/tests/CpuConsumer_test.cpp
+++ b/libs/gui/tests/CpuConsumer_test.cpp
@@ -803,6 +803,27 @@
::testing::ValuesIn(rgba8888TestSets));
#endif
+#if COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(WB_UNLIMITED_SLOTS)
+TEST(CpuConsumerSlotTest, UnlimitedSlots_AcquireReleaseAll) {
+ sp<CpuConsumer> cpuConsumer = sp<CpuConsumer>::make(3);
+ sp<Surface> surface = cpuConsumer->getSurface();
+ sp<SurfaceListener> listener = sp<StubSurfaceListener>::make();
+ ASSERT_EQ(OK, surface->connect(NATIVE_WINDOW_API_CPU, listener));
+ ASSERT_EQ(OK, surface->setMaxDequeuedBufferCount(256));
+ std::vector<Surface::BatchBuffer> buffers(256);
+ EXPECT_EQ(OK, surface->dequeueBuffers(&buffers));
+
+ for (auto& buffer : buffers) {
+ sp<GraphicBuffer> graphicBuffer = GraphicBuffer::from(buffer.buffer);
+ sp<Fence> fence = sp<Fence>::make(buffer.fenceFd);
+ EXPECT_EQ(OK, surface->queueBuffer(graphicBuffer, fence));
+
+ CpuConsumer::LockedBuffer nativeBuffer;
+ EXPECT_EQ(OK, cpuConsumer->lockNextBuffer(&nativeBuffer));
+ EXPECT_EQ(OK, cpuConsumer->unlockBuffer(nativeBuffer));
+ }
+}
+#endif
} // namespace android
diff --git a/libs/gui/tests/FillBuffer.cpp b/libs/gui/tests/FillBuffer.cpp
index b60995a..11383d9 100644
--- a/libs/gui/tests/FillBuffer.cpp
+++ b/libs/gui/tests/FillBuffer.cpp
@@ -76,7 +76,7 @@
}
void fillRGBA8Buffer(uint8_t* buf, int w, int h, int stride) {
- const size_t PIXEL_SIZE = 4;
+ constexpr size_t PIXEL_SIZE = 4;
for (int x = 0; x < w; x++) {
for (int y = 0; y < h; y++) {
off_t offset = (y * stride + x) * PIXEL_SIZE;
@@ -89,6 +89,21 @@
}
}
+void fillRGBA8Buffer(uint8_t* buf, int w, int h, int stride, uint8_t r, uint8_t g, uint8_t b,
+ uint8_t a) {
+ constexpr size_t PIXEL_SIZE = 4;
+
+ for (int x = 0; x < w; x++) {
+ for (int y = 0; y < h; y++) {
+ off_t offset = (y * stride + x) * PIXEL_SIZE;
+ buf[offset] = r;
+ buf[offset + 1] = g;
+ buf[offset + 2] = b;
+ buf[offset + 3] = a;
+ }
+ }
+}
+
void produceOneRGBA8Frame(const sp<ANativeWindow>& anw) {
android_native_buffer_t* anb;
ASSERT_EQ(NO_ERROR, native_window_dequeue_buffer_and_wait(anw.get(),
diff --git a/libs/gui/tests/FillBuffer.h b/libs/gui/tests/FillBuffer.h
index b584179..f5d6b8b 100644
--- a/libs/gui/tests/FillBuffer.h
+++ b/libs/gui/tests/FillBuffer.h
@@ -30,6 +30,8 @@
const android_native_rect_t& rect);
void fillRGBA8Buffer(uint8_t* buf, int w, int h, int stride);
+void fillRGBA8Buffer(uint8_t* buf, int w, int h, int stride, uint8_t r, uint8_t g, uint8_t b,
+ uint8_t a);
// Produce a single RGBA8 frame by filling a buffer with a checkerboard pattern
// using the CPU. This assumes that the ANativeWindow is already configured to
diff --git a/libs/gui/tests/FrameRateUtilsTest.cpp b/libs/gui/tests/FrameRateUtilsTest.cpp
index 9ffe91f..c502533 100644
--- a/libs/gui/tests/FrameRateUtilsTest.cpp
+++ b/libs/gui/tests/FrameRateUtilsTest.cpp
@@ -34,7 +34,7 @@
ANATIVEWINDOW_CHANGE_FRAME_RATE_ALWAYS, ""));
EXPECT_TRUE(ValidateFrameRate(60.0f, ANATIVEWINDOW_FRAME_RATE_COMPATIBILITY_FIXED_SOURCE,
ANATIVEWINDOW_CHANGE_FRAME_RATE_ONLY_IF_SEAMLESS, ""));
- EXPECT_TRUE(ValidateFrameRate(60.0f, ANATIVEWINDOW_FRAME_RATE_COMPATIBILITY_GTE,
+ EXPECT_TRUE(ValidateFrameRate(60.0f, ANATIVEWINDOW_FRAME_RATE_COMPATIBILITY_AT_LEAST,
ANATIVEWINDOW_CHANGE_FRAME_RATE_ONLY_IF_SEAMLESS, ""));
// Privileged APIs.
diff --git a/libs/gui/tests/SurfaceTextureGL_test.cpp b/libs/gui/tests/SurfaceTextureGL_test.cpp
index 449533a..b22b853 100644
--- a/libs/gui/tests/SurfaceTextureGL_test.cpp
+++ b/libs/gui/tests/SurfaceTextureGL_test.cpp
@@ -17,6 +17,8 @@
#define LOG_TAG "SurfaceTextureGL_test"
//#define LOG_NDEBUG 0
+#include <gmock/gmock.h>
+
#include "SurfaceTextureGL.h"
#include "DisconnectWaiter.h"
@@ -735,4 +737,30 @@
ASSERT_NE(NO_ERROR, mST->updateTexImage());
}
+#if COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(WB_CONSUMER_BASE_OWNS_BQ)
+TEST_F(SurfaceTextureGLTest, TestUnlimitedSlots) {
+ ASSERT_EQ(OK, mSTC->connect(NATIVE_WINDOW_API_CPU, sp<StubSurfaceListener>::make()));
+ ASSERT_EQ(OK, mSTC->setMaxDequeuedBufferCount(256));
+
+ std::vector<Surface::BatchBuffer> buffers(256);
+ ASSERT_EQ(OK, mSTC->dequeueBuffers(&buffers));
+ ASSERT_EQ(256u, buffers.size());
+ ASSERT_THAT(buffers, Each(Field(&Surface::BatchBuffer::buffer, ::testing::NotNull())));
+
+ for (size_t i = 0; i < buffers.size(); ++i) {
+ sp<GraphicBuffer> graphicBuffer = GraphicBuffer::from(buffers[i].buffer);
+ sp<Fence> fence = sp<Fence>::make(buffers[i].fenceFd);
+
+ void* buf;
+ ASSERT_EQ(OK, graphicBuffer->lock(AHARDWAREBUFFER_USAGE_CPU_WRITE_OFTEN, &buf));
+ fillRGBA8Buffer((uint8_t*)buf, graphicBuffer->getWidth(), graphicBuffer->getHeight(),
+ graphicBuffer->getStride(), i, i, i, i);
+ graphicBuffer->unlock();
+
+ ASSERT_EQ(OK, mSTC->queueBuffer(graphicBuffer, fence));
+ ASSERT_EQ(OK, mST->updateTexImage());
+ checkPixel(0, 0, i, i, i, i);
+ }
+}
+#endif
} // namespace android
diff --git a/libs/gui/tests/Surface_test.cpp b/libs/gui/tests/Surface_test.cpp
index 76362ff..646e30e 100644
--- a/libs/gui/tests/Surface_test.cpp
+++ b/libs/gui/tests/Surface_test.cpp
@@ -14,10 +14,6 @@
* limitations under the License.
*/
-#include "gui/view/Surface.h"
-#include "Constants.h"
-#include "MockConsumer.h"
-
#include <gtest/gtest.h>
#include <SurfaceFlingerProperties.h>
@@ -36,10 +32,13 @@
#include <gui/IConsumerListener.h>
#include <gui/IGraphicBufferConsumer.h>
#include <gui/IGraphicBufferProducer.h>
+#include <gui/IProducerListener.h>
#include <gui/ISurfaceComposer.h>
#include <gui/Surface.h>
#include <gui/SurfaceComposerClient.h>
#include <gui/SyncScreenCaptureListener.h>
+#include <gui/view/Surface.h>
+#include <nativebase/nativebase.h>
#include <private/gui/ComposerService.h>
#include <private/gui/ComposerServiceAIDL.h>
#include <sys/types.h>
@@ -47,6 +46,7 @@
#include <ui/BufferQueueDefs.h>
#include <ui/DisplayMode.h>
#include <ui/GraphicBuffer.h>
+#include <ui/PixelFormat.h>
#include <ui/Rect.h>
#include <utils/Errors.h>
#include <utils/String8.h>
@@ -55,9 +55,12 @@
#include <cstddef>
#include <cstdint>
#include <future>
+#include <iterator>
#include <limits>
#include <thread>
+#include "Constants.h"
+#include "MockConsumer.h"
#include "testserver/TestServerClient.h"
namespace android {
@@ -1016,7 +1019,11 @@
return binder::Status::ok();
}
- binder::Status setActivePictureListener(const sp<gui::IActivePictureListener>&) {
+ binder::Status addActivePictureListener(const sp<gui::IActivePictureListener>&) {
+ return binder::Status::ok();
+ }
+
+ binder::Status removeActivePictureListener(const sp<gui::IActivePictureListener>&) {
return binder::Status::ok();
}
@@ -2529,4 +2536,128 @@
}
#endif // COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(WB_PLATFORM_API_IMPROVEMENTS)
+#if COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(WB_UNLIMITED_SLOTS)
+TEST_F(SurfaceTest, UnlimitedSlots_FailsOnIncompatibleConsumer) {
+ sp<IGraphicBufferProducer> producer;
+ sp<IGraphicBufferConsumer> consumer;
+ BufferQueue::createBufferQueue(&producer, &consumer);
+
+ sp<IConsumerListener> consumerListener = sp<FakeConsumer>::make();
+
+ EXPECT_EQ(OK, consumer->allowUnlimitedSlots(false));
+ EXPECT_EQ(OK, consumer->consumerConnect(consumerListener, /* consumerListener */ true));
+
+ sp<Surface> surface = sp<Surface>::make(producer);
+ sp<SurfaceListener> surfaceListener = sp<StubSurfaceListener>::make();
+ EXPECT_EQ(OK, surface->connect(NATIVE_WINDOW_API_CPU, surfaceListener));
+
+ EXPECT_NE(OK, surface->setMaxDequeuedBufferCount(128))
+ << "We shouldn't be able to set high max buffer counts if the consumer doesn't allow "
+ "it";
+}
+
+TEST_F(SurfaceTest, UnlimitedSlots_CanDequeueAndQueueMoreThanOldMaximum) {
+ sp<IGraphicBufferProducer> producer;
+ sp<IGraphicBufferConsumer> consumer;
+ BufferQueue::createBufferQueue(&producer, &consumer);
+
+ sp<IConsumerListener> consumerListener = sp<FakeConsumer>::make();
+
+ EXPECT_EQ(OK, consumer->allowUnlimitedSlots(true));
+ EXPECT_EQ(OK, consumer->consumerConnect(consumerListener, /* consumerListener */ true));
+ EXPECT_EQ(OK, consumer->setDefaultBufferFormat(PIXEL_FORMAT_RGBA_8888));
+ EXPECT_EQ(OK, consumer->setConsumerUsageBits(AHARDWAREBUFFER_USAGE_CPU_WRITE_OFTEN));
+
+ sp<Surface> surface = sp<Surface>::make(producer);
+ sp<SurfaceListener> surfaceListener = sp<StubSurfaceListener>::make();
+ EXPECT_EQ(OK, surface->connect(NATIVE_WINDOW_API_CPU, surfaceListener));
+
+ EXPECT_EQ(OK, surface->setMaxDequeuedBufferCount(128))
+ << "If unlimited slots are allowed, we should be able increase the max dequeued buffer "
+ "count arbitrarily";
+
+ std::vector<std::tuple<sp<GraphicBuffer>, sp<Fence>, int>> buffers;
+ for (int i = 0; i < 128; i++) {
+ sp<GraphicBuffer> buffer;
+ sp<Fence> fence;
+ ASSERT_EQ(OK, surface->dequeueBuffer(&buffer, &fence)) << "Unable to dequeue buffer #" << i;
+ buffers.push_back({buffer, fence, i});
+ }
+
+ for (auto& [buffer, fence, idx] : buffers) {
+ ASSERT_EQ(OK, surface->queueBuffer(buffer, fence)) << "Unable to queue buffer #" << idx;
+ }
+}
+
+TEST_F(SurfaceTest, UnlimitedSlots_CanDequeueAndDetachMoreThanOldMaximum) {
+ sp<IGraphicBufferProducer> producer;
+ sp<IGraphicBufferConsumer> consumer;
+ BufferQueue::createBufferQueue(&producer, &consumer);
+
+ sp<IConsumerListener> consumerListener = sp<FakeConsumer>::make();
+
+ EXPECT_EQ(OK, consumer->allowUnlimitedSlots(true));
+ EXPECT_EQ(OK, consumer->consumerConnect(consumerListener, /* consumerListener */ true));
+ EXPECT_EQ(OK, consumer->setDefaultBufferFormat(PIXEL_FORMAT_RGBA_8888));
+ EXPECT_EQ(OK, consumer->setConsumerUsageBits(AHARDWAREBUFFER_USAGE_CPU_WRITE_OFTEN));
+
+ sp<Surface> surface = sp<Surface>::make(producer);
+ sp<SurfaceListener> surfaceListener = sp<StubSurfaceListener>::make();
+ EXPECT_EQ(OK, surface->connect(NATIVE_WINDOW_API_CPU, surfaceListener));
+
+ EXPECT_EQ(OK, surface->setMaxDequeuedBufferCount(128))
+ << "If unlimited slots are allowed, we should be able increase the max dequeued buffer "
+ "count arbitrarily";
+
+ std::vector<std::tuple<sp<GraphicBuffer>, sp<Fence>, int>> buffers;
+ for (int i = 0; i < 128; i++) {
+ sp<GraphicBuffer> buffer;
+ sp<Fence> fence;
+ ASSERT_EQ(OK, surface->dequeueBuffer(&buffer, &fence)) << "Unable to dequeue buffer #" << i;
+ buffers.push_back({buffer, fence, i});
+ }
+
+ for (auto& [buffer, _, idx] : buffers) {
+ ASSERT_EQ(OK, surface->detachBuffer(buffer)) << "Unable to detach buffer #" << idx;
+ }
+}
+
+TEST_F(SurfaceTest, UnlimitedSlots_BatchOperations) {
+ sp<IGraphicBufferProducer> producer;
+ sp<IGraphicBufferConsumer> consumer;
+ BufferQueue::createBufferQueue(&producer, &consumer);
+
+ sp<IConsumerListener> consumerListener = sp<FakeConsumer>::make();
+
+ EXPECT_EQ(OK, consumer->allowUnlimitedSlots(true));
+ EXPECT_EQ(OK, consumer->consumerConnect(consumerListener, /* consumerListener */ true));
+ EXPECT_EQ(OK, consumer->setDefaultBufferFormat(PIXEL_FORMAT_RGBA_8888));
+ EXPECT_EQ(OK, consumer->setConsumerUsageBits(AHARDWAREBUFFER_USAGE_CPU_WRITE_OFTEN));
+
+ sp<Surface> surface = sp<Surface>::make(producer);
+ sp<SurfaceListener> surfaceListener = sp<StubSurfaceListener>::make();
+ EXPECT_EQ(OK, surface->connect(NATIVE_WINDOW_API_CPU, surfaceListener));
+
+ EXPECT_EQ(OK, surface->setMaxDequeuedBufferCount(128))
+ << "If unlimited slots are allowed, we should be able increase the max dequeued buffer "
+ "count arbitrarily";
+
+ std::vector<Surface::BatchBuffer> buffers(128);
+ EXPECT_EQ(OK, surface->dequeueBuffers(&buffers));
+ EXPECT_EQ(128u, buffers.size());
+
+ std::vector<Surface::BatchQueuedBuffer> queuedBuffers;
+ std::transform(buffers.begin(), buffers.end(), std::back_inserter(queuedBuffers),
+ [](Surface::BatchBuffer& buffer) {
+ Surface::BatchQueuedBuffer out;
+ out.buffer = buffer.buffer;
+ out.fenceFd = buffer.fenceFd;
+ return out;
+ });
+
+ std::vector<SurfaceQueueBufferOutput> outputs;
+ EXPECT_EQ(OK, surface->queueBuffers(queuedBuffers, &outputs));
+ EXPECT_EQ(128u, outputs.size());
+}
+#endif // COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(WB_UNLIMITED_SLOTS)
} // namespace android
diff --git a/libs/gui/tests/benchmarks/Android.bp b/libs/gui/tests/benchmarks/Android.bp
new file mode 100644
index 0000000..a728bef
--- /dev/null
+++ b/libs/gui/tests/benchmarks/Android.bp
@@ -0,0 +1,27 @@
+package {
+ // See: http://go/android-license-faq
+ // A large-scale-change added 'default_applicable_licenses' to import
+ // all of the 'license_kinds' from "frameworks_native_license"
+ // to get the below license kinds:
+ // SPDX-license-identifier-Apache-2.0
+ default_applicable_licenses: ["frameworks_native_license"],
+}
+
+cc_benchmark {
+ name: "libgui_benchmarks",
+ srcs: [
+ "*.cpp",
+ ],
+ defaults: ["libgui-defaults"],
+ static_libs: [
+ "libgmock",
+ "libgtest",
+ ],
+ shared_libs: [
+ "libgui",
+ ],
+ header_libs: [
+ "libsurfaceflinger_mocks_headers",
+ "surfaceflinger_tests_common_headers",
+ ],
+}
diff --git a/libs/gui/tests/benchmarks/Transaction_benchmarks.cpp b/libs/gui/tests/benchmarks/Transaction_benchmarks.cpp
new file mode 100644
index 0000000..0a51895
--- /dev/null
+++ b/libs/gui/tests/benchmarks/Transaction_benchmarks.cpp
@@ -0,0 +1,139 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <benchmark/benchmark.h>
+#include <cstddef>
+#include <optional>
+#include <vector>
+#include "binder/Parcel.h"
+#include "gui/SurfaceComposerClient.h"
+#include "gui/SurfaceControl.h"
+#include "log/log_main.h"
+
+namespace android {
+namespace {
+using android::hardware::graphics::common::V1_1::BufferUsage;
+
+std::vector<sp<SurfaceControl>> createSurfaceControl(const char* name, size_t num) {
+ sp<SurfaceComposerClient> client = sp<SurfaceComposerClient>::make();
+ LOG_FATAL_IF(client->initCheck() != OK, "Could not init SurfaceComposerClient");
+ std::vector<sp<SurfaceControl>> surfaceControls;
+ for (size_t i = 0; i < num; i++) {
+ surfaceControls.push_back(
+ client->createSurface(String8(name), 0, 0, PIXEL_FORMAT_RGBA_8888,
+ ISurfaceComposerClient::eFXSurfaceBufferState));
+ }
+ return surfaceControls;
+}
+
+void applyTransaction(benchmark::State& state) {
+ std::vector<sp<SurfaceControl>> surfaceControls = createSurfaceControl(__func__, 5 /* num */);
+ for (auto _ : state) {
+ SurfaceComposerClient::Transaction t;
+ for (auto& sc : surfaceControls) {
+ t.setCrop(sc, FloatRect{1, 2, 3, 4});
+ t.setAutoRefresh(sc, true);
+ t.hide(sc);
+ t.setAlpha(sc, 0.5);
+ t.setCornerRadius(sc, 0.8);
+ }
+ Parcel p;
+ t.writeToParcel(&p);
+ t.clear();
+ benchmark::DoNotOptimize(t);
+ }
+}
+BENCHMARK(applyTransaction);
+
+// Mimic a buffer transaction with callbacks
+void applyBufferTransaction(benchmark::State& state) {
+ std::vector<sp<SurfaceControl>> surfaceControls = createSurfaceControl(__func__, 5 /* num */);
+ std::vector<sp<GraphicBuffer>> buffers;
+ for (size_t i = 0; i < surfaceControls.size(); i++) {
+ int64_t usageFlags = BufferUsage::CPU_READ_OFTEN | BufferUsage::CPU_WRITE_OFTEN |
+ BufferUsage::COMPOSER_OVERLAY | BufferUsage::GPU_TEXTURE;
+ buffers.emplace_back(
+ sp<GraphicBuffer>::make(5, 5, PIXEL_FORMAT_RGBA_8888, 1, usageFlags, "test"));
+ }
+
+ for (auto _ : state) {
+ SurfaceComposerClient::Transaction t;
+ int i = 0;
+ for (auto& sc : surfaceControls) {
+ std::function<void(const ReleaseCallbackId&, const sp<Fence>& /*releaseFence*/,
+ std::optional<uint32_t> currentMaxAcquiredBufferCount)>
+ releaseBufferCallback;
+ t.setBuffer(sc, buffers[i], std::nullopt, std::nullopt, 5, releaseBufferCallback);
+ }
+ Parcel p;
+ // proxy for applying the transaction
+ t.writeToParcel(&p);
+ t.clear();
+ benchmark::DoNotOptimize(t);
+ }
+}
+BENCHMARK(applyBufferTransaction);
+
+void mergeTransaction(benchmark::State& state) {
+ std::vector<sp<SurfaceControl>> surfaceControls = createSurfaceControl(__func__, 5 /* num */);
+ for (auto _ : state) {
+ SurfaceComposerClient::Transaction t1;
+ for (auto& sc : surfaceControls) {
+ t1.setCrop(sc, FloatRect{1, 2, 3, 4});
+ t1.setAutoRefresh(sc, true);
+ t1.hide(sc);
+ t1.setAlpha(sc, 0.5);
+ t1.setCornerRadius(sc, 0.8);
+ }
+
+ SurfaceComposerClient::Transaction t2;
+ for (auto& sc : surfaceControls) {
+ t2.hide(sc);
+ t2.setAlpha(sc, 0.5);
+ t2.setCornerRadius(sc, 0.8);
+ t2.setBackgroundBlurRadius(sc, 5);
+ }
+ t1.merge(std::move(t2));
+ benchmark::DoNotOptimize(t1);
+ }
+}
+BENCHMARK(mergeTransaction);
+
+void readTransactionFromParcel(benchmark::State& state) {
+ std::vector<sp<SurfaceControl>> surfaceControls = createSurfaceControl(__func__, 5 /* num */);
+ SurfaceComposerClient::Transaction t;
+ for (auto& sc : surfaceControls) {
+ t.setCrop(sc, FloatRect{1, 2, 3, 4});
+ t.setAutoRefresh(sc, true);
+ t.hide(sc);
+ t.setAlpha(sc, 0.5);
+ t.setCornerRadius(sc, 0.8);
+ }
+ Parcel p;
+ t.writeToParcel(&p);
+ t.clear();
+
+ for (auto _ : state) {
+ SurfaceComposerClient::Transaction t2;
+ t2.readFromParcel(&p);
+ p.setDataPosition(0);
+ benchmark::DoNotOptimize(t2);
+ }
+}
+BENCHMARK(readTransactionFromParcel);
+
+} // namespace
+} // namespace android
diff --git a/libs/gui/tests/benchmarks/main.cpp b/libs/gui/tests/benchmarks/main.cpp
new file mode 100644
index 0000000..685c7c6
--- /dev/null
+++ b/libs/gui/tests/benchmarks/main.cpp
@@ -0,0 +1,18 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <benchmark/benchmark.h>
+BENCHMARK_MAIN();
diff --git a/libs/input/Android.bp b/libs/input/Android.bp
index a4ae54b..389fb7f 100644
--- a/libs/input/Android.bp
+++ b/libs/input/Android.bp
@@ -262,6 +262,7 @@
shared_libs: [
"android.companion.virtualdevice.flags-aconfig-cc",
+ "libaconfig_storage_read_api_cc",
"libbase",
"libbinder",
"libbinder_ndk",
diff --git a/libs/input/KeyCharacterMap.cpp b/libs/input/KeyCharacterMap.cpp
index 90d29dd..d2c49b1 100644
--- a/libs/input/KeyCharacterMap.cpp
+++ b/libs/input/KeyCharacterMap.cpp
@@ -615,7 +615,7 @@
ALOGE("%s: Null parcel", __func__);
return nullptr;
}
- std::string loadFileName = parcel->readCString();
+ std::string loadFileName = parcel->readString8().c_str();
std::unique_ptr<KeyCharacterMap> map =
std::make_unique<KeyCharacterMap>(KeyCharacterMap(loadFileName));
map->mType = static_cast<KeyCharacterMap::KeyboardType>(parcel->readInt32());
@@ -704,7 +704,7 @@
ALOGE("%s: Null parcel", __func__);
return;
}
- parcel->writeCString(mLoadFileName.c_str());
+ parcel->writeString8(String8(mLoadFileName.c_str()));
parcel->writeInt32(static_cast<int32_t>(mType));
parcel->writeBool(mLayoutOverlayApplied);
diff --git a/libs/input/android/os/IInputConstants.aidl b/libs/input/android/os/IInputConstants.aidl
index 31592cd..6ce3fba 100644
--- a/libs/input/android/os/IInputConstants.aidl
+++ b/libs/input/android/os/IInputConstants.aidl
@@ -76,6 +76,9 @@
/* The default pointer acceleration value. */
const int DEFAULT_POINTER_ACCELERATION = 3;
+ /* The default mouse wheel acceleration value. */
+ const int DEFAULT_MOUSE_WHEEL_ACCELERATION = 4;
+
/**
* Use the default Velocity Tracker Strategy. Different axes may use different default
* strategies.
diff --git a/libs/input/tests/Android.bp b/libs/input/tests/Android.bp
index d1c564d..0167c43 100644
--- a/libs/input/tests/Android.bp
+++ b/libs/input/tests/Android.bp
@@ -66,6 +66,7 @@
},
},
shared_libs: [
+ "libaconfig_storage_read_api_cc",
"libbase",
"libbinder",
"libcutils",
diff --git a/libs/nativewindow/include/android/native_window.h b/libs/nativewindow/include/android/native_window.h
index ed3e8c1..10abb7c 100644
--- a/libs/nativewindow/include/android/native_window.h
+++ b/libs/nativewindow/include/android/native_window.h
@@ -258,11 +258,11 @@
ANATIVEWINDOW_FRAME_RATE_COMPATIBILITY_FIXED_SOURCE = 1,
/**
- * The window requests a frame rate that is greater than or equal to the specified frame rate.
+ * The window requests a frame rate that is at least the specified frame rate.
* This value should be used for UIs, animations, scrolling, and anything that is not a game
* or video.
*/
- ANATIVEWINDOW_FRAME_RATE_COMPATIBILITY_GTE = 2
+ ANATIVEWINDOW_FRAME_RATE_COMPATIBILITY_AT_LEAST = 2
};
/**
diff --git a/libs/permission/include/binder/AppOpsManager.h b/libs/permission/include/binder/AppOpsManager.h
index 243532b..7e179d6 100644
--- a/libs/permission/include/binder/AppOpsManager.h
+++ b/libs/permission/include/binder/AppOpsManager.h
@@ -148,7 +148,10 @@
OP_BLUETOOTH_ADVERTISE = 114,
OP_RECORD_INCOMING_PHONE_AUDIO = 115,
OP_NEARBY_WIFI_DEVICES = 116,
- _NUM_OP = 117
+ // 116 - 154 omitted due to lack of use in native
+ OP_CONTROL_AUDIO = 154,
+ OP_CONTROL_AUDIO_PARTIAL = 155,
+ _NUM_OP = 156,
};
enum {
diff --git a/libs/renderengine/RenderEngine.cpp b/libs/renderengine/RenderEngine.cpp
index 907590a..873fc67 100644
--- a/libs/renderengine/RenderEngine.cpp
+++ b/libs/renderengine/RenderEngine.cpp
@@ -107,16 +107,15 @@
return resultFuture;
}
-ftl::Future<FenceResult> RenderEngine::drawGainmap(
- const std::shared_ptr<ExternalTexture>& sdr, base::borrowed_fd&& sdrFence,
+ftl::Future<FenceResult> RenderEngine::tonemapAndDrawGainmap(
const std::shared_ptr<ExternalTexture>& hdr, base::borrowed_fd&& hdrFence,
- float hdrSdrRatio, ui::Dataspace dataspace,
+ float hdrSdrRatio, ui::Dataspace dataspace, const std::shared_ptr<ExternalTexture>& sdr,
const std::shared_ptr<ExternalTexture>& gainmap) {
const auto resultPromise = std::make_shared<std::promise<FenceResult>>();
std::future<FenceResult> resultFuture = resultPromise->get_future();
updateProtectedContext({}, {sdr.get(), hdr.get(), gainmap.get()});
- drawGainmapInternal(std::move(resultPromise), sdr, std::move(sdrFence), hdr,
- std::move(hdrFence), hdrSdrRatio, dataspace, gainmap);
+ tonemapAndDrawGainmapInternal(std::move(resultPromise), hdr, std::move(hdrFence), hdrSdrRatio,
+ dataspace, sdr, gainmap);
return resultFuture;
}
diff --git a/libs/renderengine/include/renderengine/RenderEngine.h b/libs/renderengine/include/renderengine/RenderEngine.h
index 95c4d03..c2dd4ae 100644
--- a/libs/renderengine/include/renderengine/RenderEngine.h
+++ b/libs/renderengine/include/renderengine/RenderEngine.h
@@ -217,12 +217,17 @@
const std::shared_ptr<ExternalTexture>& buffer,
base::unique_fd&& bufferFence);
- virtual ftl::Future<FenceResult> drawGainmap(const std::shared_ptr<ExternalTexture>& sdr,
- base::borrowed_fd&& sdrFence,
- const std::shared_ptr<ExternalTexture>& hdr,
- base::borrowed_fd&& hdrFence, float hdrSdrRatio,
- ui::Dataspace dataspace,
- const std::shared_ptr<ExternalTexture>& gainmap);
+ // Tonemaps an HDR input image and draws an SDR rendition, plus a gainmap
+ // describing how to recover the HDR image.
+ //
+ // The HDR input image is ALWAYS encoded with an sRGB transfer function and
+ // is a floating point format. Accordingly, the hdrSdrRatio describes the
+ // max luminance in the HDR input image above SDR, and the dataspace
+ // describes the input primaries.
+ virtual ftl::Future<FenceResult> tonemapAndDrawGainmap(
+ const std::shared_ptr<ExternalTexture>& hdr, base::borrowed_fd&& hdrFence,
+ float hdrSdrRatio, ui::Dataspace dataspace, const std::shared_ptr<ExternalTexture>& sdr,
+ const std::shared_ptr<ExternalTexture>& gainmap);
// Clean-up method that should be called on the main thread after the
// drawFence returned by drawLayers fires. This method will free up
@@ -310,11 +315,10 @@
const DisplaySettings& display, const std::vector<LayerSettings>& layers,
const std::shared_ptr<ExternalTexture>& buffer, base::unique_fd&& bufferFence) = 0;
- virtual void drawGainmapInternal(
+ virtual void tonemapAndDrawGainmapInternal(
const std::shared_ptr<std::promise<FenceResult>>&& resultPromise,
- const std::shared_ptr<ExternalTexture>& sdr, base::borrowed_fd&& sdrFence,
const std::shared_ptr<ExternalTexture>& hdr, base::borrowed_fd&& hdrFence,
- float hdrSdrRatio, ui::Dataspace dataspace,
+ float hdrSdrRatio, ui::Dataspace dataspace, const std::shared_ptr<ExternalTexture>& sdr,
const std::shared_ptr<ExternalTexture>& gainmap) = 0;
};
diff --git a/libs/renderengine/include/renderengine/mock/RenderEngine.h b/libs/renderengine/include/renderengine/mock/RenderEngine.h
index fb8331d..c42e403 100644
--- a/libs/renderengine/include/renderengine/mock/RenderEngine.h
+++ b/libs/renderengine/include/renderengine/mock/RenderEngine.h
@@ -46,17 +46,16 @@
ftl::Future<FenceResult>(const DisplaySettings&, const std::vector<LayerSettings>&,
const std::shared_ptr<ExternalTexture>&,
base::unique_fd&&));
- MOCK_METHOD7(drawGainmap,
+ MOCK_METHOD6(tonemapAndDrawGainmap,
ftl::Future<FenceResult>(const std::shared_ptr<ExternalTexture>&,
- base::borrowed_fd&&,
- const std::shared_ptr<ExternalTexture>&,
base::borrowed_fd&&, float, ui::Dataspace,
+ const std::shared_ptr<ExternalTexture>&,
const std::shared_ptr<ExternalTexture>&));
- MOCK_METHOD8(drawGainmapInternal,
+ MOCK_METHOD7(tonemapAndDrawGainmapInternal,
void(const std::shared_ptr<std::promise<FenceResult>>&&,
- const std::shared_ptr<ExternalTexture>&, base::borrowed_fd&&,
const std::shared_ptr<ExternalTexture>&, base::borrowed_fd&&, float,
- ui::Dataspace, const std::shared_ptr<ExternalTexture>&));
+ ui::Dataspace, const std::shared_ptr<ExternalTexture>&,
+ const std::shared_ptr<ExternalTexture>&));
MOCK_METHOD5(drawLayersInternal,
void(const std::shared_ptr<std::promise<FenceResult>>&&, const DisplaySettings&,
const std::vector<LayerSettings>&, const std::shared_ptr<ExternalTexture>&,
diff --git a/libs/renderengine/skia/SkiaRenderEngine.cpp b/libs/renderengine/skia/SkiaRenderEngine.cpp
index 7c9ea93..14d08ee 100644
--- a/libs/renderengine/skia/SkiaRenderEngine.cpp
+++ b/libs/renderengine/skia/SkiaRenderEngine.cpp
@@ -576,9 +576,7 @@
if (usingLocalTonemap) {
const float inputRatio =
hdrType == HdrRenderType::GENERIC_HDR ? 1.0f : parameters.layerDimmingRatio;
- static MouriMap kMapper;
- shader = kMapper.mouriMap(getActiveContext(), shader, inputRatio,
- parameters.display.targetHdrSdrRatio);
+ shader = localTonemap(shader, inputRatio, parameters.display.targetHdrSdrRatio);
}
// disable tonemapping if we already locally tonemapped
@@ -619,6 +617,12 @@
return shader;
}
+sk_sp<SkShader> SkiaRenderEngine::localTonemap(sk_sp<SkShader> shader, float inputMultiplier,
+ float targetHdrSdrRatio) {
+ static MouriMap kMapper;
+ return kMapper.mouriMap(getActiveContext(), shader, inputMultiplier, targetHdrSdrRatio);
+}
+
void SkiaRenderEngine::initCanvas(SkCanvas* canvas, const DisplaySettings& display) {
if (CC_UNLIKELY(mCapture->isCaptureRunning())) {
// Record display settings when capture is running.
@@ -1221,44 +1225,58 @@
resultPromise->set_value(std::move(drawFence));
}
-void SkiaRenderEngine::drawGainmapInternal(
+void SkiaRenderEngine::tonemapAndDrawGainmapInternal(
const std::shared_ptr<std::promise<FenceResult>>&& resultPromise,
- const std::shared_ptr<ExternalTexture>& sdr, base::borrowed_fd&& sdrFence,
const std::shared_ptr<ExternalTexture>& hdr, base::borrowed_fd&& hdrFence,
- float hdrSdrRatio, ui::Dataspace dataspace,
+ float hdrSdrRatio, ui::Dataspace dataspace, const std::shared_ptr<ExternalTexture>& sdr,
const std::shared_ptr<ExternalTexture>& gainmap) {
std::lock_guard<std::mutex> lock(mRenderingMutex);
auto context = getActiveContext();
- auto surfaceTextureRef = getOrCreateBackendTexture(gainmap->getBuffer(), true);
- sk_sp<SkSurface> dstSurface =
- surfaceTextureRef->getOrCreateSurface(ui::Dataspace::V0_SRGB_LINEAR);
+ auto gainmapTextureRef = getOrCreateBackendTexture(gainmap->getBuffer(), true);
+ sk_sp<SkSurface> gainmapSurface =
+ gainmapTextureRef->getOrCreateSurface(ui::Dataspace::V0_SRGB_LINEAR);
- waitFence(context, sdrFence);
- const auto sdrTextureRef = getOrCreateBackendTexture(sdr->getBuffer(), false);
- const auto sdrImage = sdrTextureRef->makeImage(dataspace, kPremul_SkAlphaType);
- const auto sdrShader =
- sdrImage->makeShader(SkTileMode::kClamp, SkTileMode::kClamp,
- SkSamplingOptions({SkFilterMode::kLinear, SkMipmapMode::kNone}),
- nullptr);
+ auto sdrTextureRef = getOrCreateBackendTexture(sdr->getBuffer(), true);
+ sk_sp<SkSurface> sdrSurface = sdrTextureRef->getOrCreateSurface(dataspace);
+
waitFence(context, hdrFence);
const auto hdrTextureRef = getOrCreateBackendTexture(hdr->getBuffer(), false);
const auto hdrImage = hdrTextureRef->makeImage(dataspace, kPremul_SkAlphaType);
const auto hdrShader =
hdrImage->makeShader(SkTileMode::kClamp, SkTileMode::kClamp,
- SkSamplingOptions({SkFilterMode::kLinear, SkMipmapMode::kNone}),
+ SkSamplingOptions({SkFilterMode::kNearest, SkMipmapMode::kNone}),
nullptr);
+ const auto tonemappedShader = localTonemap(hdrShader, 1.0f, 1.0f);
+
static GainmapFactory kGainmapFactory;
- const auto gainmapShader = kGainmapFactory.createSkShader(sdrShader, hdrShader, hdrSdrRatio);
+ const auto gainmapShader =
+ kGainmapFactory.createSkShader(tonemappedShader, hdrShader, hdrSdrRatio);
- const auto canvas = dstSurface->getCanvas();
- SkPaint paint;
- paint.setShader(gainmapShader);
- paint.setBlendMode(SkBlendMode::kSrc);
- canvas->drawPaint(paint);
+ sp<Fence> drawFence;
- auto drawFence = sp<Fence>::make(flushAndSubmit(context, dstSurface));
- trace(drawFence);
+ {
+ const auto canvas = sdrSurface->getCanvas();
+ SkPaint paint;
+ paint.setShader(tonemappedShader);
+ paint.setBlendMode(SkBlendMode::kSrc);
+ canvas->drawPaint(paint);
+
+ drawFence = sp<Fence>::make(flushAndSubmit(context, sdrSurface));
+ trace(drawFence);
+ }
+
+ {
+ const auto canvas = gainmapSurface->getCanvas();
+ SkPaint paint;
+ paint.setShader(gainmapShader);
+ paint.setBlendMode(SkBlendMode::kSrc);
+ canvas->drawPaint(paint);
+
+ auto gmFence = sp<Fence>::make(flushAndSubmit(context, gainmapSurface));
+ trace(gmFence);
+ drawFence = Fence::merge("gm-ss", drawFence, gmFence);
+ }
resultPromise->set_value(std::move(drawFence));
}
diff --git a/libs/renderengine/skia/SkiaRenderEngine.h b/libs/renderengine/skia/SkiaRenderEngine.h
index 7be4c25..92b7af9 100644
--- a/libs/renderengine/skia/SkiaRenderEngine.h
+++ b/libs/renderengine/skia/SkiaRenderEngine.h
@@ -143,13 +143,11 @@
const std::vector<LayerSettings>& layers,
const std::shared_ptr<ExternalTexture>& buffer,
base::unique_fd&& bufferFence) override final;
- void drawGainmapInternal(const std::shared_ptr<std::promise<FenceResult>>&& resultPromise,
- const std::shared_ptr<ExternalTexture>& sdr,
- base::borrowed_fd&& sdrFence,
- const std::shared_ptr<ExternalTexture>& hdr,
- base::borrowed_fd&& hdrFence, float hdrSdrRatio,
- ui::Dataspace dataspace,
- const std::shared_ptr<ExternalTexture>& gainmap) override final;
+ void tonemapAndDrawGainmapInternal(
+ const std::shared_ptr<std::promise<FenceResult>>&& resultPromise,
+ const std::shared_ptr<ExternalTexture>& hdr, base::borrowed_fd&& hdrFence,
+ float hdrSdrRatio, ui::Dataspace dataspace, const std::shared_ptr<ExternalTexture>& sdr,
+ const std::shared_ptr<ExternalTexture>& gainmap) override final;
void dump(std::string& result) override final;
@@ -168,6 +166,8 @@
};
sk_sp<SkShader> createRuntimeEffectShader(const RuntimeEffectShaderParameters&);
+ sk_sp<SkShader> localTonemap(sk_sp<SkShader>, float inputMultiplier, float targetHdrSdrRatio);
+
const PixelFormat mDefaultPixelFormat;
// Identifier used for various mappings of layers to various
diff --git a/libs/renderengine/skia/filters/LutShader.cpp b/libs/renderengine/skia/filters/LutShader.cpp
index 5e9dfbb..10218bb 100644
--- a/libs/renderengine/skia/filters/LutShader.cpp
+++ b/libs/renderengine/skia/filters/LutShader.cpp
@@ -39,10 +39,13 @@
uniform int key;
uniform int dimension;
uniform vec3 luminanceCoefficients; // for CIE_Y
+ // for hlg/pq transfer function, we need normalize it to [0.0, 1.0]
+ // we use `normalizeScalar` to do so
+ uniform float normalizeScalar;
vec4 main(vec2 xy) {
float4 rgba = image.eval(xy);
- float3 linear = toLinearSrgb(rgba.rgb);
+ float3 linear = toLinearSrgb(rgba.rgb) * normalizeScalar;
if (dimension == 1) {
// RGB
if (key == 0) {
@@ -52,19 +55,19 @@
float gainR = lut.eval(vec2(indexR, 0.0) + 0.5).r;
float gainG = lut.eval(vec2(indexG, 0.0) + 0.5).r;
float gainB = lut.eval(vec2(indexB, 0.0) + 0.5).r;
- return float4(linear.r * gainR, linear.g * gainG, linear.b * gainB, rgba.a);
+ linear = float3(linear.r * gainR, linear.g * gainG, linear.b * gainB);
// MAX_RGB
} else if (key == 1) {
float maxRGB = max(linear.r, max(linear.g, linear.b));
float index = maxRGB * float(size - 1);
float gain = lut.eval(vec2(index, 0.0) + 0.5).r;
- return float4(linear * gain, rgba.a);
+ linear = linear * gain;
// CIE_Y
} else if (key == 2) {
float y = dot(linear, luminanceCoefficients) / 3.0;
float index = y * float(size - 1);
float gain = lut.eval(vec2(index, 0.0) + 0.5).r;
- return float4(linear * gain, rgba.a);
+ linear = linear * gain;
}
} else if (dimension == 3) {
if (key == 0) {
@@ -110,12 +113,10 @@
float3 c0 = mix(c00, c10, linear.g);
float3 c1 = mix(c01, c11, linear.g);
- float3 val = mix(c0, c1, linear.b);
-
- return float4(val, rgba.a);
+ linear = mix(c0, c1, linear.b);
}
}
- return rgba;
+ return float4(linear, rgba.a);
})");
// same as shader::toColorSpace function
@@ -186,10 +187,16 @@
SkBitmap bitmap;
bitmap.allocPixels(info);
if (!bitmap.installPixels(info, buffer.data(), info.minRowBytes())) {
- LOG_ALWAYS_FATAL("unable to install pixels");
+ ALOGW("bitmap.installPixels failed, skip this Lut!");
+ return input;
}
sk_sp<SkImage> lutImage = SkImages::RasterFromBitmap(bitmap);
+ if (!lutImage) {
+ ALOGW("Got a nullptr from SkImages::RasterFromBitmap, skip this Lut!");
+ return input;
+ }
+
mBuilder->child("image") = input;
mBuilder->child("lut") =
lutImage->makeRawShader(SkTileMode::kClamp, SkTileMode::kClamp,
@@ -197,9 +204,22 @@
? SkSamplingOptions(SkFilterMode::kLinear)
: SkSamplingOptions());
+ float normalizeScalar = 1.0;
+ switch (srcDataspace & HAL_DATASPACE_TRANSFER_MASK) {
+ case HAL_DATASPACE_TRANSFER_HLG:
+ normalizeScalar = 0.203;
+ break;
+ case HAL_DATASPACE_TRANSFER_ST2084:
+ normalizeScalar = 0.0203;
+ break;
+ default:
+ normalizeScalar = 1.0;
+ }
const int uSize = static_cast<int>(size);
const int uKey = static_cast<int>(samplingKey);
const int uDimension = static_cast<int>(dimension);
+ const float uNormalizeScalar = static_cast<float>(normalizeScalar);
+
if (static_cast<LutProperties::SamplingKey>(samplingKey) == LutProperties::SamplingKey::CIE_Y) {
// Use predefined colorspaces of input dataspace so that we can get D65 illuminant
mat3 toXYZMatrix(toColorSpace(srcDataspace).getRGBtoXYZ());
@@ -211,6 +231,7 @@
mBuilder->uniform("size") = uSize;
mBuilder->uniform("key") = uKey;
mBuilder->uniform("dimension") = uDimension;
+ mBuilder->uniform("normalizeScalar") = uNormalizeScalar;
return mBuilder->makeShader();
}
diff --git a/libs/renderengine/skia/filters/MouriMap.cpp b/libs/renderengine/skia/filters/MouriMap.cpp
index b099bcf..aa12cef 100644
--- a/libs/renderengine/skia/filters/MouriMap.cpp
+++ b/libs/renderengine/skia/filters/MouriMap.cpp
@@ -30,12 +30,12 @@
}
const SkString kCrosstalkAndChunk16x16(R"(
uniform shader bitmap;
- uniform float hdrSdrRatio;
+ uniform float inputMultiplier;
vec4 main(vec2 xy) {
float maximum = 0.0;
for (int y = 0; y < 16; y++) {
for (int x = 0; x < 16; x++) {
- float3 linear = toLinearSrgb(bitmap.eval((xy - 0.5) * 16 + 0.5 + vec2(x, y)).rgb) * hdrSdrRatio;
+ float3 linear = toLinearSrgb(bitmap.eval((xy - 0.5) * 16 + 0.5 + vec2(x, y)).rgb) * inputMultiplier;
float maxRGB = max(linear.r, max(linear.g, linear.b));
maximum = max(maximum, log2(max(maxRGB, 1.0)));
}
@@ -77,12 +77,12 @@
uniform shader image;
uniform shader lux;
uniform float scaleFactor;
- uniform float hdrSdrRatio;
+ uniform float inputMultiplier;
uniform float targetHdrSdrRatio;
vec4 main(vec2 xy) {
float localMax = lux.eval(xy * scaleFactor).r;
float4 rgba = image.eval(xy);
- float3 linear = toLinearSrgb(rgba.rgb) * hdrSdrRatio;
+ float3 linear = toLinearSrgb(rgba.rgb) * inputMultiplier;
if (localMax <= targetHdrSdrRatio) {
return float4(fromLinearSrgb(linear), rgba.a);
@@ -116,19 +116,19 @@
mTonemap(makeEffect(kTonemap)) {}
sk_sp<SkShader> MouriMap::mouriMap(SkiaGpuContext* context, sk_sp<SkShader> input,
- float hdrSdrRatio, float targetHdrSdrRatio) {
- auto downchunked = downchunk(context, input, hdrSdrRatio);
+ float inputMultiplier, float targetHdrSdrRatio) {
+ auto downchunked = downchunk(context, input, inputMultiplier);
auto localLux = blur(context, downchunked.get());
- return tonemap(input, localLux.get(), hdrSdrRatio, targetHdrSdrRatio);
+ return tonemap(input, localLux.get(), inputMultiplier, targetHdrSdrRatio);
}
sk_sp<SkImage> MouriMap::downchunk(SkiaGpuContext* context, sk_sp<SkShader> input,
- float hdrSdrRatio) const {
+ float inputMultiplier) const {
SkMatrix matrix;
SkImage* image = input->isAImage(&matrix, (SkTileMode*)nullptr);
SkRuntimeShaderBuilder crosstalkAndChunk16x16Builder(mCrosstalkAndChunk16x16);
crosstalkAndChunk16x16Builder.child("bitmap") = input;
- crosstalkAndChunk16x16Builder.uniform("hdrSdrRatio") = hdrSdrRatio;
+ crosstalkAndChunk16x16Builder.uniform("inputMultiplier") = inputMultiplier;
// TODO: fp16 might be overkill. Most practical surfaces use 8-bit RGB for HDR UI and 10-bit YUV
// for HDR video. These downsample operations compute log2(max(linear RGB, 1.0)). So we don't
// care about LDR precision since they all resolve to LDR-max. For appropriately mastered HDR
@@ -168,7 +168,7 @@
LOG_ALWAYS_FATAL_IF(!blurSurface, "%s: Failed to create surface!", __func__);
return makeImage(blurSurface.get(), blurBuilder);
}
-sk_sp<SkShader> MouriMap::tonemap(sk_sp<SkShader> input, SkImage* localLux, float hdrSdrRatio,
+sk_sp<SkShader> MouriMap::tonemap(sk_sp<SkShader> input, SkImage* localLux, float inputMultiplier,
float targetHdrSdrRatio) const {
static constexpr float kScaleFactor = 1.0f / 128.0f;
SkRuntimeShaderBuilder tonemapBuilder(mTonemap);
@@ -177,7 +177,7 @@
localLux->makeRawShader(SkTileMode::kClamp, SkTileMode::kClamp,
SkSamplingOptions(SkFilterMode::kLinear, SkMipmapMode::kNone));
tonemapBuilder.uniform("scaleFactor") = kScaleFactor;
- tonemapBuilder.uniform("hdrSdrRatio") = hdrSdrRatio;
+ tonemapBuilder.uniform("inputMultiplier") = inputMultiplier;
tonemapBuilder.uniform("targetHdrSdrRatio") = targetHdrSdrRatio;
return tonemapBuilder.makeShader();
}
diff --git a/libs/renderengine/skia/filters/MouriMap.h b/libs/renderengine/skia/filters/MouriMap.h
index 9ba2b6f..f4bfa15 100644
--- a/libs/renderengine/skia/filters/MouriMap.h
+++ b/libs/renderengine/skia/filters/MouriMap.h
@@ -62,10 +62,13 @@
public:
MouriMap();
// Apply the MouriMap tonemmaping operator to the input.
- // The HDR/SDR ratio describes the luminace range of the input. 1.0 means SDR. Anything larger
- // then 1.0 means that there is headroom above the SDR region.
- // Similarly, the target HDR/SDR ratio describes the luminance range of the output.
- sk_sp<SkShader> mouriMap(SkiaGpuContext* context, sk_sp<SkShader> input, float inputHdrSdrRatio,
+ // The inputMultiplier informs how to interpret the luminance encoding of the input.
+ // For a fixed point input, this is necessary to interpret what "1.0" means for the input
+ // pixels, so that the luminance can be scaled appropriately, such that the operator can
+ // transform SDR values to be within 1.0. For a floating point input, "1.0" always means SDR,
+ // so the caller must pass 1.0.
+ // The target HDR/SDR ratio describes the luminance range of the output.
+ sk_sp<SkShader> mouriMap(SkiaGpuContext* context, sk_sp<SkShader> input, float inputMultiplier,
float targetHdrSdrRatio);
private:
diff --git a/libs/renderengine/threaded/RenderEngineThreaded.cpp b/libs/renderengine/threaded/RenderEngineThreaded.cpp
index c187f93..ea5605d 100644
--- a/libs/renderengine/threaded/RenderEngineThreaded.cpp
+++ b/libs/renderengine/threaded/RenderEngineThreaded.cpp
@@ -249,11 +249,10 @@
return;
}
-void RenderEngineThreaded::drawGainmapInternal(
+void RenderEngineThreaded::tonemapAndDrawGainmapInternal(
const std::shared_ptr<std::promise<FenceResult>>&& resultPromise,
- const std::shared_ptr<ExternalTexture>& sdr, base::borrowed_fd&& sdrFence,
const std::shared_ptr<ExternalTexture>& hdr, base::borrowed_fd&& hdrFence,
- float hdrSdrRatio, ui::Dataspace dataspace,
+ float hdrSdrRatio, ui::Dataspace dataspace, const std::shared_ptr<ExternalTexture>& sdr,
const std::shared_ptr<ExternalTexture>& gainmap) {
resultPromise->set_value(Fence::NO_FENCE);
return;
@@ -281,10 +280,9 @@
return resultFuture;
}
-ftl::Future<FenceResult> RenderEngineThreaded::drawGainmap(
- const std::shared_ptr<ExternalTexture>& sdr, base::borrowed_fd&& sdrFence,
+ftl::Future<FenceResult> RenderEngineThreaded::tonemapAndDrawGainmap(
const std::shared_ptr<ExternalTexture>& hdr, base::borrowed_fd&& hdrFence,
- float hdrSdrRatio, ui::Dataspace dataspace,
+ float hdrSdrRatio, ui::Dataspace dataspace, const std::shared_ptr<ExternalTexture>& sdr,
const std::shared_ptr<ExternalTexture>& gainmap) {
SFTRACE_CALL();
const auto resultPromise = std::make_shared<std::promise<FenceResult>>();
@@ -292,13 +290,14 @@
{
std::lock_guard lock(mThreadMutex);
mNeedsPostRenderCleanup = true;
- mFunctionCalls.push([resultPromise, sdr, sdrFence = std::move(sdrFence), hdr,
- hdrFence = std::move(hdrFence), hdrSdrRatio, dataspace,
+ mFunctionCalls.push([resultPromise, hdr, hdrFence = std::move(hdrFence), hdrSdrRatio,
+ dataspace, sdr,
gainmap](renderengine::RenderEngine& instance) mutable {
- SFTRACE_NAME("REThreaded::drawGainmap");
- instance.updateProtectedContext({}, {sdr.get(), hdr.get(), gainmap.get()});
- instance.drawGainmapInternal(std::move(resultPromise), sdr, std::move(sdrFence), hdr,
- std::move(hdrFence), hdrSdrRatio, dataspace, gainmap);
+ SFTRACE_NAME("REThreaded::tonemapAndDrawGainmap");
+ instance.updateProtectedContext({}, {hdr.get(), sdr.get(), gainmap.get()});
+ instance.tonemapAndDrawGainmapInternal(std::move(resultPromise), hdr,
+ std::move(hdrFence), hdrSdrRatio, dataspace, sdr,
+ gainmap);
});
}
mCondition.notify_one();
diff --git a/libs/renderengine/threaded/RenderEngineThreaded.h b/libs/renderengine/threaded/RenderEngineThreaded.h
index cb6e924..8554b55 100644
--- a/libs/renderengine/threaded/RenderEngineThreaded.h
+++ b/libs/renderengine/threaded/RenderEngineThreaded.h
@@ -55,12 +55,10 @@
const std::vector<LayerSettings>& layers,
const std::shared_ptr<ExternalTexture>& buffer,
base::unique_fd&& bufferFence) override;
- ftl::Future<FenceResult> drawGainmap(const std::shared_ptr<ExternalTexture>& sdr,
- base::borrowed_fd&& sdrFence,
- const std::shared_ptr<ExternalTexture>& hdr,
- base::borrowed_fd&& hdrFence, float hdrSdrRatio,
- ui::Dataspace dataspace,
- const std::shared_ptr<ExternalTexture>& gainmap) override;
+ ftl::Future<FenceResult> tonemapAndDrawGainmap(
+ const std::shared_ptr<ExternalTexture>& hdr, base::borrowed_fd&& hdrFence,
+ float hdrSdrRatio, ui::Dataspace dataspace, const std::shared_ptr<ExternalTexture>& sdr,
+ const std::shared_ptr<ExternalTexture>& gainmap) override;
int getContextPriority() override;
bool supportsBackgroundBlur() override;
@@ -77,13 +75,11 @@
const std::vector<LayerSettings>& layers,
const std::shared_ptr<ExternalTexture>& buffer,
base::unique_fd&& bufferFence) override;
- void drawGainmapInternal(const std::shared_ptr<std::promise<FenceResult>>&& resultPromise,
- const std::shared_ptr<ExternalTexture>& sdr,
- base::borrowed_fd&& sdrFence,
- const std::shared_ptr<ExternalTexture>& hdr,
- base::borrowed_fd&& hdrFence, float hdrSdrRatio,
- ui::Dataspace dataspace,
- const std::shared_ptr<ExternalTexture>& gainmap) override;
+ void tonemapAndDrawGainmapInternal(
+ const std::shared_ptr<std::promise<FenceResult>>&& resultPromise,
+ const std::shared_ptr<ExternalTexture>& hdr, base::borrowed_fd&& hdrFence,
+ float hdrSdrRatio, ui::Dataspace dataspace, const std::shared_ptr<ExternalTexture>& sdr,
+ const std::shared_ptr<ExternalTexture>& gainmap) override;
private:
void threadMain(CreateInstanceFactory factory);
diff --git a/libs/tracing_perfetto/Android.bp b/libs/tracing_perfetto/Android.bp
index 9a2d4f7..8d8884a 100644
--- a/libs/tracing_perfetto/Android.bp
+++ b/libs/tracing_perfetto/Android.bp
@@ -37,13 +37,17 @@
srcs: [
"tracing_perfetto.cpp",
"tracing_perfetto_internal.cpp",
+ "tracing_sdk.cpp",
],
shared_libs: [
"libbase",
"libcutils",
"libperfetto_c",
- "android.os.flags-aconfig-cc-host",
+ ],
+
+ export_shared_lib_headers: [
+ "libperfetto_c",
],
host_supported: true,
diff --git a/libs/tracing_perfetto/include/tracing_sdk.h b/libs/tracing_perfetto/include/tracing_sdk.h
new file mode 100644
index 0000000..4a6e849
--- /dev/null
+++ b/libs/tracing_perfetto/include/tracing_sdk.h
@@ -0,0 +1,461 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include <android-base/logging.h>
+#include <stdint.h>
+
+#include <optional>
+#include <vector>
+
+#include "perfetto/public/producer.h"
+#include "perfetto/public/te_category_macros.h"
+#include "perfetto/public/te_macros.h"
+#include "perfetto/public/track_event.h"
+
+/**
+ * The objects declared here are intended to be managed by Java.
+ * This means the Java Garbage Collector is responsible for freeing the
+ * underlying native resources.
+ *
+ * The static methods prefixed with `delete_` are special. They are designed to be
+ * invoked by Java through the `NativeAllocationRegistry` when the
+ * corresponding Java object becomes unreachable. These methods act as
+ * callbacks to ensure proper deallocation of native resources.
+ */
+namespace tracing_perfetto {
+/**
+ * @brief Represents extra data associated with a trace event.
+ * This class manages a collection of PerfettoTeHlExtra pointers.
+ */
+class Extra;
+
+/**
+ * @brief Emits a trace event.
+ * @param type The type of the event.
+ * @param cat The category of the event.
+ * @param name The name of the event.
+ * @param arg_ptr Pointer to Extra data.
+ */
+void trace_event(int type, const PerfettoTeCategory* cat, const char* name,
+ Extra* extra);
+
+/**
+ * @brief Gets the process track UUID.
+ */
+uint64_t get_process_track_uuid();
+
+/**
+ * @brief Gets the thread track UUID for a given PID.
+ */
+uint64_t get_thread_track_uuid(pid_t tid);
+
+/**
+ * @brief Holder for all the other classes in the file.
+ */
+class Extra {
+ public:
+ Extra();
+ void push_extra(PerfettoTeHlExtra* extra);
+ void pop_extra();
+ void clear_extras();
+ static void delete_extra(Extra* extra);
+
+ PerfettoTeHlExtra* const* get() const;
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(Extra);
+
+ // These PerfettoTeHlExtra pointers are really pointers to all the other
+ // types of extras: Category, DebugArg, Counter etc. Those objects are
+ // individually managed by Java.
+ std::vector<PerfettoTeHlExtra*> extras_;
+};
+
+/**
+ * @brief Represents a trace event category.
+ */
+class Category {
+ public:
+ Category(const std::string& name, const std::string& tag,
+ const std::string& severity);
+
+ ~Category();
+
+ void register_category();
+
+ void unregister_category();
+
+ bool is_category_enabled();
+
+ static void delete_category(Category* category);
+
+ const PerfettoTeCategory* get() const;
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(Category);
+ PerfettoTeCategory category_;
+ const std::string name_;
+ const std::string tag_;
+ const std::string severity_;
+};
+
+/**
+ * @brief Represents one end of a flow between two events.
+ */
+class Flow {
+ public:
+ Flow();
+
+ void set_process_flow(uint64_t id);
+ void set_process_terminating_flow(uint64_t id);
+ static void delete_flow(Flow* flow);
+
+ const PerfettoTeHlExtraFlow* get() const;
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(Flow);
+ PerfettoTeHlExtraFlow flow_;
+};
+
+/**
+ * @brief Represents a named track.
+ */
+class NamedTrack {
+ public:
+ NamedTrack(uint64_t id, uint64_t parent_uuid, const std::string& name);
+
+ static void delete_track(NamedTrack* track);
+
+ const PerfettoTeHlExtraNamedTrack* get() const;
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(NamedTrack);
+ const std::string name_;
+ PerfettoTeHlExtraNamedTrack track_;
+};
+
+/**
+ * @brief Represents a registered track.
+ */
+class RegisteredTrack {
+ public:
+ RegisteredTrack(uint64_t id, uint64_t parent_uuid, const std::string& name,
+ bool is_counter);
+ ~RegisteredTrack();
+
+ void register_track();
+ void unregister_track();
+ static void delete_track(RegisteredTrack* track);
+
+ const PerfettoTeHlExtraRegisteredTrack* get() const;
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(RegisteredTrack);
+ PerfettoTeRegisteredTrack registered_track_;
+ PerfettoTeHlExtraRegisteredTrack track_;
+ const std::string name_;
+ const uint64_t id_;
+ const uint64_t parent_uuid_;
+ const bool is_counter_;
+};
+
+/**
+ * @brief Represents a counter track event.
+ * @tparam T The data type of the counter (int64_t or double).
+ */
+template <typename T>
+class Counter {
+ public:
+ template <typename>
+ struct always_false : std::false_type {};
+
+ struct TypeMap {
+ using type = std::invoke_result_t<decltype([]() {
+ if constexpr (std::is_same_v<T, int64_t>) {
+ return std::type_identity<PerfettoTeHlExtraCounterInt64>{};
+ } else if constexpr (std::is_same_v<T, double>) {
+ return std::type_identity<PerfettoTeHlExtraCounterDouble>{};
+ } else {
+ return std::type_identity<void>{};
+ }
+ })>::type;
+
+ static constexpr int enum_value = []() {
+ if constexpr (std::is_same_v<T, int64_t>) {
+ return PERFETTO_TE_HL_EXTRA_TYPE_COUNTER_INT64;
+ } else if constexpr (std::is_same_v<T, double>) {
+ return PERFETTO_TE_HL_EXTRA_TYPE_COUNTER_DOUBLE;
+ } else {
+ static_assert(always_false<T>::value, "Unsupported type");
+ return 0; // Never reached, just to satisfy return type
+ }
+ }();
+ };
+
+ Counter() {
+ static_assert(!std::is_same_v<typename TypeMap::type, void>,
+ "Unsupported type for Counter");
+
+ typename TypeMap::type counter;
+ counter.header = {TypeMap::enum_value};
+ counter_ = std::move(counter);
+ }
+
+ void set_value(T value) {
+ if constexpr (std::is_same_v<T, int64_t>) {
+ counter_.value = value;
+ } else if constexpr (std::is_same_v<T, double>) {
+ counter_.value = value;
+ }
+ }
+
+ static void delete_counter(Counter* counter) {
+ delete counter;
+ }
+
+ const TypeMap::type* get() const {
+ return &counter_;
+ }
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(Counter);
+ TypeMap::type counter_;
+};
+
+/**
+ * @brief Represents a debug argument for a trace event.
+ * @tparam T The data type of the argument (bool, int64_t, double, const char*).
+ */
+template <typename T>
+class DebugArg {
+ public:
+ template <typename>
+ struct always_false : std::false_type {};
+
+ struct TypeMap {
+ using type = std::invoke_result_t<decltype([]() {
+ if constexpr (std::is_same_v<T, bool>) {
+ return std::type_identity<PerfettoTeHlExtraDebugArgBool>{};
+ } else if constexpr (std::is_same_v<T, int64_t>) {
+ return std::type_identity<PerfettoTeHlExtraDebugArgInt64>{};
+ } else if constexpr (std::is_same_v<T, double>) {
+ return std::type_identity<PerfettoTeHlExtraDebugArgDouble>{};
+ } else if constexpr (std::is_same_v<T, const char*>) {
+ return std::type_identity<PerfettoTeHlExtraDebugArgString>{};
+ } else {
+ return std::type_identity<void>{};
+ }
+ })>::type;
+
+ static constexpr int enum_value = []() {
+ if constexpr (std::is_same_v<T, bool>) {
+ return PERFETTO_TE_HL_EXTRA_TYPE_DEBUG_ARG_BOOL;
+ } else if constexpr (std::is_same_v<T, int64_t>) {
+ return PERFETTO_TE_HL_EXTRA_TYPE_DEBUG_ARG_INT64;
+ } else if constexpr (std::is_same_v<T, double>) {
+ return PERFETTO_TE_HL_EXTRA_TYPE_DEBUG_ARG_DOUBLE;
+ } else if constexpr (std::is_same_v<T, const char*>) {
+ return PERFETTO_TE_HL_EXTRA_TYPE_DEBUG_ARG_STRING;
+ } else {
+ static_assert(always_false<T>::value, "Unsupported type");
+ return 0; // Never reached, just to satisfy return type
+ }
+ }();
+ };
+
+ DebugArg(const std::string& name) : name_(name) {
+ static_assert(!std::is_same_v<typename TypeMap::type, void>,
+ "Unsupported type for DebugArg");
+
+ typename TypeMap::type arg;
+ arg.header = {TypeMap::enum_value};
+ arg.name = name_.c_str();
+ arg_ = std::move(arg);
+ }
+
+ ~DebugArg() {
+ free_string_value();
+ }
+
+ void set_value(T value) {
+ if constexpr (std::is_same_v<T, const char*>) {
+ free_string_value();
+ arg_.value = value;
+ } else if constexpr (std::is_same_v<T, int64_t>) {
+ arg_.value = value;
+ } else if constexpr (std::is_same_v<T, bool>) {
+ arg_.value = value;
+ } else if constexpr (std::is_same_v<T, double>) {
+ arg_.value = value;
+ }
+ }
+
+ static void delete_arg(DebugArg* arg) {
+ delete arg;
+ }
+
+ const TypeMap::type* get() const {
+ return &arg_;
+ }
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(DebugArg);
+ TypeMap::type arg_;
+ const std::string name_;
+
+ constexpr void free_string_value() {
+ if constexpr (std::is_same_v<typename TypeMap::type,
+ PerfettoTeHlExtraDebugArgString>) {
+ if (arg_.value) {
+ free((void*)arg_.value);
+ arg_.value = nullptr;
+ }
+ }
+ }
+};
+
+template <typename T>
+class ProtoField {
+ public:
+ template <typename>
+ struct always_false : std::false_type {};
+
+ struct TypeMap {
+ using type = std::invoke_result_t<decltype([]() {
+ if constexpr (std::is_same_v<T, int64_t>) {
+ return std::type_identity<PerfettoTeHlProtoFieldVarInt>{};
+ } else if constexpr (std::is_same_v<T, double>) {
+ return std::type_identity<PerfettoTeHlProtoFieldDouble>{};
+ } else if constexpr (std::is_same_v<T, const char*>) {
+ return std::type_identity<PerfettoTeHlProtoFieldCstr>{};
+ } else {
+ return std::type_identity<void>{};
+ }
+ })>::type;
+
+ static constexpr PerfettoTeHlProtoFieldType enum_value = []() {
+ if constexpr (std::is_same_v<T, int64_t>) {
+ return PERFETTO_TE_HL_PROTO_TYPE_VARINT;
+ } else if constexpr (std::is_same_v<T, double>) {
+ return PERFETTO_TE_HL_PROTO_TYPE_DOUBLE;
+ } else if constexpr (std::is_same_v<T, const char*>) {
+ return PERFETTO_TE_HL_PROTO_TYPE_CSTR;
+ } else {
+ static_assert(always_false<T>::value, "Unsupported type");
+ return 0; // Never reached, just to satisfy return type
+ }
+ }();
+ };
+
+ ProtoField() {
+ static_assert(!std::is_same_v<typename TypeMap::type, void>,
+ "Unsupported type for ProtoField");
+
+ typename TypeMap::type arg;
+ arg.header.type = TypeMap::enum_value;
+ arg_ = std::move(arg);
+ }
+
+ ~ProtoField() {
+ free_string_value();
+ }
+
+ void set_value(uint32_t id, T value) {
+ if constexpr (std::is_same_v<T, int64_t>) {
+ arg_.header.id = id;
+ arg_.value = value;
+ } else if constexpr (std::is_same_v<T, double>) {
+ arg_.header.id = id;
+ arg_.value = value;
+ } else if constexpr (std::is_same_v<T, const char*>) {
+ free_string_value();
+ arg_.header.id = id;
+ arg_.str = value;
+ }
+ }
+
+ static void delete_field(ProtoField* field) {
+ delete field;
+ }
+
+ const TypeMap::type* get() const {
+ return &arg_;
+ }
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(ProtoField);
+ TypeMap::type arg_;
+
+ constexpr void free_string_value() {
+ if constexpr (std::is_same_v<typename TypeMap::type,
+ PerfettoTeHlProtoFieldCstr>) {
+ if (arg_.str) {
+ free((void*)arg_.str);
+ arg_.str = nullptr;
+ }
+ }
+ }
+};
+
+class ProtoFieldNested {
+ public:
+ ProtoFieldNested();
+
+ void add_field(PerfettoTeHlProtoField* field);
+ void set_id(uint32_t id);
+ static void delete_field(ProtoFieldNested* field);
+
+ const PerfettoTeHlProtoFieldNested* get() const;
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(ProtoFieldNested);
+ PerfettoTeHlProtoFieldNested field_;
+ // These PerfettoTeHlProtoField pointers are really pointers to all the other
+ // types of protos: PerfettoTeHlProtoFieldVarInt, PerfettoTeHlProtoFieldVarInt,
+ // PerfettoTeHlProtoFieldVarInt, PerfettoTeHlProtoFieldNested. Those objects are
+ // individually managed by Java.
+ std::vector<PerfettoTeHlProtoField*> fields_;
+};
+
+class Proto {
+ public:
+ Proto();
+
+ void add_field(PerfettoTeHlProtoField* field);
+ void clear_fields();
+ static void delete_proto(Proto* proto);
+
+ const PerfettoTeHlExtraProtoFields* get() const;
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(Proto);
+ PerfettoTeHlExtraProtoFields proto_;
+ // These PerfettoTeHlProtoField pointers are really pointers to all the other
+ // types of protos: PerfettoTeHlProtoFieldVarInt, PerfettoTeHlProtoFieldVarInt,
+ // PerfettoTeHlProtoFieldVarInt, PerfettoTeHlProtoFieldNested. Those objects are
+ // individually managed by Java.
+ std::vector<PerfettoTeHlProtoField*> fields_;
+};
+
+/**
+ * @brief Activates a trigger.
+ * @param name The name of the trigger.
+ * @param ttl_ms The time-to-live of the trigger in milliseconds.
+ */
+void activate_trigger(const char* name, uint32_t ttl_ms);
+} // namespace tracing_perfetto
diff --git a/libs/tracing_perfetto/tests/Android.bp b/libs/tracing_perfetto/tests/Android.bp
index d203467..0dab517 100644
--- a/libs/tracing_perfetto/tests/Android.bp
+++ b/libs/tracing_perfetto/tests/Android.bp
@@ -21,12 +21,44 @@
default_applicable_licenses: ["frameworks_native_license"],
}
+cc_library_static {
+ name: "libtracing_perfetto_test_utils",
+ export_include_dirs: [
+ "include",
+ ],
+ static_libs: [
+ "libflagtest",
+ "libgmock",
+ "perfetto_trace_protos",
+ ],
+ cflags: [
+ "-Wall",
+ "-Werror",
+ "-Wno-enum-compare",
+ "-Wno-unused-function",
+ ],
+
+ srcs: [
+ "utils.cpp",
+ ],
+
+ shared_libs: [
+ "libperfetto_c",
+ "liblog",
+ "libprotobuf-cpp-lite",
+ ],
+ export_shared_lib_headers: [
+ "libperfetto_c",
+ ],
+}
+
cc_test {
name: "libtracing_perfetto_tests",
static_libs: [
"libflagtest",
"libgmock",
"perfetto_trace_protos",
+ "libtracing_perfetto_test_utils",
],
cflags: [
"-Wall",
@@ -42,7 +74,6 @@
],
srcs: [
"tracing_perfetto_test.cpp",
- "utils.cpp",
],
test_suites: ["device-tests"],
}
diff --git a/libs/tracing_perfetto/tests/include/utils.h b/libs/tracing_perfetto/tests/include/utils.h
new file mode 100644
index 0000000..b2630e1
--- /dev/null
+++ b/libs/tracing_perfetto/tests/include/utils.h
@@ -0,0 +1,124 @@
+/*
+ * Copyright 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+// Copied from //external/perfetto/src/shared_lib/test/utils.h
+
+#ifndef UTILS_H
+#define UTILS_H
+
+#include <cassert>
+#include <condition_variable>
+#include <cstdint>
+#include <functional>
+#include <iterator>
+#include <memory>
+#include <mutex>
+#include <ostream>
+#include <string>
+#include <vector>
+
+#include "perfetto/public/abi/pb_decoder_abi.h"
+#include "perfetto/public/pb_utils.h"
+#include "perfetto/public/tracing_session.h"
+
+// Pretty printer for gtest
+void PrintTo(const PerfettoPbDecoderField& field, std::ostream*);
+
+namespace perfetto {
+namespace shlib {
+namespace test_utils {
+
+class WaitableEvent {
+ public:
+ WaitableEvent() = default;
+ void Notify() {
+ std::unique_lock<std::mutex> lock(m_);
+ notified_ = true;
+ cv_.notify_one();
+ }
+ bool WaitForNotification() {
+ std::unique_lock<std::mutex> lock(m_);
+ cv_.wait(lock, [this] { return notified_; });
+ return notified_;
+ }
+ bool IsNotified() {
+ std::unique_lock<std::mutex> lock(m_);
+ return notified_;
+ }
+
+ private:
+ std::mutex m_;
+ std::condition_variable cv_;
+ bool notified_ = false;
+};
+
+class TracingSession {
+ public:
+ class Builder {
+ public:
+ Builder() = default;
+ Builder& add_enabled_category(std::string category) {
+ enabled_categories_.push_back(std::move(category));
+ return *this;
+ }
+ Builder& add_disabled_category(std::string category) {
+ disabled_categories_.push_back(std::move(category));
+ return *this;
+ }
+ Builder& add_atrace_category(std::string category) {
+ atrace_categories_.push_back(std::move(category));
+ return *this;
+ }
+ Builder& add_atrace_category_prefer_sdk(std::string category) {
+ atrace_categories_prefer_sdk_.push_back(std::move(category));
+ return *this;
+ }
+ TracingSession Build();
+
+ private:
+ std::vector<std::string> enabled_categories_;
+ std::vector<std::string> disabled_categories_;
+ std::vector<std::string> atrace_categories_;
+ std::vector<std::string> atrace_categories_prefer_sdk_;
+ };
+
+ static TracingSession Adopt(struct PerfettoTracingSessionImpl*);
+ static TracingSession FromBytes(void *buf, size_t len);
+
+ TracingSession(TracingSession&&) noexcept;
+
+ ~TracingSession();
+
+ struct PerfettoTracingSessionImpl* session() const {
+ return session_;
+ }
+
+ bool FlushBlocking(uint32_t timeout_ms);
+ void WaitForStopped();
+ void StopBlocking();
+ std::vector<uint8_t> ReadBlocking();
+
+ private:
+ TracingSession() = default;
+ struct PerfettoTracingSessionImpl* session_;
+ std::unique_ptr<WaitableEvent> stopped_;
+};
+
+} // namespace test_utils
+} // namespace shlib
+} // namespace perfetto
+
+#endif // UTILS_H
diff --git a/libs/tracing_perfetto/tests/tracing_perfetto_test.cpp b/libs/tracing_perfetto/tests/tracing_perfetto_test.cpp
index e9fee2e..b21a090 100644
--- a/libs/tracing_perfetto/tests/tracing_perfetto_test.cpp
+++ b/libs/tracing_perfetto/tests/tracing_perfetto_test.cpp
@@ -22,6 +22,7 @@
#include <unistd.h>
#include "gtest/gtest.h"
+
#include "perfetto/public/abi/data_source_abi.h"
#include "perfetto/public/abi/heap_buffer.h"
#include "perfetto/public/abi/pb_decoder_abi.h"
diff --git a/libs/tracing_perfetto/tests/utils.cpp b/libs/tracing_perfetto/tests/utils.cpp
index 8c4d4a8..af61bc2 100644
--- a/libs/tracing_perfetto/tests/utils.cpp
+++ b/libs/tracing_perfetto/tests/utils.cpp
@@ -34,36 +34,17 @@
namespace perfetto {
namespace shlib {
namespace test_utils {
-namespace {
-
-std::string ToHexChars(uint8_t val) {
- std::string ret;
- uint8_t high_nibble = (val & 0xF0) >> 4;
- uint8_t low_nibble = (val & 0xF);
- static const char hex_chars[] = "0123456789ABCDEF";
- ret.push_back(hex_chars[high_nibble]);
- ret.push_back(hex_chars[low_nibble]);
- return ret;
-}
-
-} // namespace
-
TracingSession TracingSession::Builder::Build() {
perfetto::protos::TraceConfig trace_config;
trace_config.add_buffers()->set_size_kb(1024);
- auto* track_event_ds_config = trace_config.add_data_sources()->mutable_config();
- auto* ftrace_ds_config = trace_config.add_data_sources()->mutable_config();
-
- track_event_ds_config->set_name("track_event");
- track_event_ds_config->set_target_buffer(0);
-
- ftrace_ds_config->set_name("linux.ftrace");
- ftrace_ds_config->set_target_buffer(0);
-
{
- auto* ftrace_config = ftrace_ds_config->mutable_ftrace_config();
if (!atrace_categories_.empty()) {
+ auto* ftrace_ds_config = trace_config.add_data_sources()->mutable_config();
+ ftrace_ds_config->set_name("linux.ftrace");
+ ftrace_ds_config->set_target_buffer(0);
+
+ auto* ftrace_config = ftrace_ds_config->mutable_ftrace_config();
ftrace_config->add_ftrace_events("ftrace/print");
for (const std::string& cat : atrace_categories_) {
ftrace_config->add_atrace_categories(cat);
@@ -76,8 +57,14 @@
}
{
- auto* track_event_config = track_event_ds_config->mutable_track_event_config();
if (!enabled_categories_.empty() || !disabled_categories_.empty()) {
+ auto* track_event_ds_config = trace_config.add_data_sources()->mutable_config();
+
+ track_event_ds_config->set_name("track_event");
+ track_event_ds_config->set_target_buffer(0);
+
+ auto* track_event_config = track_event_ds_config->mutable_track_event_config();
+
for (const std::string& cat : enabled_categories_) {
track_event_config->add_enabled_categories(cat);
}
@@ -88,13 +75,17 @@
}
}
- struct PerfettoTracingSessionImpl* ts =
- PerfettoTracingSessionCreate(PERFETTO_BACKEND_SYSTEM);
-
std::string trace_config_string;
trace_config.SerializeToString(&trace_config_string);
- PerfettoTracingSessionSetup(ts, trace_config_string.data(), trace_config_string.length());
+ return TracingSession::FromBytes(trace_config_string.data(), trace_config_string.length());
+}
+
+TracingSession TracingSession::FromBytes(void *buf, size_t len) {
+ struct PerfettoTracingSessionImpl* ts =
+ PerfettoTracingSessionCreate(PERFETTO_BACKEND_SYSTEM);
+
+ PerfettoTracingSessionSetup(ts, buf, len);
// Fails to start here
PerfettoTracingSessionStartBlocking(ts);
@@ -177,39 +168,3 @@
} // namespace test_utils
} // namespace shlib
} // namespace perfetto
-
-void PrintTo(const PerfettoPbDecoderField& field, std::ostream* pos) {
- std::ostream& os = *pos;
- PerfettoPbDecoderStatus status =
- static_cast<PerfettoPbDecoderStatus>(field.status);
- switch (status) {
- case PERFETTO_PB_DECODER_ERROR:
- os << "MALFORMED PROTOBUF";
- break;
- case PERFETTO_PB_DECODER_DONE:
- os << "DECODER DONE";
- break;
- case PERFETTO_PB_DECODER_OK:
- switch (field.wire_type) {
- case PERFETTO_PB_WIRE_TYPE_DELIMITED:
- os << "\"";
- for (size_t i = 0; i < field.value.delimited.len; i++) {
- os << perfetto::shlib::test_utils::ToHexChars(
- field.value.delimited.start[i])
- << " ";
- }
- os << "\"";
- break;
- case PERFETTO_PB_WIRE_TYPE_VARINT:
- os << "varint: " << field.value.integer64;
- break;
- case PERFETTO_PB_WIRE_TYPE_FIXED32:
- os << "fixed32: " << field.value.integer32;
- break;
- case PERFETTO_PB_WIRE_TYPE_FIXED64:
- os << "fixed64: " << field.value.integer64;
- break;
- }
- break;
- }
-}
diff --git a/libs/tracing_perfetto/tests/utils.h b/libs/tracing_perfetto/tests/utils.h
deleted file mode 100644
index 8edb414..0000000
--- a/libs/tracing_perfetto/tests/utils.h
+++ /dev/null
@@ -1,457 +0,0 @@
-/*
- * Copyright 2024 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-// Copied from //external/perfetto/src/shared_lib/test/utils.h
-
-#ifndef UTILS_H
-#define UTILS_H
-
-#include <cassert>
-#include <condition_variable>
-#include <cstdint>
-#include <functional>
-#include <iterator>
-#include <memory>
-#include <mutex>
-#include <ostream>
-#include <string>
-#include <vector>
-
-#include "gmock/gmock-matchers.h"
-#include "gmock/gmock-more-matchers.h"
-#include "gtest/gtest-matchers.h"
-#include "gtest/gtest.h"
-#include "perfetto/public/abi/pb_decoder_abi.h"
-#include "perfetto/public/pb_utils.h"
-#include "perfetto/public/tracing_session.h"
-
-// Pretty printer for gtest
-void PrintTo(const PerfettoPbDecoderField& field, std::ostream*);
-
-namespace perfetto {
-namespace shlib {
-namespace test_utils {
-
-class WaitableEvent {
- public:
- WaitableEvent() = default;
- void Notify() {
- std::unique_lock<std::mutex> lock(m_);
- notified_ = true;
- cv_.notify_one();
- }
- bool WaitForNotification() {
- std::unique_lock<std::mutex> lock(m_);
- cv_.wait(lock, [this] { return notified_; });
- return notified_;
- }
- bool IsNotified() {
- std::unique_lock<std::mutex> lock(m_);
- return notified_;
- }
-
- private:
- std::mutex m_;
- std::condition_variable cv_;
- bool notified_ = false;
-};
-
-class TracingSession {
- public:
- class Builder {
- public:
- Builder() = default;
- Builder& add_enabled_category(std::string category) {
- enabled_categories_.push_back(std::move(category));
- return *this;
- }
- Builder& add_disabled_category(std::string category) {
- disabled_categories_.push_back(std::move(category));
- return *this;
- }
- Builder& add_atrace_category(std::string category) {
- atrace_categories_.push_back(std::move(category));
- return *this;
- }
- Builder& add_atrace_category_prefer_sdk(std::string category) {
- atrace_categories_prefer_sdk_.push_back(std::move(category));
- return *this;
- }
- TracingSession Build();
-
- private:
- std::vector<std::string> enabled_categories_;
- std::vector<std::string> disabled_categories_;
- std::vector<std::string> atrace_categories_;
- std::vector<std::string> atrace_categories_prefer_sdk_;
- };
-
- static TracingSession Adopt(struct PerfettoTracingSessionImpl*);
-
- TracingSession(TracingSession&&) noexcept;
-
- ~TracingSession();
-
- struct PerfettoTracingSessionImpl* session() const {
- return session_;
- }
-
- bool FlushBlocking(uint32_t timeout_ms);
- void WaitForStopped();
- void StopBlocking();
- std::vector<uint8_t> ReadBlocking();
-
- private:
- TracingSession() = default;
- struct PerfettoTracingSessionImpl* session_;
- std::unique_ptr<WaitableEvent> stopped_;
-};
-
-template <typename FieldSkipper>
-class FieldViewBase {
- public:
- class Iterator {
- public:
- using iterator_category = std::input_iterator_tag;
- using value_type = const PerfettoPbDecoderField;
- using pointer = value_type;
- using reference = value_type;
- reference operator*() const {
- struct PerfettoPbDecoder decoder;
- decoder.read_ptr = read_ptr_;
- decoder.end_ptr = end_ptr_;
- struct PerfettoPbDecoderField field;
- do {
- field = PerfettoPbDecoderParseField(&decoder);
- } while (field.status == PERFETTO_PB_DECODER_OK &&
- skipper_.ShouldSkip(field));
- return field;
- }
- Iterator& operator++() {
- struct PerfettoPbDecoder decoder;
- decoder.read_ptr = read_ptr_;
- decoder.end_ptr = end_ptr_;
- PerfettoPbDecoderSkipField(&decoder);
- read_ptr_ = decoder.read_ptr;
- AdvanceToFirstInterestingField();
- return *this;
- }
- Iterator operator++(int) {
- Iterator tmp = *this;
- ++(*this);
- return tmp;
- }
-
- friend bool operator==(const Iterator& a, const Iterator& b) {
- return a.read_ptr_ == b.read_ptr_;
- }
- friend bool operator!=(const Iterator& a, const Iterator& b) {
- return a.read_ptr_ != b.read_ptr_;
- }
-
- private:
- Iterator(const uint8_t* read_ptr, const uint8_t* end_ptr,
- const FieldSkipper& skipper)
- : read_ptr_(read_ptr), end_ptr_(end_ptr), skipper_(skipper) {
- AdvanceToFirstInterestingField();
- }
- void AdvanceToFirstInterestingField() {
- struct PerfettoPbDecoder decoder;
- decoder.read_ptr = read_ptr_;
- decoder.end_ptr = end_ptr_;
- struct PerfettoPbDecoderField field;
- const uint8_t* prev_read_ptr;
- do {
- prev_read_ptr = decoder.read_ptr;
- field = PerfettoPbDecoderParseField(&decoder);
- } while (field.status == PERFETTO_PB_DECODER_OK &&
- skipper_.ShouldSkip(field));
- if (field.status == PERFETTO_PB_DECODER_OK) {
- read_ptr_ = prev_read_ptr;
- } else {
- read_ptr_ = decoder.read_ptr;
- }
- }
- friend class FieldViewBase<FieldSkipper>;
- const uint8_t* read_ptr_;
- const uint8_t* end_ptr_;
- const FieldSkipper& skipper_;
- };
- using value_type = const PerfettoPbDecoderField;
- using const_iterator = Iterator;
- template <typename... Args>
- explicit FieldViewBase(const uint8_t* begin, const uint8_t* end, Args... args)
- : begin_(begin), end_(end), s_(args...) {
- }
- template <typename... Args>
- explicit FieldViewBase(const std::vector<uint8_t>& data, Args... args)
- : FieldViewBase(data.data(), data.data() + data.size(), args...) {
- }
- template <typename... Args>
- explicit FieldViewBase(const struct PerfettoPbDecoderField& field,
- Args... args)
- : s_(args...) {
- if (field.wire_type != PERFETTO_PB_WIRE_TYPE_DELIMITED) {
- abort();
- }
- begin_ = field.value.delimited.start;
- end_ = begin_ + field.value.delimited.len;
- }
- Iterator begin() const {
- return Iterator(begin_, end_, s_);
- }
- Iterator end() const {
- return Iterator(end_, end_, s_);
- }
- PerfettoPbDecoderField front() const {
- return *begin();
- }
-
- size_t size() const {
- size_t count = 0;
- for (auto field : *this) {
- (void)field;
- count++;
- }
- return count;
- }
-
- bool ok() const {
- for (auto field : *this) {
- if (field.status != PERFETTO_PB_DECODER_OK) {
- return false;
- }
- }
- return true;
- }
-
- private:
- const uint8_t* begin_;
- const uint8_t* end_;
- FieldSkipper s_;
-};
-
-// Pretty printer for gtest
-template <typename FieldSkipper>
-void PrintTo(const FieldViewBase<FieldSkipper>& field_view, std::ostream* pos) {
- std::ostream& os = *pos;
- os << "{";
- for (PerfettoPbDecoderField f : field_view) {
- PrintTo(f, pos);
- os << ", ";
- }
- os << "}";
-}
-
-class IdFieldSkipper {
- public:
- explicit IdFieldSkipper(uint32_t id) : id_(id) {
- }
- explicit IdFieldSkipper(int32_t id) : id_(static_cast<uint32_t>(id)) {
- }
- bool ShouldSkip(const struct PerfettoPbDecoderField& field) const {
- return field.id != id_;
- }
-
- private:
- uint32_t id_;
-};
-
-class NoFieldSkipper {
- public:
- NoFieldSkipper() = default;
- bool ShouldSkip(const struct PerfettoPbDecoderField&) const {
- return false;
- }
-};
-
-// View over all the fields of a contiguous serialized protobuf message.
-//
-// Examples:
-//
-// for (struct PerfettoPbDecoderField field : FieldView(msg_begin, msg_end)) {
-// //...
-// }
-// FieldView fields2(/*PerfettoPbDecoderField*/ nested_field);
-// FieldView fields3(/*std::vector<uint8_t>*/ data);
-// size_t num = fields1.size(); // The number of fields.
-// bool ok = fields1.ok(); // Checks that the message is not malformed.
-using FieldView = FieldViewBase<NoFieldSkipper>;
-
-// Like `FieldView`, but only considers fields with a specific id.
-//
-// Examples:
-//
-// IdFieldView fields(msg_begin, msg_end, id)
-using IdFieldView = FieldViewBase<IdFieldSkipper>;
-
-// Matches a PerfettoPbDecoderField with the specified id. Accepts another
-// matcher to match the contents of the field.
-//
-// Example:
-// PerfettoPbDecoderField field = ...
-// EXPECT_THAT(field, PbField(900, VarIntField(5)));
-template <typename M>
-auto PbField(int32_t id, M m) {
- return testing::AllOf(
- testing::Field(&PerfettoPbDecoderField::status, PERFETTO_PB_DECODER_OK),
- testing::Field(&PerfettoPbDecoderField::id, id), m);
-}
-
-// Matches a PerfettoPbDecoderField submessage field. Accepts a container
-// matcher for the subfields.
-//
-// Example:
-// PerfettoPbDecoderField field = ...
-// EXPECT_THAT(field, MsgField(ElementsAre(...)));
-template <typename M>
-auto MsgField(M m) {
- auto f = [](const PerfettoPbDecoderField& field) { return FieldView(field); };
- return testing::AllOf(
- testing::Field(&PerfettoPbDecoderField::status, PERFETTO_PB_DECODER_OK),
- testing::Field(&PerfettoPbDecoderField::wire_type,
- PERFETTO_PB_WIRE_TYPE_DELIMITED),
- testing::ResultOf(f, m));
-}
-
-// Matches a PerfettoPbDecoderField length delimited field. Accepts a string
-// matcher.
-//
-// Example:
-// PerfettoPbDecoderField field = ...
-// EXPECT_THAT(field, StringField("string"));
-template <typename M>
-auto StringField(M m) {
- auto f = [](const PerfettoPbDecoderField& field) {
- return std::string(
- reinterpret_cast<const char*>(field.value.delimited.start),
- field.value.delimited.len);
- };
- return testing::AllOf(
- testing::Field(&PerfettoPbDecoderField::status, PERFETTO_PB_DECODER_OK),
- testing::Field(&PerfettoPbDecoderField::wire_type,
- PERFETTO_PB_WIRE_TYPE_DELIMITED),
- testing::ResultOf(f, m));
-}
-
-// Matches a PerfettoPbDecoderField VarInt field. Accepts an integer matcher
-//
-// Example:
-// PerfettoPbDecoderField field = ...
-// EXPECT_THAT(field, VarIntField(1)));
-template <typename M>
-auto VarIntField(M m) {
- auto f = [](const PerfettoPbDecoderField& field) {
- return field.value.integer64;
- };
- return testing::AllOf(
- testing::Field(&PerfettoPbDecoderField::status, PERFETTO_PB_DECODER_OK),
- testing::Field(&PerfettoPbDecoderField::wire_type,
- PERFETTO_PB_WIRE_TYPE_VARINT),
- testing::ResultOf(f, m));
-}
-
-// Matches a PerfettoPbDecoderField fixed64 field. Accepts an integer matcher
-//
-// Example:
-// PerfettoPbDecoderField field = ...
-// EXPECT_THAT(field, Fixed64Field(1)));
-template <typename M>
-auto Fixed64Field(M m) {
- auto f = [](const PerfettoPbDecoderField& field) {
- return field.value.integer64;
- };
- return testing::AllOf(
- testing::Field(&PerfettoPbDecoderField::status, PERFETTO_PB_DECODER_OK),
- testing::Field(&PerfettoPbDecoderField::wire_type,
- PERFETTO_PB_WIRE_TYPE_FIXED64),
- testing::ResultOf(f, m));
-}
-
-// Matches a PerfettoPbDecoderField fixed32 field. Accepts an integer matcher
-//
-// Example:
-// PerfettoPbDecoderField field = ...
-// EXPECT_THAT(field, Fixed32Field(1)));
-template <typename M>
-auto Fixed32Field(M m) {
- auto f = [](const PerfettoPbDecoderField& field) {
- return field.value.integer32;
- };
- return testing::AllOf(
- testing::Field(&PerfettoPbDecoderField::status, PERFETTO_PB_DECODER_OK),
- testing::Field(&PerfettoPbDecoderField::wire_type,
- PERFETTO_PB_WIRE_TYPE_FIXED32),
- testing::ResultOf(f, m));
-}
-
-// Matches a PerfettoPbDecoderField double field. Accepts a double matcher
-//
-// Example:
-// PerfettoPbDecoderField field = ...
-// EXPECT_THAT(field, DoubleField(1.0)));
-template <typename M>
-auto DoubleField(M m) {
- auto f = [](const PerfettoPbDecoderField& field) {
- return field.value.double_val;
- };
- return testing::AllOf(
- testing::Field(&PerfettoPbDecoderField::status, PERFETTO_PB_DECODER_OK),
- testing::Field(&PerfettoPbDecoderField::wire_type,
- PERFETTO_PB_WIRE_TYPE_FIXED64),
- testing::ResultOf(f, m));
-}
-
-// Matches a PerfettoPbDecoderField float field. Accepts a float matcher
-//
-// Example:
-// PerfettoPbDecoderField field = ...
-// EXPECT_THAT(field, FloatField(1.0)));
-template <typename M>
-auto FloatField(M m) {
- auto f = [](const PerfettoPbDecoderField& field) {
- return field.value.float_val;
- };
- return testing::AllOf(
- testing::Field(&PerfettoPbDecoderField::status, PERFETTO_PB_DECODER_OK),
- testing::Field(&PerfettoPbDecoderField::wire_type,
- PERFETTO_PB_WIRE_TYPE_FIXED32),
- testing::ResultOf(f, m));
-}
-
-// Matches a PerfettoPbDecoderField submessage field. Accepts a container
-// matcher for the subfields.
-//
-// Example:
-// PerfettoPbDecoderField field = ...
-// EXPECT_THAT(field, AllFieldsWithId(900, ElementsAre(...)));
-template <typename M>
-auto AllFieldsWithId(int32_t id, M m) {
- auto f = [id](const PerfettoPbDecoderField& field) {
- return IdFieldView(field, id);
- };
- return testing::AllOf(
- testing::Field(&PerfettoPbDecoderField::status, PERFETTO_PB_DECODER_OK),
- testing::Field(&PerfettoPbDecoderField::wire_type,
- PERFETTO_PB_WIRE_TYPE_DELIMITED),
- testing::ResultOf(f, m));
-}
-
-} // namespace test_utils
-} // namespace shlib
-} // namespace perfetto
-
-#endif // UTILS_H
diff --git a/libs/tracing_perfetto/tracing_perfetto.cpp b/libs/tracing_perfetto/tracing_perfetto.cpp
index c35e078..4b70213 100644
--- a/libs/tracing_perfetto/tracing_perfetto.cpp
+++ b/libs/tracing_perfetto/tracing_perfetto.cpp
@@ -17,6 +17,7 @@
#include "tracing_perfetto.h"
#include <cutils/trace.h>
+
#include <cstdarg>
#include "perfetto/public/te_category_macros.h"
@@ -43,8 +44,10 @@
void traceFormatBegin(uint64_t category, const char* fmt, ...) {
struct PerfettoTeCategory* perfettoTeCategory =
internal::toPerfettoCategory(category);
- const bool preferAtrace = internal::shouldPreferAtrace(perfettoTeCategory, category);
- const bool preferPerfetto = internal::isPerfettoCategoryEnabled(perfettoTeCategory);
+ const bool preferAtrace =
+ internal::shouldPreferAtrace(perfettoTeCategory, category);
+ const bool preferPerfetto =
+ internal::isPerfettoCategoryEnabled(perfettoTeCategory);
if (CC_LIKELY(!(preferAtrace || preferPerfetto))) {
return;
}
@@ -57,7 +60,6 @@
vsnprintf(buf, BUFFER_SIZE, fmt, ap);
va_end(ap);
-
if (preferAtrace) {
atrace_begin(category, buf);
} else if (preferPerfetto) {
@@ -99,26 +101,28 @@
}
void traceAsyncBeginForTrack(uint64_t category, const char* name,
- const char* trackName, int32_t cookie) {
+ const char* trackName, int32_t cookie) {
struct PerfettoTeCategory* perfettoTeCategory =
internal::toPerfettoCategory(category);
if (internal::shouldPreferAtrace(perfettoTeCategory, category)) {
atrace_async_for_track_begin(category, trackName, name, cookie);
} else if (internal::isPerfettoCategoryEnabled(perfettoTeCategory)) {
- internal::perfettoTraceAsyncBeginForTrack(*perfettoTeCategory, name, trackName, cookie);
+ internal::perfettoTraceAsyncBeginForTrack(*perfettoTeCategory, name,
+ trackName, cookie);
}
}
void traceAsyncEndForTrack(uint64_t category, const char* trackName,
- int32_t cookie) {
+ int32_t cookie) {
struct PerfettoTeCategory* perfettoTeCategory =
internal::toPerfettoCategory(category);
if (internal::shouldPreferAtrace(perfettoTeCategory, category)) {
atrace_async_for_track_end(category, trackName, cookie);
} else if (internal::isPerfettoCategoryEnabled(perfettoTeCategory)) {
- internal::perfettoTraceAsyncEndForTrack(*perfettoTeCategory, trackName, cookie);
+ internal::perfettoTraceAsyncEndForTrack(*perfettoTeCategory, trackName,
+ cookie);
}
}
@@ -136,8 +140,10 @@
void traceFormatInstant(uint64_t category, const char* fmt, ...) {
struct PerfettoTeCategory* perfettoTeCategory =
internal::toPerfettoCategory(category);
- const bool preferAtrace = internal::shouldPreferAtrace(perfettoTeCategory, category);
- const bool preferPerfetto = internal::isPerfettoCategoryEnabled(perfettoTeCategory);
+ const bool preferAtrace =
+ internal::shouldPreferAtrace(perfettoTeCategory, category);
+ const bool preferPerfetto =
+ internal::isPerfettoCategoryEnabled(perfettoTeCategory);
if (CC_LIKELY(!(preferAtrace || preferPerfetto))) {
return;
}
@@ -158,7 +164,7 @@
}
void traceInstantForTrack(uint64_t category, const char* trackName,
- const char* name) {
+ const char* name) {
struct PerfettoTeCategory* perfettoTeCategory =
internal::toPerfettoCategory(category);
@@ -181,20 +187,21 @@
}
void traceCounter32(uint64_t category, const char* name, int32_t value) {
- struct PerfettoTeCategory* perfettoTeCategory = internal::toPerfettoCategory(category);
+ struct PerfettoTeCategory* perfettoTeCategory =
+ internal::toPerfettoCategory(category);
if (internal::shouldPreferAtrace(perfettoTeCategory, category)) {
atrace_int(category, name, value);
} else if (internal::isPerfettoCategoryEnabled(perfettoTeCategory)) {
internal::perfettoTraceCounter(*perfettoTeCategory, name,
- static_cast<int64_t>(value));
+ static_cast<int64_t>(value));
}
}
bool isTagEnabled(uint64_t category) {
struct PerfettoTeCategory* perfettoTeCategory =
internal::toPerfettoCategory(category);
- return internal::isPerfettoCategoryEnabled(perfettoTeCategory)
- || atrace_is_tag_enabled(category);
+ return internal::isPerfettoCategoryEnabled(perfettoTeCategory) ||
+ atrace_is_tag_enabled(category);
}
} // namespace tracing_perfetto
diff --git a/libs/tracing_perfetto/tracing_perfetto_internal.cpp b/libs/tracing_perfetto/tracing_perfetto_internal.cpp
index a58bc77..f92f6df 100644
--- a/libs/tracing_perfetto/tracing_perfetto_internal.cpp
+++ b/libs/tracing_perfetto/tracing_perfetto_internal.cpp
@@ -47,7 +47,6 @@
#include <atomic>
#include <mutex>
-#include <android_os.h>
#include <android-base/properties.h>
#include <cutils/trace.h>
#include <inttypes.h>
@@ -228,10 +227,6 @@
}
void registerWithPerfetto(bool test) {
- if (!android::os::perfetto_sdk_tracing()) {
- return;
- }
-
static std::once_flag registration;
std::call_once(registration, [test]() {
struct PerfettoProducerInitArgs args = PERFETTO_PRODUCER_INIT_ARGS_INIT();
diff --git a/libs/tracing_perfetto/tracing_sdk.cpp b/libs/tracing_perfetto/tracing_sdk.cpp
new file mode 100644
index 0000000..02e8d10
--- /dev/null
+++ b/libs/tracing_perfetto/tracing_sdk.cpp
@@ -0,0 +1,261 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "tracing_sdk.h"
+
+#include <android-base/logging.h>
+#include <cutils/trace.h>
+
+#include <cstdarg>
+#include <cstdlib>
+
+#include "perfetto/public/abi/producer_abi.h"
+#include "perfetto/public/te_category_macros.h"
+#include "perfetto/public/te_macros.h"
+#include "perfetto/public/track_event.h"
+#include "tracing_perfetto.h"
+
+namespace tracing_perfetto {
+void trace_event(int type, const PerfettoTeCategory* perfettoTeCategory,
+ const char* name, tracing_perfetto::Extra* extra) {
+ bool enabled = PERFETTO_UNLIKELY(PERFETTO_ATOMIC_LOAD_EXPLICIT(
+ perfettoTeCategory->enabled, PERFETTO_MEMORY_ORDER_RELAXED));
+ if (enabled) {
+ extra->push_extra(nullptr);
+ PerfettoTeHlEmitImpl(perfettoTeCategory->impl, type,
+ type == PERFETTO_TE_TYPE_COUNTER ? nullptr : name,
+ extra->get());
+ extra->pop_extra();
+ }
+}
+
+uint64_t get_process_track_uuid() {
+ return PerfettoTeProcessTrackUuid();
+}
+
+uint64_t get_thread_track_uuid(pid_t tid) {
+ // Cating a signed pid_t to unsigned
+ return PerfettoTeProcessTrackUuid() ^ PERFETTO_STATIC_CAST(uint64_t, tid);
+}
+
+Extra::Extra() {
+}
+
+void Extra::push_extra(PerfettoTeHlExtra* ptr) {
+ extras_.push_back(ptr);
+}
+
+void Extra::pop_extra() {
+ extras_.pop_back();
+}
+
+void Extra::clear_extras() {
+ extras_.clear();
+}
+
+void Extra::delete_extra(Extra* ptr) {
+ delete ptr;
+}
+
+PerfettoTeHlExtra* const* Extra::get() const {
+ return extras_.data();
+}
+
+Category::Category(const std::string& name, const std::string& tag,
+ const std::string& severity)
+ : category_({.enabled = &perfetto_atomic_false}),
+ name_(name),
+ tag_(tag),
+ severity_(severity) {
+}
+
+Category::~Category() {
+ unregister_category();
+}
+
+void Category::register_category() {
+ if (category_.impl) return;
+
+ std::vector<const char*> tags;
+ if (!tag_.empty()) tags.push_back(tag_.data());
+ if (!severity_.empty()) tags.push_back(severity_.data());
+
+ category_.desc = {name_.c_str(), name_.c_str(), tags.data(), tags.size()};
+
+ PerfettoTeCategoryRegister(&category_);
+ PerfettoTePublishCategories();
+}
+
+void Category::unregister_category() {
+ if (!category_.impl) return;
+
+ PerfettoTeCategoryUnregister(&category_);
+ PerfettoTePublishCategories();
+}
+
+bool Category::is_category_enabled() {
+ return PERFETTO_UNLIKELY(PERFETTO_ATOMIC_LOAD_EXPLICIT(
+ (category_).enabled, PERFETTO_MEMORY_ORDER_RELAXED));
+}
+
+const PerfettoTeCategory* Category::get() const {
+ return &category_;
+}
+
+void Category::delete_category(Category* ptr) {
+ delete ptr;
+}
+
+Flow::Flow() : flow_{} {
+}
+
+void Flow::set_process_flow(uint64_t id) {
+ flow_.header.type = PERFETTO_TE_HL_EXTRA_TYPE_FLOW;
+ PerfettoTeFlow ret = PerfettoTeProcessScopedFlow(id);
+ flow_.id = ret.id;
+}
+
+void Flow::set_process_terminating_flow(uint64_t id) {
+ flow_.header.type = PERFETTO_TE_HL_EXTRA_TYPE_TERMINATING_FLOW;
+ PerfettoTeFlow ret = PerfettoTeProcessScopedFlow(id);
+ flow_.id = ret.id;
+}
+
+const PerfettoTeHlExtraFlow* Flow::get() const {
+ return &flow_;
+}
+
+void Flow::delete_flow(Flow* ptr) {
+ delete ptr;
+}
+
+NamedTrack::NamedTrack(uint64_t id, uint64_t parent_uuid,
+ const std::string& name)
+ : name_(name),
+ track_{{PERFETTO_TE_HL_EXTRA_TYPE_NAMED_TRACK},
+ name_.data(),
+ id,
+ parent_uuid} {
+}
+
+const PerfettoTeHlExtraNamedTrack* NamedTrack::get() const {
+ return &track_;
+}
+
+void NamedTrack::delete_track(NamedTrack* ptr) {
+ delete ptr;
+}
+
+RegisteredTrack::RegisteredTrack(uint64_t id, uint64_t parent_uuid,
+ const std::string& name, bool is_counter)
+ : registered_track_{},
+ track_{{PERFETTO_TE_HL_EXTRA_TYPE_REGISTERED_TRACK},
+ &(registered_track_.impl)},
+ name_(name),
+ id_(id),
+ parent_uuid_(parent_uuid),
+ is_counter_(is_counter) {
+ register_track();
+}
+
+RegisteredTrack::~RegisteredTrack() {
+ unregister_track();
+}
+
+void RegisteredTrack::register_track() {
+ if (registered_track_.impl.descriptor) return;
+
+ if (is_counter_) {
+ PerfettoTeCounterTrackRegister(®istered_track_, name_.data(),
+ parent_uuid_);
+ } else {
+ PerfettoTeNamedTrackRegister(®istered_track_, name_.data(), id_,
+ parent_uuid_);
+ }
+}
+
+void RegisteredTrack::unregister_track() {
+ if (!registered_track_.impl.descriptor) return;
+ PerfettoTeRegisteredTrackUnregister(®istered_track_);
+}
+
+const PerfettoTeHlExtraRegisteredTrack* RegisteredTrack::get() const {
+ return &track_;
+}
+
+void RegisteredTrack::delete_track(RegisteredTrack* ptr) {
+ delete ptr;
+}
+
+Proto::Proto() : proto_({PERFETTO_TE_HL_EXTRA_TYPE_PROTO_FIELDS}, nullptr) {
+}
+
+void Proto::add_field(PerfettoTeHlProtoField* ptr) {
+ if (!fields_.empty()) {
+ fields_.pop_back();
+ }
+
+ fields_.push_back(ptr);
+ fields_.push_back(nullptr);
+ proto_.fields = fields_.data();
+}
+
+void Proto::clear_fields() {
+ fields_.clear();
+ proto_.fields = nullptr;
+}
+
+void Proto::delete_proto(Proto* ptr) {
+ delete ptr;
+}
+
+const PerfettoTeHlExtraProtoFields* Proto::get() const {
+ return &proto_;
+}
+
+ProtoFieldNested::ProtoFieldNested()
+ : field_({PERFETTO_TE_HL_PROTO_TYPE_NESTED}, nullptr) {
+}
+
+void ProtoFieldNested::add_field(PerfettoTeHlProtoField* ptr) {
+ if (!fields_.empty()) {
+ fields_.pop_back();
+ }
+
+ fields_.push_back(ptr);
+ fields_.push_back(nullptr);
+ field_.fields = fields_.data();
+}
+
+void ProtoFieldNested::set_id(uint32_t id) {
+ fields_.clear();
+ field_.header.id = id;
+ field_.fields = nullptr;
+}
+
+void ProtoFieldNested::delete_field(ProtoFieldNested* ptr) {
+ delete ptr;
+}
+
+const PerfettoTeHlProtoFieldNested* ProtoFieldNested::get() const {
+ return &field_;
+}
+
+void activate_trigger(const char* name, uint32_t ttl_ms) {
+ const char* names[] = {name, nullptr};
+ PerfettoProducerActivateTriggers(names, ttl_ms);
+}
+} // namespace tracing_perfetto
diff --git a/services/gpuservice/vts/src/com/android/tests/gpuservice/GpuWorkTracepointTest.java b/services/gpuservice/vts/src/com/android/tests/gpuservice/GpuWorkTracepointTest.java
index 5c12323..fc0aef4 100644
--- a/services/gpuservice/vts/src/com/android/tests/gpuservice/GpuWorkTracepointTest.java
+++ b/services/gpuservice/vts/src/com/android/tests/gpuservice/GpuWorkTracepointTest.java
@@ -39,29 +39,9 @@
@RunWith(DeviceJUnit4ClassRunner.class)
public class GpuWorkTracepointTest extends BaseHostJUnit4Test {
- private static final String CPU_FREQUENCY_TRACEPOINT_FORMAT_PATH =
- "/sys/kernel/tracing/events/power/cpu_frequency/format";
private static final String GPU_WORK_PERIOD_TRACEPOINT_FORMAT_PATH =
"/sys/kernel/tracing/events/power/gpu_work_period/format";
- @Test
- public void testReadTracingEvents() throws Exception {
- // Test |testGpuWorkPeriodTracepointFormat| is dependent on whether certain tracepoint
- // paths exist. This means the test will vacuously pass if the tracepoint file system is
- // inaccessible. Thus, as a basic check, we make sure the CPU frequency tracepoint format
- // is accessible. If not, something is probably fundamentally broken about the tracing
- // file system.
- CommandResult commandResult = getDevice().executeShellV2Command(
- String.format("cat %s", CPU_FREQUENCY_TRACEPOINT_FORMAT_PATH));
-
- assertEquals(String.format(
- "Failed to read \"%s\". This probably means that the tracing file system "
- + "is fundamentally broken in some way, possibly due to bad "
- + "permissions.",
- CPU_FREQUENCY_TRACEPOINT_FORMAT_PATH),
- commandResult.getStatus(), CommandStatus.SUCCESS);
- }
-
@VsrTest(requirements={"VSR-3.3-004"})
@RequiresDevice
@Test
diff --git a/services/inputflinger/PointerChoreographer.cpp b/services/inputflinger/PointerChoreographer.cpp
index 811692f..5bf6ebb 100644
--- a/services/inputflinger/PointerChoreographer.cpp
+++ b/services/inputflinger/PointerChoreographer.cpp
@@ -98,6 +98,22 @@
return privacySensitiveDisplays;
}
+vec2 calculatePositionOnDestinationViewport(const DisplayViewport& destinationViewport,
+ float pointerOffset,
+ DisplayTopologyPosition sourceBoundary) {
+ // destination is opposite of the source boundary
+ switch (sourceBoundary) {
+ case DisplayTopologyPosition::RIGHT:
+ return {0, pointerOffset}; // left edge
+ case DisplayTopologyPosition::TOP:
+ return {pointerOffset, destinationViewport.logicalBottom}; // bottom edge
+ case DisplayTopologyPosition::LEFT:
+ return {destinationViewport.logicalRight, pointerOffset}; // right edge
+ case DisplayTopologyPosition::BOTTOM:
+ return {pointerOffset, 0}; // top edge
+ }
+}
+
} // namespace
// --- PointerChoreographer ---
@@ -327,19 +343,19 @@
// except sometimes near the corners.
// In these cases this behaviour is not noticeable. We also do not apply unconsumed delta on
// the destination display for the same reason.
- DisplayPosition sourceBoundary;
+ DisplayTopologyPosition sourceBoundary;
float cursorOffset = 0.0f;
if (rotatedUnconsumedDelta.x > 0) {
- sourceBoundary = DisplayPosition::RIGHT;
+ sourceBoundary = DisplayTopologyPosition::RIGHT;
cursorOffset = rotatedCursorPosition.y;
} else if (rotatedUnconsumedDelta.x < 0) {
- sourceBoundary = DisplayPosition::LEFT;
+ sourceBoundary = DisplayTopologyPosition::LEFT;
cursorOffset = rotatedCursorPosition.y;
} else if (rotatedUnconsumedDelta.y > 0) {
- sourceBoundary = DisplayPosition::BOTTOM;
+ sourceBoundary = DisplayTopologyPosition::BOTTOM;
cursorOffset = rotatedCursorPosition.x;
} else {
- sourceBoundary = DisplayPosition::TOP;
+ sourceBoundary = DisplayTopologyPosition::TOP;
cursorOffset = rotatedCursorPosition.x;
}
@@ -369,8 +385,9 @@
pc.fade(PointerControllerInterface::Transition::IMMEDIATE);
pc.setDisplayViewport(destinationViewport);
vec2 destinationPosition =
- calculateDestinationPosition(destinationViewport, cursorOffset - destinationOffset,
- sourceBoundary);
+ calculatePositionOnDestinationViewport(destinationViewport,
+ cursorOffset - destinationOffset,
+ sourceBoundary);
// Transform position back to un-rotated coordinate space before sending it to controller
destinationPosition = pc.getDisplayTransform().inverse().transform(destinationPosition.x,
@@ -379,22 +396,6 @@
pc.unfade(PointerControllerInterface::Transition::IMMEDIATE);
}
-vec2 PointerChoreographer::calculateDestinationPosition(const DisplayViewport& destinationViewport,
- float pointerOffset,
- DisplayPosition sourceBoundary) {
- // destination is opposite of the source boundary
- switch (sourceBoundary) {
- case DisplayPosition::RIGHT:
- return {0, pointerOffset}; // left edge
- case DisplayPosition::TOP:
- return {pointerOffset, destinationViewport.logicalBottom}; // bottom edge
- case DisplayPosition::LEFT:
- return {destinationViewport.logicalRight, pointerOffset}; // right edge
- case DisplayPosition::BOTTOM:
- return {pointerOffset, 0}; // top edge
- }
-}
-
void PointerChoreographer::processDrawingTabletEventLocked(const android::NotifyMotionArgs& args) {
if (args.displayId == ui::LogicalDisplayId::INVALID) {
return;
@@ -540,8 +541,7 @@
}
void PointerChoreographer::onControllerAddedOrRemovedLocked() {
- if (!com::android::input::flags::hide_pointer_indicators_for_secure_windows() &&
- !com::android::input::flags::connected_displays_cursor()) {
+ if (!com::android::input::flags::hide_pointer_indicators_for_secure_windows()) {
return;
}
bool requireListener = !mTouchPointersByDevice.empty() || !mMousePointersByDisplay.empty() ||
@@ -607,11 +607,16 @@
mNextListener.notify(args);
}
-void PointerChoreographer::setDisplayTopology(
- const std::unordered_map<ui::LogicalDisplayId, std::vector<AdjacentDisplay>>&
- displayTopology) {
+void PointerChoreographer::setDisplayTopology(const DisplayTopologyGraph& displayTopologyGraph) {
std::scoped_lock _l(getLock());
- mTopology = displayTopology;
+ mTopology = displayTopologyGraph;
+
+ // make primary display default mouse display, if it was not set
+ // or the existing display was removed
+ if (mDefaultMouseDisplayId == ui::LogicalDisplayId::INVALID ||
+ mTopology.graph.find(mDefaultMouseDisplayId) != mTopology.graph.end()) {
+ mDefaultMouseDisplayId = mTopology.primaryDisplayId;
+ }
}
void PointerChoreographer::dump(std::string& dump) {
@@ -985,73 +990,17 @@
return ConstructorDelegate(std::move(ctor));
}
-void PointerChoreographer::populateFakeDisplayTopologyLocked(
- const std::vector<gui::DisplayInfo>& displayInfos) {
- if (!com::android::input::flags::connected_displays_cursor()) {
- return;
- }
-
- if (displayInfos.size() == mTopology.size()) {
- bool displaysChanged = false;
- for (const auto& displayInfo : displayInfos) {
- if (mTopology.find(displayInfo.displayId) == mTopology.end()) {
- displaysChanged = true;
- break;
- }
- }
-
- if (!displaysChanged) {
- return;
- }
- }
-
- // create a fake topology assuming following order
- // default-display (top-edge) -> next-display (right-edge) -> next-display (right-edge) ...
- // This also adds a 100px offset on corresponding edge for better manual testing
- // ┌────────┐
- // │ next ├─────────┐
- // ┌─└───────┐┤ next 2 │ ...
- // │ default │└─────────┘
- // └─────────┘
- mTopology.clear();
-
- // treat default display as base, in real topology it should be the primary-display
- ui::LogicalDisplayId previousDisplay = ui::LogicalDisplayId::DEFAULT;
- for (const auto& displayInfo : displayInfos) {
- if (displayInfo.displayId == ui::LogicalDisplayId::DEFAULT) {
- continue;
- }
- if (previousDisplay == ui::LogicalDisplayId::DEFAULT) {
- mTopology[previousDisplay].push_back(
- {displayInfo.displayId, DisplayPosition::TOP, 100});
- mTopology[displayInfo.displayId].push_back(
- {previousDisplay, DisplayPosition::BOTTOM, -100});
- } else {
- mTopology[previousDisplay].push_back(
- {displayInfo.displayId, DisplayPosition::RIGHT, 100});
- mTopology[displayInfo.displayId].push_back(
- {previousDisplay, DisplayPosition::LEFT, -100});
- }
- previousDisplay = displayInfo.displayId;
- }
-
- // update default pointer display. In real topology it should be the primary-display
- if (mTopology.find(mDefaultMouseDisplayId) == mTopology.end()) {
- mDefaultMouseDisplayId = ui::LogicalDisplayId::DEFAULT;
- }
-}
-
std::optional<std::pair<const DisplayViewport*, float /*offset*/>>
PointerChoreographer::findDestinationDisplayLocked(const ui::LogicalDisplayId sourceDisplayId,
- const DisplayPosition sourceBoundary,
+ const DisplayTopologyPosition sourceBoundary,
float cursorOffset) const {
- const auto& sourceNode = mTopology.find(sourceDisplayId);
- if (sourceNode == mTopology.end()) {
+ const auto& sourceNode = mTopology.graph.find(sourceDisplayId);
+ if (sourceNode == mTopology.graph.end()) {
// Topology is likely out of sync with viewport info, wait for it to be updated
LOG(WARNING) << "Source display missing from topology " << sourceDisplayId;
return std::nullopt;
}
- for (const AdjacentDisplay& adjacentDisplay : sourceNode->second) {
+ for (const DisplayTopologyAdjacentDisplay& adjacentDisplay : sourceNode->second) {
if (adjacentDisplay.position != sourceBoundary) {
continue;
}
@@ -1064,8 +1013,8 @@
continue;
}
// target position must be within target display boundary
- const int32_t edgeSize =
- sourceBoundary == DisplayPosition::TOP || sourceBoundary == DisplayPosition::BOTTOM
+ const int32_t edgeSize = sourceBoundary == DisplayTopologyPosition::TOP ||
+ sourceBoundary == DisplayTopologyPosition::BOTTOM
? (destinationViewport->logicalRight - destinationViewport->logicalLeft)
: (destinationViewport->logicalBottom - destinationViewport->logicalTop);
if (cursorOffset >= adjacentDisplay.offsetPx &&
@@ -1093,7 +1042,6 @@
mPrivacySensitiveDisplays = std::move(newPrivacySensitiveDisplays);
mPointerChoreographer->onPrivacySensitiveDisplaysChangedLocked(mPrivacySensitiveDisplays);
}
- mPointerChoreographer->populateFakeDisplayTopologyLocked(windowInfosUpdate.displayInfos);
}
void PointerChoreographer::PointerChoreographerDisplayInfoListener::setInitialDisplayInfosLocked(
diff --git a/services/inputflinger/PointerChoreographer.h b/services/inputflinger/PointerChoreographer.h
index 939529f..c2f5ec0 100644
--- a/services/inputflinger/PointerChoreographer.h
+++ b/services/inputflinger/PointerChoreographer.h
@@ -22,6 +22,7 @@
#include <android-base/thread_annotations.h>
#include <gui/WindowInfosListener.h>
+#include <input/DisplayTopologyGraph.h>
#include <type_traits>
#include <unordered_set>
@@ -80,6 +81,11 @@
*/
virtual void setFocusedDisplay(ui::LogicalDisplayId displayId) = 0;
+ /*
+ * Used by InputManager to notify changes in the DisplayTopology
+ */
+ virtual void setDisplayTopology(const DisplayTopologyGraph& displayTopologyGraph) = 0;
+
/**
* This method may be called on any thread (usually by the input manager on a binder thread).
*/
@@ -103,6 +109,7 @@
ui::LogicalDisplayId displayId, DeviceId deviceId) override;
void setPointerIconVisibility(ui::LogicalDisplayId displayId, bool visible) override;
void setFocusedDisplay(ui::LogicalDisplayId displayId) override;
+ void setDisplayTopology(const DisplayTopologyGraph& displayTopologyGraph);
void notifyInputDevicesChanged(const NotifyInputDevicesChangedArgs& args) override;
void notifyKey(const NotifyKeyArgs& args) override;
@@ -113,24 +120,6 @@
void notifyDeviceReset(const NotifyDeviceResetArgs& args) override;
void notifyPointerCaptureChanged(const NotifyPointerCaptureChangedArgs& args) override;
- // TODO(b/362719483) remove these when real topology is available
- enum class DisplayPosition {
- RIGHT,
- TOP,
- LEFT,
- BOTTOM,
- ftl_last = BOTTOM,
- };
-
- struct AdjacentDisplay {
- ui::LogicalDisplayId displayId;
- DisplayPosition position;
- float offsetPx;
- };
- void setDisplayTopology(
- const std::unordered_map<ui::LogicalDisplayId, std::vector<AdjacentDisplay>>&
- displayTopology);
-
void dump(std::string& dump) override;
private:
@@ -174,18 +163,16 @@
void handleUnconsumedDeltaLocked(PointerControllerInterface& pc, const vec2& unconsumedDelta)
REQUIRES(getLock());
- void populateFakeDisplayTopologyLocked(const std::vector<gui::DisplayInfo>& displayInfos)
+ std::optional<std::pair<const DisplayViewport*, float /*offset*/>> findDestinationDisplayLocked(
+ const ui::LogicalDisplayId sourceDisplayId,
+ const DisplayTopologyPosition sourceBoundary, float cursorOffset) const
REQUIRES(getLock());
- std::optional<std::pair<const DisplayViewport*, float /*offset*/>> findDestinationDisplayLocked(
- const ui::LogicalDisplayId sourceDisplayId, const DisplayPosition sourceBoundary,
- float cursorOffset) const REQUIRES(getLock());
-
- static vec2 calculateDestinationPosition(const DisplayViewport& destinationViewport,
- float pointerOffset, DisplayPosition sourceBoundary);
-
- std::unordered_map<ui::LogicalDisplayId, std::vector<AdjacentDisplay>> mTopology
- GUARDED_BY(getLock());
+ /* Topology is initialized with default-constructed value, which is an empty topology. Till we
+ * receive setDisplayTopology call.
+ * Meanwhile Choreographer will treat every display as independent disconnected display.
+ */
+ DisplayTopologyGraph mTopology GUARDED_BY(getLock());
/* This listener keeps tracks of visible privacy sensitive displays and updates the
* choreographer if there are any changes.
diff --git a/services/inputflinger/include/InputReaderBase.h b/services/inputflinger/include/InputReaderBase.h
index 4d6b6c7..42b269e 100644
--- a/services/inputflinger/include/InputReaderBase.h
+++ b/services/inputflinger/include/InputReaderBase.h
@@ -279,7 +279,10 @@
static_cast<float>(
android::os::IInputConstants::
DEFAULT_POINTER_ACCELERATION)),
- wheelVelocityControlParameters(1.0f, 15.0f, 50.0f, 4.0f),
+ wheelVelocityControlParameters(1.0f, 15.0f, 50.0f,
+ static_cast<float>(
+ android::os::IInputConstants::
+ DEFAULT_MOUSE_WHEEL_ACCELERATION)),
pointerGesturesEnabled(true),
pointerGestureQuietInterval(100 * 1000000LL), // 100 ms
pointerGestureDragMinSwitchSpeed(50), // 50 pixels per second
diff --git a/services/inputflinger/tests/PointerChoreographer_test.cpp b/services/inputflinger/tests/PointerChoreographer_test.cpp
index 27da3d3..1ca2998 100644
--- a/services/inputflinger/tests/PointerChoreographer_test.cpp
+++ b/services/inputflinger/tests/PointerChoreographer_test.cpp
@@ -2633,16 +2633,14 @@
ui::ROTATION_0),
};
- std::unordered_map<ui::LogicalDisplayId, std::vector<PointerChoreographer::AdjacentDisplay>>
- mTopology{
- {DISPLAY_CENTER_ID,
- {{DISPLAY_TOP_ID, PointerChoreographer::DisplayPosition::TOP, 10.0f},
- {DISPLAY_RIGHT_ID, PointerChoreographer::DisplayPosition::RIGHT, 10.0f},
- {DISPLAY_BOTTOM_ID, PointerChoreographer::DisplayPosition::BOTTOM, 10.0f},
- {DISPLAY_LEFT_ID, PointerChoreographer::DisplayPosition::LEFT, 10.0f},
- {DISPLAY_TOP_RIGHT_CORNER_ID, PointerChoreographer::DisplayPosition::RIGHT,
- -90.0f}}},
- };
+ DisplayTopologyGraph mTopology{DISPLAY_CENTER_ID,
+ {{DISPLAY_CENTER_ID,
+ {{DISPLAY_TOP_ID, DisplayTopologyPosition::TOP, 10.0f},
+ {DISPLAY_RIGHT_ID, DisplayTopologyPosition::RIGHT, 10.0f},
+ {DISPLAY_BOTTOM_ID, DisplayTopologyPosition::BOTTOM, 10.0f},
+ {DISPLAY_LEFT_ID, DisplayTopologyPosition::LEFT, 10.0f},
+ {DISPLAY_TOP_RIGHT_CORNER_ID, DisplayTopologyPosition::RIGHT,
+ -90.0f}}}}};
private:
DisplayViewport createViewport(ui::LogicalDisplayId displayId, int32_t width, int32_t height,
@@ -2706,6 +2704,11 @@
testing::Values(
// Note: Upon viewport transition cursor will be positioned at the boundary of the
// destination, as we drop any unconsumed delta.
+ std::make_tuple("PrimaryDisplayIsDefault", AINPUT_SOURCE_MOUSE,
+ ControllerType::MOUSE, ToolType::MOUSE,
+ vec2(50, 50) /* initial x/y */, vec2(0, 0) /* delta x/y */,
+ PointerChoreographerDisplayTopologyTestFixture::DISPLAY_CENTER_ID,
+ vec2(50, 50) /* destination x/y */),
std::make_tuple("UnchangedDisplay", AINPUT_SOURCE_MOUSE, ControllerType::MOUSE,
ToolType::MOUSE, vec2(50, 50) /* initial x/y */,
vec2(25, 25) /* delta x/y */,
diff --git a/services/surfaceflinger/ActivePictureTracker.cpp b/services/surfaceflinger/ActivePictureTracker.cpp
new file mode 100644
index 0000000..4e6fa66
--- /dev/null
+++ b/services/surfaceflinger/ActivePictureTracker.cpp
@@ -0,0 +1,106 @@
+/*
+ * Copyright 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "ActivePictureTracker.h"
+
+#include <algorithm>
+
+#include "Layer.h"
+#include "LayerFE.h"
+
+namespace android {
+
+using gui::ActivePicture;
+using gui::IActivePictureListener;
+
+void ActivePictureTracker::onLayerComposed(const Layer& layer, const LayerFE& layerFE,
+ const CompositionResult& result) {
+ if (result.wasPictureProfileCommitted) {
+ gui::ActivePicture picture;
+ picture.layerId = int32_t(layer.sequence);
+ picture.ownerUid = int32_t(layer.getOwnerUid());
+ // TODO(b/337330263): Why does LayerFE coming from SF have a null composition state?
+ if (layerFE.getCompositionState()) {
+ picture.pictureProfileId = layerFE.getCompositionState()->pictureProfileHandle.getId();
+ } else {
+ picture.pictureProfileId = result.pictureProfileHandle.getId();
+ }
+ mNewActivePictures.push_back(picture);
+ }
+}
+
+void ActivePictureTracker::updateAndNotifyListeners(const Listeners& listenersToAdd,
+ const Listeners& listenersToRemove) {
+ Listeners newListeners = updateListeners(listenersToAdd, listenersToRemove);
+ if (updateAndHasChanged()) {
+ for (auto listener : mListeners) {
+ listener->onActivePicturesChanged(getActivePictures());
+ }
+ } else {
+ for (auto listener : newListeners) {
+ listener->onActivePicturesChanged(getActivePictures());
+ }
+ }
+}
+
+ActivePictureTracker::Listeners ActivePictureTracker::updateListeners(
+ const Listeners& listenersToAdd, const Listeners& listenersToRemove) {
+ Listeners newListeners;
+ for (auto listener : listenersToRemove) {
+ std::erase_if(mListeners, [listener](const sp<IActivePictureListener>& otherListener) {
+ return IInterface::asBinder(listener) == IInterface::asBinder(otherListener);
+ });
+ }
+ for (auto listener : listenersToAdd) {
+ if (std::find_if(mListeners.begin(), mListeners.end(),
+ [listener](const sp<IActivePictureListener>& otherListener) {
+ return IInterface::asBinder(listener) ==
+ IInterface::asBinder(otherListener);
+ }) == mListeners.end()) {
+ newListeners.push_back(listener);
+ }
+ }
+ for (auto listener : newListeners) {
+ mListeners.push_back(listener);
+ }
+ return newListeners;
+}
+
+bool ActivePictureTracker::updateAndHasChanged() {
+ bool hasChanged = true;
+ if (mNewActivePictures.size() == mOldActivePictures.size()) {
+ auto compare = [](const ActivePicture& lhs, const ActivePicture& rhs) -> int {
+ if (lhs.layerId == rhs.layerId) {
+ return lhs.pictureProfileId < rhs.pictureProfileId;
+ }
+ return lhs.layerId < rhs.layerId;
+ };
+ std::sort(mNewActivePictures.begin(), mNewActivePictures.end(), compare);
+ if (std::equal(mNewActivePictures.begin(), mNewActivePictures.end(),
+ mOldActivePictures.begin())) {
+ hasChanged = false;
+ }
+ }
+ std::swap(mOldActivePictures, mNewActivePictures);
+ mNewActivePictures.resize(0);
+ return hasChanged;
+}
+
+const std::vector<ActivePicture>& ActivePictureTracker::getActivePictures() const {
+ return mOldActivePictures;
+}
+
+} // namespace android
diff --git a/services/surfaceflinger/ActivePictureUpdater.h b/services/surfaceflinger/ActivePictureTracker.h
similarity index 76%
rename from services/surfaceflinger/ActivePictureUpdater.h
rename to services/surfaceflinger/ActivePictureTracker.h
index 20779bb..cb319a5 100644
--- a/services/surfaceflinger/ActivePictureUpdater.h
+++ b/services/surfaceflinger/ActivePictureTracker.h
@@ -19,6 +19,7 @@
#include <vector>
#include <android/gui/ActivePicture.h>
+#include <android/gui/IActivePictureListener.h>
namespace android {
@@ -27,21 +28,28 @@
struct CompositionResult;
// Keeps track of active pictures - layers that are undergoing picture processing.
-class ActivePictureUpdater {
+class ActivePictureTracker {
public:
+ typedef std::vector<sp<gui::IActivePictureListener>> Listeners;
+
// Called for each visible layer when SurfaceFlinger finishes composing.
void onLayerComposed(const Layer& layer, const LayerFE& layerFE,
const CompositionResult& result);
// Update internals and return whether the set of active pictures have changed.
- bool updateAndHasChanged();
+ void updateAndNotifyListeners(const Listeners& activePictureListenersToAdd,
+ const Listeners& activePictureListenersToRemove);
// The current set of active pictures.
const std::vector<gui::ActivePicture>& getActivePictures() const;
private:
+ Listeners updateListeners(const Listeners& listenersToAdd, const Listeners& listenersToRemove);
+ bool updateAndHasChanged();
+
std::vector<gui::ActivePicture> mOldActivePictures;
std::vector<gui::ActivePicture> mNewActivePictures;
+ Listeners mListeners;
};
} // namespace android
diff --git a/services/surfaceflinger/ActivePictureUpdater.cpp b/services/surfaceflinger/ActivePictureUpdater.cpp
deleted file mode 100644
index 210e948..0000000
--- a/services/surfaceflinger/ActivePictureUpdater.cpp
+++ /dev/null
@@ -1,66 +0,0 @@
-/*
- * Copyright 2024 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#include "ActivePictureUpdater.h"
-
-#include <algorithm>
-
-#include "Layer.h"
-#include "LayerFE.h"
-
-namespace android {
-
-void ActivePictureUpdater::onLayerComposed(const Layer& layer, const LayerFE& layerFE,
- const CompositionResult& result) {
- if (result.wasPictureProfileCommitted) {
- gui::ActivePicture picture;
- picture.layerId = int32_t(layer.sequence);
- picture.ownerUid = int32_t(layer.getOwnerUid());
- // TODO(b/337330263): Why does LayerFE coming from SF have a null composition state?
- if (layerFE.getCompositionState()) {
- picture.pictureProfileId = layerFE.getCompositionState()->pictureProfileHandle.getId();
- } else {
- picture.pictureProfileId = result.pictureProfileHandle.getId();
- }
- mNewActivePictures.push_back(picture);
- }
-}
-
-bool ActivePictureUpdater::updateAndHasChanged() {
- bool hasChanged = true;
- if (mNewActivePictures.size() == mOldActivePictures.size()) {
- auto compare = [](const gui::ActivePicture& lhs, const gui::ActivePicture& rhs) -> int {
- if (lhs.layerId == rhs.layerId) {
- return lhs.pictureProfileId < rhs.pictureProfileId;
- }
- return lhs.layerId < rhs.layerId;
- };
- std::sort(mNewActivePictures.begin(), mNewActivePictures.end(), compare);
- if (std::equal(mNewActivePictures.begin(), mNewActivePictures.end(),
- mOldActivePictures.begin())) {
- hasChanged = false;
- }
- }
- std::swap(mOldActivePictures, mNewActivePictures);
- mNewActivePictures.resize(0);
- return hasChanged;
-}
-
-const std::vector<gui::ActivePicture>& ActivePictureUpdater::getActivePictures() const {
- return mOldActivePictures;
-}
-
-} // namespace android
diff --git a/services/surfaceflinger/Android.bp b/services/surfaceflinger/Android.bp
index 7b90363..05f7d1b 100644
--- a/services/surfaceflinger/Android.bp
+++ b/services/surfaceflinger/Android.bp
@@ -67,6 +67,7 @@
"android.hardware.graphics.composer@2.2",
"android.hardware.graphics.composer@2.3",
"android.hardware.graphics.composer@2.4",
+ "android.os.flags-aconfig-cc-host",
"libbase",
"libbinder",
"libbinder_ndk",
@@ -198,7 +199,7 @@
name: "libsurfaceflinger_sources",
srcs: [
":libsurfaceflinger_backend_sources",
- "ActivePictureUpdater.cpp",
+ "ActivePictureTracker.cpp",
"BackgroundExecutor.cpp",
"Client.cpp",
"ClientCache.cpp",
diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/OutputLayer.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/OutputLayer.h
index 2e7a7d9..c1b864d 100644
--- a/services/surfaceflinger/CompositionEngine/include/compositionengine/OutputLayer.h
+++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/OutputLayer.h
@@ -118,7 +118,8 @@
// isPeekingThrough specifies whether this layer will be shown through a
// hole punch in a layer above it.
virtual void writeStateToHWC(bool includeGeometry, bool skipLayer, uint32_t z,
- bool zIsOverridden, bool isPeekingThrough) = 0;
+ bool zIsOverridden, bool isPeekingThrough,
+ bool isLutSupported) = 0;
// Updates the cursor position with the HWC
virtual void writeCursorPositionToHWC() const = 0;
diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/OutputLayer.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/OutputLayer.h
index 712b551..0063eee 100644
--- a/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/OutputLayer.h
+++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/OutputLayer.h
@@ -58,7 +58,7 @@
const std::optional<std::vector<std::optional<LutProperties>>>
properties = std::nullopt) override;
void writeStateToHWC(bool includeGeometry, bool skipLayer, uint32_t z, bool zIsOverridden,
- bool isPeekingThrough) override;
+ bool isPeekingThrough, bool hasLutsProperties) override;
void writeCursorPositionToHWC() const override;
HWC2::Layer* getHwcLayer() const override;
diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/mock/OutputLayer.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/mock/OutputLayer.h
index 9333ebb..09c47f0 100644
--- a/services/surfaceflinger/CompositionEngine/include/compositionengine/mock/OutputLayer.h
+++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/mock/OutputLayer.h
@@ -47,7 +47,7 @@
(bool, bool, ui::Transform::RotationFlags,
(const std::optional<std::vector<std::optional<
aidl::android::hardware::graphics::composer3::LutProperties>>>)));
- MOCK_METHOD5(writeStateToHWC, void(bool, bool, uint32_t, bool, bool));
+ MOCK_METHOD(void, writeStateToHWC, (bool, bool, uint32_t, bool, bool, bool));
MOCK_CONST_METHOD0(writeCursorPositionToHWC, void());
MOCK_CONST_METHOD0(getHwcLayer, HWC2::Layer*());
diff --git a/services/surfaceflinger/CompositionEngine/src/Output.cpp b/services/surfaceflinger/CompositionEngine/src/Output.cpp
index f9ed92d..734d764 100644
--- a/services/surfaceflinger/CompositionEngine/src/Output.cpp
+++ b/services/surfaceflinger/CompositionEngine/src/Output.cpp
@@ -810,7 +810,7 @@
}
auto compare = [](const ::android::compositionengine::OutputLayer* lhs,
const ::android::compositionengine::OutputLayer* rhs) {
- return lhs->getPictureProfilePriority() > rhs->getPictureProfilePriority();
+ return lhs->getPictureProfilePriority() < rhs->getPictureProfilePriority();
};
std::priority_queue<::android::compositionengine::OutputLayer*,
std::vector<::android::compositionengine::OutputLayer*>, decltype(compare)>
@@ -909,6 +909,9 @@
applyPictureProfile();
+ auto* properties = getOverlaySupport();
+ bool hasLutsProperties = properties && properties->lutProperties.has_value();
+
compositionengine::OutputLayer* peekThroughLayer = nullptr;
sp<GraphicBuffer> previousOverride = nullptr;
bool includeGeometry = refreshArgs.updatingGeometryThisFrame;
@@ -940,7 +943,7 @@
includeGeometry = true;
constexpr bool isPeekingThrough = true;
peekThroughLayer->writeStateToHWC(includeGeometry, false, z++, overrideZ,
- isPeekingThrough);
+ isPeekingThrough, hasLutsProperties);
outputLayerHash ^= android::hashCombine(
reinterpret_cast<uint64_t>(&peekThroughLayer->getLayerFE()),
z, includeGeometry, overrideZ, isPeekingThrough,
@@ -952,7 +955,8 @@
}
constexpr bool isPeekingThrough = false;
- layer->writeStateToHWC(includeGeometry, skipLayer, z++, overrideZ, isPeekingThrough);
+ layer->writeStateToHWC(includeGeometry, skipLayer, z++, overrideZ, isPeekingThrough,
+ hasLutsProperties);
if (!skipLayer) {
outputLayerHash ^= android::hashCombine(
reinterpret_cast<uint64_t>(&layer->getLayerFE()),
diff --git a/services/surfaceflinger/CompositionEngine/src/OutputLayer.cpp b/services/surfaceflinger/CompositionEngine/src/OutputLayer.cpp
index a040c88..96b86d5 100644
--- a/services/surfaceflinger/CompositionEngine/src/OutputLayer.cpp
+++ b/services/surfaceflinger/CompositionEngine/src/OutputLayer.cpp
@@ -449,7 +449,8 @@
}
void OutputLayer::writeStateToHWC(bool includeGeometry, bool skipLayer, uint32_t z,
- bool zIsOverridden, bool isPeekingThrough) {
+ bool zIsOverridden, bool isPeekingThrough,
+ bool hasLutsProperties) {
const auto& state = getState();
// Skip doing this if there is no HWC interface
if (!state.hwc) {
@@ -491,8 +492,9 @@
writeCompositionTypeToHWC(hwcLayer.get(), requestedCompositionType, isPeekingThrough,
skipLayer);
-
- writeLutToHWC(hwcLayer.get(), *outputIndependentState);
+ if (hasLutsProperties) {
+ writeLutToHWC(hwcLayer.get(), *outputIndependentState);
+ }
if (requestedCompositionType == Composition::SOLID_COLOR) {
writeSolidColorStateToHWC(hwcLayer.get(), *outputIndependentState);
@@ -589,28 +591,29 @@
void OutputLayer::writeLutToHWC(HWC2::Layer* hwcLayer,
const LayerFECompositionState& outputIndependentState) {
- if (!outputIndependentState.luts) {
- return;
- }
- auto& lutFileDescriptor = outputIndependentState.luts->getLutFileDescriptor();
- auto lutOffsets = outputIndependentState.luts->offsets;
- auto& lutProperties = outputIndependentState.luts->lutProperties;
-
- std::vector<LutProperties> aidlProperties;
- aidlProperties.reserve(lutProperties.size());
- for (size_t i = 0; i < lutOffsets.size(); i++) {
- LutProperties properties;
- properties.dimension = static_cast<LutProperties::Dimension>(lutProperties[i].dimension);
- properties.size = lutProperties[i].size;
- properties.samplingKeys = {
- static_cast<LutProperties::SamplingKey>(lutProperties[i].samplingKey)};
- aidlProperties.emplace_back(properties);
- }
-
Luts luts;
- luts.pfd = ndk::ScopedFileDescriptor(dup(lutFileDescriptor.get()));
- luts.offsets = lutOffsets;
- luts.lutProperties = std::move(aidlProperties);
+ // if outputIndependentState.luts is nullptr, it means we want to clear the LUTs
+ // and we pass an empty Luts object to the HWC.
+ if (outputIndependentState.luts) {
+ auto& lutFileDescriptor = outputIndependentState.luts->getLutFileDescriptor();
+ auto lutOffsets = outputIndependentState.luts->offsets;
+ auto& lutProperties = outputIndependentState.luts->lutProperties;
+
+ std::vector<LutProperties> aidlProperties;
+ aidlProperties.reserve(lutProperties.size());
+ for (size_t i = 0; i < lutOffsets.size(); i++) {
+ aidlProperties.emplace_back(
+ LutProperties{.dimension = static_cast<LutProperties::Dimension>(
+ lutProperties[i].dimension),
+ .size = lutProperties[i].size,
+ .samplingKeys = {static_cast<LutProperties::SamplingKey>(
+ lutProperties[i].samplingKey)}});
+ }
+
+ luts.pfd = ndk::ScopedFileDescriptor(dup(lutFileDescriptor.get()));
+ luts.offsets = lutOffsets;
+ luts.lutProperties = std::move(aidlProperties);
+ }
switch (auto error = hwcLayer->setLuts(luts)) {
case hal::Error::NONE:
diff --git a/services/surfaceflinger/CompositionEngine/tests/OutputLayerTest.cpp b/services/surfaceflinger/CompositionEngine/tests/OutputLayerTest.cpp
index dbffe80..ca262ee 100644
--- a/services/surfaceflinger/CompositionEngine/tests/OutputLayerTest.cpp
+++ b/services/surfaceflinger/CompositionEngine/tests/OutputLayerTest.cpp
@@ -541,6 +541,9 @@
MOCK_CONST_METHOD1(calculateOutputSourceCrop, FloatRect(uint32_t));
MOCK_CONST_METHOD0(calculateOutputDisplayFrame, Rect());
MOCK_CONST_METHOD1(calculateOutputRelativeBufferTransform, uint32_t(uint32_t));
+ MOCK_METHOD(void, updateLuts,
+ (const LayerFECompositionState&,
+ const std::optional<std::vector<std::optional<LutProperties>>>&));
// compositionengine::OutputLayer overrides
const compositionengine::Output& getOutput() const override { return mOutput; }
@@ -985,21 +988,24 @@
EXPECT_CALL(mLayerFE, getCompositionState()).WillOnce(Return(nullptr));
mOutputLayer.writeStateToHWC(/*includeGeometry*/ true, /*skipLayer*/ false, 0,
- /*zIsOverridden*/ false, /*isPeekingThrough*/ false);
+ /*zIsOverridden*/ false, /*isPeekingThrough*/ false,
+ /*hasLutsProperties*/ false);
}
TEST_F(OutputLayerWriteStateToHWCTest, doesNothingIfNoHWCState) {
mOutputLayer.editState().hwc.reset();
mOutputLayer.writeStateToHWC(/*includeGeometry*/ true, /*skipLayer*/ false, 0,
- /*zIsOverridden*/ false, /*isPeekingThrough*/ false);
+ /*zIsOverridden*/ false, /*isPeekingThrough*/ false,
+ /*hasLutsProperties*/ false);
}
TEST_F(OutputLayerWriteStateToHWCTest, doesNothingIfNoHWCLayer) {
mOutputLayer.editState().hwc = impl::OutputLayerCompositionState::Hwc(nullptr);
mOutputLayer.writeStateToHWC(/*includeGeometry*/ true, /*skipLayer*/ false, 0,
- /*zIsOverridden*/ false, /*isPeekingThrough*/ false);
+ /*zIsOverridden*/ false, /*isPeekingThrough*/ false,
+ /*hasLutsProperties*/ false);
}
TEST_F(OutputLayerWriteStateToHWCTest, canSetAllState) {
@@ -1010,7 +1016,8 @@
EXPECT_CALL(mLayerFE, hasRoundedCorners()).WillOnce(Return(false));
mOutputLayer.writeStateToHWC(/*includeGeometry*/ true, /*skipLayer*/ false, 0,
- /*zIsOverridden*/ false, /*isPeekingThrough*/ false);
+ /*zIsOverridden*/ false, /*isPeekingThrough*/ false,
+ /*hasLutsProperties*/ false);
}
TEST_F(OutputLayerTest, displayInstallOrientationBufferTransformSetTo90) {
@@ -1041,7 +1048,8 @@
expectSetColorCall();
mOutputLayer.writeStateToHWC(/*includeGeometry*/ false, /*skipLayer*/ false, 0,
- /*zIsOverridden*/ false, /*isPeekingThrough*/ false);
+ /*zIsOverridden*/ false, /*isPeekingThrough*/ false,
+ /*hasLutsProperties*/ false);
}
TEST_F(OutputLayerWriteStateToHWCTest, canSetPerFrameStateForSideband) {
@@ -1052,7 +1060,8 @@
expectSetCompositionTypeCall(Composition::SIDEBAND);
mOutputLayer.writeStateToHWC(/*includeGeometry*/ false, /*skipLayer*/ false, 0,
- /*zIsOverridden*/ false, /*isPeekingThrough*/ false);
+ /*zIsOverridden*/ false, /*isPeekingThrough*/ false,
+ /*hasLutsProperties*/ false);
}
TEST_F(OutputLayerWriteStateToHWCTest, canSetPerFrameStateForCursor) {
@@ -1063,7 +1072,8 @@
expectSetCompositionTypeCall(Composition::CURSOR);
mOutputLayer.writeStateToHWC(/*includeGeometry*/ false, /*skipLayer*/ false, 0,
- /*zIsOverridden*/ false, /*isPeekingThrough*/ false);
+ /*zIsOverridden*/ false, /*isPeekingThrough*/ false,
+ /*hasLutsProperties*/ false);
}
TEST_F(OutputLayerWriteStateToHWCTest, canSetPerFrameStateForDevice) {
@@ -1074,7 +1084,8 @@
expectSetCompositionTypeCall(Composition::DEVICE);
mOutputLayer.writeStateToHWC(/*includeGeometry*/ false, /*skipLayer*/ false, 0,
- /*zIsOverridden*/ false, /*isPeekingThrough*/ false);
+ /*zIsOverridden*/ false, /*isPeekingThrough*/ false,
+ /*hasLutsProperties*/ false);
}
TEST_F(OutputLayerWriteStateToHWCTest, compositionTypeIsNotSetIfUnchanged) {
@@ -1087,7 +1098,8 @@
expectNoSetCompositionTypeCall();
mOutputLayer.writeStateToHWC(/*includeGeometry*/ false, /*skipLayer*/ false, 0,
- /*zIsOverridden*/ false, /*isPeekingThrough*/ false);
+ /*zIsOverridden*/ false, /*isPeekingThrough*/ false,
+ /*hasLutsProperties*/ false);
}
TEST_F(OutputLayerWriteStateToHWCTest, compositionTypeIsSetToClientIfColorTransformNotSupported) {
@@ -1098,7 +1110,8 @@
expectSetCompositionTypeCall(Composition::CLIENT);
mOutputLayer.writeStateToHWC(/*includeGeometry*/ false, /*skipLayer*/ false, 0,
- /*zIsOverridden*/ false, /*isPeekingThrough*/ false);
+ /*zIsOverridden*/ false, /*isPeekingThrough*/ false,
+ /*hasLutsProperties*/ false);
}
TEST_F(OutputLayerWriteStateToHWCTest, compositionTypeIsSetToClientIfClientCompositionForced) {
@@ -1111,7 +1124,8 @@
expectSetCompositionTypeCall(Composition::CLIENT);
mOutputLayer.writeStateToHWC(/*includeGeometry*/ false, /*skipLayer*/ false, 0,
- /*zIsOverridden*/ false, /*isPeekingThrough*/ false);
+ /*zIsOverridden*/ false, /*isPeekingThrough*/ false,
+ /*hasLutsProperties*/ false);
}
TEST_F(OutputLayerWriteStateToHWCTest, allStateIncludesMetadataIfPresent) {
@@ -1125,7 +1139,8 @@
expectSetCompositionTypeCall(Composition::DEVICE);
mOutputLayer.writeStateToHWC(/*includeGeometry*/ true, /*skipLayer*/ false, 0,
- /*zIsOverridden*/ false, /*isPeekingThrough*/ false);
+ /*zIsOverridden*/ false, /*isPeekingThrough*/ false,
+ /*hasLutsProperties*/ false);
}
TEST_F(OutputLayerWriteStateToHWCTest, perFrameStateDoesNotIncludeMetadataIfPresent) {
@@ -1137,7 +1152,8 @@
expectSetCompositionTypeCall(Composition::DEVICE);
mOutputLayer.writeStateToHWC(/*includeGeometry*/ false, /*skipLayer*/ false, 0,
- /*zIsOverridden*/ false, /*isPeekingThrough*/ false);
+ /*zIsOverridden*/ false, /*isPeekingThrough*/ false,
+ /*hasLutsProperties*/ false);
}
TEST_F(OutputLayerWriteStateToHWCTest, overriddenSkipLayerDoesNotSendBuffer) {
@@ -1152,7 +1168,8 @@
expectSetCompositionTypeCall(Composition::DEVICE);
mOutputLayer.writeStateToHWC(/*includeGeometry*/ true, /*skipLayer*/ true, 0,
- /*zIsOverridden*/ false, /*isPeekingThrough*/ false);
+ /*zIsOverridden*/ false, /*isPeekingThrough*/ false,
+ /*hasLutsProperties*/ false);
}
TEST_F(OutputLayerWriteStateToHWCTest, overriddenSkipLayerForSolidColorDoesNotSendBuffer) {
@@ -1167,7 +1184,8 @@
expectSetCompositionTypeCall(Composition::DEVICE);
mOutputLayer.writeStateToHWC(/*includeGeometry*/ true, /*skipLayer*/ true, 0,
- /*zIsOverridden*/ false, /*isPeekingThrough*/ false);
+ /*zIsOverridden*/ false, /*isPeekingThrough*/ false,
+ /*hasLutsProperties*/ false);
}
TEST_F(OutputLayerWriteStateToHWCTest, includesOverrideInfoIfPresent) {
@@ -1182,7 +1200,8 @@
expectSetCompositionTypeCall(Composition::DEVICE);
mOutputLayer.writeStateToHWC(/*includeGeometry*/ true, /*skipLayer*/ false, 0,
- /*zIsOverridden*/ false, /*isPeekingThrough*/ false);
+ /*zIsOverridden*/ false, /*isPeekingThrough*/ false,
+ /*hasLutsProperties*/ false);
}
TEST_F(OutputLayerWriteStateToHWCTest, includesOverrideInfoForSolidColorIfPresent) {
@@ -1197,7 +1216,8 @@
expectSetCompositionTypeCall(Composition::DEVICE);
mOutputLayer.writeStateToHWC(/*includeGeometry*/ true, /*skipLayer*/ false, 0,
- /*zIsOverridden*/ false, /*isPeekingThrough*/ false);
+ /*zIsOverridden*/ false, /*isPeekingThrough*/ false,
+ /*hasLutsProperties*/ false);
}
TEST_F(OutputLayerWriteStateToHWCTest, previousOverriddenLayerSendsSurfaceDamage) {
@@ -1211,7 +1231,8 @@
expectSetCompositionTypeCall(Composition::DEVICE);
mOutputLayer.writeStateToHWC(/*includeGeometry*/ true, /*skipLayer*/ false, 0,
- /*zIsOverridden*/ false, /*isPeekingThrough*/ false);
+ /*zIsOverridden*/ false, /*isPeekingThrough*/ false,
+ /*hasLutsProperties*/ false);
}
TEST_F(OutputLayerWriteStateToHWCTest, previousSkipLayerSendsUpdatedDeviceCompositionInfo) {
@@ -1227,7 +1248,8 @@
expectSetCompositionTypeCall(Composition::DEVICE);
mOutputLayer.writeStateToHWC(/*includeGeometry*/ true, /*skipLayer*/ false, 0,
- /*zIsOverridden*/ false, /*isPeekingThrough*/ false);
+ /*zIsOverridden*/ false, /*isPeekingThrough*/ false,
+ /*hasLutsProperties*/ false);
}
TEST_F(OutputLayerWriteStateToHWCTest, previousSkipLayerSendsUpdatedClientCompositionInfo) {
@@ -1244,7 +1266,8 @@
expectSetCompositionTypeCall(Composition::CLIENT);
mOutputLayer.writeStateToHWC(/*includeGeometry*/ true, /*skipLayer*/ false, 0,
- /*zIsOverridden*/ false, /*isPeekingThrough*/ false);
+ /*zIsOverridden*/ false, /*isPeekingThrough*/ false,
+ /*hasLutsProperties*/ false);
}
TEST_F(OutputLayerWriteStateToHWCTest, peekThroughChangesBlendMode) {
@@ -1258,7 +1281,8 @@
expectPerFrameCommonCalls();
mOutputLayer.writeStateToHWC(/*includeGeometry*/ true, /*skipLayer*/ false, 0,
- /*zIsOverridden*/ false, /*isPeekingThrough*/ false);
+ /*zIsOverridden*/ false, /*isPeekingThrough*/ false,
+ /*hasLutsProperties*/ false);
}
TEST_F(OutputLayerWriteStateToHWCTest, isPeekingThroughSetsOverride) {
@@ -1266,7 +1290,8 @@
expectPerFrameCommonCalls();
mOutputLayer.writeStateToHWC(/*includeGeometry*/ true, /*skipLayer*/ false, 0,
- /*zIsOverridden*/ false, /*isPeekingThrough*/ true);
+ /*zIsOverridden*/ false, /*isPeekingThrough*/ true,
+ /*hasLutsProperties*/ false);
EXPECT_TRUE(mOutputLayer.getState().hwc->stateOverridden);
}
@@ -1276,7 +1301,7 @@
mOutputLayer.writeStateToHWC(/*includeGeometry*/ true, /*skipLayer*/ false, 0,
/*zIsOverridden*/ true, /*isPeekingThrough*/
- false);
+ false, /*hasLutsProperties*/ false);
EXPECT_TRUE(mOutputLayer.getState().hwc->stateOverridden);
}
@@ -1288,7 +1313,7 @@
mOutputLayer.writeStateToHWC(/*includeGeometry*/ true, /*skipLayer*/ false, 0,
/*zIsOverridden*/ false, /*isPeekingThrough*/
- false);
+ false, /*hasLutsProperties*/ false);
}
TEST_F(OutputLayerWriteStateToHWCTest, roundedCornersPeekingThroughAllowsDeviceComposition) {
@@ -1301,7 +1326,7 @@
mLayerFEState.compositionType = Composition::DEVICE;
mOutputLayer.writeStateToHWC(/*includeGeometry*/ true, /*skipLayer*/ false, 0,
/*zIsOverridden*/ false, /*isPeekingThrough*/
- true);
+ true, /*hasLutsProperties*/ false);
EXPECT_EQ(Composition::DEVICE, mOutputLayer.getState().hwc->hwcCompositionType);
}
@@ -1318,7 +1343,7 @@
mOutputLayer.writeStateToHWC(/*includeGeometry*/ true, /*skipLayer*/ false, 0,
/*zIsOverridden*/ false, /*isPeekingThrough*/
- false);
+ false, /*hasLutsProperties*/ false);
}
TEST_F(OutputLayerWriteStateToHWCTest, setCompositionTypeRefreshRateIndicator) {
@@ -1330,7 +1355,8 @@
expectSetCompositionTypeCall(Composition::REFRESH_RATE_INDICATOR);
mOutputLayer.writeStateToHWC(/*includeGeometry*/ true, /*skipLayer*/ false, 0,
- /*zIsOverridden*/ false, /*isPeekingThrough*/ false);
+ /*zIsOverridden*/ false, /*isPeekingThrough*/ false,
+ /*hasLutsProperties*/ false);
}
TEST_F(OutputLayerWriteStateToHWCTest, setsPictureProfileWhenCommitted) {
@@ -1349,7 +1375,8 @@
mOutputLayer.commitPictureProfileToCompositionState();
mOutputLayer.writeStateToHWC(/*includeGeometry*/ true, /*skipLayer*/ false, 0,
- /*zIsOverridden*/ false, /*isPeekingThrough*/ false);
+ /*zIsOverridden*/ false, /*isPeekingThrough*/ false,
+ /*hasLutsProperties*/ false);
}
TEST_F(OutputLayerWriteStateToHWCTest, doesNotSetPictureProfileWhenNotCommitted) {
@@ -1367,7 +1394,8 @@
EXPECT_CALL(*mHwcLayer, setPictureProfileHandle(_)).Times(0);
mOutputLayer.writeStateToHWC(/*includeGeometry*/ true, /*skipLayer*/ false, 0,
- /*zIsOverridden*/ false, /*isPeekingThrough*/ false);
+ /*zIsOverridden*/ false, /*isPeekingThrough*/ false,
+ /*hasLutsProperties*/ false);
}
TEST_F(OutputLayerWriteStateToHWCTest, doesNotSetPictureProfileWhenNotCommittedLater) {
@@ -1386,7 +1414,8 @@
mOutputLayer.commitPictureProfileToCompositionState();
mOutputLayer.writeStateToHWC(/*includeGeometry*/ true, /*skipLayer*/ false, 0,
- /*zIsOverridden*/ false, /*isPeekingThrough*/ false);
+ /*zIsOverridden*/ false, /*isPeekingThrough*/ false,
+ /*hasLutsProperties*/ false);
expectGeometryCommonCalls();
expectPerFrameCommonCalls();
@@ -1395,7 +1424,8 @@
EXPECT_CALL(*mHwcLayer, setPictureProfileHandle(PictureProfileHandle(1))).Times(0);
// No committing of picture profile before writing the state
mOutputLayer.writeStateToHWC(/*includeGeometry*/ true, /*skipLayer*/ false, 0,
- /*zIsOverridden*/ false, /*isPeekingThrough*/ false);
+ /*zIsOverridden*/ false, /*isPeekingThrough*/ false,
+ /*hasLutsProperties*/ false);
}
/*
@@ -1441,21 +1471,24 @@
mLayerFEState.buffer = kBuffer1;
EXPECT_CALL(mHwcLayer, setBuffer(/*slot*/ 0, kBuffer1, kFence));
mOutputLayer.writeStateToHWC(/*includeGeometry*/ false, /*skipLayer*/ false, 0,
- /*zIsOverridden*/ false, /*isPeekingThrough*/ false);
+ /*zIsOverridden*/ false, /*isPeekingThrough*/ false,
+ /*hasLutsProperties*/ false);
Mock::VerifyAndClearExpectations(&mHwcLayer);
// Buffer2 is stored in slot 1
mLayerFEState.buffer = kBuffer2;
EXPECT_CALL(mHwcLayer, setBuffer(/*slot*/ 1, kBuffer2, kFence));
mOutputLayer.writeStateToHWC(/*includeGeometry*/ false, /*skipLayer*/ false, 0,
- /*zIsOverridden*/ false, /*isPeekingThrough*/ false);
+ /*zIsOverridden*/ false, /*isPeekingThrough*/ false,
+ /*hasLutsProperties*/ false);
Mock::VerifyAndClearExpectations(&mHwcLayer);
// Buffer3 is stored in slot 2
mLayerFEState.buffer = kBuffer3;
EXPECT_CALL(mHwcLayer, setBuffer(/*slot*/ 2, kBuffer3, kFence));
mOutputLayer.writeStateToHWC(/*includeGeometry*/ false, /*skipLayer*/ false, 0,
- /*zIsOverridden*/ false, /*isPeekingThrough*/ false);
+ /*zIsOverridden*/ false, /*isPeekingThrough*/ false,
+ /*hasLutsProperties*/ false);
Mock::VerifyAndClearExpectations(&mHwcLayer);
// Buffer2 becomes the active buffer again (with a nullptr) and reuses slot 1
@@ -1463,7 +1496,8 @@
sp<GraphicBuffer> nullBuffer = nullptr;
EXPECT_CALL(mHwcLayer, setBuffer(/*slot*/ 1, nullBuffer, kFence));
mOutputLayer.writeStateToHWC(/*includeGeometry*/ false, /*skipLayer*/ false, 0,
- /*zIsOverridden*/ false, /*isPeekingThrough*/ false);
+ /*zIsOverridden*/ false, /*isPeekingThrough*/ false,
+ /*hasLutsProperties*/ false);
Mock::VerifyAndClearExpectations(&mHwcLayer);
// Buffer slots are cleared
@@ -1481,7 +1515,8 @@
mLayerFEState.buffer = kBuffer1;
EXPECT_CALL(mHwcLayer, setBuffer(/*slot*/ 1, kBuffer1, kFence));
mOutputLayer.writeStateToHWC(/*includeGeometry*/ false, /*skipLayer*/ false, 0,
- /*zIsOverridden*/ false, /*isPeekingThrough*/ false);
+ /*zIsOverridden*/ false, /*isPeekingThrough*/ false,
+ /*hasLutsProperties*/ false);
Mock::VerifyAndClearExpectations(&mHwcLayer);
}
diff --git a/services/surfaceflinger/CompositionEngine/tests/OutputTest.cpp b/services/surfaceflinger/CompositionEngine/tests/OutputTest.cpp
index 442b603..09ad9fa 100644
--- a/services/surfaceflinger/CompositionEngine/tests/OutputTest.cpp
+++ b/services/surfaceflinger/CompositionEngine/tests/OutputTest.cpp
@@ -813,19 +813,22 @@
updateCompositionState(false, false, ui::Transform::ROT_180, _));
EXPECT_CALL(*layer1.outputLayer,
writeStateToHWC(/*includeGeometry*/ false, /*skipLayer*/ false, z++,
- /*zIsOverridden*/ false, /*isPeekingThrough*/ false));
+ /*zIsOverridden*/ false, /*isPeekingThrough*/ false,
+ /*hasLutsProperties*/ false));
EXPECT_CALL(*layer1.outputLayer, requiresClientComposition()).WillRepeatedly(Return(false));
EXPECT_CALL(*layer2.outputLayer,
updateCompositionState(false, false, ui::Transform::ROT_180, _));
EXPECT_CALL(*layer2.outputLayer,
writeStateToHWC(/*includeGeometry*/ false, /*skipLayer*/ false, z++,
- /*zIsOverridden*/ false, /*isPeekingThrough*/ false));
+ /*zIsOverridden*/ false, /*isPeekingThrough*/ false,
+ /*hasLutsProperties*/ false));
EXPECT_CALL(*layer2.outputLayer, requiresClientComposition()).WillRepeatedly(Return(false));
EXPECT_CALL(*layer3.outputLayer,
updateCompositionState(false, false, ui::Transform::ROT_180, _));
EXPECT_CALL(*layer3.outputLayer,
writeStateToHWC(/*includeGeometry*/ false, /*skipLayer*/ false, z++,
- /*zIsOverridden*/ false, /*isPeekingThrough*/ false));
+ /*zIsOverridden*/ false, /*isPeekingThrough*/ false,
+ /*hasLutsProperties*/ false));
EXPECT_CALL(*layer3.outputLayer, requiresClientComposition()).WillRepeatedly(Return(false));
injectOutputLayer(layer1);
@@ -852,17 +855,20 @@
EXPECT_CALL(*layer1.outputLayer, updateCompositionState(true, false, ui::Transform::ROT_0, _));
EXPECT_CALL(*layer1.outputLayer,
writeStateToHWC(/*includeGeometry*/ true, /*skipLayer*/ false, z++,
- /*zIsOverridden*/ false, /*isPeekingThrough*/ false));
+ /*zIsOverridden*/ false, /*isPeekingThrough*/ false,
+ /*hasLutsProperties*/ false));
EXPECT_CALL(*layer1.outputLayer, requiresClientComposition()).WillRepeatedly(Return(false));
EXPECT_CALL(*layer2.outputLayer, updateCompositionState(true, false, ui::Transform::ROT_0, _));
EXPECT_CALL(*layer2.outputLayer,
writeStateToHWC(/*includeGeometry*/ true, /*skipLayer*/ false, z++,
- /*zIsOverridden*/ false, /*isPeekingThrough*/ false));
+ /*zIsOverridden*/ false, /*isPeekingThrough*/ false,
+ /*hasLutsProperties*/ false));
EXPECT_CALL(*layer2.outputLayer, requiresClientComposition()).WillRepeatedly(Return(false));
EXPECT_CALL(*layer3.outputLayer, updateCompositionState(true, false, ui::Transform::ROT_0, _));
EXPECT_CALL(*layer3.outputLayer,
writeStateToHWC(/*includeGeometry*/ true, /*skipLayer*/ false, z++,
- /*zIsOverridden*/ false, /*isPeekingThrough*/ false));
+ /*zIsOverridden*/ false, /*isPeekingThrough*/ false,
+ /*hasLutsProperties*/ false));
EXPECT_CALL(*layer3.outputLayer, requiresClientComposition()).WillRepeatedly(Return(false));
injectOutputLayer(layer1);
@@ -888,17 +894,20 @@
EXPECT_CALL(*layer1.outputLayer, updateCompositionState(false, true, ui::Transform::ROT_0, _));
EXPECT_CALL(*layer1.outputLayer,
writeStateToHWC(/*includeGeometry*/ false, /*skipLayer*/ false, z++,
- /*zIsOverridden*/ false, /*isPeekingThrough*/ false));
+ /*zIsOverridden*/ false, /*isPeekingThrough*/ false,
+ /*hasLutsProperties*/ false));
EXPECT_CALL(*layer1.outputLayer, requiresClientComposition()).WillRepeatedly(Return(false));
EXPECT_CALL(*layer2.outputLayer, updateCompositionState(false, true, ui::Transform::ROT_0, _));
EXPECT_CALL(*layer2.outputLayer,
writeStateToHWC(/*includeGeometry*/ false, /*skipLayer*/ false, z++,
- /*zIsOverridden*/ false, /*isPeekingThrough*/ false));
+ /*zIsOverridden*/ false, /*isPeekingThrough*/ false,
+ /*hasLutsProperties*/ false));
EXPECT_CALL(*layer2.outputLayer, requiresClientComposition()).WillRepeatedly(Return(false));
EXPECT_CALL(*layer3.outputLayer, updateCompositionState(false, true, ui::Transform::ROT_0, _));
EXPECT_CALL(*layer3.outputLayer,
writeStateToHWC(/*includeGeometry*/ false, /*skipLayer*/ false, z++,
- /*zIsOverridden*/ false, /*isPeekingThrough*/ false));
+ /*zIsOverridden*/ false, /*isPeekingThrough*/ false,
+ /*hasLutsProperties*/ false));
EXPECT_CALL(*layer3.outputLayer, requiresClientComposition()).WillRepeatedly(Return(false));
injectOutputLayer(layer1);
@@ -932,7 +941,8 @@
uint32_t z = 0;
EXPECT_CALL(*layer0.outputLayer,
writeStateToHWC(/*includeGeometry*/ true, /*skipLayer*/ false, z++,
- /*zIsOverridden*/ false, /*isPeekingThrough*/ false));
+ /*zIsOverridden*/ false, /*isPeekingThrough*/ false,
+ /*hasLutsProperties*/ false));
EXPECT_CALL(*layer0.outputLayer, requiresClientComposition()).WillRepeatedly(Return(false));
// After calling planComposition (which clears overrideInfo), this test sets
@@ -942,15 +952,17 @@
EXPECT_CALL(*layer3.outputLayer,
writeStateToHWC(/*includeGeometry*/ true, /*skipLayer*/ false, z++,
/*zIsOverridden*/ true, /*isPeekingThrough*/
- true));
+ true, /*hasLutsProperties*/ false));
EXPECT_CALL(*layer3.outputLayer, requiresClientComposition()).WillRepeatedly(Return(false));
EXPECT_CALL(*layer1.outputLayer,
writeStateToHWC(/*includeGeometry*/ true, /*skipLayer*/ false, z++,
- /*zIsOverridden*/ true, /*isPeekingThrough*/ false));
+ /*zIsOverridden*/ true, /*isPeekingThrough*/ false,
+ /*hasLutsProperties*/ false));
EXPECT_CALL(*layer1.outputLayer, requiresClientComposition()).WillRepeatedly(Return(false));
EXPECT_CALL(*layer2.outputLayer,
writeStateToHWC(/*includeGeometry*/ true, /*skipLayer*/ true, z++,
- /*zIsOverridden*/ true, /*isPeekingThrough*/ false));
+ /*zIsOverridden*/ true, /*isPeekingThrough*/ false,
+ /*hasLutsProperties*/ false));
EXPECT_CALL(*layer2.outputLayer, requiresClientComposition()).WillRepeatedly(Return(false));
injectOutputLayer(layer0);
@@ -4962,12 +4974,14 @@
EXPECT_CALL(*layer1.outputLayer, updateCompositionState(false, false, ui::Transform::ROT_0, _));
EXPECT_CALL(*layer1.outputLayer,
writeStateToHWC(/*includeGeometry*/ false, /*skipLayer*/ false, z++,
- /*zIsOverridden*/ false, /*isPeekingThrough*/ false));
+ /*zIsOverridden*/ false, /*isPeekingThrough*/ false,
+ /*hasLutsProperties*/ false));
EXPECT_CALL(*layer1.outputLayer, requiresClientComposition()).WillRepeatedly(Return(false));
EXPECT_CALL(*layer2.outputLayer, updateCompositionState(false, false, ui::Transform::ROT_0, _));
EXPECT_CALL(*layer2.outputLayer,
writeStateToHWC(/*includeGeometry*/ false, /*skipLayer*/ false, z++,
- /*zIsOverridden*/ false, /*isPeekingThrough*/ false));
+ /*zIsOverridden*/ false, /*isPeekingThrough*/ false,
+ /*hasLutsProperties*/ false));
EXPECT_CALL(*layer2.outputLayer, requiresClientComposition()).WillRepeatedly(Return(false));
layer2.layerFEState.backgroundBlurRadius = 10;
@@ -4996,17 +5010,20 @@
EXPECT_CALL(*layer1.outputLayer, updateCompositionState(false, true, ui::Transform::ROT_0, _));
EXPECT_CALL(*layer1.outputLayer,
writeStateToHWC(/*includeGeometry*/ false, /*skipLayer*/ false, z++,
- /*zIsOverridden*/ false, /*isPeekingThrough*/ false));
+ /*zIsOverridden*/ false, /*isPeekingThrough*/ false,
+ /*hasLutsProperties*/ false));
EXPECT_CALL(*layer1.outputLayer, requiresClientComposition()).WillRepeatedly(Return(false));
EXPECT_CALL(*layer2.outputLayer, updateCompositionState(false, true, ui::Transform::ROT_0, _));
EXPECT_CALL(*layer2.outputLayer,
writeStateToHWC(/*includeGeometry*/ false, /*skipLayer*/ false, z++,
- /*zIsOverridden*/ false, /*isPeekingThrough*/ false));
+ /*zIsOverridden*/ false, /*isPeekingThrough*/ false,
+ /*hasLutsProperties*/ false));
EXPECT_CALL(*layer2.outputLayer, requiresClientComposition()).WillRepeatedly(Return(false));
EXPECT_CALL(*layer3.outputLayer, updateCompositionState(false, false, ui::Transform::ROT_0, _));
EXPECT_CALL(*layer3.outputLayer,
writeStateToHWC(/*includeGeometry*/ false, /*skipLayer*/ false, z++,
- /*zIsOverridden*/ false, /*isPeekingThrough*/ false));
+ /*zIsOverridden*/ false, /*isPeekingThrough*/ false,
+ /*hasLutsProperties*/ false));
EXPECT_CALL(*layer3.outputLayer, requiresClientComposition()).WillRepeatedly(Return(false));
layer2.layerFEState.backgroundBlurRadius = 10;
@@ -5036,17 +5053,20 @@
EXPECT_CALL(*layer1.outputLayer, updateCompositionState(false, true, ui::Transform::ROT_0, _));
EXPECT_CALL(*layer1.outputLayer,
writeStateToHWC(/*includeGeometry*/ false, /*skipLayer*/ false, z++,
- /*zIsOverridden*/ false, /*isPeekingThrough*/ false));
+ /*zIsOverridden*/ false, /*isPeekingThrough*/ false,
+ /*hasLutsProperties*/ false));
EXPECT_CALL(*layer1.outputLayer, requiresClientComposition()).WillRepeatedly(Return(false));
EXPECT_CALL(*layer2.outputLayer, updateCompositionState(false, true, ui::Transform::ROT_0, _));
EXPECT_CALL(*layer2.outputLayer,
writeStateToHWC(/*includeGeometry*/ false, /*skipLayer*/ false, z++,
- /*zIsOverridden*/ false, /*isPeekingThrough*/ false));
+ /*zIsOverridden*/ false, /*isPeekingThrough*/ false,
+ /*hasLutsProperties*/ false));
EXPECT_CALL(*layer2.outputLayer, requiresClientComposition()).WillRepeatedly(Return(false));
EXPECT_CALL(*layer3.outputLayer, updateCompositionState(false, false, ui::Transform::ROT_0, _));
EXPECT_CALL(*layer3.outputLayer,
writeStateToHWC(/*includeGeometry*/ false, /*skipLayer*/ false, z++,
- /*zIsOverridden*/ false, /*isPeekingThrough*/ false));
+ /*zIsOverridden*/ false, /*isPeekingThrough*/ false,
+ /*hasLutsProperties*/ false));
EXPECT_CALL(*layer3.outputLayer, requiresClientComposition()).WillRepeatedly(Return(false));
BlurRegion region;
@@ -5080,14 +5100,14 @@
InjectedLayer layer1;
injectOutputLayer(layer1);
PictureProfileHandle profileForLayer1(1);
- EXPECT_CALL(*layer1.outputLayer, getPictureProfilePriority()).WillRepeatedly(Return(3));
+ EXPECT_CALL(*layer1.outputLayer, getPictureProfilePriority()).WillRepeatedly(Return(1));
EXPECT_CALL(*layer1.outputLayer, getPictureProfileHandle())
.WillRepeatedly(ReturnRef(profileForLayer1));
InjectedLayer layer2;
injectOutputLayer(layer2);
PictureProfileHandle profileForLayer2(2);
- EXPECT_CALL(*layer2.outputLayer, getPictureProfilePriority()).WillRepeatedly(Return(1));
+ EXPECT_CALL(*layer2.outputLayer, getPictureProfilePriority()).WillRepeatedly(Return(3));
EXPECT_CALL(*layer2.outputLayer, getPictureProfileHandle())
.WillRepeatedly(ReturnRef(profileForLayer2));
@@ -5101,13 +5121,13 @@
// Because StrictMock
EXPECT_CALL(*layer1.outputLayer, requiresClientComposition()).WillRepeatedly(Return(false));
EXPECT_CALL(*layer1.outputLayer, updateCompositionState(_, _, _, _));
- EXPECT_CALL(*layer1.outputLayer, writeStateToHWC(_, _, _, _, _));
+ EXPECT_CALL(*layer1.outputLayer, writeStateToHWC(_, _, _, _, _, _));
EXPECT_CALL(*layer2.outputLayer, requiresClientComposition()).WillRepeatedly(Return(false));
EXPECT_CALL(*layer2.outputLayer, updateCompositionState(_, _, _, _));
- EXPECT_CALL(*layer2.outputLayer, writeStateToHWC(_, _, _, _, _));
+ EXPECT_CALL(*layer2.outputLayer, writeStateToHWC(_, _, _, _, _, _));
EXPECT_CALL(*layer3.outputLayer, requiresClientComposition()).WillRepeatedly(Return(false));
EXPECT_CALL(*layer3.outputLayer, updateCompositionState(_, _, _, _));
- EXPECT_CALL(*layer3.outputLayer, writeStateToHWC(_, _, _, _, _));
+ EXPECT_CALL(*layer3.outputLayer, writeStateToHWC(_, _, _, _, _, _));
// No layer picture profiles should be committed
EXPECT_CALL(*layer1.outputLayer, commitPictureProfileToCompositionState).Times(0);
@@ -5143,14 +5163,14 @@
InjectedLayer layer1;
injectOutputLayer(layer1);
PictureProfileHandle profileForLayer1(1);
- EXPECT_CALL(*layer1.outputLayer, getPictureProfilePriority()).WillRepeatedly(Return(3));
+ EXPECT_CALL(*layer1.outputLayer, getPictureProfilePriority()).WillRepeatedly(Return(1));
EXPECT_CALL(*layer1.outputLayer, getPictureProfileHandle())
.WillRepeatedly(ReturnRef(profileForLayer1));
InjectedLayer layer2;
injectOutputLayer(layer2);
PictureProfileHandle profileForLayer2(2);
- EXPECT_CALL(*layer2.outputLayer, getPictureProfilePriority()).WillRepeatedly(Return(1));
+ EXPECT_CALL(*layer2.outputLayer, getPictureProfilePriority()).WillRepeatedly(Return(3));
EXPECT_CALL(*layer2.outputLayer, getPictureProfileHandle())
.WillRepeatedly(ReturnRef(profileForLayer2));
@@ -5164,13 +5184,13 @@
// Because StrictMock
EXPECT_CALL(*layer1.outputLayer, requiresClientComposition()).WillRepeatedly(Return(false));
EXPECT_CALL(*layer1.outputLayer, updateCompositionState(_, _, _, _));
- EXPECT_CALL(*layer1.outputLayer, writeStateToHWC(_, _, _, _, _));
+ EXPECT_CALL(*layer1.outputLayer, writeStateToHWC(_, _, _, _, _, _));
EXPECT_CALL(*layer2.outputLayer, requiresClientComposition()).WillRepeatedly(Return(false));
EXPECT_CALL(*layer2.outputLayer, updateCompositionState(_, _, _, _));
- EXPECT_CALL(*layer2.outputLayer, writeStateToHWC(_, _, _, _, _));
+ EXPECT_CALL(*layer2.outputLayer, writeStateToHWC(_, _, _, _, _, _));
EXPECT_CALL(*layer3.outputLayer, requiresClientComposition()).WillRepeatedly(Return(false));
EXPECT_CALL(*layer3.outputLayer, updateCompositionState(_, _, _, _));
- EXPECT_CALL(*layer3.outputLayer, writeStateToHWC(_, _, _, _, _));
+ EXPECT_CALL(*layer3.outputLayer, writeStateToHWC(_, _, _, _, _, _));
// The two highest priority layers should have their picture profiles committed
EXPECT_CALL(*layer1.outputLayer, commitPictureProfileToCompositionState).Times(0);
diff --git a/services/surfaceflinger/DisplayHardware/AidlComposerHal.cpp b/services/surfaceflinger/DisplayHardware/AidlComposerHal.cpp
index 25f6513..8529c72 100644
--- a/services/surfaceflinger/DisplayHardware/AidlComposerHal.cpp
+++ b/services/surfaceflinger/DisplayHardware/AidlComposerHal.cpp
@@ -1678,6 +1678,29 @@
return error;
}
+Error AidlComposer::getLuts(Display display, const std::vector<sp<GraphicBuffer>>& buffers,
+ std::vector<aidl::android::hardware::graphics::composer3::Luts>* luts) {
+ std::vector<aidl::android::hardware::graphics::composer3::Buffer> aidlBuffers;
+ aidlBuffers.reserve(buffers.size());
+
+ for (auto& buffer : buffers) {
+ if (buffer.get()) {
+ aidl::android::hardware::graphics::composer3::Buffer aidlBuffer;
+ aidlBuffer.handle.emplace(::android::dupToAidl(buffer->getNativeBuffer()->handle));
+ aidlBuffers.emplace_back(std::move(aidlBuffer));
+ }
+ }
+
+ const auto status =
+ mAidlComposerClient->getLuts(translate<int64_t>(display), aidlBuffers, luts);
+ if (!status.isOk()) {
+ ALOGE("getLuts failed %s", status.getDescription().c_str());
+ return static_cast<Error>(status.getServiceSpecificError());
+ }
+
+ return Error::NONE;
+}
+
ftl::Optional<std::reference_wrapper<ComposerClientWriter>> AidlComposer::getWriter(Display display)
REQUIRES_SHARED(mMutex) {
return mWriters.get(display);
diff --git a/services/surfaceflinger/DisplayHardware/AidlComposerHal.h b/services/surfaceflinger/DisplayHardware/AidlComposerHal.h
index 6b5ebc5..82006f4 100644
--- a/services/surfaceflinger/DisplayHardware/AidlComposerHal.h
+++ b/services/surfaceflinger/DisplayHardware/AidlComposerHal.h
@@ -245,6 +245,8 @@
Error getMaxLayerPictureProfiles(Display, int32_t* outMaxProfiles) override;
Error setDisplayPictureProfileId(Display, PictureProfileId id) override;
Error setLayerPictureProfileId(Display, Layer, PictureProfileId id) override;
+ Error getLuts(Display, const std::vector<sp<GraphicBuffer>>&,
+ std::vector<aidl::android::hardware::graphics::composer3::Luts>*) override;
private:
// Many public functions above simply write a command into the command
diff --git a/services/surfaceflinger/DisplayHardware/ComposerHal.h b/services/surfaceflinger/DisplayHardware/ComposerHal.h
index ff292fa..6e431bb 100644
--- a/services/surfaceflinger/DisplayHardware/ComposerHal.h
+++ b/services/surfaceflinger/DisplayHardware/ComposerHal.h
@@ -46,6 +46,7 @@
#include <aidl/android/hardware/graphics/composer3/DisplayConfiguration.h>
#include <aidl/android/hardware/graphics/composer3/DisplayLuts.h>
#include <aidl/android/hardware/graphics/composer3/IComposerCallback.h>
+#include <aidl/android/hardware/graphics/composer3/Luts.h>
#include <aidl/android/hardware/graphics/composer3/OverlayProperties.h>
#include <optional>
@@ -313,6 +314,8 @@
virtual Error getMaxLayerPictureProfiles(Display display, int32_t* outMaxProfiles) = 0;
virtual Error setDisplayPictureProfileId(Display display, PictureProfileId id) = 0;
virtual Error setLayerPictureProfileId(Display display, Layer layer, PictureProfileId id) = 0;
+ virtual Error getLuts(Display display, const std::vector<sp<GraphicBuffer>>&,
+ std::vector<V3_0::Luts>*) = 0;
};
} // namespace Hwc2
diff --git a/services/surfaceflinger/DisplayHardware/HWC2.cpp b/services/surfaceflinger/DisplayHardware/HWC2.cpp
index 081f4aa..e63a14b 100644
--- a/services/surfaceflinger/DisplayHardware/HWC2.cpp
+++ b/services/surfaceflinger/DisplayHardware/HWC2.cpp
@@ -629,7 +629,7 @@
auto layer = getLayerById(layerIds[i]);
if (layer) {
auto& layerLut = tmpLuts[i];
- if (layerLut.luts.pfd.get() > 0 && layerLut.luts.offsets.has_value()) {
+ if (layerLut.luts.pfd.get() >= 0 && layerLut.luts.offsets.has_value()) {
const auto& offsets = layerLut.luts.offsets.value();
std::vector<std::pair<int32_t, LutProperties>> lutOffsetsAndProperties;
lutOffsetsAndProperties.reserve(offsets.size());
@@ -669,6 +669,12 @@
return static_cast<Error>(error);
}
+Error Display::getLuts(const std::vector<sp<GraphicBuffer>>& buffers,
+ std::vector<aidl::android::hardware::graphics::composer3::Luts>* outLuts) {
+ const auto error = mComposer.getLuts(mId, buffers, outLuts);
+ return static_cast<Error>(error);
+}
+
// For use by Device
void Display::setConnected(bool connected) {
diff --git a/services/surfaceflinger/DisplayHardware/HWC2.h b/services/surfaceflinger/DisplayHardware/HWC2.h
index 6740d8a..c3deb84 100644
--- a/services/surfaceflinger/DisplayHardware/HWC2.h
+++ b/services/surfaceflinger/DisplayHardware/HWC2.h
@@ -203,6 +203,9 @@
[[nodiscard]] virtual hal::Error getMaxLayerPictureProfiles(int32_t* maxProfiles) = 0;
[[nodiscard]] virtual hal::Error setPictureProfileHandle(
const PictureProfileHandle& handle) = 0;
+ [[nodiscard]] virtual hal::Error getLuts(
+ const std::vector<android::sp<android::GraphicBuffer>>&,
+ std::vector<aidl::android::hardware::graphics::composer3::Luts>*) = 0;
};
namespace impl {
@@ -288,6 +291,8 @@
hal::Error setIdleTimerEnabled(std::chrono::milliseconds timeout) override;
hal::Error getMaxLayerPictureProfiles(int32_t* maxProfiles) override;
hal::Error setPictureProfileHandle(const android::PictureProfileHandle& handle) override;
+ hal::Error getLuts(const std::vector<android::sp<android::GraphicBuffer>>&,
+ std::vector<aidl::android::hardware::graphics::composer3::Luts>*) override;
// Other Display methods
hal::HWDisplayId getId() const override { return mId; }
diff --git a/services/surfaceflinger/DisplayHardware/HWComposer.cpp b/services/surfaceflinger/DisplayHardware/HWComposer.cpp
index 721cfd3..7f94428 100644
--- a/services/surfaceflinger/DisplayHardware/HWComposer.cpp
+++ b/services/surfaceflinger/DisplayHardware/HWComposer.cpp
@@ -1046,6 +1046,16 @@
return NO_ERROR;
}
+status_t HWComposer::getLuts(
+ PhysicalDisplayId displayId, const std::vector<sp<GraphicBuffer>>& buffers,
+ std::vector<aidl::android::hardware::graphics::composer3::Luts>* luts) {
+ RETURN_IF_INVALID_DISPLAY(displayId, BAD_INDEX);
+ auto& hwcDisplay = mDisplayData[displayId].hwcDisplay;
+ auto error = hwcDisplay->getLuts(buffers, luts);
+ RETURN_IF_HWC_ERROR(error, displayId, UNKNOWN_ERROR);
+ return NO_ERROR;
+}
+
const std::unordered_map<std::string, bool>& HWComposer::getSupportedLayerGenericMetadata() const {
return mSupportedLayerGenericMetadata;
}
diff --git a/services/surfaceflinger/DisplayHardware/HWComposer.h b/services/surfaceflinger/DisplayHardware/HWComposer.h
index 52662cf..b1b997a 100644
--- a/services/surfaceflinger/DisplayHardware/HWComposer.h
+++ b/services/surfaceflinger/DisplayHardware/HWComposer.h
@@ -55,6 +55,7 @@
#include <aidl/android/hardware/graphics/composer3/DisplayCapability.h>
#include <aidl/android/hardware/graphics/composer3/DisplayLuts.h>
#include <aidl/android/hardware/graphics/composer3/LutProperties.h>
+#include <aidl/android/hardware/graphics/composer3/Luts.h>
#include <aidl/android/hardware/graphics/composer3/OutputType.h>
#include <aidl/android/hardware/graphics/composer3/OverlayProperties.h>
@@ -324,6 +325,8 @@
virtual int32_t getMaxLayerPictureProfiles(PhysicalDisplayId) = 0;
virtual status_t setDisplayPictureProfileHandle(PhysicalDisplayId,
const PictureProfileHandle& handle) = 0;
+ virtual status_t getLuts(PhysicalDisplayId, const std::vector<sp<GraphicBuffer>>&,
+ std::vector<aidl::android::hardware::graphics::composer3::Luts>*) = 0;
};
static inline bool operator==(const android::HWComposer::DeviceRequestedChanges& lhs,
@@ -491,6 +494,8 @@
int32_t getMaxLayerPictureProfiles(PhysicalDisplayId) override;
status_t setDisplayPictureProfileHandle(PhysicalDisplayId,
const android::PictureProfileHandle& profile) override;
+ status_t getLuts(PhysicalDisplayId, const std::vector<sp<GraphicBuffer>>&,
+ std::vector<aidl::android::hardware::graphics::composer3::Luts>*) override;
// for debugging ----------------------------------------------------------
void dump(std::string& out) const override;
diff --git a/services/surfaceflinger/DisplayHardware/HidlComposerHal.cpp b/services/surfaceflinger/DisplayHardware/HidlComposerHal.cpp
index 5703a2d..ec15539 100644
--- a/services/surfaceflinger/DisplayHardware/HidlComposerHal.cpp
+++ b/services/surfaceflinger/DisplayHardware/HidlComposerHal.cpp
@@ -1452,6 +1452,11 @@
return Error::UNSUPPORTED;
}
+Error HidlComposer::getLuts(Display, const std::vector<sp<GraphicBuffer>>&,
+ std::vector<aidl::android::hardware::graphics::composer3::Luts>*) {
+ return Error::UNSUPPORTED;
+}
+
Error HidlComposer::setDisplayPictureProfileId(Display, PictureProfileId) {
return Error::UNSUPPORTED;
}
diff --git a/services/surfaceflinger/DisplayHardware/HidlComposerHal.h b/services/surfaceflinger/DisplayHardware/HidlComposerHal.h
index 42ba9a9..cacdb8c 100644
--- a/services/surfaceflinger/DisplayHardware/HidlComposerHal.h
+++ b/services/surfaceflinger/DisplayHardware/HidlComposerHal.h
@@ -359,6 +359,8 @@
Error getMaxLayerPictureProfiles(Display, int32_t* outMaxProfiles) override;
Error setDisplayPictureProfileId(Display, PictureProfileId) override;
Error setLayerPictureProfileId(Display, Layer, PictureProfileId) override;
+ Error getLuts(Display, const std::vector<sp<GraphicBuffer>>&,
+ std::vector<aidl::android::hardware::graphics::composer3::Luts>*) override;
private:
class CommandWriter : public CommandWriterBase {
diff --git a/services/surfaceflinger/FrontEnd/LayerSnapshot.cpp b/services/surfaceflinger/FrontEnd/LayerSnapshot.cpp
index 58f6b96..367132c 100644
--- a/services/surfaceflinger/FrontEnd/LayerSnapshot.cpp
+++ b/services/surfaceflinger/FrontEnd/LayerSnapshot.cpp
@@ -418,7 +418,7 @@
}
if (forceUpdate || requested.what & layer_state_t::eAppContentPriorityChanged) {
// TODO(b/337330263): Also consider the system-determined priority of the app
- pictureProfilePriority = requested.appContentPriority;
+ pictureProfilePriority = int64_t(requested.appContentPriority) + INT_MAX;
}
if (forceUpdate || requested.what & layer_state_t::eDefaultFrameRateCompatibilityChanged) {
diff --git a/services/surfaceflinger/FrontEnd/LayerSnapshotBuilder.cpp b/services/surfaceflinger/FrontEnd/LayerSnapshotBuilder.cpp
index 4d9a9ca..022588d 100644
--- a/services/surfaceflinger/FrontEnd/LayerSnapshotBuilder.cpp
+++ b/services/surfaceflinger/FrontEnd/LayerSnapshotBuilder.cpp
@@ -262,7 +262,10 @@
snapshot.isVisible = visible;
if (FlagManager::getInstance().skip_invisible_windows_in_input()) {
- snapshot.inputInfo.setInputConfig(gui::WindowInfo::InputConfig::NOT_VISIBLE, !visible);
+ const bool visibleForInput =
+ snapshot.isVisible || (snapshot.hasInputInfo() && !snapshot.isHiddenByPolicy());
+ snapshot.inputInfo.setInputConfig(gui::WindowInfo::InputConfig::NOT_VISIBLE,
+ !visibleForInput);
} else {
// TODO(b/238781169) we are ignoring this compat for now, since we will have
// to remove any optimization based on visibility.
diff --git a/services/surfaceflinger/RegionSamplingThread.cpp b/services/surfaceflinger/RegionSamplingThread.cpp
index 21d3396..d3483b0 100644
--- a/services/surfaceflinger/RegionSamplingThread.cpp
+++ b/services/surfaceflinger/RegionSamplingThread.cpp
@@ -346,7 +346,6 @@
constexpr bool kRegionSampling = true;
constexpr bool kGrayscale = false;
constexpr bool kIsProtected = false;
- constexpr bool kAttachGainmap = false;
SurfaceFlinger::RenderAreaBuilderVariant
renderAreaBuilder(std::in_place_type<DisplayRenderAreaBuilder>, sampledBounds,
@@ -358,7 +357,7 @@
mFlinger.getSnapshotsFromMainThread(renderAreaBuilder, getLayerSnapshotsFn, layers);
FenceResult fenceResult =
mFlinger.captureScreenshot(renderAreaBuilder, buffer, kRegionSampling, kGrayscale,
- kIsProtected, kAttachGainmap, nullptr, displayState, layers)
+ kIsProtected, nullptr, displayState, layers)
.get();
if (fenceResult.ok()) {
fenceResult.value()->waitForever(LOG_TAG);
diff --git a/services/surfaceflinger/Scheduler/LayerInfo.cpp b/services/surfaceflinger/Scheduler/LayerInfo.cpp
index 6e2b943..8c22de1 100644
--- a/services/surfaceflinger/Scheduler/LayerInfo.cpp
+++ b/services/surfaceflinger/Scheduler/LayerInfo.cpp
@@ -504,7 +504,7 @@
return FrameRateCompatibility::Exact;
case ANATIVEWINDOW_FRAME_RATE_MIN:
return FrameRateCompatibility::Min;
- case ANATIVEWINDOW_FRAME_RATE_COMPATIBILITY_GTE:
+ case ANATIVEWINDOW_FRAME_RATE_COMPATIBILITY_AT_LEAST:
return FrameRateCompatibility::Gte;
case ANATIVEWINDOW_FRAME_RATE_NO_VOTE:
return FrameRateCompatibility::NoVote;
diff --git a/services/surfaceflinger/Scheduler/Scheduler.h b/services/surfaceflinger/Scheduler/Scheduler.h
index a2cdd46..3fdddac 100644
--- a/services/surfaceflinger/Scheduler/Scheduler.h
+++ b/services/surfaceflinger/Scheduler/Scheduler.h
@@ -209,7 +209,6 @@
ftl::FakeGuard guard(kMainThreadContext);
resyncToHardwareVsyncLocked(id, allowToEnable, modePtr);
}
- void resync() override EXCLUDES(mDisplayLock);
void forceNextResync() { mLastResyncTime = 0; }
// Passes a vsync sample to VsyncController. Returns true if
@@ -471,6 +470,7 @@
bool throttleVsync(TimePoint, uid_t) override;
// Get frame interval
Period getVsyncPeriod(uid_t) override EXCLUDES(mDisplayLock);
+ void resync() override EXCLUDES(mDisplayLock);
void onExpectedPresentTimePosted(TimePoint expectedPresentTime) override EXCLUDES(mDisplayLock);
std::unique_ptr<EventThread> mRenderEventThread;
diff --git a/services/surfaceflinger/SurfaceFlinger.cpp b/services/surfaceflinger/SurfaceFlinger.cpp
index e6b6f4e..e05c5bd9 100644
--- a/services/surfaceflinger/SurfaceFlinger.cpp
+++ b/services/surfaceflinger/SurfaceFlinger.cpp
@@ -37,6 +37,7 @@
#include <android/hardware/configstore/1.1/types.h>
#include <android/native_window.h>
#include <android/os/IInputFlinger.h>
+#include <android_os.h>
#include <binder/IPCThreadState.h>
#include <binder/IServiceManager.h>
#include <binder/PermissionCache.h>
@@ -126,7 +127,6 @@
#include <gui/SchedulingPolicy.h>
#include <gui/SyncScreenCaptureListener.h>
#include <ui/DisplayIdentification.h>
-#include "ActivePictureUpdater.h"
#include "BackgroundExecutor.h"
#include "Client.h"
#include "ClientCache.h"
@@ -742,7 +742,10 @@
mBootFinished = true;
FlagManager::getMutableInstance().markBootCompleted();
- ::tracing_perfetto::registerWithPerfetto();
+ if (android::os::perfetto_sdk_tracing()) {
+ ::tracing_perfetto::registerWithPerfetto();
+ }
+
mInitBootPropsFuture.wait();
mRenderEnginePrimeCacheFuture.wait();
@@ -1444,14 +1447,14 @@
return future.get();
}
-void SurfaceFlinger::finalizeDisplayModeChange(PhysicalDisplayId displayId) {
+bool SurfaceFlinger::finalizeDisplayModeChange(PhysicalDisplayId displayId) {
SFTRACE_NAME(ftl::Concat(__func__, ' ', displayId.value).c_str());
const auto pendingModeOpt = mDisplayModeController.getPendingMode(displayId);
if (!pendingModeOpt) {
// There is no pending mode change. This can happen if the active
// display changed and the mode change happened on a different display.
- return;
+ return true;
}
const auto& activeMode = pendingModeOpt->mode;
@@ -1466,8 +1469,8 @@
state.physical->activeMode = activeMode.modePtr.get();
processDisplayChangesLocked();
- // processDisplayChangesLocked will update all necessary components so we're done here.
- return;
+ // The DisplayDevice has been destroyed, so abort the commit for the now dead FrameTargeter.
+ return false;
}
mDisplayModeController.finalizeModeChange(displayId, activeMode.modePtr->getId(),
@@ -1478,6 +1481,8 @@
if (pendingModeOpt->emitEvent) {
mScheduler->onDisplayModeChanged(displayId, activeMode, /*clearContentRequirements*/ true);
}
+
+ return true;
}
void SurfaceFlinger::dropModeRequest(PhysicalDisplayId displayId) {
@@ -2659,7 +2664,10 @@
for (const auto [displayId, _] : frameTargets) {
if (mDisplayModeController.isModeSetPending(displayId)) {
- finalizeDisplayModeChange(displayId);
+ if (!finalizeDisplayModeChange(displayId)) {
+ mScheduler->scheduleFrame();
+ return false;
+ }
}
}
}
@@ -2788,12 +2796,43 @@
const auto& displays = FTL_FAKE_GUARD(mStateLock, mDisplays);
refreshArgs.outputs.reserve(displays.size());
+ // Track layer stacks of physical displays that might be added to CompositionEngine
+ // output. Layer stacks are not tracked in Display when we iterate through
+ // frameTargeters. Cross-referencing layer stacks allows us to filter out displays
+ // by ID with duplicate layer stacks before adding them to CompositionEngine output.
+ ui::DisplayMap<DisplayId, ui::LayerStack> physicalDisplayLayerStacks;
+ for (auto& [_, display] : displays) {
+ const auto id = PhysicalDisplayId::tryCast(display->getId());
+ if (id && frameTargeters.contains(*id)) {
+ physicalDisplayLayerStacks.try_emplace(*id, display->getLayerStack());
+ }
+ }
+
+ // Tracks layer stacks of displays that are added to CompositionEngine output.
+ ui::DisplayMap<ui::LayerStack, ftl::Unit> outputLayerStacks;
+ auto isOutputLayerStack = [&outputLayerStacks](DisplayId id, ui::LayerStack layerStack) {
+ if (FlagManager::getInstance().reject_dupe_layerstacks() &&
+ outputLayerStacks.contains(layerStack)) {
+ // TODO: remove log and DisplayId from params once reject_dupe_layerstacks flag is
+ // removed
+ ALOGD("Existing layer stack ID %d output to another display %" PRIu64
+ ", dropping display from outputs",
+ layerStack.id, id.value);
+ return true;
+ }
+ outputLayerStacks.try_emplace(layerStack);
+ return false;
+ };
+
// Add outputs for physical displays.
for (const auto& [id, targeter] : frameTargeters) {
ftl::FakeGuard guard(mStateLock);
if (const auto display = getCompositionDisplayLocked(id)) {
- refreshArgs.outputs.push_back(display);
+ const auto layerStack = physicalDisplayLayerStacks.get(id)->get();
+ if (!isOutputLayerStack(display->getId(), layerStack)) {
+ refreshArgs.outputs.push_back(display);
+ }
}
refreshArgs.frameTargets.try_emplace(id, &targeter->target());
@@ -2810,7 +2849,9 @@
if (!refreshRate.isValid() ||
mScheduler->isVsyncInPhase(pacesetterTarget.frameBeginTime(), refreshRate)) {
- refreshArgs.outputs.push_back(display->getCompositionDisplay());
+ if (!isOutputLayerStack(display->getId(), display->getLayerStack())) {
+ refreshArgs.outputs.push_back(display->getCompositionDisplay());
+ }
}
}
}
@@ -2914,7 +2955,7 @@
layer->setWasClientComposed(compositionResult.lastClientCompositionFence);
}
if (com_android_graphics_libgui_flags_apply_picture_profiles()) {
- mActivePictureUpdater.onLayerComposed(*layer, *layerFE, compositionResult);
+ mActivePictureTracker.onLayerComposed(*layer, *layerFE, compositionResult);
}
}
@@ -3226,8 +3267,8 @@
std::vector<std::pair<std::shared_ptr<compositionengine::Display>, sp<HdrLayerInfoReporter>>>
hdrInfoListeners;
bool haveNewHdrInfoListeners = false;
- sp<gui::IActivePictureListener> activePictureListener;
- bool haveNewActivePictureListener = false;
+ ActivePictureTracker::Listeners activePictureListenersToAdd;
+ ActivePictureTracker::Listeners activePictureListenersToRemove;
{
Mutex::Autolock lock(mStateLock);
if (mFpsReporter) {
@@ -3249,9 +3290,8 @@
haveNewHdrInfoListeners = mAddingHDRLayerInfoListener; // grab this with state lock
mAddingHDRLayerInfoListener = false;
- activePictureListener = mActivePictureListener;
- haveNewActivePictureListener = mHaveNewActivePictureListener;
- mHaveNewActivePictureListener = false;
+ std::swap(activePictureListenersToAdd, mActivePictureListenersToAdd);
+ std::swap(activePictureListenersToRemove, mActivePictureListenersToRemove);
}
if (haveNewHdrInfoListeners || mHdrLayerInfoChanged) {
@@ -3315,14 +3355,10 @@
mHdrLayerInfoChanged = false;
if (com_android_graphics_libgui_flags_apply_picture_profiles()) {
- // Track, update and notify changes to active pictures - layers that are undergoing picture
- // processing
- if (mActivePictureUpdater.updateAndHasChanged() || haveNewActivePictureListener) {
- if (activePictureListener) {
- activePictureListener->onActivePicturesChanged(
- mActivePictureUpdater.getActivePictures());
- }
- }
+ // Track, update and notify changes to active pictures - layers that are undergoing
+ // picture processing
+ mActivePictureTracker.updateAndNotifyListeners(activePictureListenersToAdd,
+ activePictureListenersToRemove);
}
mTransactionCallbackInvoker.sendCallbacks(false /* onCommitOnly */);
@@ -4569,7 +4605,6 @@
SFTRACE_INT("mTransactionFlags", transactionFlags);
if (const bool scheduled = transactionFlags & mask; !scheduled) {
- mScheduler->resync();
scheduleCommit(frameHint);
} else if (frameHint == FrameHint::kActive) {
// Even if the next frame is already scheduled, we should reset the idle timer
@@ -7152,8 +7187,7 @@
displayWeak, options),
getLayerSnapshotsFn, reqSize,
static_cast<ui::PixelFormat>(captureArgs.pixelFormat),
- captureArgs.allowProtected, captureArgs.grayscale,
- captureArgs.attachGainmap, captureListener);
+ captureArgs.allowProtected, captureArgs.grayscale, captureListener);
}
void SurfaceFlinger::captureDisplay(DisplayId displayId, const CaptureArgs& args,
@@ -7210,7 +7244,7 @@
static_cast<ui::Dataspace>(args.dataspace),
displayWeak, options),
getLayerSnapshotsFn, size, static_cast<ui::PixelFormat>(args.pixelFormat),
- kAllowProtected, kGrayscale, args.attachGainmap, captureListener);
+ kAllowProtected, kGrayscale, captureListener);
}
ScreenCaptureResults SurfaceFlinger::captureLayersSync(const LayerCaptureArgs& args) {
@@ -7321,8 +7355,7 @@
options),
getLayerSnapshotsFn, reqSize,
static_cast<ui::PixelFormat>(captureArgs.pixelFormat),
- captureArgs.allowProtected, captureArgs.grayscale,
- captureArgs.attachGainmap, captureListener);
+ captureArgs.allowProtected, captureArgs.grayscale, captureListener);
}
// Creates a Future release fence for a layer and keeps track of it in a list to
@@ -7380,7 +7413,7 @@
void SurfaceFlinger::captureScreenCommon(RenderAreaBuilderVariant renderAreaBuilder,
GetLayerSnapshotsFunction getLayerSnapshotsFn,
ui::Size bufferSize, ui::PixelFormat reqPixelFormat,
- bool allowProtected, bool grayscale, bool attachGainmap,
+ bool allowProtected, bool grayscale,
const sp<IScreenCaptureListener>& captureListener) {
SFTRACE_CALL();
@@ -7395,6 +7428,10 @@
std::vector<std::pair<Layer*, sp<LayerFE>>> layers;
auto displayState = getSnapshotsFromMainThread(renderAreaBuilder, getLayerSnapshotsFn, layers);
+ const bool hasHdrLayer = std::any_of(layers.cbegin(), layers.cend(), [this](const auto& layer) {
+ return isHdrLayer(*(layer.second->mSnapshot.get()));
+ });
+
const bool supportsProtected = getRenderEngine().supportsProtectedContent();
bool hasProtectedLayer = false;
if (allowProtected && supportsProtected) {
@@ -7423,9 +7460,51 @@
renderengine::impl::ExternalTexture>(buffer, getRenderEngine(),
renderengine::impl::ExternalTexture::Usage::
WRITEABLE);
- auto futureFence =
- captureScreenshot(renderAreaBuilder, texture, false /* regionSampling */, grayscale,
- isProtected, attachGainmap, captureListener, displayState, layers);
+
+ std::shared_ptr<renderengine::impl::ExternalTexture> hdrTexture;
+ std::shared_ptr<renderengine::impl::ExternalTexture> gainmapTexture;
+
+ bool hintForSeamless = std::visit(
+ [](auto&& arg) {
+ return arg.options.test(RenderArea::Options::HINT_FOR_SEAMLESS_TRANSITION);
+ },
+ renderAreaBuilder);
+ if (hasHdrLayer && !hintForSeamless && FlagManager::getInstance().true_hdr_screenshots()) {
+ const auto hdrBuffer =
+ getFactory().createGraphicBuffer(buffer->getWidth(), buffer->getHeight(),
+ HAL_PIXEL_FORMAT_RGBA_FP16, 1 /* layerCount */,
+ buffer->getUsage(), "screenshot-hdr");
+ const auto gainmapBuffer =
+ getFactory().createGraphicBuffer(buffer->getWidth(), buffer->getHeight(),
+ buffer->getPixelFormat(), 1 /* layerCount */,
+ buffer->getUsage(), "screenshot-gainmap");
+
+ const status_t hdrBufferStatus = hdrBuffer->initCheck();
+ const status_t gainmapBufferStatus = gainmapBuffer->initCheck();
+
+ if (hdrBufferStatus != OK || gainmapBufferStatus != -OK) {
+ if (hdrBufferStatus != OK) {
+ ALOGW("%s: Buffer failed to allocate for hdr: %d. Screenshoting SDR instead.",
+ __func__, hdrBufferStatus);
+ } else {
+ ALOGW("%s: Buffer failed to allocate for gainmap: %d. Screenshoting SDR instead.",
+ __func__, gainmapBufferStatus);
+ }
+ } else {
+ hdrTexture = std::make_shared<
+ renderengine::impl::ExternalTexture>(hdrBuffer, getRenderEngine(),
+ renderengine::impl::ExternalTexture::
+ Usage::WRITEABLE);
+ gainmapTexture = std::make_shared<
+ renderengine::impl::ExternalTexture>(gainmapBuffer, getRenderEngine(),
+ renderengine::impl::ExternalTexture::
+ Usage::WRITEABLE);
+ }
+ }
+
+ auto futureFence = captureScreenshot(renderAreaBuilder, texture, false /* regionSampling */,
+ grayscale, isProtected, captureListener, displayState,
+ layers, hdrTexture, gainmapTexture);
futureFence.get();
}
@@ -7468,10 +7547,11 @@
ftl::SharedFuture<FenceResult> SurfaceFlinger::captureScreenshot(
const RenderAreaBuilderVariant& renderAreaBuilder,
const std::shared_ptr<renderengine::ExternalTexture>& buffer, bool regionSampling,
- bool grayscale, bool isProtected, bool attachGainmap,
- const sp<IScreenCaptureListener>& captureListener,
- std::optional<OutputCompositionState>& displayState,
- std::vector<std::pair<Layer*, sp<LayerFE>>>& layers) {
+ bool grayscale, bool isProtected, const sp<IScreenCaptureListener>& captureListener,
+ const std::optional<OutputCompositionState>& displayState,
+ const std::vector<std::pair<Layer*, sp<LayerFE>>>& layers,
+ const std::shared_ptr<renderengine::ExternalTexture>& hdrBuffer,
+ const std::shared_ptr<renderengine::ExternalTexture>& gainmapBuffer) {
SFTRACE_CALL();
ScreenCaptureResults captureResults;
@@ -7487,74 +7567,40 @@
}
return ftl::yield<FenceResult>(base::unexpected(NO_ERROR)).share();
}
+
float displayBrightnessNits = displayState.value().displayBrightnessNits;
float sdrWhitePointNits = displayState.value().sdrWhitePointNits;
- ftl::SharedFuture<FenceResult> renderFuture =
- renderScreenImpl(renderArea.get(), buffer, regionSampling, grayscale, isProtected,
- captureResults, displayState, layers);
+ ftl::SharedFuture<FenceResult> renderFuture;
- if (captureResults.capturedHdrLayers && attachGainmap &&
- FlagManager::getInstance().true_hdr_screenshots()) {
- sp<GraphicBuffer> hdrBuffer =
- getFactory().createGraphicBuffer(buffer->getWidth(), buffer->getHeight(),
- HAL_PIXEL_FORMAT_RGBA_FP16, 1 /* layerCount */,
- buffer->getUsage(), "screenshot-hdr");
- sp<GraphicBuffer> gainmapBuffer =
- getFactory().createGraphicBuffer(buffer->getWidth(), buffer->getHeight(),
- buffer->getPixelFormat(), 1 /* layerCount */,
- buffer->getUsage(), "screenshot-gainmap");
+ if (hdrBuffer && gainmapBuffer) {
+ ftl::SharedFuture<FenceResult> hdrRenderFuture =
+ renderScreenImpl(renderArea.get(), hdrBuffer, regionSampling, grayscale,
+ isProtected, captureResults, displayState, layers);
+ captureResults.buffer = buffer->getBuffer();
+ captureResults.optionalGainMap = gainmapBuffer->getBuffer();
- const status_t bufferStatus = hdrBuffer->initCheck();
- const status_t gainmapBufferStatus = gainmapBuffer->initCheck();
+ renderFuture =
+ ftl::Future(std::move(hdrRenderFuture))
+ .then([&, displayBrightnessNits, sdrWhitePointNits,
+ dataspace = captureResults.capturedDataspace, buffer, hdrBuffer,
+ gainmapBuffer](FenceResult fenceResult) -> FenceResult {
+ if (!fenceResult.ok()) {
+ return fenceResult;
+ }
- if (bufferStatus != OK) {
- ALOGW("%s: Buffer failed to allocate for hdr: %d. Screenshoting SDR instead.", __func__,
- bufferStatus);
- } else if (gainmapBufferStatus != OK) {
- ALOGW("%s: Buffer failed to allocate for gainmap: %d. Screenshoting SDR instead.",
- __func__, gainmapBufferStatus);
- } else {
- captureResults.optionalGainMap = gainmapBuffer;
- const auto hdrTexture = std::make_shared<
- renderengine::impl::ExternalTexture>(hdrBuffer, getRenderEngine(),
- renderengine::impl::ExternalTexture::
- Usage::WRITEABLE);
- const auto gainmapTexture = std::make_shared<
- renderengine::impl::ExternalTexture>(gainmapBuffer, getRenderEngine(),
- renderengine::impl::ExternalTexture::
- Usage::WRITEABLE);
- ScreenCaptureResults unusedResults;
- ftl::SharedFuture<FenceResult> hdrRenderFuture =
- renderScreenImpl(renderArea.get(), hdrTexture, regionSampling, grayscale,
- isProtected, unusedResults, displayState, layers);
-
- renderFuture =
- ftl::Future(std::move(renderFuture))
- .then([&, hdrRenderFuture = std::move(hdrRenderFuture),
- displayBrightnessNits, sdrWhitePointNits,
- dataspace = captureResults.capturedDataspace, buffer, hdrTexture,
- gainmapTexture](FenceResult fenceResult) -> FenceResult {
- if (!fenceResult.ok()) {
- return fenceResult;
- }
-
- auto hdrFenceResult = hdrRenderFuture.get();
-
- if (!hdrFenceResult.ok()) {
- return hdrFenceResult;
- }
-
- return getRenderEngine()
- .drawGainmap(buffer, fenceResult.value()->get(), hdrTexture,
- hdrFenceResult.value()->get(),
- displayBrightnessNits / sdrWhitePointNits,
- static_cast<ui::Dataspace>(dataspace),
- gainmapTexture)
- .get();
- })
- .share();
- };
+ return getRenderEngine()
+ .tonemapAndDrawGainmap(hdrBuffer, fenceResult.value()->get(),
+ displayBrightnessNits /
+ sdrWhitePointNits,
+ static_cast<ui::Dataspace>(dataspace),
+ buffer, gainmapBuffer)
+ .get();
+ })
+ .share();
+ } else {
+ renderFuture = renderScreenImpl(renderArea.get(), buffer, regionSampling, grayscale,
+ isProtected, captureResults, displayState, layers);
}
if (captureListener) {
@@ -7576,8 +7622,8 @@
ftl::SharedFuture<FenceResult> SurfaceFlinger::renderScreenImpl(
const RenderArea* renderArea, const std::shared_ptr<renderengine::ExternalTexture>& buffer,
bool regionSampling, bool grayscale, bool isProtected, ScreenCaptureResults& captureResults,
- std::optional<OutputCompositionState>& displayState,
- std::vector<std::pair<Layer*, sp<LayerFE>>>& layers) {
+ const std::optional<OutputCompositionState>& displayState,
+ const std::vector<std::pair<Layer*, sp<LayerFE>>>& layers) {
SFTRACE_CALL();
for (auto& [_, layerFE] : layers) {
@@ -7645,9 +7691,8 @@
}
auto present = [this, buffer = capturedBuffer, dataspace = captureResults.capturedDataspace,
- sdrWhitePointNits, displayBrightnessNits, grayscale, isProtected,
- layers = std::move(layers), layerStack, regionSampling,
- renderArea = std::move(renderArea), renderIntent,
+ sdrWhitePointNits, displayBrightnessNits, grayscale, isProtected, layers,
+ layerStack, regionSampling, renderArea = std::move(renderArea), renderIntent,
enableLocalTonemapping]() -> FenceResult {
std::unique_ptr<compositionengine::CompositionEngine> compositionEngine =
mFactory.createCompositionEngine();
@@ -8178,12 +8223,20 @@
}));
}
-void SurfaceFlinger::setActivePictureListener(const sp<gui::IActivePictureListener>& listener) {
- if (com_android_graphics_libgui_flags_apply_picture_profiles()) {
- Mutex::Autolock lock(mStateLock);
- mActivePictureListener = listener;
- mHaveNewActivePictureListener = listener != nullptr;
- }
+void SurfaceFlinger::addActivePictureListener(const sp<gui::IActivePictureListener>& listener) {
+ Mutex::Autolock lock(mStateLock);
+ std::erase_if(mActivePictureListenersToRemove, [listener](const auto& otherListener) {
+ return IInterface::asBinder(listener) == IInterface::asBinder(otherListener);
+ });
+ mActivePictureListenersToAdd.push_back(listener);
+}
+
+void SurfaceFlinger::removeActivePictureListener(const sp<gui::IActivePictureListener>& listener) {
+ Mutex::Autolock lock(mStateLock);
+ std::erase_if(mActivePictureListenersToAdd, [listener](const auto& otherListener) {
+ return IInterface::asBinder(listener) == IInterface::asBinder(otherListener);
+ });
+ mActivePictureListenersToRemove.push_back(listener);
}
std::shared_ptr<renderengine::ExternalTexture> SurfaceFlinger::getExternalTextureFromBufferData(
@@ -9136,11 +9189,20 @@
return binderStatusFromStatusT(status);
}
-binder::Status SurfaceComposerAIDL::setActivePictureListener(
+binder::Status SurfaceComposerAIDL::addActivePictureListener(
const sp<gui::IActivePictureListener>& listener) {
status_t status = checkObservePictureProfilesPermission();
if (status == OK) {
- mFlinger->setActivePictureListener(listener);
+ mFlinger->addActivePictureListener(listener);
+ }
+ return binderStatusFromStatusT(status);
+}
+
+binder::Status SurfaceComposerAIDL::removeActivePictureListener(
+ const sp<gui::IActivePictureListener>& listener) {
+ status_t status = checkObservePictureProfilesPermission();
+ if (status == OK) {
+ mFlinger->removeActivePictureListener(listener);
}
return binderStatusFromStatusT(status);
}
diff --git a/services/surfaceflinger/SurfaceFlinger.h b/services/surfaceflinger/SurfaceFlinger.h
index 1e2c087..c85c084 100644
--- a/services/surfaceflinger/SurfaceFlinger.h
+++ b/services/surfaceflinger/SurfaceFlinger.h
@@ -69,7 +69,7 @@
#include <ui/FenceResult.h>
#include <common/FlagManager.h>
-#include "ActivePictureUpdater.h"
+#include "ActivePictureTracker.h"
#include "BackgroundExecutor.h"
#include "Display/DisplayModeController.h"
#include "Display/PhysicalDisplay.h"
@@ -667,7 +667,9 @@
void updateHdcpLevels(hal::HWDisplayId hwcDisplayId, int32_t connectedLevel, int32_t maxLevel);
- void setActivePictureListener(const sp<gui::IActivePictureListener>& listener);
+ void addActivePictureListener(const sp<gui::IActivePictureListener>& listener);
+
+ void removeActivePictureListener(const sp<gui::IActivePictureListener>& listener);
// IBinder::DeathRecipient overrides:
void binderDied(const wp<IBinder>& who) override;
@@ -730,7 +732,11 @@
Fps maxFps);
void initiateDisplayModeChanges() REQUIRES(kMainThreadContext) REQUIRES(mStateLock);
- void finalizeDisplayModeChange(PhysicalDisplayId) REQUIRES(kMainThreadContext)
+
+ // Returns whether the commit stage should proceed. The return value is ignored when finalizing
+ // immediate mode changes, which happen toward the end of the commit stage.
+ // TODO: b/355427258 - Remove the return value once the `synced_resolution_switch` flag is live.
+ bool finalizeDisplayModeChange(PhysicalDisplayId) REQUIRES(kMainThreadContext)
REQUIRES(mStateLock);
void dropModeRequest(PhysicalDisplayId) REQUIRES(kMainThreadContext);
@@ -872,7 +878,7 @@
void captureScreenCommon(RenderAreaBuilderVariant, GetLayerSnapshotsFunction,
ui::Size bufferSize, ui::PixelFormat, bool allowProtected,
- bool grayscale, bool attachGainmap, const sp<IScreenCaptureListener>&);
+ bool grayscale, const sp<IScreenCaptureListener>&);
std::optional<OutputCompositionState> getDisplayStateFromRenderAreaBuilder(
RenderAreaBuilderVariant& renderAreaBuilder) REQUIRES(kMainThreadContext);
@@ -880,16 +886,17 @@
ftl::SharedFuture<FenceResult> captureScreenshot(
const RenderAreaBuilderVariant& renderAreaBuilder,
const std::shared_ptr<renderengine::ExternalTexture>& buffer, bool regionSampling,
- bool grayscale, bool isProtected, bool attachGainmap,
- const sp<IScreenCaptureListener>& captureListener,
- std::optional<OutputCompositionState>& displayState,
- std::vector<std::pair<Layer*, sp<LayerFE>>>& layers);
+ bool grayscale, bool isProtected, const sp<IScreenCaptureListener>& captureListener,
+ const std::optional<OutputCompositionState>& displayState,
+ const std::vector<std::pair<Layer*, sp<LayerFE>>>& layers,
+ const std::shared_ptr<renderengine::ExternalTexture>& hdrBuffer = nullptr,
+ const std::shared_ptr<renderengine::ExternalTexture>& gainmapBuffer = nullptr);
ftl::SharedFuture<FenceResult> renderScreenImpl(
const RenderArea*, const std::shared_ptr<renderengine::ExternalTexture>&,
bool regionSampling, bool grayscale, bool isProtected, ScreenCaptureResults&,
- std::optional<OutputCompositionState>& displayState,
- std::vector<std::pair<Layer*, sp<LayerFE>>>& layers);
+ const std::optional<OutputCompositionState>& displayState,
+ const std::vector<std::pair<Layer*, sp<LayerFE>>>& layers);
void readPersistentProperties();
@@ -1402,9 +1409,9 @@
std::unordered_map<DisplayId, sp<HdrLayerInfoReporter>> mHdrLayerInfoListeners
GUARDED_BY(mStateLock);
- sp<gui::IActivePictureListener> mActivePictureListener GUARDED_BY(mStateLock);
- bool mHaveNewActivePictureListener GUARDED_BY(mStateLock);
- ActivePictureUpdater mActivePictureUpdater GUARDED_BY(kMainThreadContext);
+ ActivePictureTracker mActivePictureTracker GUARDED_BY(kMainThreadContext);
+ ActivePictureTracker::Listeners mActivePictureListenersToAdd GUARDED_BY(mStateLock);
+ ActivePictureTracker::Listeners mActivePictureListenersToRemove GUARDED_BY(mStateLock);
std::atomic<ui::Transform::RotationFlags> mActiveDisplayTransformHint;
@@ -1641,8 +1648,8 @@
binder::Status flushJankData(int32_t layerId) override;
binder::Status removeJankListener(int32_t layerId, const sp<gui::IJankListener>& listener,
int64_t afterVsync) override;
- binder::Status setActivePictureListener(const sp<gui::IActivePictureListener>& listener);
- binder::Status clearActivePictureListener();
+ binder::Status addActivePictureListener(const sp<gui::IActivePictureListener>& listener);
+ binder::Status removeActivePictureListener(const sp<gui::IActivePictureListener>& listener);
private:
static const constexpr bool kUsePermissionCache = true;
diff --git a/services/surfaceflinger/common/FlagManager.cpp b/services/surfaceflinger/common/FlagManager.cpp
index 5e78426..e80cd78 100644
--- a/services/surfaceflinger/common/FlagManager.cpp
+++ b/services/surfaceflinger/common/FlagManager.cpp
@@ -112,60 +112,63 @@
DUMP_LEGACY_SERVER_FLAG(use_skia_tracing);
/// Trunk stable server (R/W) flags ///
- DUMP_ACONFIG_FLAG(refresh_rate_overlay_on_external_display);
DUMP_ACONFIG_FLAG(adpf_gpu_sf);
DUMP_ACONFIG_FLAG(adpf_native_session_manager);
DUMP_ACONFIG_FLAG(adpf_use_fmq_channel);
DUMP_ACONFIG_FLAG(graphite_renderengine_preview_rollout);
+ DUMP_ACONFIG_FLAG(refresh_rate_overlay_on_external_display);
/// Trunk stable readonly flags ///
- DUMP_ACONFIG_FLAG(adpf_fmq_sf);
- DUMP_ACONFIG_FLAG(arr_setframerate_gte_enum);
- DUMP_ACONFIG_FLAG(connected_display);
- DUMP_ACONFIG_FLAG(enable_small_area_detection);
- DUMP_ACONFIG_FLAG(stable_edid_ids);
- DUMP_ACONFIG_FLAG(frame_rate_category_mrr);
- DUMP_ACONFIG_FLAG(misc1);
- DUMP_ACONFIG_FLAG(vrr_config);
- DUMP_ACONFIG_FLAG(hdcp_level_hal);
- DUMP_ACONFIG_FLAG(multithreaded_present);
+ /// IMPORTANT - please keep alphabetize to reduce merge conflicts
DUMP_ACONFIG_FLAG(add_sf_skipped_frames_to_trace);
- DUMP_ACONFIG_FLAG(use_known_refresh_rate_for_fps_consistency);
- DUMP_ACONFIG_FLAG(cache_when_source_crop_layer_only_moved);
- DUMP_ACONFIG_FLAG(enable_fro_dependent_features);
- DUMP_ACONFIG_FLAG(display_protected);
- DUMP_ACONFIG_FLAG(fp16_client_target);
- DUMP_ACONFIG_FLAG(game_default_frame_rate);
- DUMP_ACONFIG_FLAG(enable_layer_command_batching);
- DUMP_ACONFIG_FLAG(vulkan_renderengine);
- DUMP_ACONFIG_FLAG(renderable_buffer_usage);
- DUMP_ACONFIG_FLAG(vrr_bugfix_24q4);
- DUMP_ACONFIG_FLAG(vrr_bugfix_dropped_frame);
- DUMP_ACONFIG_FLAG(restore_blur_step);
- DUMP_ACONFIG_FLAG(dont_skip_on_early_ro);
- DUMP_ACONFIG_FLAG(no_vsyncs_on_screen_off);
- DUMP_ACONFIG_FLAG(protected_if_client);
- DUMP_ACONFIG_FLAG(idle_screen_refresh_rate_timeout);
- DUMP_ACONFIG_FLAG(graphite_renderengine);
- DUMP_ACONFIG_FLAG(filter_frames_before_trace_starts);
- DUMP_ACONFIG_FLAG(latch_unsignaled_with_auto_refresh_changed);
- DUMP_ACONFIG_FLAG(deprecate_vsync_sf);
+ DUMP_ACONFIG_FLAG(adpf_fmq_sf);
DUMP_ACONFIG_FLAG(allow_n_vsyncs_in_targeter);
- DUMP_ACONFIG_FLAG(detached_mirror);
+ DUMP_ACONFIG_FLAG(arr_setframerate_gte_enum);
+ DUMP_ACONFIG_FLAG(begone_bright_hlg);
+ DUMP_ACONFIG_FLAG(cache_when_source_crop_layer_only_moved);
DUMP_ACONFIG_FLAG(commit_not_composited);
+ DUMP_ACONFIG_FLAG(connected_display);
+ DUMP_ACONFIG_FLAG(connected_display_hdr);
DUMP_ACONFIG_FLAG(correct_dpi_with_display_size);
- DUMP_ACONFIG_FLAG(local_tonemap_screenshots);
- DUMP_ACONFIG_FLAG(override_trusted_overlay);
+ DUMP_ACONFIG_FLAG(deprecate_frame_tracker);
+ DUMP_ACONFIG_FLAG(deprecate_vsync_sf);
+ DUMP_ACONFIG_FLAG(detached_mirror);
+ DUMP_ACONFIG_FLAG(display_config_error_hal);
+ DUMP_ACONFIG_FLAG(display_protected);
+ DUMP_ACONFIG_FLAG(dont_skip_on_early_ro);
+ DUMP_ACONFIG_FLAG(enable_fro_dependent_features);
+ DUMP_ACONFIG_FLAG(enable_layer_command_batching);
+ DUMP_ACONFIG_FLAG(enable_small_area_detection);
+ DUMP_ACONFIG_FLAG(filter_frames_before_trace_starts);
DUMP_ACONFIG_FLAG(flush_buffer_slots_to_uncache);
DUMP_ACONFIG_FLAG(force_compile_graphite_renderengine);
+ DUMP_ACONFIG_FLAG(fp16_client_target);
+ DUMP_ACONFIG_FLAG(frame_rate_category_mrr);
+ DUMP_ACONFIG_FLAG(game_default_frame_rate);
+ DUMP_ACONFIG_FLAG(graphite_renderengine);
+ DUMP_ACONFIG_FLAG(hdcp_level_hal);
+ DUMP_ACONFIG_FLAG(idle_screen_refresh_rate_timeout);
+ DUMP_ACONFIG_FLAG(latch_unsignaled_with_auto_refresh_changed);
+ DUMP_ACONFIG_FLAG(local_tonemap_screenshots);
+ DUMP_ACONFIG_FLAG(misc1);
+ DUMP_ACONFIG_FLAG(multithreaded_present);
+ DUMP_ACONFIG_FLAG(no_vsyncs_on_screen_off);
+ DUMP_ACONFIG_FLAG(override_trusted_overlay);
+ DUMP_ACONFIG_FLAG(protected_if_client);
+ DUMP_ACONFIG_FLAG(reject_dupe_layerstacks);
+ DUMP_ACONFIG_FLAG(renderable_buffer_usage);
+ DUMP_ACONFIG_FLAG(restore_blur_step);
+ DUMP_ACONFIG_FLAG(skip_invisible_windows_in_input);
+ DUMP_ACONFIG_FLAG(stable_edid_ids);
DUMP_ACONFIG_FLAG(trace_frame_rate_override);
DUMP_ACONFIG_FLAG(true_hdr_screenshots);
- DUMP_ACONFIG_FLAG(display_config_error_hal);
- DUMP_ACONFIG_FLAG(connected_display_hdr);
- DUMP_ACONFIG_FLAG(deprecate_frame_tracker);
- DUMP_ACONFIG_FLAG(skip_invisible_windows_in_input);
- DUMP_ACONFIG_FLAG(begone_bright_hlg);
+ DUMP_ACONFIG_FLAG(use_known_refresh_rate_for_fps_consistency);
+ DUMP_ACONFIG_FLAG(vrr_bugfix_24q4);
+ DUMP_ACONFIG_FLAG(vrr_bugfix_dropped_frame);
+ DUMP_ACONFIG_FLAG(vrr_config);
+ DUMP_ACONFIG_FLAG(vulkan_renderengine);
DUMP_ACONFIG_FLAG(window_blur_kawase2);
+ /// IMPORTANT - please keep alphabetize to reduce merge conflicts
#undef DUMP_ACONFIG_FLAG
#undef DUMP_LEGACY_SERVER_FLAG
@@ -266,6 +269,7 @@
FLAG_MANAGER_ACONFIG_FLAG(skip_invisible_windows_in_input, "");
FLAG_MANAGER_ACONFIG_FLAG(begone_bright_hlg, "debug.sf.begone_bright_hlg");
FLAG_MANAGER_ACONFIG_FLAG(window_blur_kawase2, "");
+FLAG_MANAGER_ACONFIG_FLAG(reject_dupe_layerstacks, "");
/// Trunk stable server (R/W) flags ///
FLAG_MANAGER_ACONFIG_FLAG(refresh_rate_overlay_on_external_display, "")
diff --git a/services/surfaceflinger/common/include/common/FlagManager.h b/services/surfaceflinger/common/include/common/FlagManager.h
index d8887f5..c7f97b4 100644
--- a/services/surfaceflinger/common/include/common/FlagManager.h
+++ b/services/surfaceflinger/common/include/common/FlagManager.h
@@ -48,62 +48,65 @@
bool use_skia_tracing() const;
/// Trunk stable server (R/W) flags ///
- bool refresh_rate_overlay_on_external_display() const;
bool adpf_gpu_sf() const;
- bool adpf_use_fmq_channel() const;
bool adpf_native_session_manager() const;
+ bool adpf_use_fmq_channel() const;
bool adpf_use_fmq_channel_fixed() const;
bool graphite_renderengine_preview_rollout() const;
+ bool refresh_rate_overlay_on_external_display() const;
/// Trunk stable readonly flags ///
- bool arr_setframerate_gte_enum() const;
- bool adpf_fmq_sf() const;
- bool connected_display() const;
- bool frame_rate_category_mrr() const;
- bool enable_small_area_detection() const;
- bool stable_edid_ids() const;
- bool misc1() const;
- bool vrr_config() const;
- bool hdcp_level_hal() const;
- bool multithreaded_present() const;
+ /// IMPORTANT - please keep alphabetize to reduce merge conflicts
bool add_sf_skipped_frames_to_trace() const;
- bool use_known_refresh_rate_for_fps_consistency() const;
- bool cache_when_source_crop_layer_only_moved() const;
- bool enable_fro_dependent_features() const;
- bool display_protected() const;
- bool fp16_client_target() const;
- bool game_default_frame_rate() const;
- bool enable_layer_command_batching() const;
- bool vulkan_renderengine() const;
- bool vrr_bugfix_24q4() const;
- bool vrr_bugfix_dropped_frame() const;
- bool renderable_buffer_usage() const;
- bool restore_blur_step() const;
- bool dont_skip_on_early_ro() const;
- bool no_vsyncs_on_screen_off() const;
- bool protected_if_client() const;
- bool idle_screen_refresh_rate_timeout() const;
- bool graphite_renderengine() const;
- bool filter_frames_before_trace_starts() const;
- bool latch_unsignaled_with_auto_refresh_changed() const;
- bool deprecate_vsync_sf() const;
+ bool adpf_fmq_sf() const;
bool allow_n_vsyncs_in_targeter() const;
- bool detached_mirror() const;
+ bool arr_setframerate_gte_enum() const;
+ bool begone_bright_hlg() const;
+ bool cache_when_source_crop_layer_only_moved() const;
bool commit_not_composited() const;
+ bool connected_display() const;
+ bool connected_display_hdr() const;
bool correct_dpi_with_display_size() const;
- bool local_tonemap_screenshots() const;
- bool override_trusted_overlay() const;
+ bool deprecate_frame_tracker() const;
+ bool deprecate_vsync_sf() const;
+ bool detached_mirror() const;
+ bool display_config_error_hal() const;
+ bool display_protected() const;
+ bool dont_skip_on_early_ro() const;
+ bool enable_fro_dependent_features() const;
+ bool enable_layer_command_batching() const;
+ bool enable_small_area_detection() const;
+ bool filter_frames_before_trace_starts() const;
bool flush_buffer_slots_to_uncache() const;
bool force_compile_graphite_renderengine() const;
+ bool fp16_client_target() const;
+ bool frame_rate_category_mrr() const;
+ bool game_default_frame_rate() const;
+ bool graphite_renderengine() const;
+ bool hdcp_level_hal() const;
+ bool idle_screen_refresh_rate_timeout() const;
+ bool latch_unsignaled_with_auto_refresh_changed() const;
+ bool local_tonemap_screenshots() const;
+ bool luts_api() const;
+ bool misc1() const;
+ bool multithreaded_present() const;
+ bool no_vsyncs_on_screen_off() const;
+ bool override_trusted_overlay() const;
+ bool protected_if_client() const;
+ bool reject_dupe_layerstacks() const;
+ bool renderable_buffer_usage() const;
+ bool restore_blur_step() const;
+ bool skip_invisible_windows_in_input() const;
+ bool stable_edid_ids() const;
bool trace_frame_rate_override() const;
bool true_hdr_screenshots() const;
- bool display_config_error_hal() const;
- bool connected_display_hdr() const;
- bool deprecate_frame_tracker() const;
- bool skip_invisible_windows_in_input() const;
- bool begone_bright_hlg() const;
- bool luts_api() const;
+ bool use_known_refresh_rate_for_fps_consistency() const;
+ bool vrr_bugfix_24q4() const;
+ bool vrr_bugfix_dropped_frame() const;
+ bool vrr_config() const;
+ bool vulkan_renderengine() const;
bool window_blur_kawase2() const;
+ /// IMPORTANT - please keep alphabetize to reduce merge conflicts
protected:
// overridden for unit tests
diff --git a/services/surfaceflinger/surfaceflinger_flags_new.aconfig b/services/surfaceflinger/surfaceflinger_flags_new.aconfig
index bdd826d..b28d269 100644
--- a/services/surfaceflinger/surfaceflinger_flags_new.aconfig
+++ b/services/surfaceflinger/surfaceflinger_flags_new.aconfig
@@ -217,6 +217,17 @@
} # no_vsyncs_on_screen_off
flag {
+ name: "reject_dupe_layerstacks"
+ namespace: "window_surfaces"
+ description: "Reject duplicate layerstacks for displays"
+ bug: "370358572"
+ is_fixed_read_only: true
+ metadata {
+ purpose: PURPOSE_BUGFIX
+ }
+ } # reject_dupe_layerstacks
+
+flag {
name: "single_hop_screenshot"
namespace: "window_surfaces"
description: "Only access SF main thread once during a screenshot"
diff --git a/services/surfaceflinger/tests/Android.bp b/services/surfaceflinger/tests/Android.bp
index 4d5c0fd..b5f7a74 100644
--- a/services/surfaceflinger/tests/Android.bp
+++ b/services/surfaceflinger/tests/Android.bp
@@ -52,7 +52,7 @@
"LayerTypeTransaction_test.cpp",
"LayerUpdate_test.cpp",
"MirrorLayer_test.cpp",
- "MultiDisplayLayerBounds_test.cpp",
+ "MultiDisplay_test.cpp",
"RefreshRateOverlay_test.cpp",
"RelativeZ_test.cpp",
"ReleaseBufferCallback_test.cpp",
diff --git a/services/surfaceflinger/tests/MultiDisplayLayerBounds_test.cpp b/services/surfaceflinger/tests/MultiDisplay_test.cpp
similarity index 76%
rename from services/surfaceflinger/tests/MultiDisplayLayerBounds_test.cpp
rename to services/surfaceflinger/tests/MultiDisplay_test.cpp
index 65add63..54bb253 100644
--- a/services/surfaceflinger/tests/MultiDisplayLayerBounds_test.cpp
+++ b/services/surfaceflinger/tests/MultiDisplay_test.cpp
@@ -33,7 +33,7 @@
::testing::Environment* const binderEnv =
::testing::AddGlobalTestEnvironment(new BinderEnvironment());
-class MultiDisplayLayerBoundsTest : public LayerTransactionTest {
+class MultiDisplayTest : public LayerTransactionTest {
protected:
virtual void SetUp() {
LayerTransactionTest::SetUp();
@@ -105,7 +105,7 @@
Color mExpectedColor = {63, 63, 195, 255};
};
-TEST_F(MultiDisplayLayerBoundsTest, RenderLayerInVirtualDisplay) {
+TEST_F(MultiDisplayTest, RenderLayerInVirtualDisplay) {
constexpr ui::LayerStack kLayerStack{1u};
createDisplay(mMainDisplayState.layerStackSpaceRect, kLayerStack);
createColorLayer(kLayerStack);
@@ -124,7 +124,7 @@
sc->expectColor(Rect(1, 1, 9, 9), {0, 0, 0, 255});
}
-TEST_F(MultiDisplayLayerBoundsTest, RenderLayerInMirroredVirtualDisplay) {
+TEST_F(MultiDisplayTest, RenderLayerInMirroredVirtualDisplay) {
// Create a display and set its layer stack to the main display's layer stack so
// the contents of the main display are mirrored on to the virtual display.
@@ -150,7 +150,7 @@
sc->expectColor(Rect(0, 0, 9, 9), {0, 0, 0, 255});
}
-TEST_F(MultiDisplayLayerBoundsTest, RenderLayerWithPromisedFenceInMirroredVirtualDisplay) {
+TEST_F(MultiDisplayTest, RenderLayerWithPromisedFenceInMirroredVirtualDisplay) {
// Create a display and use a unique layerstack ID for mirrorDisplay() so
// the contents of the main display are mirrored on to the virtual display.
@@ -181,6 +181,51 @@
sc->expectColor(Rect(0, 0, 9, 9), {0, 0, 0, 255});
}
+#if COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(WB_CONSUMER_BASE_OWNS_BQ)
+TEST_F(MultiDisplayTest, rejectDuplicateLayerStacks) {
+ if (!FlagManager::getInstance().reject_dupe_layerstacks()) return;
+
+ // Setup
+ sp<CpuConsumer> cpuConsumer1 = sp<CpuConsumer>::make(static_cast<size_t>(1));
+ cpuConsumer1->setName(String8("consumer 1"));
+ cpuConsumer1->setDefaultBufferSize(100, 100);
+ sp<IGraphicBufferProducer> cpuProducer1 =
+ cpuConsumer1->getSurface()->getIGraphicBufferProducer();
+ CpuConsumer::LockedBuffer buffer1;
+
+ sp<CpuConsumer> cpuConsumer2 = sp<CpuConsumer>::make(static_cast<size_t>(1));
+ cpuConsumer2->setName(String8("consumer 2"));
+ cpuConsumer2->setDefaultBufferSize(100, 100);
+ sp<IGraphicBufferProducer> cpuProducer2 =
+ cpuConsumer2->getSurface()->getIGraphicBufferProducer();
+ CpuConsumer::LockedBuffer buffer2;
+
+ SurfaceComposerClient::Transaction t;
+ constexpr ui::LayerStack layerStack = {123u};
+ createColorLayer(layerStack);
+
+ static const std::string kDisplayName1("VirtualDisplay1 - rejectDuplicateLayerStacks");
+ sp<IBinder> virtualDisplay1 =
+ SurfaceComposerClient::createVirtualDisplay(kDisplayName1, false /*isSecure*/);
+
+ t.setDisplaySurface(virtualDisplay1, cpuProducer1);
+ t.setDisplayLayerStack(virtualDisplay1, layerStack);
+ t.apply(true);
+
+ static const std::string kDisplayName2("VirtualDisplay2 - rejectDuplicateLayerStacks");
+ sp<IBinder> virtualDisplay2 =
+ SurfaceComposerClient::createVirtualDisplay(kDisplayName2, false /*isSecure*/);
+
+ t.setDisplaySurface(virtualDisplay2, cpuProducer2);
+ t.setDisplayLayerStack(virtualDisplay2, layerStack);
+ t.apply(true);
+
+ // The second consumer will not be able to lock a buffer because
+ // the duplicate layer stack should be rejected.
+ ASSERT_EQ(NO_ERROR, cpuConsumer1->lockNextBuffer(&buffer1));
+ ASSERT_NE(NO_ERROR, cpuConsumer2->lockNextBuffer(&buffer2));
+}
+#endif // COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(WB_CONSUMER_BASE_OWNS_BQ)
} // namespace android
// TODO(b/129481165): remove the #pragma below and fix conversion issues
diff --git a/services/surfaceflinger/tests/unittests/ActivePictureTrackerTest.cpp b/services/surfaceflinger/tests/unittests/ActivePictureTrackerTest.cpp
new file mode 100644
index 0000000..8011309
--- /dev/null
+++ b/services/surfaceflinger/tests/unittests/ActivePictureTrackerTest.cpp
@@ -0,0 +1,482 @@
+/*
+ * Copyright 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <gmock/gmock.h>
+#include <gtest/gtest.h>
+
+#include <android/gui/ActivePicture.h>
+#include <android/gui/IActivePictureListener.h>
+#include <compositionengine/mock/CompositionEngine.h>
+#include <mock/DisplayHardware/MockComposer.h>
+#include <mock/MockLayer.h>
+#include <renderengine/mock/RenderEngine.h>
+
+#include "ActivePictureTracker.h"
+#include "LayerFE.h"
+#include "TestableSurfaceFlinger.h"
+
+namespace android {
+
+using android::compositionengine::LayerFECompositionState;
+using android::gui::ActivePicture;
+using android::gui::IActivePictureListener;
+using android::mock::MockLayer;
+using surfaceflinger::frontend::LayerSnapshot;
+using testing::_;
+using testing::NiceMock;
+using testing::Return;
+using testing::SizeIs;
+using testing::StrictMock;
+
+class TestableLayerFE : public LayerFE {
+public:
+ TestableLayerFE() : LayerFE("TestableLayerFE"), snapshot(*(new LayerSnapshot)) {
+ mSnapshot = std::unique_ptr<LayerSnapshot>(&snapshot);
+ }
+
+ LayerSnapshot& snapshot;
+};
+
+class MockActivePictureListener : public gui::BnActivePictureListener {
+public:
+ operator ActivePictureTracker::Listeners const() {
+ return {sp<IActivePictureListener>::fromExisting(this)};
+ }
+
+ MOCK_METHOD(binder::Status, onActivePicturesChanged, (const std::vector<ActivePicture>&),
+ (override));
+};
+
+class ActivePictureTrackerTest : public testing::Test {
+protected:
+ const static ActivePictureTracker::Listeners NO_LISTENERS;
+
+ SurfaceFlinger* flinger() {
+ if (!mFlingerSetup) {
+ mFlinger.setupMockScheduler();
+ mFlinger.setupComposer(std::make_unique<Hwc2::mock::Composer>());
+ mFlinger.setupRenderEngine(std::make_unique<renderengine::mock::RenderEngine>());
+ mFlingerSetup = true;
+ }
+ return mFlinger.flinger();
+ }
+
+ sp<NiceMock<MockLayer>> createMockLayer(int layerId, int ownerUid) {
+ auto layer = sp<NiceMock<MockLayer>>::make(flinger(), layerId);
+ EXPECT_CALL(*layer, getOwnerUid()).WillRepeatedly(Return(uid_t(ownerUid)));
+ return layer;
+ }
+
+ sp<StrictMock<MockActivePictureListener>> createMockListener() {
+ return sp<StrictMock<MockActivePictureListener>>::make();
+ }
+
+ ActivePictureTracker::Listeners mListenersToAdd;
+ ActivePictureTracker::Listeners mListenersToRemove;
+
+private:
+ TestableSurfaceFlinger mFlinger;
+ bool mFlingerSetup = false;
+};
+
+const ActivePictureTracker::Listeners ActivePictureTrackerTest::NO_LISTENERS;
+
+// Hack to workaround initializer lists not working for parcelables because parcelables inherit from
+// Parcelable, which has a virtual destructor.
+auto UnorderedElementsAre(std::initializer_list<std::tuple<int32_t, int32_t, int64_t>> tuples) {
+ std::vector<ActivePicture> activePictures;
+ for (auto tuple : tuples) {
+ ActivePicture ap;
+ ap.layerId = std::get<0>(tuple);
+ ap.ownerUid = std::get<1>(tuple);
+ ap.pictureProfileId = std::get<2>(tuple);
+ activePictures.push_back(ap);
+ }
+ return testing::UnorderedElementsAreArray(activePictures);
+}
+
+// Parcelables don't define this for matchers, which is unfortunate
+void PrintTo(const ActivePicture& activePicture, std::ostream* os) {
+ *os << activePicture.toString();
+}
+
+TEST_F(ActivePictureTrackerTest, whenListenerAdded_called) {
+ ActivePictureTracker tracker;
+ auto listener = createMockListener();
+ EXPECT_CALL(*listener, onActivePicturesChanged(SizeIs(0))).Times(1);
+ tracker.updateAndNotifyListeners(*listener, NO_LISTENERS);
+}
+
+TEST_F(ActivePictureTrackerTest, whenListenerAdded_withListenerAlreadyAdded_notCalled) {
+ ActivePictureTracker tracker;
+ auto listener = createMockListener();
+ {
+ EXPECT_CALL(*listener, onActivePicturesChanged(SizeIs(0))).Times(1);
+ tracker.updateAndNotifyListeners(*listener, NO_LISTENERS);
+ }
+ {
+ EXPECT_CALL(*listener, onActivePicturesChanged(_)).Times(0);
+ tracker.updateAndNotifyListeners(*listener, NO_LISTENERS);
+ }
+}
+
+TEST_F(ActivePictureTrackerTest, whenListenerAdded_withUncommittedProfile_calledWithNone) {
+ auto layer = createMockLayer(100, 10);
+ TestableLayerFE layerFE;
+
+ ActivePictureTracker tracker;
+ {
+ layerFE.snapshot.pictureProfileHandle = PictureProfileHandle(1);
+ tracker.onLayerComposed(*layer, layerFE, layerFE.stealCompositionResult());
+ tracker.updateAndNotifyListeners(NO_LISTENERS, NO_LISTENERS);
+ }
+ {
+ auto listener = createMockListener();
+ EXPECT_CALL(*listener, onActivePicturesChanged(SizeIs(0))).Times(1);
+ tracker.updateAndNotifyListeners(*listener, NO_LISTENERS);
+ }
+}
+
+TEST_F(ActivePictureTrackerTest, whenListenerAdded_withCommittedProfile_calledWithActivePicture) {
+ auto layer = createMockLayer(100, 10);
+ TestableLayerFE layerFE;
+
+ ActivePictureTracker tracker;
+ {
+ layerFE.snapshot.pictureProfileHandle = PictureProfileHandle(1);
+ layerFE.onPictureProfileCommitted();
+ tracker.onLayerComposed(*layer, layerFE, layerFE.stealCompositionResult());
+
+ auto listener = createMockListener();
+ EXPECT_CALL(*listener, onActivePicturesChanged(_))
+ .WillOnce([](const std::vector<gui::ActivePicture>& activePictures) {
+ EXPECT_THAT(activePictures, UnorderedElementsAre({{100, 10, 1}}));
+ return binder::Status::ok();
+ });
+ tracker.updateAndNotifyListeners(*listener, NO_LISTENERS);
+ }
+}
+
+TEST_F(ActivePictureTrackerTest, whenProfileAdded_calledWithActivePicture) {
+ auto layer = createMockLayer(100, 10);
+ TestableLayerFE layerFE;
+
+ ActivePictureTracker tracker;
+ auto listener = createMockListener();
+ {
+ tracker.onLayerComposed(*layer, layerFE, layerFE.stealCompositionResult());
+
+ EXPECT_CALL(*listener, onActivePicturesChanged(SizeIs(0))).Times(1);
+ tracker.updateAndNotifyListeners(*listener, NO_LISTENERS);
+ }
+ {
+ layerFE.snapshot.pictureProfileHandle = PictureProfileHandle(1);
+ layerFE.onPictureProfileCommitted();
+ tracker.onLayerComposed(*layer, layerFE, layerFE.stealCompositionResult());
+
+ EXPECT_CALL(*listener, onActivePicturesChanged(_))
+ .WillOnce([](const std::vector<gui::ActivePicture>& activePictures) {
+ EXPECT_THAT(activePictures, UnorderedElementsAre({{100, 10, 1}}));
+ return binder::Status::ok();
+ });
+ tracker.updateAndNotifyListeners(NO_LISTENERS, NO_LISTENERS);
+ }
+}
+
+TEST_F(ActivePictureTrackerTest, whenContinuesUsingProfile_notCalled) {
+ auto layer = createMockLayer(100, 10);
+ TestableLayerFE layerFE;
+
+ ActivePictureTracker tracker;
+ auto listener = createMockListener();
+ {
+ layerFE.snapshot.pictureProfileHandle = PictureProfileHandle(1);
+ layerFE.onPictureProfileCommitted();
+ tracker.onLayerComposed(*layer, layerFE, layerFE.stealCompositionResult());
+
+ EXPECT_CALL(*listener, onActivePicturesChanged(SizeIs(1))).Times(1);
+ tracker.updateAndNotifyListeners(*listener, NO_LISTENERS);
+ }
+ {
+ layerFE.snapshot.pictureProfileHandle = PictureProfileHandle(1);
+ layerFE.onPictureProfileCommitted();
+ tracker.onLayerComposed(*layer, layerFE, layerFE.stealCompositionResult());
+
+ EXPECT_CALL(*listener, onActivePicturesChanged(_)).Times(0);
+ tracker.updateAndNotifyListeners(NO_LISTENERS, NO_LISTENERS);
+ }
+}
+
+TEST_F(ActivePictureTrackerTest, whenProfileIsRemoved_calledWithNoActivePictures) {
+ auto layer = createMockLayer(100, 10);
+ TestableLayerFE layerFE;
+
+ ActivePictureTracker tracker;
+ auto listener = createMockListener();
+ {
+ layerFE.snapshot.pictureProfileHandle = PictureProfileHandle(1);
+ layerFE.onPictureProfileCommitted();
+ tracker.onLayerComposed(*layer, layerFE, layerFE.stealCompositionResult());
+
+ EXPECT_CALL(*listener, onActivePicturesChanged(SizeIs(1))).Times(1);
+ tracker.updateAndNotifyListeners(*listener, NO_LISTENERS);
+ }
+ {
+ layerFE.snapshot.pictureProfileHandle = PictureProfileHandle::NONE;
+ tracker.onLayerComposed(*layer, layerFE, layerFE.stealCompositionResult());
+
+ EXPECT_CALL(*listener, onActivePicturesChanged(SizeIs(0))).Times(1);
+ tracker.updateAndNotifyListeners(NO_LISTENERS, NO_LISTENERS);
+ }
+}
+
+TEST_F(ActivePictureTrackerTest, whenProfileIsNotCommitted_calledWithNoActivePictures) {
+ auto layer = createMockLayer(100, 10);
+ TestableLayerFE layerFE;
+
+ ActivePictureTracker tracker;
+ auto listener = createMockListener();
+ {
+ layerFE.snapshot.pictureProfileHandle = PictureProfileHandle(1);
+ layerFE.onPictureProfileCommitted();
+ tracker.onLayerComposed(*layer, layerFE, layerFE.stealCompositionResult());
+
+ EXPECT_CALL(*listener, onActivePicturesChanged(SizeIs(1))).Times(1);
+ tracker.updateAndNotifyListeners(*listener, NO_LISTENERS);
+ }
+ {
+ layerFE.snapshot.pictureProfileHandle = PictureProfileHandle(1);
+ tracker.onLayerComposed(*layer, layerFE, layerFE.stealCompositionResult());
+
+ EXPECT_CALL(*listener, onActivePicturesChanged(SizeIs(0))).Times(1);
+ tracker.updateAndNotifyListeners(NO_LISTENERS, NO_LISTENERS);
+ }
+}
+
+TEST_F(ActivePictureTrackerTest, whenProfileChanges_calledWithDifferentProfile) {
+ auto layer = createMockLayer(100, 10);
+ TestableLayerFE layerFE;
+
+ ActivePictureTracker tracker;
+ auto listener = createMockListener();
+ {
+ layerFE.snapshot.pictureProfileHandle = PictureProfileHandle(1);
+ layerFE.onPictureProfileCommitted();
+ tracker.onLayerComposed(*layer, layerFE, layerFE.stealCompositionResult());
+
+ EXPECT_CALL(*listener, onActivePicturesChanged(SizeIs(1)))
+ .WillOnce([](const std::vector<gui::ActivePicture>& activePictures) {
+ EXPECT_THAT(activePictures, UnorderedElementsAre({{100, 10, 1}}));
+ return binder::Status::ok();
+ });
+ tracker.updateAndNotifyListeners(*listener, NO_LISTENERS);
+ }
+ {
+ layerFE.snapshot.pictureProfileHandle = PictureProfileHandle(2);
+ layerFE.onPictureProfileCommitted();
+ tracker.onLayerComposed(*layer, layerFE, layerFE.stealCompositionResult());
+
+ EXPECT_CALL(*listener, onActivePicturesChanged(SizeIs(1)))
+ .WillOnce([](const std::vector<gui::ActivePicture>& activePictures) {
+ EXPECT_THAT(activePictures, UnorderedElementsAre({{100, 10, 2}}));
+ return binder::Status::ok();
+ });
+ tracker.updateAndNotifyListeners(NO_LISTENERS, NO_LISTENERS);
+ }
+}
+
+TEST_F(ActivePictureTrackerTest, whenMultipleCommittedProfiles_calledWithMultipleActivePictures) {
+ auto layer1 = createMockLayer(100, 10);
+ TestableLayerFE layerFE1;
+
+ auto layer2 = createMockLayer(200, 20);
+ TestableLayerFE layerFE2;
+
+ ActivePictureTracker tracker;
+ auto listener = createMockListener();
+ {
+ layerFE1.snapshot.pictureProfileHandle = PictureProfileHandle(1);
+ layerFE1.onPictureProfileCommitted();
+ tracker.onLayerComposed(*layer1, layerFE1, layerFE1.stealCompositionResult());
+
+ layerFE2.snapshot.pictureProfileHandle = PictureProfileHandle(2);
+ layerFE2.onPictureProfileCommitted();
+ tracker.onLayerComposed(*layer2, layerFE2, layerFE2.stealCompositionResult());
+
+ EXPECT_CALL(*listener, onActivePicturesChanged(SizeIs(2)))
+ .WillOnce([](const std::vector<gui::ActivePicture>& activePictures) {
+ EXPECT_THAT(activePictures, UnorderedElementsAre({{100, 10, 1}, {200, 20, 2}}));
+ return binder::Status::ok();
+ });
+ tracker.updateAndNotifyListeners(*listener, NO_LISTENERS);
+ }
+}
+
+TEST_F(ActivePictureTrackerTest, whenNonCommittedProfileChanges_notCalled) {
+ auto layer1 = createMockLayer(100, 10);
+ TestableLayerFE layerFE1;
+
+ auto layer2 = createMockLayer(200, 20);
+ TestableLayerFE layerFE2;
+
+ ActivePictureTracker tracker;
+ auto listener = createMockListener();
+ {
+ layerFE1.snapshot.pictureProfileHandle = PictureProfileHandle(1);
+ layerFE1.onPictureProfileCommitted();
+ tracker.onLayerComposed(*layer1, layerFE1, layerFE1.stealCompositionResult());
+
+ layerFE2.snapshot.pictureProfileHandle = PictureProfileHandle(1);
+ tracker.onLayerComposed(*layer2, layerFE2, layerFE2.stealCompositionResult());
+
+ EXPECT_CALL(*listener, onActivePicturesChanged(SizeIs(1))).Times(1);
+ tracker.updateAndNotifyListeners(*listener, NO_LISTENERS);
+ }
+ {
+ layerFE1.snapshot.pictureProfileHandle = PictureProfileHandle(1);
+ layerFE1.onPictureProfileCommitted();
+ tracker.onLayerComposed(*layer1, layerFE1, layerFE1.stealCompositionResult());
+
+ layerFE2.snapshot.pictureProfileHandle = PictureProfileHandle(2);
+ tracker.onLayerComposed(*layer2, layerFE2, layerFE2.stealCompositionResult());
+
+ EXPECT_CALL(*listener, onActivePicturesChanged(_)).Times(0);
+ tracker.updateAndNotifyListeners(*listener, NO_LISTENERS);
+ }
+}
+
+TEST_F(ActivePictureTrackerTest, whenDifferentLayerUsesSameProfile_called) {
+ auto layer1 = createMockLayer(100, 10);
+ TestableLayerFE layerFE1;
+
+ auto layer2 = createMockLayer(200, 20);
+ TestableLayerFE layerFE2;
+
+ ActivePictureTracker tracker;
+ auto listener = createMockListener();
+ {
+ layerFE1.snapshot.pictureProfileHandle = PictureProfileHandle(1);
+ layerFE1.onPictureProfileCommitted();
+ tracker.onLayerComposed(*layer1, layerFE1, layerFE1.stealCompositionResult());
+
+ layerFE2.snapshot.pictureProfileHandle = PictureProfileHandle(2);
+ layerFE2.onPictureProfileCommitted();
+ tracker.onLayerComposed(*layer2, layerFE2, layerFE2.stealCompositionResult());
+
+ EXPECT_CALL(*listener, onActivePicturesChanged(_))
+ .WillOnce([](const std::vector<gui::ActivePicture>& activePictures) {
+ EXPECT_THAT(activePictures, UnorderedElementsAre({{100, 10, 1}, {200, 20, 2}}));
+ return binder::Status::ok();
+ });
+ tracker.updateAndNotifyListeners(*listener, NO_LISTENERS);
+ }
+ {
+ layerFE1.snapshot.pictureProfileHandle = PictureProfileHandle(2);
+ layerFE1.onPictureProfileCommitted();
+ tracker.onLayerComposed(*layer1, layerFE1, layerFE1.stealCompositionResult());
+
+ layerFE2.snapshot.pictureProfileHandle = PictureProfileHandle(1);
+ layerFE2.onPictureProfileCommitted();
+ tracker.onLayerComposed(*layer2, layerFE2, layerFE2.stealCompositionResult());
+
+ EXPECT_CALL(*listener, onActivePicturesChanged(_))
+ .WillOnce([](const std::vector<gui::ActivePicture>& activePictures) {
+ EXPECT_THAT(activePictures, UnorderedElementsAre({{100, 10, 2}, {200, 20, 1}}));
+ return binder::Status::ok();
+ });
+ tracker.updateAndNotifyListeners(NO_LISTENERS, NO_LISTENERS);
+ }
+}
+
+TEST_F(ActivePictureTrackerTest, whenSameUidDifferentLayerUsesSameProfile_called) {
+ auto layer1 = createMockLayer(100, 10);
+ TestableLayerFE layerFE1;
+
+ auto layer2 = createMockLayer(200, 10);
+ TestableLayerFE layerFE2;
+
+ ActivePictureTracker tracker;
+ auto listener = createMockListener();
+ {
+ layerFE1.snapshot.pictureProfileHandle = PictureProfileHandle(1);
+ layerFE1.onPictureProfileCommitted();
+ tracker.onLayerComposed(*layer1, layerFE1, layerFE1.stealCompositionResult());
+
+ layerFE2.snapshot.pictureProfileHandle = PictureProfileHandle(2);
+ layerFE2.onPictureProfileCommitted();
+ tracker.onLayerComposed(*layer2, layerFE2, layerFE2.stealCompositionResult());
+
+ EXPECT_CALL(*listener, onActivePicturesChanged(_))
+ .WillOnce([](const std::vector<gui::ActivePicture>& activePictures) {
+ EXPECT_THAT(activePictures, UnorderedElementsAre({{100, 10, 1}, {200, 10, 2}}));
+ return binder::Status::ok();
+ });
+ tracker.updateAndNotifyListeners(*listener, NO_LISTENERS);
+ }
+ {
+ layerFE1.snapshot.pictureProfileHandle = PictureProfileHandle(2);
+ layerFE1.onPictureProfileCommitted();
+ tracker.onLayerComposed(*layer1, layerFE1, layerFE1.stealCompositionResult());
+
+ layerFE2.snapshot.pictureProfileHandle = PictureProfileHandle(1);
+ layerFE2.onPictureProfileCommitted();
+ tracker.onLayerComposed(*layer2, layerFE2, layerFE2.stealCompositionResult());
+
+ EXPECT_CALL(*listener, onActivePicturesChanged(_))
+ .WillOnce([](const std::vector<gui::ActivePicture>& activePictures) {
+ EXPECT_THAT(activePictures, UnorderedElementsAre({{100, 10, 2}, {200, 10, 1}}));
+ return binder::Status::ok();
+ });
+ tracker.updateAndNotifyListeners(NO_LISTENERS, NO_LISTENERS);
+ }
+}
+
+TEST_F(ActivePictureTrackerTest, whenNewLayerUsesSameProfile_called) {
+ auto layer1 = createMockLayer(100, 10);
+ TestableLayerFE layerFE1;
+
+ ActivePictureTracker tracker;
+ auto listener = createMockListener();
+ {
+ layerFE1.snapshot.pictureProfileHandle = PictureProfileHandle(1);
+ layerFE1.onPictureProfileCommitted();
+ tracker.onLayerComposed(*layer1, layerFE1, layerFE1.stealCompositionResult());
+
+ EXPECT_CALL(*listener, onActivePicturesChanged(SizeIs(1))).Times(1);
+ tracker.updateAndNotifyListeners(*listener, NO_LISTENERS);
+ }
+
+ auto layer2 = createMockLayer(200, 10);
+ TestableLayerFE layerFE2;
+ {
+ layerFE1.snapshot.pictureProfileHandle = PictureProfileHandle(1);
+ layerFE1.onPictureProfileCommitted();
+ tracker.onLayerComposed(*layer1, layerFE1, layerFE1.stealCompositionResult());
+
+ layerFE2.snapshot.pictureProfileHandle = PictureProfileHandle(1);
+ layerFE2.onPictureProfileCommitted();
+ tracker.onLayerComposed(*layer2, layerFE2, layerFE2.stealCompositionResult());
+
+ EXPECT_CALL(*listener, onActivePicturesChanged(_))
+ .WillOnce([](const std::vector<gui::ActivePicture>& activePictures) {
+ EXPECT_THAT(activePictures, UnorderedElementsAre({{100, 10, 1}, {200, 10, 1}}));
+ return binder::Status::ok();
+ });
+ tracker.updateAndNotifyListeners(NO_LISTENERS, NO_LISTENERS);
+ }
+}
+
+} // namespace android
diff --git a/services/surfaceflinger/tests/unittests/ActivePictureUpdaterTest.cpp b/services/surfaceflinger/tests/unittests/ActivePictureUpdaterTest.cpp
deleted file mode 100644
index b926d2f..0000000
--- a/services/surfaceflinger/tests/unittests/ActivePictureUpdaterTest.cpp
+++ /dev/null
@@ -1,336 +0,0 @@
-/*
- * Copyright 2024 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#include <gmock/gmock.h>
-#include <gtest/gtest.h>
-
-#include <android/gui/ActivePicture.h>
-#include <android/gui/IActivePictureListener.h>
-#include <compositionengine/mock/CompositionEngine.h>
-#include <mock/DisplayHardware/MockComposer.h>
-#include <mock/MockLayer.h>
-#include <renderengine/mock/RenderEngine.h>
-
-#include "ActivePictureUpdater.h"
-#include "LayerFE.h"
-#include "TestableSurfaceFlinger.h"
-
-namespace android {
-
-using android::compositionengine::LayerFECompositionState;
-using android::gui::ActivePicture;
-using android::gui::IActivePictureListener;
-using android::mock::MockLayer;
-using surfaceflinger::frontend::LayerSnapshot;
-using testing::_;
-using testing::NiceMock;
-using testing::Return;
-
-class TestableLayerFE : public LayerFE {
-public:
- TestableLayerFE() : LayerFE("TestableLayerFE"), snapshot(*(new LayerSnapshot)) {
- mSnapshot = std::unique_ptr<LayerSnapshot>(&snapshot);
- }
-
- LayerSnapshot& snapshot;
-};
-
-class ActivePictureUpdaterTest : public testing::Test {
-protected:
- SurfaceFlinger* flinger() {
- if (!mFlingerSetup) {
- mFlinger.setupMockScheduler();
- mFlinger.setupComposer(std::make_unique<Hwc2::mock::Composer>());
- mFlinger.setupRenderEngine(std::make_unique<renderengine::mock::RenderEngine>());
- mFlingerSetup = true;
- }
- return mFlinger.flinger();
- }
-
-private:
- TestableSurfaceFlinger mFlinger;
- bool mFlingerSetup = false;
-};
-
-// Hack to workaround initializer lists not working for parcelables because parcelables inherit from
-// Parcelable, which has a virtual destructor.
-auto UnorderedElementsAre(std::initializer_list<std::tuple<int32_t, int32_t, int64_t>> tuples) {
- std::vector<ActivePicture> activePictures;
- for (auto tuple : tuples) {
- ActivePicture ap;
- ap.layerId = std::get<0>(tuple);
- ap.ownerUid = std::get<1>(tuple);
- ap.pictureProfileId = std::get<2>(tuple);
- activePictures.push_back(ap);
- }
- return testing::UnorderedElementsAreArray(activePictures);
-}
-
-// Parcelables don't define this for matchers, which is unfortunate
-void PrintTo(const ActivePicture& activePicture, std::ostream* os) {
- *os << activePicture.toString();
-}
-
-TEST_F(ActivePictureUpdaterTest, notCalledWithNoProfile) {
- sp<NiceMock<MockLayer>> layer = sp<NiceMock<MockLayer>>::make(flinger(), 100);
- TestableLayerFE layerFE;
- EXPECT_CALL(*layer, getOwnerUid()).WillRepeatedly(Return(uid_t(10)));
-
- ActivePictureUpdater updater;
- {
- layerFE.snapshot.pictureProfileHandle = PictureProfileHandle::NONE;
- updater.onLayerComposed(*layer, layerFE, layerFE.stealCompositionResult());
-
- ASSERT_FALSE(updater.updateAndHasChanged());
- }
-}
-
-TEST_F(ActivePictureUpdaterTest, calledWhenLayerStartsUsingProfile) {
- sp<NiceMock<MockLayer>> layer = sp<NiceMock<MockLayer>>::make(flinger(), 100);
- TestableLayerFE layerFE;
- EXPECT_CALL(*layer, getOwnerUid()).WillRepeatedly(Return(uid_t(10)));
-
- ActivePictureUpdater updater;
- {
- layerFE.snapshot.pictureProfileHandle = PictureProfileHandle::NONE;
- updater.onLayerComposed(*layer, layerFE, layerFE.stealCompositionResult());
-
- ASSERT_FALSE(updater.updateAndHasChanged());
- }
- {
- layerFE.snapshot.pictureProfileHandle = PictureProfileHandle(1);
- layerFE.onPictureProfileCommitted();
- updater.onLayerComposed(*layer, layerFE, layerFE.stealCompositionResult());
-
- ASSERT_TRUE(updater.updateAndHasChanged());
- EXPECT_THAT(updater.getActivePictures(), UnorderedElementsAre({{100, 10, 1}}));
- }
-}
-
-TEST_F(ActivePictureUpdaterTest, notCalledWhenLayerContinuesUsingProfile) {
- sp<NiceMock<MockLayer>> layer = sp<NiceMock<MockLayer>>::make(flinger(), 100);
- TestableLayerFE layerFE;
- EXPECT_CALL(*layer, getOwnerUid()).WillRepeatedly(Return(uid_t(10)));
-
- ActivePictureUpdater updater;
- {
- layerFE.snapshot.pictureProfileHandle = PictureProfileHandle(1);
- layerFE.onPictureProfileCommitted();
- updater.onLayerComposed(*layer, layerFE, layerFE.stealCompositionResult());
-
- ASSERT_TRUE(updater.updateAndHasChanged());
- EXPECT_THAT(updater.getActivePictures(), UnorderedElementsAre({{100, 10, 1}}));
- }
- {
- layerFE.snapshot.pictureProfileHandle = PictureProfileHandle(1);
- layerFE.onPictureProfileCommitted();
- updater.onLayerComposed(*layer, layerFE, layerFE.stealCompositionResult());
-
- ASSERT_FALSE(updater.updateAndHasChanged());
- }
-}
-
-TEST_F(ActivePictureUpdaterTest, calledWhenLayerStopsUsingProfile) {
- sp<NiceMock<MockLayer>> layer = sp<NiceMock<MockLayer>>::make(flinger(), 100);
- TestableLayerFE layerFE;
- EXPECT_CALL(*layer, getOwnerUid()).WillRepeatedly(Return(uid_t(10)));
-
- ActivePictureUpdater updater;
- {
- layerFE.snapshot.pictureProfileHandle = PictureProfileHandle(1);
- layerFE.onPictureProfileCommitted();
- updater.onLayerComposed(*layer, layerFE, layerFE.stealCompositionResult());
-
- ASSERT_TRUE(updater.updateAndHasChanged());
- EXPECT_THAT(updater.getActivePictures(), UnorderedElementsAre({{100, 10, 1}}));
- }
- {
- layerFE.snapshot.pictureProfileHandle = PictureProfileHandle::NONE;
- updater.onLayerComposed(*layer, layerFE, layerFE.stealCompositionResult());
-
- ASSERT_TRUE(updater.updateAndHasChanged());
- EXPECT_THAT(updater.getActivePictures(), UnorderedElementsAre({}));
- }
-}
-
-TEST_F(ActivePictureUpdaterTest, calledWhenLayerChangesProfile) {
- sp<NiceMock<MockLayer>> layer = sp<NiceMock<MockLayer>>::make(flinger(), 100);
- TestableLayerFE layerFE;
- EXPECT_CALL(*layer, getOwnerUid()).WillRepeatedly(Return(uid_t(10)));
-
- ActivePictureUpdater updater;
- {
- layerFE.snapshot.pictureProfileHandle = PictureProfileHandle(1);
- layerFE.onPictureProfileCommitted();
- updater.onLayerComposed(*layer, layerFE, layerFE.stealCompositionResult());
-
- ASSERT_TRUE(updater.updateAndHasChanged());
- EXPECT_THAT(updater.getActivePictures(), UnorderedElementsAre({{100, 10, 1}}));
- }
- {
- layerFE.snapshot.pictureProfileHandle = PictureProfileHandle(2);
- layerFE.onPictureProfileCommitted();
- updater.onLayerComposed(*layer, layerFE, layerFE.stealCompositionResult());
-
- ASSERT_TRUE(updater.updateAndHasChanged());
- EXPECT_THAT(updater.getActivePictures(), UnorderedElementsAre({{100, 10, 2}}));
- }
-}
-
-TEST_F(ActivePictureUpdaterTest, notCalledWhenUncommittedLayerChangesProfile) {
- sp<NiceMock<MockLayer>> layer1 = sp<NiceMock<MockLayer>>::make(flinger(), 100);
- TestableLayerFE layerFE1;
- EXPECT_CALL(*layer1, getOwnerUid()).WillRepeatedly(Return(uid_t(10)));
-
- sp<NiceMock<MockLayer>> layer2 = sp<NiceMock<MockLayer>>::make(flinger(), 200);
- TestableLayerFE layerFE2;
- EXPECT_CALL(*layer2, getOwnerUid()).WillRepeatedly(Return(uid_t(20)));
-
- ActivePictureUpdater updater;
- {
- layerFE1.snapshot.pictureProfileHandle = PictureProfileHandle(1);
- layerFE1.onPictureProfileCommitted();
- updater.onLayerComposed(*layer1, layerFE1, layerFE1.stealCompositionResult());
-
- layerFE2.snapshot.pictureProfileHandle = PictureProfileHandle(1);
- updater.onLayerComposed(*layer2, layerFE2, layerFE2.stealCompositionResult());
-
- ASSERT_TRUE(updater.updateAndHasChanged());
- EXPECT_THAT(updater.getActivePictures(), UnorderedElementsAre({{100, 10, 1}}));
- }
- {
- layerFE1.snapshot.pictureProfileHandle = PictureProfileHandle(1);
- layerFE1.onPictureProfileCommitted();
- updater.onLayerComposed(*layer1, layerFE1, layerFE1.stealCompositionResult());
-
- layerFE2.snapshot.pictureProfileHandle = PictureProfileHandle(2);
- updater.onLayerComposed(*layer2, layerFE2, layerFE2.stealCompositionResult());
-
- ASSERT_FALSE(updater.updateAndHasChanged());
- }
-}
-
-TEST_F(ActivePictureUpdaterTest, calledWhenDifferentLayerUsesSameProfile) {
- sp<NiceMock<MockLayer>> layer1 = sp<NiceMock<MockLayer>>::make(flinger(), 100);
- TestableLayerFE layerFE1;
- EXPECT_CALL(*layer1, getOwnerUid()).WillRepeatedly(Return(uid_t(10)));
-
- sp<NiceMock<MockLayer>> layer2 = sp<NiceMock<MockLayer>>::make(flinger(), 200);
- TestableLayerFE layerFE2;
- EXPECT_CALL(*layer2, getOwnerUid()).WillRepeatedly(Return(uid_t(20)));
-
- ActivePictureUpdater updater;
- {
- layerFE1.snapshot.pictureProfileHandle = PictureProfileHandle(1);
- layerFE1.onPictureProfileCommitted();
- updater.onLayerComposed(*layer1, layerFE1, layerFE1.stealCompositionResult());
-
- layerFE2.snapshot.pictureProfileHandle = PictureProfileHandle(2);
- layerFE2.onPictureProfileCommitted();
- updater.onLayerComposed(*layer2, layerFE2, layerFE2.stealCompositionResult());
-
- ASSERT_TRUE(updater.updateAndHasChanged());
- EXPECT_THAT(updater.getActivePictures(),
- UnorderedElementsAre({{100, 10, 1}, {200, 20, 2}}));
- }
- {
- layerFE1.snapshot.pictureProfileHandle = PictureProfileHandle(2);
- layerFE1.onPictureProfileCommitted();
- updater.onLayerComposed(*layer1, layerFE1, layerFE1.stealCompositionResult());
-
- layerFE2.snapshot.pictureProfileHandle = PictureProfileHandle(1);
- layerFE2.onPictureProfileCommitted();
- updater.onLayerComposed(*layer2, layerFE2, layerFE2.stealCompositionResult());
-
- ASSERT_TRUE(updater.updateAndHasChanged());
- EXPECT_THAT(updater.getActivePictures(),
- UnorderedElementsAre({{100, 10, 2}, {200, 20, 1}}));
- }
-}
-
-TEST_F(ActivePictureUpdaterTest, calledWhenSameUidUsesSameProfile) {
- sp<NiceMock<MockLayer>> layer1 = sp<NiceMock<MockLayer>>::make(flinger(), 100);
- TestableLayerFE layerFE1;
- EXPECT_CALL(*layer1, getOwnerUid()).WillRepeatedly(Return(uid_t(10)));
-
- sp<NiceMock<MockLayer>> layer2 = sp<NiceMock<MockLayer>>::make(flinger(), 200);
- TestableLayerFE layerFE2;
- EXPECT_CALL(*layer2, getOwnerUid()).WillRepeatedly(Return(uid_t(10)));
-
- ActivePictureUpdater updater;
- {
- layerFE1.snapshot.pictureProfileHandle = PictureProfileHandle(1);
- layerFE1.onPictureProfileCommitted();
- updater.onLayerComposed(*layer1, layerFE1, layerFE1.stealCompositionResult());
-
- layerFE2.snapshot.pictureProfileHandle = PictureProfileHandle(2);
- layerFE2.onPictureProfileCommitted();
- updater.onLayerComposed(*layer2, layerFE2, layerFE2.stealCompositionResult());
-
- ASSERT_TRUE(updater.updateAndHasChanged());
- EXPECT_THAT(updater.getActivePictures(),
- UnorderedElementsAre({{100, 10, 1}, {200, 10, 2}}));
- }
- {
- layerFE1.snapshot.pictureProfileHandle = PictureProfileHandle(2);
- layerFE1.onPictureProfileCommitted();
- updater.onLayerComposed(*layer1, layerFE1, layerFE1.stealCompositionResult());
-
- layerFE2.snapshot.pictureProfileHandle = PictureProfileHandle(1);
- layerFE2.onPictureProfileCommitted();
- updater.onLayerComposed(*layer2, layerFE2, layerFE2.stealCompositionResult());
-
- ASSERT_TRUE(updater.updateAndHasChanged());
- EXPECT_THAT(updater.getActivePictures(),
- UnorderedElementsAre({{100, 10, 2}, {200, 10, 1}}));
- }
-}
-
-TEST_F(ActivePictureUpdaterTest, calledWhenNewLayerUsesSameProfile) {
- sp<NiceMock<MockLayer>> layer1 = sp<NiceMock<MockLayer>>::make(flinger(), 100);
- TestableLayerFE layerFE1;
- EXPECT_CALL(*layer1, getOwnerUid()).WillRepeatedly(Return(uid_t(10)));
-
- ActivePictureUpdater updater;
- {
- layerFE1.snapshot.pictureProfileHandle = PictureProfileHandle(1);
- layerFE1.onPictureProfileCommitted();
- updater.onLayerComposed(*layer1, layerFE1, layerFE1.stealCompositionResult());
-
- ASSERT_TRUE(updater.updateAndHasChanged());
- EXPECT_THAT(updater.getActivePictures(), UnorderedElementsAre({{100, 10, 1}}));
- }
-
- sp<NiceMock<MockLayer>> layer2 = sp<NiceMock<MockLayer>>::make(flinger(), 200);
- TestableLayerFE layerFE2;
- EXPECT_CALL(*layer2, getOwnerUid()).WillRepeatedly(Return(uid_t(10)));
-
- {
- layerFE1.snapshot.pictureProfileHandle = PictureProfileHandle(1);
- layerFE1.onPictureProfileCommitted();
- updater.onLayerComposed(*layer1, layerFE1, layerFE1.stealCompositionResult());
-
- layerFE2.snapshot.pictureProfileHandle = PictureProfileHandle(1);
- layerFE2.onPictureProfileCommitted();
- updater.onLayerComposed(*layer2, layerFE2, layerFE2.stealCompositionResult());
-
- ASSERT_TRUE(updater.updateAndHasChanged());
- EXPECT_THAT(updater.getActivePictures(),
- UnorderedElementsAre({{100, 10, 1}, {200, 10, 1}}));
- }
-}
-
-} // namespace android
diff --git a/services/surfaceflinger/tests/unittests/LayerHistoryIntegrationTest.cpp b/services/surfaceflinger/tests/unittests/LayerHistoryIntegrationTest.cpp
index 53a9062..f3d6dcc 100644
--- a/services/surfaceflinger/tests/unittests/LayerHistoryIntegrationTest.cpp
+++ b/services/surfaceflinger/tests/unittests/LayerHistoryIntegrationTest.cpp
@@ -584,7 +584,7 @@
auto layer = createLegacyAndFrontedEndLayer(1);
showLayer(1);
- setFrameRate(1, (33_Hz).getValue(), ANATIVEWINDOW_FRAME_RATE_COMPATIBILITY_GTE,
+ setFrameRate(1, (33_Hz).getValue(), ANATIVEWINDOW_FRAME_RATE_COMPATIBILITY_AT_LEAST,
ANATIVEWINDOW_CHANGE_FRAME_RATE_ONLY_IF_SEAMLESS);
setFrameRateCategory(1, 0);
@@ -623,7 +623,7 @@
auto layer = createLegacyAndFrontedEndLayer(1);
showLayer(1);
- setFrameRate(1, (33_Hz).getValue(), ANATIVEWINDOW_FRAME_RATE_COMPATIBILITY_GTE,
+ setFrameRate(1, (33_Hz).getValue(), ANATIVEWINDOW_FRAME_RATE_COMPATIBILITY_AT_LEAST,
ANATIVEWINDOW_CHANGE_FRAME_RATE_ONLY_IF_SEAMLESS);
setFrameRateCategory(1, 0);
@@ -662,7 +662,7 @@
auto layer = createLegacyAndFrontedEndLayer(1);
showLayer(1);
- setFrameRate(1, (0_Hz).getValue(), ANATIVEWINDOW_FRAME_RATE_COMPATIBILITY_GTE,
+ setFrameRate(1, (0_Hz).getValue(), ANATIVEWINDOW_FRAME_RATE_COMPATIBILITY_AT_LEAST,
ANATIVEWINDOW_CHANGE_FRAME_RATE_ONLY_IF_SEAMLESS);
EXPECT_EQ(1u, layerCount());
@@ -694,7 +694,7 @@
auto layer = createLegacyAndFrontedEndLayer(1);
showLayer(1);
- setFrameRate(1, (0_Hz).getValue(), ANATIVEWINDOW_FRAME_RATE_COMPATIBILITY_GTE,
+ setFrameRate(1, (0_Hz).getValue(), ANATIVEWINDOW_FRAME_RATE_COMPATIBILITY_AT_LEAST,
ANATIVEWINDOW_CHANGE_FRAME_RATE_ONLY_IF_SEAMLESS);
setFrameRateCategory(1, 0);
diff --git a/services/surfaceflinger/tests/unittests/LayerSnapshotTest.cpp b/services/surfaceflinger/tests/unittests/LayerSnapshotTest.cpp
index 8c53eef..4d322ef 100644
--- a/services/surfaceflinger/tests/unittests/LayerSnapshotTest.cpp
+++ b/services/surfaceflinger/tests/unittests/LayerSnapshotTest.cpp
@@ -2021,16 +2021,13 @@
EXPECT_FALSE(getSnapshot(1)->contentDirty);
}
TEST_F(LayerSnapshotTest, shouldUpdatePictureProfileHandle) {
- if (!com_android_graphics_libgui_flags_apply_picture_profiles()) {
- GTEST_SKIP() << "Flag disabled, skipping test";
- }
std::vector<TransactionState> transactions;
transactions.emplace_back();
transactions.back().states.push_back({});
- transactions.back().states.front().layerId = 1;
- transactions.back().states.front().state.layerId = 1;
- transactions.back().states.front().state.what = layer_state_t::ePictureProfileHandleChanged;
- transactions.back().states.front().state.pictureProfileHandle = PictureProfileHandle(3);
+ transactions.back().states.back().layerId = 1;
+ transactions.back().states.back().state.layerId = 1;
+ transactions.back().states.back().state.what = layer_state_t::ePictureProfileHandleChanged;
+ transactions.back().states.back().state.pictureProfileHandle = PictureProfileHandle(3);
mLifecycleManager.applyTransactions(transactions);
EXPECT_EQ(mLifecycleManager.getGlobalChanges(), RequestedLayerState::Changes::Content);
@@ -2042,23 +2039,50 @@
}
TEST_F(LayerSnapshotTest, shouldUpdatePictureProfilePriorityFromAppContentPriority) {
- if (!com_android_graphics_libgui_flags_apply_picture_profiles()) {
- GTEST_SKIP() << "Flag disabled, skipping test";
+ {
+ std::vector<TransactionState> transactions;
+ transactions.emplace_back();
+ transactions.back().states.push_back({});
+ transactions.back().states.back().layerId = 1;
+ transactions.back().states.back().state.layerId = 1;
+ transactions.back().states.back().state.what = layer_state_t::eAppContentPriorityChanged;
+ transactions.back().states.back().state.appContentPriority = 1;
+ transactions.back().states.push_back({});
+ transactions.back().states.back().layerId = 2;
+ transactions.back().states.back().state.layerId = 2;
+ transactions.back().states.back().state.what = layer_state_t::eAppContentPriorityChanged;
+ transactions.back().states.back().state.appContentPriority = -1;
+
+ mLifecycleManager.applyTransactions(transactions);
+ EXPECT_EQ(mLifecycleManager.getGlobalChanges(), RequestedLayerState::Changes::Content);
+
+ update(mSnapshotBuilder);
+
+ EXPECT_GT(getSnapshot(1)->pictureProfilePriority, getSnapshot(2)->pictureProfilePriority);
+ EXPECT_EQ(getSnapshot(1)->pictureProfilePriority - getSnapshot(2)->pictureProfilePriority,
+ 2);
}
- std::vector<TransactionState> transactions;
- transactions.emplace_back();
- transactions.back().states.push_back({});
- transactions.back().states.front().layerId = 1;
- transactions.back().states.front().state.layerId = 1;
- transactions.back().states.front().state.what = layer_state_t::eAppContentPriorityChanged;
- transactions.back().states.front().state.appContentPriority = 3;
+ {
+ std::vector<TransactionState> transactions;
+ transactions.emplace_back();
+ transactions.back().states.push_back({});
+ transactions.back().states.back().layerId = 1;
+ transactions.back().states.back().state.layerId = 1;
+ transactions.back().states.back().state.what = layer_state_t::eAppContentPriorityChanged;
+ transactions.back().states.back().state.appContentPriority = INT_MIN;
+ transactions.back().states.push_back({});
+ transactions.back().states.back().layerId = 2;
+ transactions.back().states.back().state.layerId = 2;
+ transactions.back().states.back().state.what = layer_state_t::eAppContentPriorityChanged;
+ transactions.back().states.back().state.appContentPriority = INT_MAX;
- mLifecycleManager.applyTransactions(transactions);
- EXPECT_EQ(mLifecycleManager.getGlobalChanges(), RequestedLayerState::Changes::Content);
+ mLifecycleManager.applyTransactions(transactions);
+ EXPECT_EQ(mLifecycleManager.getGlobalChanges(), RequestedLayerState::Changes::Content);
- update(mSnapshotBuilder);
+ update(mSnapshotBuilder);
- EXPECT_EQ(getSnapshot(1)->pictureProfilePriority, 3);
+ EXPECT_GT(getSnapshot(2)->pictureProfilePriority, getSnapshot(1)->pictureProfilePriority);
+ }
}
} // namespace android::surfaceflinger::frontend
diff --git a/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockComposer.h b/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockComposer.h
index 0d5266e..2bf66ac 100644
--- a/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockComposer.h
+++ b/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockComposer.h
@@ -190,6 +190,9 @@
MOCK_METHOD(Error, getMaxLayerPictureProfiles, (Display, int32_t*));
MOCK_METHOD(Error, setDisplayPictureProfileId, (Display, PictureProfileId id));
MOCK_METHOD(Error, setLayerPictureProfileId, (Display, Layer, PictureProfileId id));
+ MOCK_METHOD(Error, getLuts,
+ (Display, const std::vector<sp<GraphicBuffer>>&,
+ std::vector<aidl::android::hardware::graphics::composer3::Luts>*));
};
} // namespace Hwc2::mock
diff --git a/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockHWC2.h b/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockHWC2.h
index ec065a7..4ca6fe0 100644
--- a/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockHWC2.h
+++ b/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockHWC2.h
@@ -116,6 +116,10 @@
MOCK_METHOD(hal::Error, getMaxLayerPictureProfiles, (int32_t*), (override));
MOCK_METHOD(hal::Error, setPictureProfileHandle, (const android::PictureProfileHandle&),
(override));
+ MOCK_METHOD(hal::Error, getLuts,
+ (const std::vector<android::sp<android::GraphicBuffer>>&,
+ std::vector<aidl::android::hardware::graphics::composer3::Luts>*),
+ (override));
};
class Layer : public HWC2::Layer {
diff --git a/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockHWComposer.h b/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockHWComposer.h
index 88f83d2..7bd85cd 100644
--- a/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockHWComposer.h
+++ b/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockHWComposer.h
@@ -151,6 +151,9 @@
MOCK_METHOD(int32_t, getMaxLayerPictureProfiles, (PhysicalDisplayId));
MOCK_METHOD(status_t, setDisplayPictureProfileHandle,
(PhysicalDisplayId, const PictureProfileHandle&));
+ MOCK_METHOD(status_t, getLuts,
+ (PhysicalDisplayId, const std::vector<sp<GraphicBuffer>>&,
+ std::vector<aidl::android::hardware::graphics::composer3::Luts>*));
};
} // namespace android::mock