Merge "Support for KEYCODE_LANGUAGE_SWITCH in native VirtualInputDevice" into main
diff --git a/cmds/installd/InstalldNativeService.cpp b/cmds/installd/InstalldNativeService.cpp
index d7c9b40..e14af77 100644
--- a/cmds/installd/InstalldNativeService.cpp
+++ b/cmds/installd/InstalldNativeService.cpp
@@ -471,6 +471,49 @@
return NO_ERROR;
}
+constexpr const char kXattrRestoreconInProgress[] = "user.restorecon_in_progress";
+
+static std::string lgetfilecon(const std::string& path) {
+ char* context;
+ if (::lgetfilecon(path.c_str(), &context) < 0) {
+ PLOG(ERROR) << "Failed to lgetfilecon for " << path;
+ return {};
+ }
+ std::string result{context};
+ free(context);
+ return result;
+}
+
+static bool getRestoreconInProgress(const std::string& path) {
+ bool inProgress = false;
+ if (getxattr(path.c_str(), kXattrRestoreconInProgress, &inProgress, sizeof(inProgress)) !=
+ sizeof(inProgress)) {
+ if (errno != ENODATA) {
+ PLOG(ERROR) << "Failed to check in-progress restorecon for " << path;
+ }
+ return false;
+ }
+ return inProgress;
+}
+
+struct RestoreconInProgress {
+ explicit RestoreconInProgress(const std::string& path) : mPath(path) {
+ bool inProgress = true;
+ if (setxattr(mPath.c_str(), kXattrRestoreconInProgress, &inProgress, sizeof(inProgress),
+ 0) != 0) {
+ PLOG(ERROR) << "Failed to set in-progress restorecon for " << path;
+ }
+ }
+ ~RestoreconInProgress() {
+ if (removexattr(mPath.c_str(), kXattrRestoreconInProgress) < 0) {
+ PLOG(ERROR) << "Failed to clear in-progress restorecon for " << mPath;
+ }
+ }
+
+private:
+ const std::string& mPath;
+};
+
/**
* Perform restorecon of the given path, but only perform recursive restorecon
* if the label of that top-level file actually changed. This can save us
@@ -479,56 +522,56 @@
static int restorecon_app_data_lazy(const std::string& path, const std::string& seInfo, uid_t uid,
bool existing) {
ScopedTrace tracer("restorecon-lazy");
- int res = 0;
- char* before = nullptr;
- char* after = nullptr;
if (!existing) {
ScopedTrace tracer("new-path");
if (selinux_android_restorecon_pkgdir(path.c_str(), seInfo.c_str(), uid,
SELINUX_ANDROID_RESTORECON_RECURSE) < 0) {
PLOG(ERROR) << "Failed recursive restorecon for " << path;
- goto fail;
+ return -1;
}
- return res;
+ return 0;
}
- // Note that SELINUX_ANDROID_RESTORECON_DATADATA flag is set by
- // libselinux. Not needed here.
- if (lgetfilecon(path.c_str(), &before) < 0) {
- PLOG(ERROR) << "Failed before getfilecon for " << path;
- goto fail;
- }
- if (selinux_android_restorecon_pkgdir(path.c_str(), seInfo.c_str(), uid, 0) < 0) {
- PLOG(ERROR) << "Failed top-level restorecon for " << path;
- goto fail;
- }
- if (lgetfilecon(path.c_str(), &after) < 0) {
- PLOG(ERROR) << "Failed after getfilecon for " << path;
- goto fail;
+ // Note that SELINUX_ANDROID_RESTORECON_DATADATA flag is set by libselinux. Not needed here.
+
+ // Check to see if there was an interrupted operation.
+ bool inProgress = getRestoreconInProgress(path);
+ std::string before, after;
+ if (!inProgress) {
+ if (before = lgetfilecon(path); before.empty()) {
+ PLOG(ERROR) << "Failed before getfilecon for " << path;
+ return -1;
+ }
+ if (selinux_android_restorecon_pkgdir(path.c_str(), seInfo.c_str(), uid, 0) < 0) {
+ PLOG(ERROR) << "Failed top-level restorecon for " << path;
+ return -1;
+ }
+ if (after = lgetfilecon(path); after.empty()) {
+ PLOG(ERROR) << "Failed after getfilecon for " << path;
+ return -1;
+ }
}
// If the initial top-level restorecon above changed the label, then go
// back and restorecon everything recursively
- if (strcmp(before, after)) {
+ if (inProgress || before != after) {
ScopedTrace tracer("label-change");
if (existing) {
LOG(DEBUG) << "Detected label change from " << before << " to " << after << " at "
<< path << "; running recursive restorecon";
}
+
+ // Temporary mark the folder as "in-progress" to resume in case of reboot/other failure.
+ RestoreconInProgress fence(path);
+
if (selinux_android_restorecon_pkgdir(path.c_str(), seInfo.c_str(), uid,
SELINUX_ANDROID_RESTORECON_RECURSE) < 0) {
PLOG(ERROR) << "Failed recursive restorecon for " << path;
- goto fail;
+ return -1;
}
}
- goto done;
-fail:
- res = -1;
-done:
- free(before);
- free(after);
- return res;
+ return 0;
}
static bool internal_storage_has_project_id() {
// The following path is populated in setFirstBoot, so if this file is present
@@ -3283,7 +3326,7 @@
}
char *con = nullptr;
- if (lgetfilecon(pkgdir, &con) < 0) {
+ if (::lgetfilecon(pkgdir, &con) < 0) {
return error("Failed to lgetfilecon " + _pkgdir);
}
diff --git a/include/android/performance_hint.h b/include/android/performance_hint.h
index ba8b02d..9d2c791 100644
--- a/include/android/performance_hint.h
+++ b/include/android/performance_hint.h
@@ -60,6 +60,27 @@
struct APerformanceHintManager;
struct APerformanceHintSession;
+struct AWorkDuration;
+
+/**
+ * {@link AWorkDuration} is an opaque type that represents the breakdown of the
+ * actual workload duration in each component internally.
+ *
+ * A new {@link AWorkDuration} can be obtained using
+ * {@link AWorkDuration_create()}, when the client finishes using
+ * {@link AWorkDuration}, {@link AWorkDuration_release()} must be
+ * called to destroy and free up the resources associated with
+ * {@link AWorkDuration}.
+ *
+ * This file provides a set of functions to allow clients to set the measured
+ * work duration of each component on {@link AWorkDuration}.
+ *
+ * - AWorkDuration_setWorkPeriodStartTimestampNanos()
+ * - AWorkDuration_setActualTotalDurationNanos()
+ * - AWorkDuration_setActualCpuDurationNanos()
+ * - AWorkDuration_setActualGpuDurationNanos()
+ */
+typedef struct AWorkDuration AWorkDuration;
/**
* An opaque type representing a handle to a performance hint manager.
@@ -102,7 +123,7 @@
*
* @return APerformanceHintManager instance on success, nullptr on failure.
*/
-APerformanceHintManager* APerformanceHint_getManager() __INTRODUCED_IN(__ANDROID_API_T__);
+APerformanceHintManager* _Nullable APerformanceHint_getManager() __INTRODUCED_IN(__ANDROID_API_T__);
/**
* Creates a session for the given set of threads and sets their initial target work
@@ -116,9 +137,9 @@
* This must be positive if using the work duration API, or 0 otherwise.
* @return APerformanceHintManager instance on success, nullptr on failure.
*/
-APerformanceHintSession* APerformanceHint_createSession(
- APerformanceHintManager* manager,
- const int32_t* threadIds, size_t size,
+APerformanceHintSession* _Nullable APerformanceHint_createSession(
+ APerformanceHintManager* _Nonnull manager,
+ const int32_t* _Nonnull threadIds, size_t size,
int64_t initialTargetWorkDurationNanos) __INTRODUCED_IN(__ANDROID_API_T__);
/**
@@ -128,7 +149,7 @@
* @return the preferred update rate supported by device software.
*/
int64_t APerformanceHint_getPreferredUpdateRateNanos(
- APerformanceHintManager* manager) __INTRODUCED_IN(__ANDROID_API_T__);
+ APerformanceHintManager* _Nonnull manager) __INTRODUCED_IN(__ANDROID_API_T__);
/**
* Updates this session's target duration for each cycle of work.
@@ -140,7 +161,7 @@
* EPIPE if communication with the system service has failed.
*/
int APerformanceHint_updateTargetWorkDuration(
- APerformanceHintSession* session,
+ APerformanceHintSession* _Nonnull session,
int64_t targetDurationNanos) __INTRODUCED_IN(__ANDROID_API_T__);
/**
@@ -157,7 +178,7 @@
* EPIPE if communication with the system service has failed.
*/
int APerformanceHint_reportActualWorkDuration(
- APerformanceHintSession* session,
+ APerformanceHintSession* _Nonnull session,
int64_t actualDurationNanos) __INTRODUCED_IN(__ANDROID_API_T__);
/**
@@ -167,7 +188,7 @@
* @param session The performance hint session instance to release.
*/
void APerformanceHint_closeSession(
- APerformanceHintSession* session) __INTRODUCED_IN(__ANDROID_API_T__);
+ APerformanceHintSession* _Nonnull session) __INTRODUCED_IN(__ANDROID_API_T__);
/**
* Set a list of threads to the performance hint session. This operation will replace
@@ -184,8 +205,8 @@
* EPERM if any thread id doesn't belong to the application.
*/
int APerformanceHint_setThreads(
- APerformanceHintSession* session,
- const pid_t* threadIds,
+ APerformanceHintSession* _Nonnull session,
+ const pid_t* _Nonnull threadIds,
size_t size) __INTRODUCED_IN(__ANDROID_API_U__);
/**
@@ -198,11 +219,92 @@
* EPIPE if communication with the system service has failed.
*/
int APerformanceHint_setPreferPowerEfficiency(
- APerformanceHintSession* session,
+ APerformanceHintSession* _Nonnull session,
bool enabled) __INTRODUCED_IN(__ANDROID_API_V__);
+/**
+ * Reports the durations for the last cycle of work.
+ *
+ * The system will attempt to adjust the scheduling and performance of the
+ * threads within the thread group to bring the actual duration close to the target duration.
+ *
+ * @param session The {@link APerformanceHintSession} instance to update.
+ * @param workDuration The {@link AWorkDuration} structure of times the thread group took to
+ * complete its last task in nanoseconds breaking down into different components.
+ *
+ * The work period start timestamp, actual total duration and actual CPU duration must be
+ * positive.
+ *
+ * The actual GPU duration must be non-negative. If the actual GPU duration is 0, it means
+ * the actual GPU duration is not measured.
+ *
+ * @return 0 on success.
+ * EINVAL if session is nullptr or any duration is an invalid number.
+ * EPIPE if communication with the system service has failed.
+ */
+int APerformanceHint_reportActualWorkDuration2(
+ APerformanceHintSession* _Nonnull session,
+ AWorkDuration* _Nonnull workDuration) __INTRODUCED_IN(__ANDROID_API_V__);
+
+/**
+ * Creates a new AWorkDuration. When the client finishes using {@link AWorkDuration}, it should
+ * call {@link AWorkDuration_release()} to destroy {@link AWorkDuration} and release all resources
+ * associated with it.
+ *
+ * @return AWorkDuration on success and nullptr otherwise.
+ */
+AWorkDuration* _Nonnull AWorkDuration_create() __INTRODUCED_IN(__ANDROID_API_V__);
+
+/**
+ * Destroys {@link AWorkDuration} and free all resources associated to it.
+ *
+ * @param aWorkDuration The {@link AWorkDuration} created by calling {@link AWorkDuration_create()}
+ */
+void AWorkDuration_release(AWorkDuration* _Nonnull WorkDuration) __INTRODUCED_IN(__ANDROID_API_V__);
+
+/**
+ * Sets the work period start timestamp in nanoseconds.
+ *
+ * @param aWorkDuration The {@link AWorkDuration} created by calling {@link AWorkDuration_create()}
+ * @param workPeriodStartTimestampNanos The work period start timestamp in nanoseconds based on
+ * CLOCK_MONOTONIC about when the work starts, the timestamp must be positive.
+ */
+void AWorkDuration_setWorkPeriodStartTimestampNanos(AWorkDuration* _Nonnull aWorkDuration,
+ int64_t workPeriodStartTimestampNanos) __INTRODUCED_IN(__ANDROID_API_V__);
+
+/**
+ * Sets the actual total work duration in nanoseconds.
+ *
+ * @param aWorkDuration The {@link AWorkDuration} created by calling {@link AWorkDuration_create()}
+ * @param actualTotalDurationNanos The actual total work duration in nanoseconds, the number must be
+ * positive.
+ */
+void AWorkDuration_setActualTotalDurationNanos(AWorkDuration* _Nonnull aWorkDuration,
+ int64_t actualTotalDurationNanos) __INTRODUCED_IN(__ANDROID_API_V__);
+
+/**
+ * Sets the actual CPU work duration in nanoseconds.
+ *
+ * @param aWorkDuration The {@link AWorkDuration} created by calling {@link AWorkDuration_create()}
+ * @param actualCpuDurationNanos The actual CPU work duration in nanoseconds, the number must be
+ * positive.
+ */
+void AWorkDuration_setActualCpuDurationNanos(AWorkDuration* _Nonnull aWorkDuration,
+ int64_t actualCpuDurationNanos) __INTRODUCED_IN(__ANDROID_API_V__);
+
+/**
+ * Sets the actual GPU work duration in nanoseconds.
+ *
+ * @param aWorkDuration The {@link AWorkDuration} created by calling {@link AWorkDuration_create()}.
+ * @param actualGpuDurationNanos The actual GPU work duration in nanoseconds, the number must be
+ * non-negative. If the actual GPU duration is 0, it means the actual GPU duration is
+ * measured.
+ */
+void AWorkDuration_setActualGpuDurationNanos(AWorkDuration* _Nonnull aWorkDuration,
+ int64_t actualGpuDurationNanos) __INTRODUCED_IN(__ANDROID_API_V__);
+
__END_DECLS
#endif // ANDROID_NATIVE_PERFORMANCE_HINT_H
-/** @} */
\ No newline at end of file
+/** @} */
diff --git a/libs/binder/FdTrigger.cpp b/libs/binder/FdTrigger.cpp
index a1fbbf3..895690f 100644
--- a/libs/binder/FdTrigger.cpp
+++ b/libs/binder/FdTrigger.cpp
@@ -53,7 +53,7 @@
#ifdef BINDER_RPC_SINGLE_THREADED
return mTriggered;
#else
- return mWrite == -1;
+ return !mWrite.ok();
#endif
}
diff --git a/libs/binder/RpcServer.cpp b/libs/binder/RpcServer.cpp
index 1ba20b3..fefaa81 100644
--- a/libs/binder/RpcServer.cpp
+++ b/libs/binder/RpcServer.cpp
@@ -168,7 +168,7 @@
void RpcServer::setServerSocketModifier(std::function<void(base::borrowed_fd)>&& modifier) {
RpcMutexLockGuard _l(mLock);
- LOG_ALWAYS_FATAL_IF(mServer.fd != -1, "Already started");
+ LOG_ALWAYS_FATAL_IF(mServer.fd.ok(), "Already started");
mServerSocketModifier = std::move(modifier);
}
@@ -200,7 +200,7 @@
status_t RpcServer::acceptSocketConnection(const RpcServer& server, RpcTransportFd* out) {
RpcTransportFd clientSocket(unique_fd(TEMP_FAILURE_RETRY(
accept4(server.mServer.fd.get(), nullptr, nullptr, SOCK_CLOEXEC | SOCK_NONBLOCK))));
- if (clientSocket.fd < 0) {
+ if (!clientSocket.fd.ok()) {
int savedErrno = errno;
ALOGE("Could not accept4 socket: %s", strerror(savedErrno));
return -savedErrno;
diff --git a/libs/binder/RpcSession.cpp b/libs/binder/RpcSession.cpp
index c895b21..cd8f417 100644
--- a/libs/binder/RpcSession.cpp
+++ b/libs/binder/RpcSession.cpp
@@ -208,7 +208,7 @@
unique_fd serverFd(TEMP_FAILURE_RETRY(open("/dev/null", O_WRONLY | O_CLOEXEC)));
- if (serverFd == -1) {
+ if (!serverFd.ok()) {
int savedErrno = errno;
ALOGE("Could not connect to /dev/null: %s", strerror(savedErrno));
return -savedErrno;
@@ -594,7 +594,7 @@
unique_fd serverFd(TEMP_FAILURE_RETRY(
socket(addr.addr()->sa_family, SOCK_STREAM | SOCK_CLOEXEC | SOCK_NONBLOCK, 0)));
- if (serverFd == -1) {
+ if (!serverFd.ok()) {
int savedErrno = errno;
ALOGE("Could not create socket at %s: %s", addr.toString().c_str(),
strerror(savedErrno));
diff --git a/libs/binder/Utils.h b/libs/binder/Utils.h
index b8aaf67..eec09eb 100644
--- a/libs/binder/Utils.h
+++ b/libs/binder/Utils.h
@@ -14,6 +14,8 @@
* limitations under the License.
*/
+#pragma once
+
#include <stddef.h>
#include <sys/uio.h>
#include <cstdint>
diff --git a/libs/binder/rust/src/binder.rs b/libs/binder/rust/src/binder.rs
index 78f8877..6d122c5 100644
--- a/libs/binder/rust/src/binder.rs
+++ b/libs/binder/rust/src/binder.rs
@@ -27,7 +27,7 @@
use std::convert::TryFrom;
use std::ffi::{c_void, CStr, CString};
use std::fmt;
-use std::fs::File;
+use std::io::Write;
use std::marker::PhantomData;
use std::ops::Deref;
use std::os::raw::c_char;
@@ -62,7 +62,7 @@
///
/// This handler is a no-op by default and should be implemented for each
/// Binder service struct that wishes to respond to dump transactions.
- fn dump(&self, _file: &File, _args: &[&CStr]) -> Result<()> {
+ fn dump(&self, _writer: &mut dyn Write, _args: &[&CStr]) -> Result<()> {
Ok(())
}
}
@@ -165,7 +165,7 @@
/// Handle a request to invoke the dump transaction on this
/// object.
- fn on_dump(&self, file: &File, args: &[&CStr]) -> Result<()>;
+ fn on_dump(&self, file: &mut dyn Write, args: &[&CStr]) -> Result<()>;
/// Retrieve the class of this remote object.
///
@@ -934,8 +934,8 @@
}
}
- fn on_dump(&self, file: &std::fs::File, args: &[&std::ffi::CStr]) -> std::result::Result<(), $crate::StatusCode> {
- self.0.dump(file, args)
+ fn on_dump(&self, writer: &mut dyn std::io::Write, args: &[&std::ffi::CStr]) -> std::result::Result<(), $crate::StatusCode> {
+ self.0.dump(writer, args)
}
fn get_class() -> $crate::binder_impl::InterfaceClass {
diff --git a/libs/binder/rust/src/native.rs b/libs/binder/rust/src/native.rs
index b248f5e..b250012 100644
--- a/libs/binder/rust/src/native.rs
+++ b/libs/binder/rust/src/native.rs
@@ -25,6 +25,7 @@
use std::convert::TryFrom;
use std::ffi::{c_void, CStr, CString};
use std::fs::File;
+use std::io::Write;
use std::mem::ManuallyDrop;
use std::ops::Deref;
use std::os::raw::c_char;
@@ -341,7 +342,7 @@
}
// Safety: Our caller promised that fd is a file descriptor. We don't
// own this file descriptor, so we need to be careful not to drop it.
- let file = unsafe { ManuallyDrop::new(File::from_raw_fd(fd)) };
+ let mut file = unsafe { ManuallyDrop::new(File::from_raw_fd(fd)) };
if args.is_null() && num_args != 0 {
return StatusCode::UNEXPECTED_NULL as status_t;
@@ -366,7 +367,7 @@
// Safety: Our caller promised that the binder has a `T` pointer in its
// user data.
let binder: &T = unsafe { &*(object as *const T) };
- let res = binder.on_dump(&file, &args);
+ let res = binder.on_dump(&mut *file, &args);
match res {
Ok(()) => 0,
@@ -569,7 +570,7 @@
Ok(())
}
- fn on_dump(&self, _file: &File, _args: &[&CStr]) -> Result<()> {
+ fn on_dump(&self, _writer: &mut dyn Write, _args: &[&CStr]) -> Result<()> {
Ok(())
}
diff --git a/libs/binder/rust/tests/integration.rs b/libs/binder/rust/tests/integration.rs
index c049b80..c87fa89 100644
--- a/libs/binder/rust/tests/integration.rs
+++ b/libs/binder/rust/tests/integration.rs
@@ -26,7 +26,7 @@
use std::convert::{TryFrom, TryInto};
use std::ffi::CStr;
-use std::fs::File;
+use std::io::Write;
use std::sync::Mutex;
/// Name of service runner.
@@ -118,7 +118,7 @@
}
impl Interface for TestService {
- fn dump(&self, _file: &File, args: &[&CStr]) -> Result<(), StatusCode> {
+ fn dump(&self, _writer: &mut dyn Write, args: &[&CStr]) -> Result<(), StatusCode> {
let mut dump_args = self.dump_args.lock().unwrap();
dump_args.extend(args.iter().map(|s| s.to_str().unwrap().to_owned()));
Ok(())
diff --git a/libs/binder/tests/binderRpcTest.cpp b/libs/binder/tests/binderRpcTest.cpp
index b86eb94..624edba 100644
--- a/libs/binder/tests/binderRpcTest.cpp
+++ b/libs/binder/tests/binderRpcTest.cpp
@@ -287,8 +287,10 @@
auto writeFd = std::to_string(writeEnd.get());
auto readFd = std::to_string(readEnd.get());
- execl(servicePath.c_str(), servicePath.c_str(), writeFd.c_str(), readFd.c_str(),
- NULL);
+ auto status = execl(servicePath.c_str(), servicePath.c_str(), writeFd.c_str(),
+ readFd.c_str(), NULL);
+ PLOGF("execl('%s', _, %s, %s) should not return at all, but it returned %d",
+ servicePath.c_str(), writeFd.c_str(), readFd.c_str(), status);
}));
BinderRpcTestServerConfig serverConfig;
@@ -1157,7 +1159,7 @@
return false;
}
- LOG_ALWAYS_FATAL_IF(serverFd == -1, "Could not create socket: %s", strerror(errno));
+ LOG_ALWAYS_FATAL_IF(!serverFd.ok(), "Could not create socket: %s", strerror(errno));
sockaddr_vm serverAddr{
.svm_family = AF_VSOCK,
@@ -1179,7 +1181,7 @@
// and they return ETIMEDOUT after that.
android::base::unique_fd connectFd(
TEMP_FAILURE_RETRY(socket(AF_VSOCK, SOCK_STREAM | SOCK_CLOEXEC | SOCK_NONBLOCK, 0)));
- LOG_ALWAYS_FATAL_IF(connectFd == -1, "Could not create socket for port %u: %s", vsockPort,
+ LOG_ALWAYS_FATAL_IF(!connectFd.ok(), "Could not create socket for port %u: %s", vsockPort,
strerror(errno));
bool success = false;
diff --git a/libs/gui/WindowInfo.cpp b/libs/gui/WindowInfo.cpp
index 2eb6bd6..6a4460b 100644
--- a/libs/gui/WindowInfo.cpp
+++ b/libs/gui/WindowInfo.cpp
@@ -66,8 +66,9 @@
bool WindowInfo::operator==(const WindowInfo& info) const {
return info.token == token && info.id == id && info.name == name &&
info.dispatchingTimeout == dispatchingTimeout && info.frame == frame &&
- info.surfaceInset == surfaceInset && info.globalScaleFactor == globalScaleFactor &&
- info.transform == transform && info.touchableRegion.hasSameRects(touchableRegion) &&
+ info.contentSize == contentSize && info.surfaceInset == surfaceInset &&
+ info.globalScaleFactor == globalScaleFactor && info.transform == transform &&
+ info.touchableRegion.hasSameRects(touchableRegion) &&
info.touchOcclusionMode == touchOcclusionMode && info.ownerPid == ownerPid &&
info.ownerUid == ownerUid && info.packageName == packageName &&
info.inputConfig == inputConfig && info.displayId == displayId &&
@@ -101,6 +102,8 @@
parcel->writeInt32(
static_cast<std::underlying_type_t<WindowInfo::Type>>(layoutParamsType)) ?:
parcel->write(frame) ?:
+ parcel->writeInt32(contentSize.width) ?:
+ parcel->writeInt32(contentSize.height) ?:
parcel->writeInt32(surfaceInset) ?:
parcel->writeFloat(globalScaleFactor) ?:
parcel->writeFloat(alpha) ?:
@@ -150,6 +153,8 @@
status = parcel->readInt32(&lpFlags) ?:
parcel->readInt32(&lpType) ?:
parcel->read(frame) ?:
+ parcel->readInt32(&contentSize.width) ?:
+ parcel->readInt32(&contentSize.height) ?:
parcel->readInt32(&surfaceInset) ?:
parcel->readFloat(&globalScaleFactor) ?:
parcel->readFloat(&alpha) ?:
diff --git a/libs/gui/include/gui/WindowInfo.h b/libs/gui/include/gui/WindowInfo.h
index bd2eb74..dcc38d7 100644
--- a/libs/gui/include/gui/WindowInfo.h
+++ b/libs/gui/include/gui/WindowInfo.h
@@ -26,6 +26,7 @@
#include <gui/constants.h>
#include <ui/Rect.h>
#include <ui/Region.h>
+#include <ui/Size.h>
#include <ui/Transform.h>
#include <utils/RefBase.h>
#include <utils/Timers.h>
@@ -196,6 +197,9 @@
/* These values are filled in by SurfaceFlinger. */
Rect frame = Rect::INVALID_RECT;
+ // The real size of the content, excluding any crop. If no buffer is rendered, this is 0,0
+ ui::Size contentSize = ui::Size(0, 0);
+
/*
* SurfaceFlinger consumes this value to shrink the computed frame. This is
* different from shrinking the touchable region in that it DOES shift the coordinate
diff --git a/libs/gui/tests/WindowInfo_test.cpp b/libs/gui/tests/WindowInfo_test.cpp
index f2feaef..5eb5d3b 100644
--- a/libs/gui/tests/WindowInfo_test.cpp
+++ b/libs/gui/tests/WindowInfo_test.cpp
@@ -28,6 +28,7 @@
using gui::InputApplicationInfo;
using gui::TouchOcclusionMode;
using gui::WindowInfo;
+using ui::Size;
namespace test {
@@ -53,6 +54,7 @@
i.layoutParamsType = WindowInfo::Type::INPUT_METHOD;
i.dispatchingTimeout = 12s;
i.frame = Rect(93, 34, 16, 19);
+ i.contentSize = Size(10, 40);
i.surfaceInset = 17;
i.globalScaleFactor = 0.3;
i.alpha = 0.7;
@@ -83,6 +85,7 @@
ASSERT_EQ(i.layoutParamsType, i2.layoutParamsType);
ASSERT_EQ(i.dispatchingTimeout, i2.dispatchingTimeout);
ASSERT_EQ(i.frame, i2.frame);
+ ASSERT_EQ(i.contentSize, i2.contentSize);
ASSERT_EQ(i.surfaceInset, i2.surfaceInset);
ASSERT_EQ(i.globalScaleFactor, i2.globalScaleFactor);
ASSERT_EQ(i.alpha, i2.alpha);
diff --git a/opengl/libs/EGL/MultifileBlobCache.cpp b/opengl/libs/EGL/MultifileBlobCache.cpp
index ed3c616..9905210 100644
--- a/opengl/libs/EGL/MultifileBlobCache.cpp
+++ b/opengl/libs/EGL/MultifileBlobCache.cpp
@@ -18,6 +18,7 @@
#include "MultifileBlobCache.h"
+#include <android-base/properties.h>
#include <dirent.h>
#include <fcntl.h>
#include <inttypes.h>
@@ -62,12 +63,15 @@
namespace android {
MultifileBlobCache::MultifileBlobCache(size_t maxKeySize, size_t maxValueSize, size_t maxTotalSize,
- const std::string& baseDir)
+ size_t maxTotalEntries, const std::string& baseDir)
: mInitialized(false),
+ mCacheVersion(0),
mMaxKeySize(maxKeySize),
mMaxValueSize(maxValueSize),
mMaxTotalSize(maxTotalSize),
+ mMaxTotalEntries(maxTotalEntries),
mTotalCacheSize(0),
+ mTotalCacheEntries(0),
mHotCacheLimit(0),
mHotCacheSize(0),
mWorkerThreadIdle(true) {
@@ -76,6 +80,26 @@
return;
}
+ // Set the cache version, override if debug value set
+ mCacheVersion = kMultifileBlobCacheVersion;
+ int debugCacheVersion = base::GetIntProperty("debug.egl.blobcache.cache_version", -1);
+ if (debugCacheVersion >= 0) {
+ ALOGV("INIT: Using %u as cacheVersion instead of %u", debugCacheVersion, mCacheVersion);
+ mCacheVersion = debugCacheVersion;
+ }
+
+ // Set the platform build ID, override if debug value set
+ mBuildId = base::GetProperty("ro.build.id", "");
+ std::string debugBuildId = base::GetProperty("debug.egl.blobcache.build_id", "");
+ if (!debugBuildId.empty()) {
+ ALOGV("INIT: Using %s as buildId instead of %s", debugBuildId.c_str(), mBuildId.c_str());
+ if (debugBuildId.length() > PROP_VALUE_MAX) {
+ ALOGV("INIT: debugBuildId is too long (%zu), reduce it to %u", debugBuildId.length(),
+ PROP_VALUE_MAX);
+ }
+ mBuildId = debugBuildId;
+ }
+
// Establish the name of our multifile directory
mMultifileDirName = baseDir + ".multifile";
@@ -93,14 +117,30 @@
mTaskThread = std::thread(&MultifileBlobCache::processTasks, this);
// See if the dir exists, and initialize using its contents
+ bool statusGood = false;
+
+ // Check that our cacheVersion and buildId match
struct stat st;
if (stat(mMultifileDirName.c_str(), &st) == 0) {
+ if (checkStatus(mMultifileDirName.c_str())) {
+ statusGood = true;
+ } else {
+ ALOGV("INIT: Cache status has changed, clearing the cache");
+ if (!clearCache()) {
+ ALOGE("INIT: Unable to clear cache");
+ return;
+ }
+ }
+ }
+
+ if (statusGood) {
// Read all the files and gather details, then preload their contents
DIR* dir;
struct dirent* entry;
if ((dir = opendir(mMultifileDirName.c_str())) != nullptr) {
while ((entry = readdir(dir)) != nullptr) {
- if (entry->d_name == "."s || entry->d_name == ".."s) {
+ if (entry->d_name == "."s || entry->d_name == ".."s ||
+ strcmp(entry->d_name, kMultifileBlobCacheStatusFile) == 0) {
continue;
}
@@ -123,7 +163,8 @@
if (st.st_size <= 0 || st.st_atime <= 0) {
ALOGE("INIT: Entry %u has invalid stats! Removing.", entryHash);
if (remove(fullPath.c_str()) != 0) {
- ALOGE("Error removing %s: %s", fullPath.c_str(), std::strerror(errno));
+ ALOGE("INIT: Error removing %s: %s", fullPath.c_str(),
+ std::strerror(errno));
}
continue;
}
@@ -140,7 +181,7 @@
MultifileHeader header;
size_t result = read(fd, static_cast<void*>(&header), sizeof(MultifileHeader));
if (result != sizeof(MultifileHeader)) {
- ALOGE("Error reading MultifileHeader from cache entry (%s): %s",
+ ALOGE("INIT: Error reading MultifileHeader from cache entry (%s): %s",
fullPath.c_str(), std::strerror(errno));
close(fd);
return;
@@ -150,7 +191,8 @@
if (header.magic != kMultifileMagic) {
ALOGE("INIT: Entry %u has bad magic (%u)! Removing.", entryHash, header.magic);
if (remove(fullPath.c_str()) != 0) {
- ALOGE("Error removing %s: %s", fullPath.c_str(), std::strerror(errno));
+ ALOGE("INIT: Error removing %s: %s", fullPath.c_str(),
+ std::strerror(errno));
}
close(fd);
continue;
@@ -175,7 +217,7 @@
if (header.crc !=
crc32c(mappedEntry + sizeof(MultifileHeader),
fileSize - sizeof(MultifileHeader))) {
- ALOGE("INIT: Entry %u failed CRC check! Removing.", entryHash);
+ ALOGV("INIT: Entry %u failed CRC check! Removing.", entryHash);
if (remove(fullPath.c_str()) != 0) {
ALOGE("Error removing %s: %s", fullPath.c_str(), std::strerror(errno));
}
@@ -184,11 +226,12 @@
// If the cache entry is damaged or no good, remove it
if (header.keySize <= 0 || header.valueSize <= 0) {
- ALOGE("INIT: Entry %u has a bad header keySize (%lu) or valueSize (%lu), "
+ ALOGV("INIT: Entry %u has a bad header keySize (%lu) or valueSize (%lu), "
"removing.",
entryHash, header.keySize, header.valueSize);
if (remove(fullPath.c_str()) != 0) {
- ALOGE("Error removing %s: %s", fullPath.c_str(), std::strerror(errno));
+ ALOGE("INIT: Error removing %s: %s", fullPath.c_str(),
+ std::strerror(errno));
}
continue;
}
@@ -226,9 +269,17 @@
// If the multifile directory does not exist, create it and start from scratch
if (mkdir(mMultifileDirName.c_str(), 0755) != 0 && (errno != EEXIST)) {
ALOGE("Unable to create directory (%s), errno (%i)", mMultifileDirName.c_str(), errno);
+ return;
+ }
+
+ // Create new status file
+ if (!createStatus(mMultifileDirName.c_str())) {
+ ALOGE("INIT: Failed to create status file!");
+ return;
}
}
+ ALOGV("INIT: Multifile BlobCache initialization succeeded");
mInitialized = true;
}
@@ -270,7 +321,7 @@
size_t fileSize = sizeof(MultifileHeader) + keySize + valueSize;
// If we're going to be over the cache limit, kick off a trim to clear space
- if (getTotalSize() + fileSize > mMaxTotalSize) {
+ if (getTotalSize() + fileSize > mMaxTotalSize || getTotalEntries() + 1 > mMaxTotalEntries) {
ALOGV("SET: Cache is full, calling trimCache to clear space");
trimCache();
}
@@ -469,6 +520,112 @@
}
}
+bool MultifileBlobCache::createStatus(const std::string& baseDir) {
+ // Populate the status struct
+ struct MultifileStatus status;
+ memset(&status, 0, sizeof(status));
+ status.magic = kMultifileMagic;
+ status.cacheVersion = mCacheVersion;
+
+ // Copy the buildId string in, up to our allocated space
+ strncpy(status.buildId, mBuildId.c_str(),
+ mBuildId.length() > PROP_VALUE_MAX ? PROP_VALUE_MAX : mBuildId.length());
+
+ // Finally update the crc, using cacheVersion and everything the follows
+ status.crc =
+ crc32c(reinterpret_cast<uint8_t*>(&status) + offsetof(MultifileStatus, cacheVersion),
+ sizeof(status) - offsetof(MultifileStatus, cacheVersion));
+
+ // Create the status file
+ std::string cacheStatus = baseDir + "/" + kMultifileBlobCacheStatusFile;
+ int fd = open(cacheStatus.c_str(), O_WRONLY | O_CREAT | O_TRUNC, S_IRUSR | S_IWUSR);
+ if (fd == -1) {
+ ALOGE("STATUS(CREATE): Unable to create status file: %s, error: %s", cacheStatus.c_str(),
+ std::strerror(errno));
+ return false;
+ }
+
+ // Write the buffer contents to disk
+ ssize_t result = write(fd, &status, sizeof(status));
+ close(fd);
+ if (result != sizeof(status)) {
+ ALOGE("STATUS(CREATE): Error writing cache status file: %s, error %s", cacheStatus.c_str(),
+ std::strerror(errno));
+ return false;
+ }
+
+ ALOGV("STATUS(CREATE): Created status file: %s", cacheStatus.c_str());
+ return true;
+}
+
+bool MultifileBlobCache::checkStatus(const std::string& baseDir) {
+ std::string cacheStatus = baseDir + "/" + kMultifileBlobCacheStatusFile;
+
+ // Does status exist
+ struct stat st;
+ if (stat(cacheStatus.c_str(), &st) != 0) {
+ ALOGV("STATUS(CHECK): Status file (%s) missing", cacheStatus.c_str());
+ return false;
+ }
+
+ // If the status entry is damaged or no good, remove it
+ if (st.st_size <= 0 || st.st_atime <= 0) {
+ ALOGE("STATUS(CHECK): Cache status has invalid stats!");
+ return false;
+ }
+
+ // Open the file so we can read its header
+ int fd = open(cacheStatus.c_str(), O_RDONLY);
+ if (fd == -1) {
+ ALOGE("STATUS(CHECK): Cache error - failed to open cacheStatus: %s, error: %s",
+ cacheStatus.c_str(), std::strerror(errno));
+ return false;
+ }
+
+ // Read in the status header
+ MultifileStatus status;
+ size_t result = read(fd, static_cast<void*>(&status), sizeof(MultifileStatus));
+ close(fd);
+ if (result != sizeof(MultifileStatus)) {
+ ALOGE("STATUS(CHECK): Error reading cache status (%s): %s", cacheStatus.c_str(),
+ std::strerror(errno));
+ return false;
+ }
+
+ // Verify header magic
+ if (status.magic != kMultifileMagic) {
+ ALOGE("STATUS(CHECK): Cache status has bad magic (%u)!", status.magic);
+ return false;
+ }
+
+ // Ensure we have a good CRC
+ if (status.crc !=
+ crc32c(reinterpret_cast<uint8_t*>(&status) + offsetof(MultifileStatus, cacheVersion),
+ sizeof(status) - offsetof(MultifileStatus, cacheVersion))) {
+ ALOGE("STATUS(CHECK): Cache status failed CRC check!");
+ return false;
+ }
+
+ // Check cacheVersion
+ if (status.cacheVersion != mCacheVersion) {
+ ALOGV("STATUS(CHECK): Cache version has changed! old(%u) new(%u)", status.cacheVersion,
+ mCacheVersion);
+ return false;
+ }
+
+ // Check buildId
+ if (strcmp(status.buildId, mBuildId.c_str()) != 0) {
+ ALOGV("STATUS(CHECK): BuildId has changed! old(%s) new(%s)", status.buildId,
+ mBuildId.c_str());
+ return false;
+ }
+
+ // All checks passed!
+ ALOGV("STATUS(CHECK): Status file is good! cacheVersion(%u), buildId(%s) file(%s)",
+ status.cacheVersion, status.buildId, cacheStatus.c_str());
+ return true;
+}
+
void MultifileBlobCache::trackEntry(uint32_t entryHash, EGLsizeiANDROID valueSize, size_t fileSize,
time_t accessTime) {
mEntries.insert(entryHash);
@@ -485,10 +642,12 @@
void MultifileBlobCache::increaseTotalCacheSize(size_t fileSize) {
mTotalCacheSize += fileSize;
+ mTotalCacheEntries++;
}
void MultifileBlobCache::decreaseTotalCacheSize(size_t fileSize) {
mTotalCacheSize -= fileSize;
+ mTotalCacheEntries--;
}
bool MultifileBlobCache::addToHotCache(uint32_t newEntryHash, int newFd, uint8_t* newEntryBuffer,
@@ -557,7 +716,7 @@
return false;
}
-bool MultifileBlobCache::applyLRU(size_t cacheLimit) {
+bool MultifileBlobCache::applyLRU(size_t cacheSizeLimit, size_t cacheEntryLimit) {
// Walk through our map of sorted last access times and remove files until under the limit
for (auto cacheEntryIter = mEntryStats.begin(); cacheEntryIter != mEntryStats.end();) {
uint32_t entryHash = cacheEntryIter->first;
@@ -590,9 +749,10 @@
// See if it has been reduced enough
size_t totalCacheSize = getTotalSize();
- if (totalCacheSize <= cacheLimit) {
+ size_t totalCacheEntries = getTotalEntries();
+ if (totalCacheSize <= cacheSizeLimit && totalCacheEntries <= cacheEntryLimit) {
// Success
- ALOGV("LRU: Reduced cache to %zu", totalCacheSize);
+ ALOGV("LRU: Reduced cache to size %zu entries %zu", totalCacheSize, totalCacheEntries);
return true;
}
}
@@ -601,8 +761,43 @@
return false;
}
+// Clear the cache by removing all entries and deleting the directory
+bool MultifileBlobCache::clearCache() {
+ DIR* dir;
+ struct dirent* entry;
+ dir = opendir(mMultifileDirName.c_str());
+ if (dir == nullptr) {
+ ALOGE("CLEAR: Unable to open multifile dir: %s", mMultifileDirName.c_str());
+ return false;
+ }
+
+ // Delete all entries and the status file
+ while ((entry = readdir(dir)) != nullptr) {
+ if (entry->d_name == "."s || entry->d_name == ".."s) {
+ continue;
+ }
+
+ std::string entryName = entry->d_name;
+ std::string fullPath = mMultifileDirName + "/" + entryName;
+ if (remove(fullPath.c_str()) != 0) {
+ ALOGE("CLEAR: Error removing %s: %s", fullPath.c_str(), std::strerror(errno));
+ return false;
+ }
+ }
+
+ // Delete the directory
+ if (remove(mMultifileDirName.c_str()) != 0) {
+ ALOGE("CLEAR: Error removing %s: %s", mMultifileDirName.c_str(), std::strerror(errno));
+ return false;
+ }
+
+ ALOGV("CLEAR: Cleared the multifile blobcache");
+ return true;
+}
+
// When removing files, what fraction of the overall limit should be reached when removing files
// A divisor of two will decrease the cache to 50%, four to 25% and so on
+// We use the same limit to manage size and entry count
constexpr uint32_t kCacheLimitDivisor = 2;
// Calculate the cache size and remove old entries until under the limit
@@ -611,8 +806,9 @@
ALOGV("TRIM: Waiting for work to complete.");
waitForWorkComplete();
- ALOGV("TRIM: Reducing multifile cache size to %zu", mMaxTotalSize / kCacheLimitDivisor);
- if (!applyLRU(mMaxTotalSize / kCacheLimitDivisor)) {
+ ALOGV("TRIM: Reducing multifile cache size to %zu, entries %zu",
+ mMaxTotalSize / kCacheLimitDivisor, mMaxTotalEntries / kCacheLimitDivisor);
+ if (!applyLRU(mMaxTotalSize / kCacheLimitDivisor, mMaxTotalEntries / kCacheLimitDivisor)) {
ALOGE("Error when clearing multifile shader cache");
return;
}
diff --git a/opengl/libs/EGL/MultifileBlobCache.h b/opengl/libs/EGL/MultifileBlobCache.h
index 5e527dc..18566c2 100644
--- a/opengl/libs/EGL/MultifileBlobCache.h
+++ b/opengl/libs/EGL/MultifileBlobCache.h
@@ -21,6 +21,7 @@
#include <EGL/eglext.h>
#include <android-base/thread_annotations.h>
+#include <cutils/properties.h>
#include <future>
#include <map>
#include <queue>
@@ -33,6 +34,9 @@
namespace android {
+constexpr uint32_t kMultifileBlobCacheVersion = 1;
+constexpr char kMultifileBlobCacheStatusFile[] = "cache.status";
+
struct MultifileHeader {
uint32_t magic;
uint32_t crc;
@@ -46,6 +50,13 @@
time_t accessTime;
};
+struct MultifileStatus {
+ uint32_t magic;
+ uint32_t crc;
+ uint32_t cacheVersion;
+ char buildId[PROP_VALUE_MAX];
+};
+
struct MultifileHotCache {
int entryFd;
uint8_t* entryBuffer;
@@ -92,7 +103,7 @@
class MultifileBlobCache {
public:
MultifileBlobCache(size_t maxKeySize, size_t maxValueSize, size_t maxTotalSize,
- const std::string& baseDir);
+ size_t maxTotalEntries, const std::string& baseDir);
~MultifileBlobCache();
void set(const void* key, EGLsizeiANDROID keySize, const void* value,
@@ -103,6 +114,13 @@
void finish();
size_t getTotalSize() const { return mTotalCacheSize; }
+ size_t getTotalEntries() const { return mTotalCacheEntries; }
+
+ const std::string& getCurrentBuildId() const { return mBuildId; }
+ void setCurrentBuildId(const std::string& buildId) { mBuildId = buildId; }
+
+ uint32_t getCurrentCacheVersion() const { return mCacheVersion; }
+ void setCurrentCacheVersion(uint32_t cacheVersion) { mCacheVersion = cacheVersion; }
private:
void trackEntry(uint32_t entryHash, EGLsizeiANDROID valueSize, size_t fileSize,
@@ -111,6 +129,9 @@
bool removeEntry(uint32_t entryHash);
MultifileEntryStats getEntryStats(uint32_t entryHash);
+ bool createStatus(const std::string& baseDir);
+ bool checkStatus(const std::string& baseDir);
+
size_t getFileSize(uint32_t entryHash);
size_t getValueSize(uint32_t entryHash);
@@ -120,12 +141,16 @@
bool addToHotCache(uint32_t entryHash, int fd, uint8_t* entryBufer, size_t entrySize);
bool removeFromHotCache(uint32_t entryHash);
+ bool clearCache();
void trimCache();
- bool applyLRU(size_t cacheLimit);
+ bool applyLRU(size_t cacheSizeLimit, size_t cacheEntryLimit);
bool mInitialized;
std::string mMultifileDirName;
+ std::string mBuildId;
+ uint32_t mCacheVersion;
+
std::unordered_set<uint32_t> mEntries;
std::unordered_map<uint32_t, MultifileEntryStats> mEntryStats;
std::unordered_map<uint32_t, MultifileHotCache> mHotCache;
@@ -133,7 +158,9 @@
size_t mMaxKeySize;
size_t mMaxValueSize;
size_t mMaxTotalSize;
+ size_t mMaxTotalEntries;
size_t mTotalCacheSize;
+ size_t mTotalCacheEntries;
size_t mHotCacheLimit;
size_t mHotCacheEntryLimit;
size_t mHotCacheSize;
diff --git a/opengl/libs/EGL/MultifileBlobCache_test.cpp b/opengl/libs/EGL/MultifileBlobCache_test.cpp
index 1639be6..90a0f1e 100644
--- a/opengl/libs/EGL/MultifileBlobCache_test.cpp
+++ b/opengl/libs/EGL/MultifileBlobCache_test.cpp
@@ -16,13 +16,17 @@
#include "MultifileBlobCache.h"
+#include <android-base/properties.h>
#include <android-base/test_utils.h>
#include <fcntl.h>
#include <gtest/gtest.h>
#include <stdio.h>
+#include <fstream>
#include <memory>
+using namespace std::literals;
+
namespace android {
template <typename T>
@@ -31,23 +35,40 @@
constexpr size_t kMaxKeySize = 2 * 1024;
constexpr size_t kMaxValueSize = 6 * 1024;
constexpr size_t kMaxTotalSize = 32 * 1024;
+constexpr size_t kMaxTotalEntries = 64;
class MultifileBlobCacheTest : public ::testing::Test {
protected:
virtual void SetUp() {
+ clearProperties();
mTempFile.reset(new TemporaryFile());
mMBC.reset(new MultifileBlobCache(kMaxKeySize, kMaxValueSize, kMaxTotalSize,
- &mTempFile->path[0]));
+ kMaxTotalEntries, &mTempFile->path[0]));
}
- virtual void TearDown() { mMBC.reset(); }
+ virtual void TearDown() {
+ clearProperties();
+ mMBC.reset();
+ }
int getFileDescriptorCount();
+ std::vector<std::string> getCacheEntries();
+
+ void clearProperties();
std::unique_ptr<TemporaryFile> mTempFile;
std::unique_ptr<MultifileBlobCache> mMBC;
};
+void MultifileBlobCacheTest::clearProperties() {
+ // Clear any debug properties used in the tests
+ base::SetProperty("debug.egl.blobcache.cache_version", "");
+ base::WaitForProperty("debug.egl.blobcache.cache_version", "");
+
+ base::SetProperty("debug.egl.blobcache.build_id", "");
+ base::WaitForProperty("debug.egl.blobcache.build_id", "");
+}
+
TEST_F(MultifileBlobCacheTest, CacheSingleValueSucceeds) {
unsigned char buf[4] = {0xee, 0xee, 0xee, 0xee};
mMBC->set("abcd", 4, "efgh", 4);
@@ -211,6 +232,23 @@
}
}
+TEST_F(MultifileBlobCacheTest, CacheMaxEntrySucceeds) {
+ // Fill the cache with max entries
+ int i = 0;
+ for (i = 0; i < kMaxTotalEntries; i++) {
+ mMBC->set(std::to_string(i).c_str(), sizeof(i), std::to_string(i).c_str(), sizeof(i));
+ }
+
+ // Ensure it is full
+ ASSERT_EQ(mMBC->getTotalEntries(), kMaxTotalEntries);
+
+ // Add another entry
+ mMBC->set(std::to_string(i).c_str(), sizeof(i), std::to_string(i).c_str(), sizeof(i));
+
+ // Ensure total entries is cut in half + 1
+ ASSERT_EQ(mMBC->getTotalEntries(), kMaxTotalEntries / 2 + 1);
+}
+
TEST_F(MultifileBlobCacheTest, CacheMinKeyAndValueSizeSucceeds) {
unsigned char buf[1] = {0xee};
mMBC->set("x", 1, "y", 1);
@@ -234,8 +272,7 @@
TEST_F(MultifileBlobCacheTest, EnsureFileDescriptorsClosed) {
// Populate the cache with a bunch of entries
- size_t kLargeNumberOfEntries = 1024;
- for (int i = 0; i < kLargeNumberOfEntries; i++) {
+ for (int i = 0; i < kMaxTotalEntries; i++) {
// printf("Caching: %i", i);
// Use the index as the key and value
@@ -247,27 +284,223 @@
}
// Ensure we don't have a bunch of open fds
- ASSERT_LT(getFileDescriptorCount(), kLargeNumberOfEntries / 2);
+ ASSERT_LT(getFileDescriptorCount(), kMaxTotalEntries / 2);
// Close the cache so everything writes out
mMBC->finish();
mMBC.reset();
// Now open it again and ensure we still don't have a bunch of open fds
- mMBC.reset(
- new MultifileBlobCache(kMaxKeySize, kMaxValueSize, kMaxTotalSize, &mTempFile->path[0]));
+ mMBC.reset(new MultifileBlobCache(kMaxKeySize, kMaxValueSize, kMaxTotalSize, kMaxTotalEntries,
+ &mTempFile->path[0]));
// Check after initialization
- ASSERT_LT(getFileDescriptorCount(), kLargeNumberOfEntries / 2);
+ ASSERT_LT(getFileDescriptorCount(), kMaxTotalEntries / 2);
- for (int i = 0; i < kLargeNumberOfEntries; i++) {
+ for (int i = 0; i < kMaxTotalEntries; i++) {
int result = 0;
ASSERT_EQ(sizeof(i), mMBC->get(&i, sizeof(i), &result, sizeof(result)));
ASSERT_EQ(i, result);
}
// And again after we've actually used it
- ASSERT_LT(getFileDescriptorCount(), kLargeNumberOfEntries / 2);
+ ASSERT_LT(getFileDescriptorCount(), kMaxTotalEntries / 2);
+}
+
+std::vector<std::string> MultifileBlobCacheTest::getCacheEntries() {
+ std::string cachePath = &mTempFile->path[0];
+ std::string multifileDirName = cachePath + ".multifile";
+ std::vector<std::string> cacheEntries;
+
+ struct stat info;
+ if (stat(multifileDirName.c_str(), &info) == 0) {
+ // We have a multifile dir. Skip the status file and return the only entry.
+ DIR* dir;
+ struct dirent* entry;
+ if ((dir = opendir(multifileDirName.c_str())) != nullptr) {
+ while ((entry = readdir(dir)) != nullptr) {
+ if (entry->d_name == "."s || entry->d_name == ".."s) {
+ continue;
+ }
+ if (strcmp(entry->d_name, kMultifileBlobCacheStatusFile) == 0) {
+ continue;
+ }
+ cacheEntries.push_back(multifileDirName + "/" + entry->d_name);
+ }
+ } else {
+ printf("Unable to open %s, error: %s\n", multifileDirName.c_str(),
+ std::strerror(errno));
+ }
+ } else {
+ printf("Unable to stat %s, error: %s\n", multifileDirName.c_str(), std::strerror(errno));
+ }
+
+ return cacheEntries;
+}
+
+TEST_F(MultifileBlobCacheTest, CacheContainsStatus) {
+ struct stat info;
+ std::stringstream statusFile;
+ statusFile << &mTempFile->path[0] << ".multifile/" << kMultifileBlobCacheStatusFile;
+
+ // After INIT, cache should have a status
+ ASSERT_TRUE(stat(statusFile.str().c_str(), &info) == 0);
+
+ // Set one entry
+ mMBC->set("abcd", 4, "efgh", 4);
+
+ // Close the cache so everything writes out
+ mMBC->finish();
+ mMBC.reset();
+
+ // Ensure status lives after closing the cache
+ ASSERT_TRUE(stat(statusFile.str().c_str(), &info) == 0);
+
+ // Open the cache again
+ mMBC.reset(new MultifileBlobCache(kMaxKeySize, kMaxValueSize, kMaxTotalSize, kMaxTotalEntries,
+ &mTempFile->path[0]));
+
+ // Ensure we still have a status
+ ASSERT_TRUE(stat(statusFile.str().c_str(), &info) == 0);
+}
+
+// Verify missing cache status file causes cache the be cleared
+TEST_F(MultifileBlobCacheTest, MissingCacheStatusClears) {
+ // Set one entry
+ mMBC->set("abcd", 4, "efgh", 4);
+
+ // Close the cache so everything writes out
+ mMBC->finish();
+ mMBC.reset();
+
+ // Ensure there is one cache entry
+ ASSERT_EQ(getCacheEntries().size(), 1);
+
+ // Delete the status file
+ std::stringstream statusFile;
+ statusFile << &mTempFile->path[0] << ".multifile/" << kMultifileBlobCacheStatusFile;
+ remove(statusFile.str().c_str());
+
+ // Open the cache again and ensure no cache hits
+ mMBC.reset(new MultifileBlobCache(kMaxKeySize, kMaxValueSize, kMaxTotalSize, kMaxTotalEntries,
+ &mTempFile->path[0]));
+
+ // Ensure we have no entries
+ ASSERT_EQ(getCacheEntries().size(), 0);
+}
+
+// Verify modified cache status file BEGIN causes cache to be cleared
+TEST_F(MultifileBlobCacheTest, ModifiedCacheStatusBeginClears) {
+ // Set one entry
+ mMBC->set("abcd", 4, "efgh", 4);
+
+ // Close the cache so everything writes out
+ mMBC->finish();
+ mMBC.reset();
+
+ // Ensure there is one cache entry
+ ASSERT_EQ(getCacheEntries().size(), 1);
+
+ // Modify the status file
+ std::stringstream statusFile;
+ statusFile << &mTempFile->path[0] << ".multifile/" << kMultifileBlobCacheStatusFile;
+
+ // Stomp on the beginning of the cache file
+ const char* stomp = "BADF00D";
+ std::fstream fs(statusFile.str());
+ fs.seekp(0, std::ios_base::beg);
+ fs.write(stomp, strlen(stomp));
+ fs.flush();
+ fs.close();
+
+ // Open the cache again and ensure no cache hits
+ mMBC.reset(new MultifileBlobCache(kMaxKeySize, kMaxValueSize, kMaxTotalSize, kMaxTotalEntries,
+ &mTempFile->path[0]));
+
+ // Ensure we have no entries
+ ASSERT_EQ(getCacheEntries().size(), 0);
+}
+
+// Verify modified cache status file END causes cache to be cleared
+TEST_F(MultifileBlobCacheTest, ModifiedCacheStatusEndClears) {
+ // Set one entry
+ mMBC->set("abcd", 4, "efgh", 4);
+
+ // Close the cache so everything writes out
+ mMBC->finish();
+ mMBC.reset();
+
+ // Ensure there is one cache entry
+ ASSERT_EQ(getCacheEntries().size(), 1);
+
+ // Modify the status file
+ std::stringstream statusFile;
+ statusFile << &mTempFile->path[0] << ".multifile/" << kMultifileBlobCacheStatusFile;
+
+ // Stomp on the END of the cache status file, modifying its contents
+ const char* stomp = "BADF00D";
+ std::fstream fs(statusFile.str());
+ fs.seekp(-strlen(stomp), std::ios_base::end);
+ fs.write(stomp, strlen(stomp));
+ fs.flush();
+ fs.close();
+
+ // Open the cache again and ensure no cache hits
+ mMBC.reset(new MultifileBlobCache(kMaxKeySize, kMaxValueSize, kMaxTotalSize, kMaxTotalEntries,
+ &mTempFile->path[0]));
+
+ // Ensure we have no entries
+ ASSERT_EQ(getCacheEntries().size(), 0);
+}
+
+// Verify mismatched cacheVersion causes cache to be cleared
+TEST_F(MultifileBlobCacheTest, MismatchedCacheVersionClears) {
+ // Set one entry
+ mMBC->set("abcd", 4, "efgh", 4);
+
+ // Close the cache so everything writes out
+ mMBC->finish();
+ mMBC.reset();
+
+ // Ensure there is one cache entry
+ ASSERT_EQ(getCacheEntries().size(), 1);
+
+ // Set a debug cacheVersion
+ std::string newCacheVersion = std::to_string(kMultifileBlobCacheVersion + 1);
+ ASSERT_TRUE(base::SetProperty("debug.egl.blobcache.cache_version", newCacheVersion.c_str()));
+ ASSERT_TRUE(
+ base::WaitForProperty("debug.egl.blobcache.cache_version", newCacheVersion.c_str()));
+
+ // Open the cache again and ensure no cache hits
+ mMBC.reset(new MultifileBlobCache(kMaxKeySize, kMaxValueSize, kMaxTotalSize, kMaxTotalEntries,
+ &mTempFile->path[0]));
+
+ // Ensure we have no entries
+ ASSERT_EQ(getCacheEntries().size(), 0);
+}
+
+// Verify mismatched buildId causes cache to be cleared
+TEST_F(MultifileBlobCacheTest, MismatchedBuildIdClears) {
+ // Set one entry
+ mMBC->set("abcd", 4, "efgh", 4);
+
+ // Close the cache so everything writes out
+ mMBC->finish();
+ mMBC.reset();
+
+ // Ensure there is one cache entry
+ ASSERT_EQ(getCacheEntries().size(), 1);
+
+ // Set a debug buildId
+ base::SetProperty("debug.egl.blobcache.build_id", "foo");
+ base::WaitForProperty("debug.egl.blobcache.build_id", "foo");
+
+ // Open the cache again and ensure no cache hits
+ mMBC.reset(new MultifileBlobCache(kMaxKeySize, kMaxValueSize, kMaxTotalSize, kMaxTotalEntries,
+ &mTempFile->path[0]));
+
+ // Ensure we have no entries
+ ASSERT_EQ(getCacheEntries().size(), 0);
}
} // namespace android
diff --git a/opengl/libs/EGL/egl_cache.cpp b/opengl/libs/EGL/egl_cache.cpp
index 1b68344..98ff1d1 100644
--- a/opengl/libs/EGL/egl_cache.cpp
+++ b/opengl/libs/EGL/egl_cache.cpp
@@ -41,6 +41,7 @@
constexpr uint32_t kMaxMultifileKeySize = 1 * 1024 * 1024;
constexpr uint32_t kMaxMultifileValueSize = 8 * 1024 * 1024;
constexpr uint32_t kMaxMultifileTotalSize = 32 * 1024 * 1024;
+constexpr uint32_t kMaxMultifileTotalEntries = 4 * 1024;
namespace android {
@@ -277,7 +278,7 @@
if (mMultifileBlobCache == nullptr) {
mMultifileBlobCache.reset(new MultifileBlobCache(kMaxMultifileKeySize,
kMaxMultifileValueSize, mCacheByteLimit,
- mFilename));
+ kMaxMultifileTotalEntries, mFilename));
}
return mMultifileBlobCache.get();
}
diff --git a/opengl/libs/EGL/fuzzer/MultifileBlobCache_fuzzer.cpp b/opengl/libs/EGL/fuzzer/MultifileBlobCache_fuzzer.cpp
index 633cc9c..2d9ee3a 100644
--- a/opengl/libs/EGL/fuzzer/MultifileBlobCache_fuzzer.cpp
+++ b/opengl/libs/EGL/fuzzer/MultifileBlobCache_fuzzer.cpp
@@ -28,6 +28,7 @@
constexpr size_t kMaxKeySize = 2 * 1024;
constexpr size_t kMaxValueSize = 6 * 1024;
constexpr size_t kMaxTotalSize = 32 * 1024;
+constexpr size_t kMaxTotalEntries = 64;
extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) {
// To fuzz this, we're going to create a key/value pair from data
@@ -79,8 +80,8 @@
std::unique_ptr<MultifileBlobCache> mbc;
tempFile.reset(new TemporaryFile());
- mbc.reset(
- new MultifileBlobCache(kMaxKeySize, kMaxValueSize, kMaxTotalSize, &tempFile->path[0]));
+ mbc.reset(new MultifileBlobCache(kMaxKeySize, kMaxValueSize, kMaxTotalSize, kMaxTotalEntries,
+ &tempFile->path[0]));
// With remaining data, select different paths below
int loopCount = 1;
uint8_t bumpCount = 0;
@@ -131,8 +132,8 @@
// Place the maxKey/maxValue twice
// The first will fit, the second will trigger hot cache trimming
tempFile.reset(new TemporaryFile());
- mbc.reset(
- new MultifileBlobCache(kMaxKeySize, kMaxValueSize, kMaxTotalSize, &tempFile->path[0]));
+ mbc.reset(new MultifileBlobCache(kMaxKeySize, kMaxValueSize, kMaxTotalSize, kMaxTotalEntries,
+ &tempFile->path[0]));
uint8_t* buffer = new uint8_t[kMaxValueSize];
mbc->set(maxKey1.data(), kMaxKeySize, maxValue1.data(), kMaxValueSize);
mbc->set(maxKey2.data(), kMaxKeySize, maxValue2.data(), kMaxValueSize);
@@ -145,7 +146,7 @@
// overflow
tempFile.reset(new TemporaryFile());
mbc.reset(new MultifileBlobCache(kMaxKeySize, kMaxValueSize, 2 * (kMaxKeySize + kMaxValueSize),
- &tempFile->path[0]));
+ kMaxTotalEntries, &tempFile->path[0]));
mbc->set(maxKey1.data(), kMaxKeySize, maxValue1.data(), kMaxValueSize);
mbc->set(maxKey2.data(), kMaxKeySize, maxValue2.data(), kMaxValueSize);
mbc->get(maxKey1.data(), kMaxKeySize, buffer, kMaxValueSize);
diff --git a/opengl/tests/EGLTest/egl_cache_test.cpp b/opengl/tests/EGLTest/egl_cache_test.cpp
index f81c68f..ce58182 100644
--- a/opengl/tests/EGLTest/egl_cache_test.cpp
+++ b/opengl/tests/EGLTest/egl_cache_test.cpp
@@ -114,25 +114,26 @@
struct stat info;
if (stat(multifileDirName.c_str(), &info) == 0) {
// Ensure we only have one file to manage
- int realFileCount = 0;
+ int entryFileCount = 0;
- // We have a multifile dir. Return the only real file in it.
+ // We have a multifile dir. Return the only entry file in it.
DIR* dir;
struct dirent* entry;
if ((dir = opendir(multifileDirName.c_str())) != nullptr) {
while ((entry = readdir(dir)) != nullptr) {
- if (entry->d_name == "."s || entry->d_name == ".."s) {
+ if (entry->d_name == "."s || entry->d_name == ".."s ||
+ strcmp(entry->d_name, kMultifileBlobCacheStatusFile) == 0) {
continue;
}
cachefileName = multifileDirName + "/" + entry->d_name;
- realFileCount++;
+ entryFileCount++;
}
} else {
printf("Unable to open %s, error: %s\n",
multifileDirName.c_str(), std::strerror(errno));
}
- if (realFileCount != 1) {
+ if (entryFileCount != 1) {
// If there was more than one real file in the directory, this
// violates test assumptions
cachefileName = "";
diff --git a/services/powermanager/Android.bp b/services/powermanager/Android.bp
index 8b16890..1f72e8b 100644
--- a/services/powermanager/Android.bp
+++ b/services/powermanager/Android.bp
@@ -19,6 +19,7 @@
"PowerHalWrapper.cpp",
"PowerSaveState.cpp",
"Temperature.cpp",
+ "WorkDuration.cpp",
"WorkSource.cpp",
":libpowermanager_aidl",
],
diff --git a/services/powermanager/WorkDuration.cpp b/services/powermanager/WorkDuration.cpp
new file mode 100644
index 0000000..ef723c2
--- /dev/null
+++ b/services/powermanager/WorkDuration.cpp
@@ -0,0 +1,51 @@
+/**
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#define LOG_TAG "WorkDuration"
+
+#include <android/WorkDuration.h>
+#include <android/performance_hint.h>
+#include <binder/Parcel.h>
+#include <utils/Log.h>
+
+namespace android::os {
+
+WorkDuration::WorkDuration(int64_t startTimestampNanos, int64_t totalDurationNanos,
+ int64_t cpuDurationNanos, int64_t gpuDurationNanos)
+ : workPeriodStartTimestampNanos(startTimestampNanos),
+ actualTotalDurationNanos(totalDurationNanos),
+ actualCpuDurationNanos(cpuDurationNanos),
+ actualGpuDurationNanos(gpuDurationNanos) {}
+
+status_t WorkDuration::writeToParcel(Parcel* parcel) const {
+ if (parcel == nullptr) {
+ ALOGE("%s: Null parcel", __func__);
+ return BAD_VALUE;
+ }
+
+ parcel->writeInt64(workPeriodStartTimestampNanos);
+ parcel->writeInt64(actualTotalDurationNanos);
+ parcel->writeInt64(actualCpuDurationNanos);
+ parcel->writeInt64(actualGpuDurationNanos);
+ parcel->writeInt64(timestampNanos);
+ return OK;
+}
+
+status_t WorkDuration::readFromParcel(const Parcel*) {
+ return INVALID_OPERATION;
+}
+
+} // namespace android::os
diff --git a/services/powermanager/include/android/WorkDuration.h b/services/powermanager/include/android/WorkDuration.h
new file mode 100644
index 0000000..99b5b8b
--- /dev/null
+++ b/services/powermanager/include/android/WorkDuration.h
@@ -0,0 +1,71 @@
+/**
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include <binder/Parcelable.h>
+#include <math.h>
+
+struct AWorkDuration {};
+
+namespace android::os {
+
+/**
+ * C++ Parcelable version of {@link PerformanceHintManager.WorkDuration} that can be used in
+ * binder calls.
+ * This file needs to be kept in sync with the WorkDuration in
+ * frameworks/base/core/java/android/os/WorkDuration.java
+ */
+struct WorkDuration : AWorkDuration, android::Parcelable {
+ WorkDuration() = default;
+ ~WorkDuration() = default;
+
+ WorkDuration(int64_t workPeriodStartTimestampNanos, int64_t actualTotalDurationNanos,
+ int64_t actualCpuDurationNanos, int64_t actualGpuDurationNanos);
+ status_t writeToParcel(Parcel* parcel) const override;
+ status_t readFromParcel(const Parcel* parcel) override;
+
+ inline bool equalsWithoutTimestamp(const WorkDuration& other) const {
+ return workPeriodStartTimestampNanos == other.workPeriodStartTimestampNanos &&
+ actualTotalDurationNanos == other.actualTotalDurationNanos &&
+ actualCpuDurationNanos == other.actualCpuDurationNanos &&
+ actualGpuDurationNanos == other.actualGpuDurationNanos;
+ }
+
+ bool operator==(const WorkDuration& other) const {
+ return timestampNanos == other.timestampNanos && equalsWithoutTimestamp(other);
+ }
+
+ bool operator!=(const WorkDuration& other) const { return !(*this == other); }
+
+ friend std::ostream& operator<<(std::ostream& os, const WorkDuration& workDuration) {
+ os << "{"
+ << "workPeriodStartTimestampNanos: " << workDuration.workPeriodStartTimestampNanos
+ << ", actualTotalDurationNanos: " << workDuration.actualTotalDurationNanos
+ << ", actualCpuDurationNanos: " << workDuration.actualCpuDurationNanos
+ << ", actualGpuDurationNanos: " << workDuration.actualGpuDurationNanos
+ << ", timestampNanos: " << workDuration.timestampNanos << "}";
+ return os;
+ }
+
+ int64_t workPeriodStartTimestampNanos;
+ int64_t actualTotalDurationNanos;
+ int64_t actualCpuDurationNanos;
+ int64_t actualGpuDurationNanos;
+ int64_t timestampNanos;
+};
+
+} // namespace android::os
diff --git a/services/surfaceflinger/Android.bp b/services/surfaceflinger/Android.bp
index 17fa7be..0989863 100644
--- a/services/surfaceflinger/Android.bp
+++ b/services/surfaceflinger/Android.bp
@@ -93,6 +93,7 @@
"libscheduler",
"libserviceutils",
"libshaders",
+ "libsurfaceflinger_common",
"libtimestats",
"libtonemap",
"libsurfaceflingerflags",
@@ -175,7 +176,6 @@
"FrontEnd/LayerLifecycleManager.cpp",
"FrontEnd/RequestedLayerState.cpp",
"FrontEnd/TransactionHandler.cpp",
- "FlagManager.cpp",
"FpsReporter.cpp",
"FrameTracer/FrameTracer.cpp",
"FrameTracker.cpp",
diff --git a/services/surfaceflinger/CompositionEngine/Android.bp b/services/surfaceflinger/CompositionEngine/Android.bp
index 370e4b6..2740a97 100644
--- a/services/surfaceflinger/CompositionEngine/Android.bp
+++ b/services/surfaceflinger/CompositionEngine/Android.bp
@@ -63,7 +63,10 @@
cc_library {
name: "libcompositionengine",
defaults: ["libcompositionengine_defaults"],
- static_libs: ["libsurfaceflingerflags"],
+ static_libs: [
+ "libsurfaceflinger_common",
+ "libsurfaceflingerflags",
+ ],
srcs: [
"src/planner/CachedSet.cpp",
"src/planner/Flattener.cpp",
@@ -108,6 +111,7 @@
"libgtest",
"libgmock",
"libcompositionengine",
+ "libsurfaceflinger_common_test",
"libsurfaceflingerflags_test",
],
local_include_dirs: ["include"],
@@ -143,6 +147,7 @@
"librenderengine_mocks",
"libgmock",
"libgtest",
+ "libsurfaceflinger_common_test",
"libsurfaceflingerflags_test",
],
// For some reason, libvulkan isn't picked up from librenderengine
diff --git a/services/surfaceflinger/DisplayDevice.cpp b/services/surfaceflinger/DisplayDevice.cpp
index 5b6591a..2ffe92b 100644
--- a/services/surfaceflinger/DisplayDevice.cpp
+++ b/services/surfaceflinger/DisplayDevice.cpp
@@ -63,13 +63,14 @@
mDisplayToken(args.displayToken),
mSequenceId(args.sequenceId),
mCompositionDisplay{args.compositionDisplay},
- mActiveModeFPSTrace("ActiveModeFPS -" + to_string(getId())),
- mActiveModeFPSHwcTrace("ActiveModeFPS_HWC -" + to_string(getId())),
- mRenderFrameRateFPSTrace("RenderRateFPS -" + to_string(getId())),
+ mActiveModeFPSTrace(concatId("ActiveModeFPS")),
+ mActiveModeFPSHwcTrace(concatId("ActiveModeFPS_HWC")),
+ mRenderFrameRateFPSTrace(concatId("RenderRateFPS")),
mPhysicalOrientation(args.physicalOrientation),
mIsPrimary(args.isPrimary),
mRequestedRefreshRate(args.requestedRefreshRate),
- mRefreshRateSelector(std::move(args.refreshRateSelector)) {
+ mRefreshRateSelector(std::move(args.refreshRateSelector)),
+ mDesiredActiveModeChanged(concatId("DesiredActiveModeChanged"), false) {
mCompositionDisplay->editState().isSecure = args.isSecure;
mCompositionDisplay->createRenderSurface(
compositionengine::RenderSurfaceCreationArgsBuilder()
diff --git a/services/surfaceflinger/DisplayDevice.h b/services/surfaceflinger/DisplayDevice.h
index a044534..a40f310 100644
--- a/services/surfaceflinger/DisplayDevice.h
+++ b/services/surfaceflinger/DisplayDevice.h
@@ -269,6 +269,11 @@
void dump(utils::Dumper&) const;
private:
+ template <size_t N>
+ inline std::string concatId(const char (&str)[N]) const {
+ return std::string(ftl::Concat(str, ' ', getId().value).str());
+ }
+
const sp<SurfaceFlinger> mFlinger;
HWComposer& mHwComposer;
const wp<IBinder> mDisplayToken;
@@ -316,8 +321,7 @@
mutable std::mutex mActiveModeLock;
ActiveModeInfo mDesiredActiveMode GUARDED_BY(mActiveModeLock);
- TracedOrdinal<bool> mDesiredActiveModeChanged GUARDED_BY(mActiveModeLock) =
- {ftl::Concat("DesiredActiveModeChanged-", getId().value).c_str(), false};
+ TracedOrdinal<bool> mDesiredActiveModeChanged GUARDED_BY(mActiveModeLock);
ActiveModeInfo mUpcomingActiveMode GUARDED_BY(kMainThreadContext);
bool mIsModeSetPending GUARDED_BY(kMainThreadContext) = false;
diff --git a/services/surfaceflinger/DisplayHardware/AidlComposerHal.cpp b/services/surfaceflinger/DisplayHardware/AidlComposerHal.cpp
index 317f2b0..2d957e6 100644
--- a/services/surfaceflinger/DisplayHardware/AidlComposerHal.cpp
+++ b/services/surfaceflinger/DisplayHardware/AidlComposerHal.cpp
@@ -19,12 +19,12 @@
#define ATRACE_TAG ATRACE_TAG_GRAPHICS
#include "AidlComposerHal.h"
-#include "FlagManager.h"
#include <SurfaceFlingerProperties.h>
#include <android-base/file.h>
#include <android/binder_ibinder_platform.h>
#include <android/binder_manager.h>
+#include <common/FlagManager.h>
#include <gui/TraceUtils.h>
#include <log/log.h>
#include <utils/Trace.h>
diff --git a/services/surfaceflinger/DisplayHardware/DisplayMode.h b/services/surfaceflinger/DisplayHardware/DisplayMode.h
index f32fb3a..ba0825c 100644
--- a/services/surfaceflinger/DisplayHardware/DisplayMode.h
+++ b/services/surfaceflinger/DisplayHardware/DisplayMode.h
@@ -29,8 +29,8 @@
#include <scheduler/Fps.h>
+#include <common/FlagManager.h>
#include "DisplayHardware/Hal.h"
-#include "FlagManager.h"
#include "Scheduler/StrongTyping.h"
namespace android {
diff --git a/services/surfaceflinger/DisplayHardware/PowerAdvisor.cpp b/services/surfaceflinger/DisplayHardware/PowerAdvisor.cpp
index f00ef67..e005ad3 100644
--- a/services/surfaceflinger/DisplayHardware/PowerAdvisor.cpp
+++ b/services/surfaceflinger/DisplayHardware/PowerAdvisor.cpp
@@ -251,7 +251,12 @@
actualDuration = std::make_optional(*actualDuration + sTargetSafetyMargin);
mActualDuration = actualDuration;
WorkDuration duration;
+ duration.workPeriodStartTimestampNanos = mCommitStartTimes[0].ns();
+ // TODO(b/284324521): Correctly calculate total duration.
duration.durationNanos = actualDuration->ns();
+ duration.cpuDurationNanos = actualDuration->ns();
+ // TODO(b/284324521): Calculate RenderEngine GPU time.
+ duration.gpuDurationNanos = 0;
duration.timeStampNanos = TimePoint::now().ns();
mHintSessionQueue.push_back(duration);
diff --git a/services/surfaceflinger/FrontEnd/LayerSnapshotBuilder.cpp b/services/surfaceflinger/FrontEnd/LayerSnapshotBuilder.cpp
index 2a0857d..9476ff4 100644
--- a/services/surfaceflinger/FrontEnd/LayerSnapshotBuilder.cpp
+++ b/services/surfaceflinger/FrontEnd/LayerSnapshotBuilder.cpp
@@ -739,6 +739,7 @@
!snapshot.changes.test(RequestedLayerState::Changes::Created)) {
if (forceUpdate ||
snapshot.changes.any(RequestedLayerState::Changes::Geometry |
+ RequestedLayerState::Changes::BufferSize |
RequestedLayerState::Changes::Input)) {
updateInput(snapshot, requested, parentSnapshot, path, args);
}
@@ -1095,6 +1096,8 @@
snapshot.inputInfo.inputConfig |= gui::WindowInfo::InputConfig::TRUSTED_OVERLAY;
}
+ snapshot.inputInfo.contentSize = snapshot.croppedBufferSize.getSize();
+
// If the layer is a clone, we need to crop the input region to cloned root to prevent
// touches from going outside the cloned area.
if (path.isClone()) {
diff --git a/services/surfaceflinger/Layer.cpp b/services/surfaceflinger/Layer.cpp
index 66ea15c..3ca83e0 100644
--- a/services/surfaceflinger/Layer.cpp
+++ b/services/surfaceflinger/Layer.cpp
@@ -136,6 +136,7 @@
using gui::GameMode;
using gui::LayerMetadata;
using gui::WindowInfo;
+using ui::Size;
using PresentState = frametimeline::SurfaceFrame::PresentState;
@@ -2591,6 +2592,9 @@
}
}
+ Rect bufferSize = getBufferSize(getDrawingState());
+ info.contentSize = Size(bufferSize.width(), bufferSize.height());
+
return info;
}
diff --git a/services/surfaceflinger/RefreshRateOverlay.cpp b/services/surfaceflinger/RefreshRateOverlay.cpp
index 6752a0b..b960e33 100644
--- a/services/surfaceflinger/RefreshRateOverlay.cpp
+++ b/services/surfaceflinger/RefreshRateOverlay.cpp
@@ -16,8 +16,8 @@
#include <algorithm>
+#include <common/FlagManager.h>
#include "Client.h"
-#include "FlagManager.h"
#include "Layer.h"
#include "RefreshRateOverlay.h"
diff --git a/services/surfaceflinger/Scheduler/EventThread.cpp b/services/surfaceflinger/Scheduler/EventThread.cpp
index 7f627f8..693a357 100644
--- a/services/surfaceflinger/Scheduler/EventThread.cpp
+++ b/services/surfaceflinger/Scheduler/EventThread.cpp
@@ -43,9 +43,9 @@
#include <utils/Errors.h>
#include <utils/Trace.h>
+#include <common/FlagManager.h>
#include <scheduler/VsyncConfig.h>
#include "DisplayHardware/DisplayMode.h"
-#include "FlagManager.h"
#include "FrameTimeline.h"
#include "VSyncDispatch.h"
#include "VSyncTracker.h"
diff --git a/services/surfaceflinger/Scheduler/LayerHistory.cpp b/services/surfaceflinger/Scheduler/LayerHistory.cpp
index 450ba1d..d309adc 100644
--- a/services/surfaceflinger/Scheduler/LayerHistory.cpp
+++ b/services/surfaceflinger/Scheduler/LayerHistory.cpp
@@ -31,9 +31,9 @@
#include <string>
#include <utility>
+#include <common/FlagManager.h>
#include "../Layer.h"
#include "EventThread.h"
-#include "FlagManager.h"
#include "LayerInfo.h"
namespace android::scheduler {
diff --git a/services/surfaceflinger/Scheduler/Scheduler.cpp b/services/surfaceflinger/Scheduler/Scheduler.cpp
index f41243c..b54f334 100644
--- a/services/surfaceflinger/Scheduler/Scheduler.cpp
+++ b/services/surfaceflinger/Scheduler/Scheduler.cpp
@@ -45,9 +45,9 @@
#include <memory>
#include <numeric>
+#include <common/FlagManager.h>
#include "../Layer.h"
#include "EventThread.h"
-#include "FlagManager.h"
#include "FrameRateOverrideMappings.h"
#include "FrontEnd/LayerHandle.h"
#include "OneShotTimer.h"
diff --git a/services/surfaceflinger/Scheduler/VSyncDispatchTimerQueue.cpp b/services/surfaceflinger/Scheduler/VSyncDispatchTimerQueue.cpp
index 3e7ec49..ef30887 100644
--- a/services/surfaceflinger/Scheduler/VSyncDispatchTimerQueue.cpp
+++ b/services/surfaceflinger/Scheduler/VSyncDispatchTimerQueue.cpp
@@ -25,7 +25,7 @@
#include <scheduler/TimeKeeper.h>
-#include "FlagManager.h"
+#include <common/FlagManager.h>
#include "VSyncDispatchTimerQueue.h"
#include "VSyncTracker.h"
diff --git a/services/surfaceflinger/Scheduler/VSyncPredictor.cpp b/services/surfaceflinger/Scheduler/VSyncPredictor.cpp
index 57aa010..f5f93ce 100644
--- a/services/surfaceflinger/Scheduler/VSyncPredictor.cpp
+++ b/services/surfaceflinger/Scheduler/VSyncPredictor.cpp
@@ -29,13 +29,13 @@
#include <android-base/logging.h>
#include <android-base/stringprintf.h>
+#include <common/FlagManager.h>
#include <cutils/compiler.h>
#include <cutils/properties.h>
#include <ftl/concat.h>
#include <gui/TraceUtils.h>
#include <utils/Log.h>
-#include "FlagManager.h"
#include "RefreshRateSelector.h"
#include "VSyncPredictor.h"
diff --git a/services/surfaceflinger/SurfaceFlinger.cpp b/services/surfaceflinger/SurfaceFlinger.cpp
index 9c8555e..d6753c3 100644
--- a/services/surfaceflinger/SurfaceFlinger.cpp
+++ b/services/surfaceflinger/SurfaceFlinger.cpp
@@ -113,6 +113,7 @@
#include <unordered_map>
#include <vector>
+#include <common/FlagManager.h>
#include <gui/LayerStatePermissions.h>
#include <gui/SchedulingPolicy.h>
#include <ui/DisplayIdentification.h>
@@ -129,7 +130,6 @@
#include "DisplayHardware/VirtualDisplaySurface.h"
#include "DisplayRenderArea.h"
#include "Effects/Daltonizer.h"
-#include "FlagManager.h"
#include "FpsReporter.h"
#include "FrameTimeline/FrameTimeline.h"
#include "FrameTracer/FrameTracer.h"
@@ -2922,7 +2922,6 @@
const scheduler::FrameTargeters& frameTargeters,
nsecs_t presentStartTime) {
ATRACE_CALL();
- ALOGV(__func__);
ui::PhysicalDisplayMap<PhysicalDisplayId, std::shared_ptr<FenceTime>> presentFences;
ui::PhysicalDisplayMap<PhysicalDisplayId, const sp<Fence>> gpuCompositionDoneFences;
@@ -3810,7 +3809,6 @@
// first frame before the display is available, we rely
// on WMS and DMS to provide the right information
// so the client can calculate the hint.
- ALOGV("Skipping reporting transform hint update for %s", layer->getDebugName());
layer->skipReportingTransformHint();
} else {
layer->updateTransformHint(hintDisplay->getTransformHint());
diff --git a/services/surfaceflinger/SurfaceFlinger.h b/services/surfaceflinger/SurfaceFlinger.h
index 1e90340..fa48549 100644
--- a/services/surfaceflinger/SurfaceFlinger.h
+++ b/services/surfaceflinger/SurfaceFlinger.h
@@ -63,13 +63,13 @@
#include <scheduler/interface/ICompositor.h>
#include <ui/FenceResult.h>
+#include <common/FlagManager.h>
#include "Display/PhysicalDisplay.h"
#include "DisplayDevice.h"
#include "DisplayHardware/HWC2.h"
#include "DisplayHardware/PowerAdvisor.h"
#include "DisplayIdGenerator.h"
#include "Effects/Daltonizer.h"
-#include "FlagManager.h"
#include "FrontEnd/DisplayInfo.h"
#include "FrontEnd/LayerCreationArgs.h"
#include "FrontEnd/LayerLifecycleManager.h"
diff --git a/services/surfaceflinger/common/Android.bp b/services/surfaceflinger/common/Android.bp
new file mode 100644
index 0000000..5ef22b5
--- /dev/null
+++ b/services/surfaceflinger/common/Android.bp
@@ -0,0 +1,48 @@
+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_defaults {
+ name: "libsurfaceflinger_common_defaults",
+ defaults: [
+ "android.hardware.graphics.composer3-ndk_shared",
+ "surfaceflinger_defaults",
+ ],
+ shared_libs: [
+ "libSurfaceFlingerProp",
+ "server_configurable_flags",
+ ],
+ static_libs: [
+ "librenderengine",
+ ],
+ srcs: [
+ "FlagManager.cpp",
+ ],
+ local_include_dirs: ["include"],
+ export_include_dirs: ["include"],
+}
+
+cc_library_static {
+ name: "libsurfaceflinger_common",
+ defaults: [
+ "libsurfaceflinger_common_defaults",
+ ],
+ static_libs: [
+ "libsurfaceflingerflags",
+ ],
+}
+
+cc_library_static {
+ name: "libsurfaceflinger_common_test",
+ defaults: [
+ "libsurfaceflinger_common_defaults",
+ ],
+ static_libs: [
+ "libsurfaceflingerflags_test",
+ ],
+}
diff --git a/services/surfaceflinger/FlagManager.cpp b/services/surfaceflinger/common/FlagManager.cpp
similarity index 99%
rename from services/surfaceflinger/FlagManager.cpp
rename to services/surfaceflinger/common/FlagManager.cpp
index 13b8a6c..8da7d8e 100644
--- a/services/surfaceflinger/FlagManager.cpp
+++ b/services/surfaceflinger/common/FlagManager.cpp
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-#include "FlagManager.h"
+#include <common/FlagManager.h>
#include <SurfaceFlingerProperties.sysprop.h>
#include <android-base/parsebool.h>
diff --git a/services/surfaceflinger/FlagManager.h b/services/surfaceflinger/common/include/common/FlagManager.h
similarity index 100%
rename from services/surfaceflinger/FlagManager.h
rename to services/surfaceflinger/common/include/common/FlagManager.h
diff --git a/services/surfaceflinger/fuzzer/Android.bp b/services/surfaceflinger/fuzzer/Android.bp
index 243b8e0..ab3b352 100644
--- a/services/surfaceflinger/fuzzer/Android.bp
+++ b/services/surfaceflinger/fuzzer/Android.bp
@@ -39,6 +39,7 @@
"libgtest_ndk_c++",
"libgmock_main_ndk",
"librenderengine_mocks",
+ "libsurfaceflinger_common",
"perfetto_trace_protos",
"libcompositionengine_mocks",
"perfetto_trace_protos",
diff --git a/services/surfaceflinger/main_surfaceflinger.cpp b/services/surfaceflinger/main_surfaceflinger.cpp
index 9889cb9..6c8972f 100644
--- a/services/surfaceflinger/main_surfaceflinger.cpp
+++ b/services/surfaceflinger/main_surfaceflinger.cpp
@@ -29,12 +29,12 @@
#include <binder/IPCThreadState.h>
#include <binder/IServiceManager.h>
#include <binder/ProcessState.h>
+#include <common/FlagManager.h>
#include <configstore/Utils.h>
#include <displayservice/DisplayService.h>
#include <errno.h>
#include <hidl/LegacySupport.h>
#include <processgroup/sched_policy.h>
-#include "FlagManager.h"
#include "SurfaceFlinger.h"
#include "SurfaceFlingerFactory.h"
#include "SurfaceFlingerProperties.h"
diff --git a/services/surfaceflinger/tests/unittests/Android.bp b/services/surfaceflinger/tests/unittests/Android.bp
index 6e6c6d8..dea0194 100644
--- a/services/surfaceflinger/tests/unittests/Android.bp
+++ b/services/surfaceflinger/tests/unittests/Android.bp
@@ -170,6 +170,7 @@
"librenderengine_mocks",
"libscheduler",
"libserviceutils",
+ "libsurfaceflinger_common_test",
"libtimestats",
"libtimestats_atoms_proto",
"libtimestats_proto",
diff --git a/services/surfaceflinger/tests/unittests/FlagManagerTest.cpp b/services/surfaceflinger/tests/unittests/FlagManagerTest.cpp
index aa37754..c040f29 100644
--- a/services/surfaceflinger/tests/unittests/FlagManagerTest.cpp
+++ b/services/surfaceflinger/tests/unittests/FlagManagerTest.cpp
@@ -17,7 +17,7 @@
#undef LOG_TAG
#define LOG_TAG "FlagManagerTest"
-#include "FlagManager.h"
+#include <common/FlagManager.h>
#include "FlagUtils.h"
#include <gmock/gmock.h>
diff --git a/services/surfaceflinger/tests/unittests/FlagUtils.h b/services/surfaceflinger/tests/unittests/FlagUtils.h
index 333e4e7..550c70d 100644
--- a/services/surfaceflinger/tests/unittests/FlagUtils.h
+++ b/services/surfaceflinger/tests/unittests/FlagUtils.h
@@ -16,7 +16,7 @@
#pragma once
-#include "FlagManager.h"
+#include <common/FlagManager.h>
#define SET_FLAG_FOR_TEST(name, value) TestFlagSetter _testflag_((name), (name), (value))
diff --git a/services/surfaceflinger/tests/unittests/HWComposerTest.cpp b/services/surfaceflinger/tests/unittests/HWComposerTest.cpp
index 788fa51..f1e841b 100644
--- a/services/surfaceflinger/tests/unittests/HWComposerTest.cpp
+++ b/services/surfaceflinger/tests/unittests/HWComposerTest.cpp
@@ -30,6 +30,7 @@
#include <gmock/gmock.h>
#pragma clang diagnostic pop
+#include <common/FlagManager.h>
#include <gui/LayerMetadata.h>
#include <log/log.h>
#include <chrono>
@@ -38,7 +39,6 @@
#include "DisplayHardware/HWComposer.h"
#include "DisplayHardware/Hal.h"
#include "DisplayIdentificationTestHelpers.h"
-#include "FlagManager.h"
#include "FlagUtils.h"
#include "mock/DisplayHardware/MockComposer.h"
#include "mock/DisplayHardware/MockHWC2.h"