Merge "Return zero delta when delta is not computed"
diff --git a/include/android/choreographer.h b/include/android/choreographer.h
index b743f49..0389e57 100644
--- a/include/android/choreographer.h
+++ b/include/android/choreographer.h
@@ -39,6 +39,12 @@
*/
typedef struct AChoreographer AChoreographer;
+struct AChoreographerFrameCallbackData;
+/**
+ * Opaque type that provides access to an AChoreographerFrameCallbackData object.
+ */
+typedef struct AChoreographerFrameCallbackData AChoreographerFrameCallbackData;
+
/**
* Prototype of the function that is called when a new frame is being rendered.
* It's passed the time that the frame is being rendered as nanoseconds in the
@@ -60,6 +66,14 @@
typedef void (*AChoreographer_frameCallback64)(int64_t frameTimeNanos, void* data);
/**
+ * Prototype of the function that is called when a new frame is being rendered.
+ * It's passed the frame data that should not outlive the callback, as well as the data pointer
+ * provided by the application that registered a callback.
+ */
+typedef void (*AChoreographer_extendedFrameCallback)(
+ const AChoreographerFrameCallbackData* callbackData, void* data);
+
+/**
* Prototype of the function that is called when the display refresh rate
* changes. It's passed the new vsync period in nanoseconds, as well as the data
* pointer provided by the application that registered a callback.
@@ -111,6 +125,14 @@
uint32_t delayMillis) __INTRODUCED_IN(29);
/**
+ * Posts a callback to run on the next frame. The data pointer provided will
+ * be passed to the callback function when it's called.
+ */
+void AChoreographer_postExtendedFrameCallback(AChoreographer* choreographer,
+ AChoreographer_extendedFrameCallback callback, void* data)
+ __INTRODUCED_IN(33);
+
+/**
* Registers a callback to be run when the display refresh rate changes. The
* data pointer provided will be passed to the callback function when it's
* called. The same callback may be registered multiple times, provided that a
@@ -160,6 +182,42 @@
AChoreographer_refreshRateCallback, void* data)
__INTRODUCED_IN(30);
+/**
+ * The time in nanoseconds when the frame started being rendered.
+ */
+int64_t AChoreographerFrameCallbackData_getFrameTimeNanos(
+ const AChoreographerFrameCallbackData* data) __INTRODUCED_IN(33);
+
+/**
+ * The number of possible frame timelines.
+ */
+size_t AChoreographerFrameCallbackData_getFrameTimelinesLength(
+ const AChoreographerFrameCallbackData* data) __INTRODUCED_IN(33);
+
+/**
+ * Get index of the platform-preferred FrameTimeline.
+ */
+size_t AChoreographerFrameCallbackData_getPreferredFrameTimelineIndex(
+ const AChoreographerFrameCallbackData* data) __INTRODUCED_IN(33);
+
+/**
+ * The vsync ID token used to map Choreographer data.
+ */
+int64_t AChoreographerFrameCallbackData_getFrameTimelineVsyncId(
+ const AChoreographerFrameCallbackData* data, size_t index) __INTRODUCED_IN(33);
+
+/**
+ * The time in nanoseconds which the frame at given index is expected to be presented.
+ */
+int64_t AChoreographerFrameCallbackData_getFrameTimelineExpectedPresentTime(
+ const AChoreographerFrameCallbackData* data, size_t index) __INTRODUCED_IN(33);
+
+/**
+ * The time in nanoseconds which the frame at given index needs to be ready by.
+ */
+int64_t AChoreographerFrameCallbackData_getFrameTimelineDeadline(
+ const AChoreographerFrameCallbackData* data, size_t index) __INTRODUCED_IN(33);
+
__END_DECLS
#endif // ANDROID_CHOREOGRAPHER_H
diff --git a/libs/binder/RpcServer.cpp b/libs/binder/RpcServer.cpp
index 4c61a59..ba2920e 100644
--- a/libs/binder/RpcServer.cpp
+++ b/libs/binder/RpcServer.cpp
@@ -278,7 +278,7 @@
RpcConnectionHeader header;
if (status == OK) {
status = client->interruptableReadFully(server->mShutdownTrigger.get(), &header,
- sizeof(header));
+ sizeof(header), {});
if (status != OK) {
ALOGE("Failed to read ID for client connecting to RPC server: %s",
statusToString(status).c_str());
@@ -291,7 +291,7 @@
if (header.sessionIdSize > 0) {
sessionId.resize(header.sessionIdSize);
status = client->interruptableReadFully(server->mShutdownTrigger.get(),
- sessionId.data(), sessionId.size());
+ sessionId.data(), sessionId.size(), {});
if (status != OK) {
ALOGE("Failed to read session ID for client connecting to RPC server: %s",
statusToString(status).c_str());
@@ -316,7 +316,7 @@
};
status = client->interruptableWriteFully(server->mShutdownTrigger.get(), &response,
- sizeof(response));
+ sizeof(response), {});
if (status != OK) {
ALOGE("Failed to send new session response: %s", statusToString(status).c_str());
// still need to cleanup before we can return
diff --git a/libs/binder/RpcSession.cpp b/libs/binder/RpcSession.cpp
index dafb339..65f6bc6 100644
--- a/libs/binder/RpcSession.cpp
+++ b/libs/binder/RpcSession.cpp
@@ -560,7 +560,7 @@
}
auto sendHeaderStatus =
- server->interruptableWriteFully(mShutdownTrigger.get(), &header, sizeof(header));
+ server->interruptableWriteFully(mShutdownTrigger.get(), &header, sizeof(header), {});
if (sendHeaderStatus != OK) {
ALOGE("Could not write connection header to socket: %s",
statusToString(sendHeaderStatus).c_str());
@@ -570,7 +570,7 @@
if (sessionId.size() > 0) {
auto sendSessionIdStatus =
server->interruptableWriteFully(mShutdownTrigger.get(), sessionId.data(),
- sessionId.size());
+ sessionId.size(), {});
if (sendSessionIdStatus != OK) {
ALOGE("Could not write session ID ('%s') to socket: %s",
base::HexString(sessionId.data(), sessionId.size()).c_str(),
diff --git a/libs/binder/RpcState.cpp b/libs/binder/RpcState.cpp
index 3ff13bc..86cc91c 100644
--- a/libs/binder/RpcState.cpp
+++ b/libs/binder/RpcState.cpp
@@ -307,7 +307,7 @@
status_t RpcState::rpcSend(const sp<RpcSession::RpcConnection>& connection,
const sp<RpcSession>& session, const char* what, const void* data,
- size_t size) {
+ size_t size, const std::function<status_t()>& altPoll) {
LOG_RPC_DETAIL("Sending %s on RpcTransport %p: %s", what, connection->rpcTransport.get(),
android::base::HexString(data, size).c_str());
@@ -319,7 +319,7 @@
if (status_t status =
connection->rpcTransport->interruptableWriteFully(session->mShutdownTrigger.get(),
- data, size);
+ data, size, altPoll);
status != OK) {
LOG_RPC_DETAIL("Failed to write %s (%zu bytes) on RpcTransport %p, error: %s", what, size,
connection->rpcTransport.get(), statusToString(status).c_str());
@@ -341,7 +341,7 @@
if (status_t status =
connection->rpcTransport->interruptableReadFully(session->mShutdownTrigger.get(),
- data, size);
+ data, size, {});
status != OK) {
LOG_RPC_DETAIL("Failed to read %s (%zu bytes) on RpcTransport %p, error: %s", what, size,
connection->rpcTransport.get(), statusToString(status).c_str());
@@ -523,21 +523,44 @@
memcpy(transactionData.data() + sizeof(RpcWireHeader) + sizeof(RpcWireTransaction), data.data(),
data.dataSize());
+ constexpr size_t kWaitMaxUs = 1000000;
+ constexpr size_t kWaitLogUs = 10000;
+ size_t waitUs = 0;
+
+ // Oneway calls have no sync point, so if many are sent before, whether this
+ // is a twoway or oneway transaction, they may have filled up the socket.
+ // So, make sure we drain them before polling.
+ std::function<status_t()> drainRefs = [&] {
+ if (waitUs > kWaitLogUs) {
+ ALOGE("Cannot send command, trying to process pending refcounts. Waiting %zuus. Too "
+ "many oneway calls?",
+ waitUs);
+ }
+
+ if (waitUs > 0) {
+ usleep(waitUs);
+ waitUs = std::min(kWaitMaxUs, waitUs * 2);
+ } else {
+ waitUs = 1;
+ }
+
+ return drainCommands(connection, session, CommandType::CONTROL_ONLY);
+ };
+
if (status_t status = rpcSend(connection, session, "transaction", transactionData.data(),
- transactionData.size());
- status != OK)
+ transactionData.size(), drainRefs);
+ status != OK) {
// TODO(b/167966510): need to undo onBinderLeaving - we know the
// refcount isn't successfully transferred.
return status;
+ }
if (flags & IBinder::FLAG_ONEWAY) {
LOG_RPC_DETAIL("Oneway command, so no longer waiting on RpcTransport %p",
connection->rpcTransport.get());
// Do not wait on result.
- // However, too many oneway calls may cause refcounts to build up and fill up the socket,
- // so process those.
- return drainCommands(connection, session, CommandType::CONTROL_ONLY);
+ return OK;
}
LOG_ALWAYS_FATAL_IF(reply == nullptr, "Reply parcel must be used for synchronous transaction.");
@@ -723,7 +746,7 @@
// for 'recursive' calls to this, we have already read and processed the
// binder from the transaction data and taken reference counts into account,
// so it is cached here.
- sp<IBinder> targetRef;
+ sp<IBinder> target;
processTransactInternalTailCall:
if (transactionData.size() < sizeof(RpcWireTransaction)) {
@@ -738,12 +761,9 @@
bool oneway = transaction->flags & IBinder::FLAG_ONEWAY;
status_t replyStatus = OK;
- sp<IBinder> target;
if (addr != 0) {
- if (!targetRef) {
+ if (!target) {
replyStatus = onBinderEntering(session, addr, &target);
- } else {
- target = targetRef;
}
if (replyStatus != OK) {
@@ -910,7 +930,8 @@
// reset up arguments
transactionData = std::move(todo.data);
- targetRef = std::move(todo.ref);
+ LOG_ALWAYS_FATAL_IF(target != todo.ref,
+ "async list should be associated with a binder");
it->second.asyncTodo.pop();
goto processTransactInternalTailCall;
diff --git a/libs/binder/RpcState.h b/libs/binder/RpcState.h
index 42e95e0..50de22b 100644
--- a/libs/binder/RpcState.h
+++ b/libs/binder/RpcState.h
@@ -177,7 +177,8 @@
[[nodiscard]] status_t rpcSend(const sp<RpcSession::RpcConnection>& connection,
const sp<RpcSession>& session, const char* what,
- const void* data, size_t size);
+ const void* data, size_t size,
+ const std::function<status_t()>& altPoll = nullptr);
[[nodiscard]] status_t rpcRec(const sp<RpcSession::RpcConnection>& connection,
const sp<RpcSession>& session, const char* what, void* data,
size_t size);
diff --git a/libs/binder/RpcTransportRaw.cpp b/libs/binder/RpcTransportRaw.cpp
index 41f4a9f..7669518 100644
--- a/libs/binder/RpcTransportRaw.cpp
+++ b/libs/binder/RpcTransportRaw.cpp
@@ -43,56 +43,72 @@
return ret;
}
- status_t interruptableWriteFully(FdTrigger* fdTrigger, const void* data, size_t size) override {
- const uint8_t* buffer = reinterpret_cast<const uint8_t*>(data);
- const uint8_t* end = buffer + size;
+ template <typename Buffer, typename SendOrReceive>
+ status_t interruptableReadOrWrite(FdTrigger* fdTrigger, Buffer buffer, size_t size,
+ SendOrReceive sendOrReceiveFun, const char* funName,
+ int16_t event, const std::function<status_t()>& altPoll) {
+ const Buffer end = buffer + size;
MAYBE_WAIT_IN_FLAKE_MODE;
- status_t status;
- while ((status = fdTrigger->triggerablePoll(mSocket.get(), POLLOUT)) == OK) {
- ssize_t writeSize =
- TEMP_FAILURE_RETRY(::send(mSocket.get(), buffer, end - buffer, MSG_NOSIGNAL));
- if (writeSize < 0) {
+ // Since we didn't poll, we need to manually check to see if it was triggered. Otherwise, we
+ // may never know we should be shutting down.
+ if (fdTrigger->isTriggered()) {
+ return DEAD_OBJECT;
+ }
+
+ bool havePolled = false;
+ while (true) {
+ ssize_t processSize = TEMP_FAILURE_RETRY(
+ sendOrReceiveFun(mSocket.get(), buffer, end - buffer, MSG_NOSIGNAL));
+
+ if (processSize < 0) {
int savedErrno = errno;
- LOG_RPC_DETAIL("RpcTransport send(): %s", strerror(savedErrno));
- return -savedErrno;
+
+ // Still return the error on later passes, since it would expose
+ // a problem with polling
+ if (havePolled ||
+ (!havePolled && savedErrno != EAGAIN && savedErrno != EWOULDBLOCK)) {
+ LOG_RPC_DETAIL("RpcTransport %s(): %s", funName, strerror(savedErrno));
+ return -savedErrno;
+ }
+ } else if (processSize == 0) {
+ return DEAD_OBJECT;
+ } else {
+ buffer += processSize;
+ if (buffer == end) {
+ return OK;
+ }
}
- if (writeSize == 0) return DEAD_OBJECT;
-
- buffer += writeSize;
- if (buffer == end) return OK;
+ if (altPoll) {
+ if (status_t status = altPoll(); status != OK) return status;
+ if (fdTrigger->isTriggered()) {
+ return DEAD_OBJECT;
+ }
+ } else {
+ if (status_t status = fdTrigger->triggerablePoll(mSocket.get(), event);
+ status != OK)
+ return status;
+ if (!havePolled) havePolled = true;
+ }
}
- return status;
}
- status_t interruptableReadFully(FdTrigger* fdTrigger, void* data, size_t size) override {
- uint8_t* buffer = reinterpret_cast<uint8_t*>(data);
- uint8_t* end = buffer + size;
+ status_t interruptableWriteFully(FdTrigger* fdTrigger, const void* data, size_t size,
+ const std::function<status_t()>& altPoll) override {
+ return interruptableReadOrWrite(fdTrigger, reinterpret_cast<const uint8_t*>(data), size,
+ send, "send", POLLOUT, altPoll);
+ }
- MAYBE_WAIT_IN_FLAKE_MODE;
-
- status_t status;
- while ((status = fdTrigger->triggerablePoll(mSocket.get(), POLLIN)) == OK) {
- ssize_t readSize =
- TEMP_FAILURE_RETRY(::recv(mSocket.get(), buffer, end - buffer, MSG_NOSIGNAL));
- if (readSize < 0) {
- int savedErrno = errno;
- LOG_RPC_DETAIL("RpcTransport recv(): %s", strerror(savedErrno));
- return -savedErrno;
- }
-
- if (readSize == 0) return DEAD_OBJECT; // EOF
-
- buffer += readSize;
- if (buffer == end) return OK;
- }
- return status;
+ status_t interruptableReadFully(FdTrigger* fdTrigger, void* data, size_t size,
+ const std::function<status_t()>& altPoll) override {
+ return interruptableReadOrWrite(fdTrigger, reinterpret_cast<uint8_t*>(data), size, recv,
+ "recv", POLLIN, altPoll);
}
private:
- android::base::unique_fd mSocket;
+ base::unique_fd mSocket;
};
// RpcTransportCtx with TLS disabled.
diff --git a/libs/binder/RpcTransportTls.cpp b/libs/binder/RpcTransportTls.cpp
index f8cd71d..7f810b1 100644
--- a/libs/binder/RpcTransportTls.cpp
+++ b/libs/binder/RpcTransportTls.cpp
@@ -169,12 +169,13 @@
// If |sslError| is WANT_READ / WANT_WRITE, poll for POLLIN / POLLOUT respectively. Otherwise
// return error. Also return error if |fdTrigger| is triggered before or during poll().
status_t pollForSslError(android::base::borrowed_fd fd, int sslError, FdTrigger* fdTrigger,
- const char* fnString, int additionalEvent = 0) {
+ const char* fnString, int additionalEvent,
+ const std::function<status_t()>& altPoll) {
switch (sslError) {
case SSL_ERROR_WANT_READ:
- return handlePoll(POLLIN | additionalEvent, fd, fdTrigger, fnString);
+ return handlePoll(POLLIN | additionalEvent, fd, fdTrigger, fnString, altPoll);
case SSL_ERROR_WANT_WRITE:
- return handlePoll(POLLOUT | additionalEvent, fd, fdTrigger, fnString);
+ return handlePoll(POLLOUT | additionalEvent, fd, fdTrigger, fnString, altPoll);
case SSL_ERROR_SYSCALL: {
auto queue = toString();
LOG_TLS_DETAIL("%s(): %s. Treating as DEAD_OBJECT. Error queue: %s", fnString,
@@ -194,11 +195,17 @@
bool mHandled = false;
status_t handlePoll(int event, android::base::borrowed_fd fd, FdTrigger* fdTrigger,
- const char* fnString) {
- status_t ret = fdTrigger->triggerablePoll(fd, event);
+ const char* fnString, const std::function<status_t()>& altPoll) {
+ status_t ret;
+ if (altPoll) {
+ ret = altPoll();
+ if (fdTrigger->isTriggered()) ret = DEAD_OBJECT;
+ } else {
+ ret = fdTrigger->triggerablePoll(fd, event);
+ }
+
if (ret != OK && ret != DEAD_OBJECT) {
- ALOGE("triggerablePoll error while poll()-ing after %s(): %s", fnString,
- statusToString(ret).c_str());
+ ALOGE("poll error while after %s(): %s", fnString, statusToString(ret).c_str());
}
clear();
return ret;
@@ -268,8 +275,10 @@
RpcTransportTls(android::base::unique_fd socket, Ssl ssl)
: mSocket(std::move(socket)), mSsl(std::move(ssl)) {}
Result<size_t> peek(void* buf, size_t size) override;
- status_t interruptableWriteFully(FdTrigger* fdTrigger, const void* data, size_t size) override;
- status_t interruptableReadFully(FdTrigger* fdTrigger, void* data, size_t size) override;
+ status_t interruptableWriteFully(FdTrigger* fdTrigger, const void* data, size_t size,
+ const std::function<status_t()>& altPoll) override;
+ status_t interruptableReadFully(FdTrigger* fdTrigger, void* data, size_t size,
+ const std::function<status_t()>& altPoll) override;
private:
android::base::unique_fd mSocket;
@@ -295,7 +304,8 @@
}
status_t RpcTransportTls::interruptableWriteFully(FdTrigger* fdTrigger, const void* data,
- size_t size) {
+ size_t size,
+ const std::function<status_t()>& altPoll) {
auto buffer = reinterpret_cast<const uint8_t*>(data);
const uint8_t* end = buffer + size;
@@ -317,8 +327,8 @@
int sslError = mSsl.getError(writeSize);
// TODO(b/195788248): BIO should contain the FdTrigger, and send(2) / recv(2) should be
// triggerablePoll()-ed. Then additionalEvent is no longer necessary.
- status_t pollStatus =
- errorQueue.pollForSslError(mSocket.get(), sslError, fdTrigger, "SSL_write", POLLIN);
+ status_t pollStatus = errorQueue.pollForSslError(mSocket.get(), sslError, fdTrigger,
+ "SSL_write", POLLIN, altPoll);
if (pollStatus != OK) return pollStatus;
// Do not advance buffer. Try SSL_write() again.
}
@@ -326,7 +336,8 @@
return OK;
}
-status_t RpcTransportTls::interruptableReadFully(FdTrigger* fdTrigger, void* data, size_t size) {
+status_t RpcTransportTls::interruptableReadFully(FdTrigger* fdTrigger, void* data, size_t size,
+ const std::function<status_t()>& altPoll) {
auto buffer = reinterpret_cast<uint8_t*>(data);
uint8_t* end = buffer + size;
@@ -350,8 +361,8 @@
return DEAD_OBJECT;
}
int sslError = mSsl.getError(readSize);
- status_t pollStatus =
- errorQueue.pollForSslError(mSocket.get(), sslError, fdTrigger, "SSL_read");
+ status_t pollStatus = errorQueue.pollForSslError(mSocket.get(), sslError, fdTrigger,
+ "SSL_read", 0, altPoll);
if (pollStatus != OK) return pollStatus;
// Do not advance buffer. Try SSL_read() again.
}
@@ -382,7 +393,7 @@
}
int sslError = ssl->getError(ret);
status_t pollStatus =
- errorQueue.pollForSslError(fd, sslError, fdTrigger, "SSL_do_handshake");
+ errorQueue.pollForSslError(fd, sslError, fdTrigger, "SSL_do_handshake", 0, {});
if (pollStatus != OK) return false;
}
}
diff --git a/libs/binder/include/binder/RpcTransport.h b/libs/binder/include/binder/RpcTransport.h
index 4fe2324..db8b5e9 100644
--- a/libs/binder/include/binder/RpcTransport.h
+++ b/libs/binder/include/binder/RpcTransport.h
@@ -18,6 +18,7 @@
#pragma once
+#include <functional>
#include <memory>
#include <string>
@@ -43,14 +44,20 @@
/**
* Read (or write), but allow to be interrupted by a trigger.
*
+ * altPoll - function to be called instead of polling, when needing to wait
+ * to read/write data. If this returns an error, that error is returned from
+ * this function.
+ *
* Return:
* OK - succeeded in completely processing 'size'
* error - interrupted (failure or trigger)
*/
- [[nodiscard]] virtual status_t interruptableWriteFully(FdTrigger *fdTrigger, const void *buf,
- size_t size) = 0;
- [[nodiscard]] virtual status_t interruptableReadFully(FdTrigger *fdTrigger, void *buf,
- size_t size) = 0;
+ [[nodiscard]] virtual status_t interruptableWriteFully(
+ FdTrigger *fdTrigger, const void *buf, size_t size,
+ const std::function<status_t()> &altPoll) = 0;
+ [[nodiscard]] virtual status_t interruptableReadFully(
+ FdTrigger *fdTrigger, void *buf, size_t size,
+ const std::function<status_t()> &altPoll) = 0;
protected:
RpcTransport() = default;
diff --git a/libs/binder/rust/src/native.rs b/libs/binder/rust/src/native.rs
index e7c3396..a91092e 100644
--- a/libs/binder/rust/src/native.rs
+++ b/libs/binder/rust/src/native.rs
@@ -441,6 +441,8 @@
///
/// Registers the given binder object with the given identifier. If successful,
/// this service can then be retrieved using that identifier.
+///
+/// This function will panic if the identifier contains a 0 byte (NUL).
pub fn add_service(identifier: &str, mut binder: SpIBinder) -> Result<()> {
let instance = CString::new(identifier).unwrap();
let status = unsafe {
@@ -462,6 +464,8 @@
///
/// If any service in the process is registered as lazy, all should be, otherwise
/// the process may be shut down while a service is in use.
+///
+/// This function will panic if the identifier contains a 0 byte (NUL).
pub fn register_lazy_service(identifier: &str, mut binder: SpIBinder) -> Result<()> {
let instance = CString::new(identifier).unwrap();
let status = unsafe {
diff --git a/libs/binder/tests/binderRpcTest.cpp b/libs/binder/tests/binderRpcTest.cpp
index a2558f5..a1058bc 100644
--- a/libs/binder/tests/binderRpcTest.cpp
+++ b/libs/binder/tests/binderRpcTest.cpp
@@ -1573,7 +1573,7 @@
FdTrigger* fdTrigger) {
std::string message(kMessage);
auto status = serverTransport->interruptableWriteFully(fdTrigger, message.data(),
- message.size());
+ message.size(), {});
if (status != OK) return AssertionFailure() << statusToString(status);
return AssertionSuccess();
}
@@ -1606,7 +1606,7 @@
std::string readMessage(expectedMessage.size(), '\0');
status_t readStatus =
mClientTransport->interruptableReadFully(mFdTrigger.get(), readMessage.data(),
- readMessage.size());
+ readMessage.size(), {});
if (readStatus != OK) {
return AssertionFailure() << statusToString(readStatus);
}
@@ -1800,8 +1800,8 @@
bool shouldContinueWriting = false;
auto serverPostConnect = [&](RpcTransport* serverTransport, FdTrigger* fdTrigger) {
std::string message(RpcTransportTestUtils::kMessage);
- auto status =
- serverTransport->interruptableWriteFully(fdTrigger, message.data(), message.size());
+ auto status = serverTransport->interruptableWriteFully(fdTrigger, message.data(),
+ message.size(), {});
if (status != OK) return AssertionFailure() << statusToString(status);
{
@@ -1811,7 +1811,7 @@
}
}
- status = serverTransport->interruptableWriteFully(fdTrigger, msg2.data(), msg2.size());
+ status = serverTransport->interruptableWriteFully(fdTrigger, msg2.data(), msg2.size(), {});
if (status != DEAD_OBJECT)
return AssertionFailure() << "When FdTrigger is shut down, interruptableWriteFully "
"should return DEAD_OBJECT, but it is "
diff --git a/libs/gui/DisplayEventDispatcher.cpp b/libs/gui/DisplayEventDispatcher.cpp
index e1b1efc..6f1a7ae 100644
--- a/libs/gui/DisplayEventDispatcher.cpp
+++ b/libs/gui/DisplayEventDispatcher.cpp
@@ -153,6 +153,7 @@
outVsyncEventData->id = ev.vsync.vsyncId;
outVsyncEventData->deadlineTimestamp = ev.vsync.deadlineTimestamp;
outVsyncEventData->frameInterval = ev.vsync.frameInterval;
+ outVsyncEventData->expectedPresentTime = ev.vsync.expectedVSyncTimestamp;
break;
case DisplayEventReceiver::DISPLAY_EVENT_HOTPLUG:
dispatchHotplug(ev.header.timestamp, ev.header.displayId, ev.hotplug.connected);
diff --git a/libs/gui/include/gui/DisplayEventDispatcher.h b/libs/gui/include/gui/DisplayEventDispatcher.h
index 4ade240..f3bd139 100644
--- a/libs/gui/include/gui/DisplayEventDispatcher.h
+++ b/libs/gui/include/gui/DisplayEventDispatcher.h
@@ -33,6 +33,9 @@
// The current frame interval in ns when this frame was scheduled.
int64_t frameInterval = 0;
+
+ // The anticipated Vsync present time.
+ int64_t expectedPresentTime = 0;
};
class DisplayEventDispatcher : public LooperCallback {
diff --git a/libs/nativedisplay/AChoreographer.cpp b/libs/nativedisplay/AChoreographer.cpp
index ba71960..4c4a34f 100644
--- a/libs/nativedisplay/AChoreographer.cpp
+++ b/libs/nativedisplay/AChoreographer.cpp
@@ -77,6 +77,7 @@
struct FrameCallback {
AChoreographer_frameCallback callback;
AChoreographer_frameCallback64 callback64;
+ AChoreographer_extendedFrameCallback extendedCallback;
void* data;
nsecs_t dueTime;
@@ -95,6 +96,27 @@
class Choreographer;
+/**
+ * Implementation of AChoreographerFrameCallbackData.
+ */
+struct ChoreographerFrameCallbackDataImpl {
+ struct FrameTimeline {
+ int64_t vsyncId{0};
+ int64_t expectedPresentTimeNanos{0};
+ int64_t deadlineNanos{0};
+ };
+
+ int64_t frameTimeNanos{0};
+
+ size_t frameTimelinesLength;
+
+ std::vector<FrameTimeline> frameTimelines;
+
+ size_t preferredFrameTimelineIndex;
+
+ const Choreographer* choreographer;
+};
+
struct {
std::mutex lock;
std::vector<Choreographer*> ptrs GUARDED_BY(lock);
@@ -107,7 +129,9 @@
public:
explicit Choreographer(const sp<Looper>& looper) EXCLUDES(gChoreographers.lock);
void postFrameCallbackDelayed(AChoreographer_frameCallback cb,
- AChoreographer_frameCallback64 cb64, void* data, nsecs_t delay);
+ AChoreographer_frameCallback64 cb64,
+ AChoreographer_extendedFrameCallback extendedCallback, void* data,
+ nsecs_t delay);
void registerRefreshRateCallback(AChoreographer_refreshRateCallback cb, void* data)
EXCLUDES(gChoreographers.lock);
void unregisterRefreshRateCallback(AChoreographer_refreshRateCallback cb, void* data);
@@ -130,6 +154,7 @@
int64_t getVsyncId() const;
int64_t getFrameDeadline() const;
int64_t getFrameInterval() const;
+ bool inCallback() const;
private:
Choreographer(const Choreographer&) = delete;
@@ -145,6 +170,8 @@
void scheduleCallbacks();
+ ChoreographerFrameCallbackDataImpl createFrameCallbackData(nsecs_t timestamp) const;
+
std::mutex mLock;
// Protected by mLock
std::priority_queue<FrameCallback> mFrameCallbacks;
@@ -152,6 +179,7 @@
nsecs_t mLatestVsyncPeriod = -1;
VsyncEventData mLastVsyncEventData;
+ bool mInCallback = false;
const sp<Looper> mLooper;
const std::thread::id mThreadId;
@@ -211,10 +239,12 @@
}
}
-void Choreographer::postFrameCallbackDelayed(
- AChoreographer_frameCallback cb, AChoreographer_frameCallback64 cb64, void* data, nsecs_t delay) {
+void Choreographer::postFrameCallbackDelayed(AChoreographer_frameCallback cb,
+ AChoreographer_frameCallback64 cb64,
+ AChoreographer_extendedFrameCallback extendedCallback,
+ void* data, nsecs_t delay) {
nsecs_t now = systemTime(SYSTEM_TIME_MONOTONIC);
- FrameCallback callback{cb, cb64, data, now + delay};
+ FrameCallback callback{cb, cb64, extendedCallback, data, now + delay};
{
std::lock_guard<std::mutex> _l{mLock};
mFrameCallbacks.push(callback);
@@ -370,7 +400,15 @@
}
mLastVsyncEventData = vsyncEventData;
for (const auto& cb : callbacks) {
- if (cb.callback64 != nullptr) {
+ if (cb.extendedCallback != nullptr) {
+ const ChoreographerFrameCallbackDataImpl frameCallbackData =
+ createFrameCallbackData(timestamp);
+ mInCallback = true;
+ cb.extendedCallback(reinterpret_cast<const AChoreographerFrameCallbackData*>(
+ &frameCallbackData),
+ cb.data);
+ mInCallback = false;
+ } else if (cb.callback64 != nullptr) {
cb.callback64(timestamp, cb.data);
} else if (cb.callback != nullptr) {
cb.callback(timestamp, cb.data);
@@ -379,8 +417,8 @@
}
void Choreographer::dispatchHotplug(nsecs_t, PhysicalDisplayId displayId, bool connected) {
- ALOGV("choreographer %p ~ received hotplug event (displayId=%s, connected=%s), ignoring.",
- this, to_string(displayId).c_str(), toString(connected));
+ ALOGV("choreographer %p ~ received hotplug event (displayId=%s, connected=%s), ignoring.", this,
+ to_string(displayId).c_str(), toString(connected));
}
void Choreographer::dispatchModeChanged(nsecs_t, PhysicalDisplayId, int32_t, nsecs_t) {
@@ -399,15 +437,15 @@
void Choreographer::handleMessage(const Message& message) {
switch (message.what) {
- case MSG_SCHEDULE_CALLBACKS:
- scheduleCallbacks();
- break;
- case MSG_SCHEDULE_VSYNC:
- scheduleVsync();
- break;
- case MSG_HANDLE_REFRESH_RATE_UPDATES:
- handleRefreshRateUpdates();
- break;
+ case MSG_SCHEDULE_CALLBACKS:
+ scheduleCallbacks();
+ break;
+ case MSG_SCHEDULE_VSYNC:
+ scheduleVsync();
+ break;
+ case MSG_HANDLE_REFRESH_RATE_UPDATES:
+ handleRefreshRateUpdates();
+ break;
}
}
@@ -423,6 +461,22 @@
return mLastVsyncEventData.frameInterval;
}
+bool Choreographer::inCallback() const {
+ return mInCallback;
+}
+
+ChoreographerFrameCallbackDataImpl Choreographer::createFrameCallbackData(nsecs_t timestamp) const {
+ std::vector<ChoreographerFrameCallbackDataImpl::FrameTimeline> frameTimelines;
+ frameTimelines.push_back({.vsyncId = mLastVsyncEventData.id,
+ .expectedPresentTimeNanos = mLastVsyncEventData.expectedPresentTime,
+ .deadlineNanos = mLastVsyncEventData.deadlineTimestamp});
+ return {.frameTimeNanos = timestamp,
+ .frameTimelinesLength = 1,
+ .preferredFrameTimelineIndex = 0,
+ .frameTimelines = frameTimelines,
+ .choreographer = this};
+}
+
} // namespace android
using namespace android;
@@ -435,6 +489,12 @@
return reinterpret_cast<const Choreographer*>(choreographer);
}
+static inline const ChoreographerFrameCallbackDataImpl*
+AChoreographerFrameCallbackData_to_ChoreographerFrameCallbackDataImpl(
+ const AChoreographerFrameCallbackData* data) {
+ return reinterpret_cast<const ChoreographerFrameCallbackDataImpl*>(data);
+}
+
// Glue for private C api
namespace android {
void AChoreographer_signalRefreshRateCallbacks(nsecs_t vsyncPeriod) EXCLUDES(gChoreographers.lock) {
@@ -487,6 +547,11 @@
void* data, uint32_t delayMillis) {
return AChoreographer_postFrameCallbackDelayed64(choreographer, callback, data, delayMillis);
}
+void AChoreographer_routePostExtendedFrameCallback(AChoreographer* choreographer,
+ AChoreographer_extendedFrameCallback callback,
+ void* data) {
+ return AChoreographer_postExtendedFrameCallback(choreographer, callback, data);
+}
void AChoreographer_routeRegisterRefreshRateCallback(AChoreographer* choreographer,
AChoreographer_refreshRateCallback callback,
void* data) {
@@ -497,6 +562,30 @@
void* data) {
return AChoreographer_unregisterRefreshRateCallback(choreographer, callback, data);
}
+int64_t AChoreographerFrameCallbackData_routeGetFrameTimeNanos(
+ const AChoreographerFrameCallbackData* data) {
+ return AChoreographerFrameCallbackData_getFrameTimeNanos(data);
+}
+size_t AChoreographerFrameCallbackData_routeGetFrameTimelinesLength(
+ const AChoreographerFrameCallbackData* data) {
+ return AChoreographerFrameCallbackData_getFrameTimelinesLength(data);
+}
+size_t AChoreographerFrameCallbackData_routeGetPreferredFrameTimelineIndex(
+ const AChoreographerFrameCallbackData* data) {
+ return AChoreographerFrameCallbackData_getPreferredFrameTimelineIndex(data);
+}
+int64_t AChoreographerFrameCallbackData_routeGetFrameTimelineVsyncId(
+ const AChoreographerFrameCallbackData* data, size_t index) {
+ return AChoreographerFrameCallbackData_getFrameTimelineVsyncId(data, index);
+}
+int64_t AChoreographerFrameCallbackData_routeGetFrameTimelineExpectedPresentTime(
+ const AChoreographerFrameCallbackData* data, size_t index) {
+ return AChoreographerFrameCallbackData_getFrameTimelineExpectedPresentTime(data, index);
+}
+int64_t AChoreographerFrameCallbackData_routeGetFrameTimelineDeadline(
+ const AChoreographerFrameCallbackData* data, size_t index) {
+ return AChoreographerFrameCallbackData_getFrameTimelineDeadline(data, index);
+}
int64_t AChoreographer_getVsyncId(const AChoreographer* choreographer) {
return AChoreographer_to_Choreographer(choreographer)->getVsyncId();
@@ -523,24 +612,32 @@
}
void AChoreographer_postFrameCallback(AChoreographer* choreographer,
- AChoreographer_frameCallback callback, void* data) {
- AChoreographer_to_Choreographer(choreographer)->postFrameCallbackDelayed(
- callback, nullptr, data, 0);
+ AChoreographer_frameCallback callback, void* data) {
+ AChoreographer_to_Choreographer(choreographer)
+ ->postFrameCallbackDelayed(callback, nullptr, nullptr, data, 0);
}
void AChoreographer_postFrameCallbackDelayed(AChoreographer* choreographer,
- AChoreographer_frameCallback callback, void* data, long delayMillis) {
- AChoreographer_to_Choreographer(choreographer)->postFrameCallbackDelayed(
- callback, nullptr, data, ms2ns(delayMillis));
+ AChoreographer_frameCallback callback, void* data,
+ long delayMillis) {
+ AChoreographer_to_Choreographer(choreographer)
+ ->postFrameCallbackDelayed(callback, nullptr, nullptr, data, ms2ns(delayMillis));
+}
+void AChoreographer_postExtendedFrameCallback(AChoreographer* choreographer,
+ AChoreographer_extendedFrameCallback callback,
+ void* data) {
+ AChoreographer_to_Choreographer(choreographer)
+ ->postFrameCallbackDelayed(nullptr, nullptr, callback, data, 0);
}
void AChoreographer_postFrameCallback64(AChoreographer* choreographer,
- AChoreographer_frameCallback64 callback, void* data) {
- AChoreographer_to_Choreographer(choreographer)->postFrameCallbackDelayed(
- nullptr, callback, data, 0);
+ AChoreographer_frameCallback64 callback, void* data) {
+ AChoreographer_to_Choreographer(choreographer)
+ ->postFrameCallbackDelayed(nullptr, callback, nullptr, data, 0);
}
void AChoreographer_postFrameCallbackDelayed64(AChoreographer* choreographer,
- AChoreographer_frameCallback64 callback, void* data, uint32_t delayMillis) {
- AChoreographer_to_Choreographer(choreographer)->postFrameCallbackDelayed(
- nullptr, callback, data, ms2ns(delayMillis));
+ AChoreographer_frameCallback64 callback, void* data,
+ uint32_t delayMillis) {
+ AChoreographer_to_Choreographer(choreographer)
+ ->postFrameCallbackDelayed(nullptr, callback, nullptr, data, ms2ns(delayMillis));
}
void AChoreographer_registerRefreshRateCallback(AChoreographer* choreographer,
AChoreographer_refreshRateCallback callback,
@@ -553,6 +650,58 @@
AChoreographer_to_Choreographer(choreographer)->unregisterRefreshRateCallback(callback, data);
}
+int64_t AChoreographerFrameCallbackData_getFrameTimeNanos(
+ const AChoreographerFrameCallbackData* data) {
+ const ChoreographerFrameCallbackDataImpl* frameCallbackData =
+ AChoreographerFrameCallbackData_to_ChoreographerFrameCallbackDataImpl(data);
+ LOG_ALWAYS_FATAL_IF(!frameCallbackData->choreographer->inCallback(),
+ "Data is only valid in callback");
+ return frameCallbackData->frameTimeNanos;
+}
+size_t AChoreographerFrameCallbackData_getFrameTimelinesLength(
+ const AChoreographerFrameCallbackData* data) {
+ const ChoreographerFrameCallbackDataImpl* frameCallbackData =
+ AChoreographerFrameCallbackData_to_ChoreographerFrameCallbackDataImpl(data);
+ LOG_ALWAYS_FATAL_IF(!frameCallbackData->choreographer->inCallback(),
+ "Data is only valid in callback");
+ return frameCallbackData->frameTimelinesLength;
+}
+size_t AChoreographerFrameCallbackData_getPreferredFrameTimelineIndex(
+ const AChoreographerFrameCallbackData* data) {
+ const ChoreographerFrameCallbackDataImpl* frameCallbackData =
+ AChoreographerFrameCallbackData_to_ChoreographerFrameCallbackDataImpl(data);
+ LOG_ALWAYS_FATAL_IF(!frameCallbackData->choreographer->inCallback(),
+ "Data is only valid in callback");
+ return frameCallbackData->preferredFrameTimelineIndex;
+}
+int64_t AChoreographerFrameCallbackData_getFrameTimelineVsyncId(
+ const AChoreographerFrameCallbackData* data, size_t index) {
+ const ChoreographerFrameCallbackDataImpl* frameCallbackData =
+ AChoreographerFrameCallbackData_to_ChoreographerFrameCallbackDataImpl(data);
+ LOG_ALWAYS_FATAL_IF(!frameCallbackData->choreographer->inCallback(),
+ "Data is only valid in callback");
+ LOG_ALWAYS_FATAL_IF(index >= frameCallbackData->frameTimelinesLength, "Index out of bounds");
+ return frameCallbackData->frameTimelines[index].vsyncId;
+}
+int64_t AChoreographerFrameCallbackData_getFrameTimelineExpectedPresentTime(
+ const AChoreographerFrameCallbackData* data, size_t index) {
+ const ChoreographerFrameCallbackDataImpl* frameCallbackData =
+ AChoreographerFrameCallbackData_to_ChoreographerFrameCallbackDataImpl(data);
+ LOG_ALWAYS_FATAL_IF(!frameCallbackData->choreographer->inCallback(),
+ "Data is only valid in callback");
+ LOG_ALWAYS_FATAL_IF(index >= frameCallbackData->frameTimelinesLength, "Index out of bounds");
+ return frameCallbackData->frameTimelines[index].expectedPresentTimeNanos;
+}
+int64_t AChoreographerFrameCallbackData_getFrameTimelineDeadline(
+ const AChoreographerFrameCallbackData* data, size_t index) {
+ const ChoreographerFrameCallbackDataImpl* frameCallbackData =
+ AChoreographerFrameCallbackData_to_ChoreographerFrameCallbackDataImpl(data);
+ LOG_ALWAYS_FATAL_IF(!frameCallbackData->choreographer->inCallback(),
+ "Data is only valid in callback");
+ LOG_ALWAYS_FATAL_IF(index >= frameCallbackData->frameTimelinesLength, "Index out of bounds");
+ return frameCallbackData->frameTimelines[index].deadlineNanos;
+}
+
AChoreographer* AChoreographer_create() {
Choreographer* choreographer = new Choreographer(nullptr);
status_t result = choreographer->initialize();
diff --git a/libs/nativedisplay/include-private/private/android/choreographer.h b/libs/nativedisplay/include-private/private/android/choreographer.h
index 7d25ce8..6e90853 100644
--- a/libs/nativedisplay/include-private/private/android/choreographer.h
+++ b/libs/nativedisplay/include-private/private/android/choreographer.h
@@ -63,11 +63,26 @@
void AChoreographer_routePostFrameCallbackDelayed64(AChoreographer* choreographer,
AChoreographer_frameCallback64 callback,
void* data, uint32_t delayMillis);
+void AChoreographer_routePostExtendedFrameCallback(AChoreographer* choreographer,
+ AChoreographer_extendedFrameCallback callback,
+ void* data);
void AChoreographer_routeRegisterRefreshRateCallback(AChoreographer* choreographer,
AChoreographer_refreshRateCallback callback,
void* data);
void AChoreographer_routeUnregisterRefreshRateCallback(AChoreographer* choreographer,
AChoreographer_refreshRateCallback callback,
void* data);
+int64_t AChoreographerFrameCallbackData_routeGetFrameTimeNanos(
+ const AChoreographerFrameCallbackData* data);
+size_t AChoreographerFrameCallbackData_routeGetFrameTimelinesLength(
+ const AChoreographerFrameCallbackData* data);
+size_t AChoreographerFrameCallbackData_routeGetPreferredFrameTimelineIndex(
+ const AChoreographerFrameCallbackData* data);
+int64_t AChoreographerFrameCallbackData_routeGetFrameTimelineVsyncId(
+ const AChoreographerFrameCallbackData* data, size_t index);
+int64_t AChoreographerFrameCallbackData_routeGetFrameTimelineExpectedPresentTime(
+ const AChoreographerFrameCallbackData* data, size_t index);
+int64_t AChoreographerFrameCallbackData_routeGetFrameTimelineDeadline(
+ const AChoreographerFrameCallbackData* data, size_t index);
} // namespace android
diff --git a/libs/nativedisplay/libnativedisplay.map.txt b/libs/nativedisplay/libnativedisplay.map.txt
index 9ed4915..b4a70e8 100644
--- a/libs/nativedisplay/libnativedisplay.map.txt
+++ b/libs/nativedisplay/libnativedisplay.map.txt
@@ -7,6 +7,13 @@
AChoreographer_postFrameCallbackDelayed64; # apex # introduced=30
AChoreographer_registerRefreshRateCallback; # apex # introduced=30
AChoreographer_unregisterRefreshRateCallback; # apex # introduced=30
+ AChoreographer_postExtendedFrameCallback; # apex # introduced=33
+ AChoreographerFrameCallbackData_getFrameTimeNanos; # apex # introduced=33
+ AChoreographerFrameCallbackData_getFrameTimelinesLength; # apex # introduced=33
+ AChoreographerFrameCallbackData_getPreferredFrameTimelineIndex; # apex # introduced=33
+ AChoreographerFrameCallbackData_getFrameTimelineVsyncId; # apex # introduced=33
+ AChoreographerFrameCallbackData_getFrameTimelineExpectedPresentTime; # apex # introduced=33
+ AChoreographerFrameCallbackData_getFrameTimelineDeadline; # apex # introduced=33
AChoreographer_create; # apex # introduced=30
AChoreographer_destroy; # apex # introduced=30
AChoreographer_getFd; # apex # introduced=30
@@ -28,6 +35,13 @@
android::AChoreographer_routePostFrameCallbackDelayed64*;
android::AChoreographer_routeRegisterRefreshRateCallback*;
android::AChoreographer_routeUnregisterRefreshRateCallback*;
+ android::AChoreographer_routePostExtendedFrameCallback*;
+ android::AChoreographerFrameCallbackData_routeGetFrameTimeNanos*;
+ android::AChoreographerFrameCallbackData_routeGetFrameTimelinesLength*;
+ android::AChoreographerFrameCallbackData_routeGetPreferredFrameTimelineIndex*;
+ android::AChoreographerFrameCallbackData_routeGetFrameTimelineVsyncId*;
+ android::AChoreographerFrameCallbackData_routeGetFrameTimelineExpectedPresentTime*;
+ android::AChoreographerFrameCallbackData_routeGetFrameTimelineDeadline*;
android::AChoreographer_signalRefreshRateCallbacks*;
android::AChoreographer_getVsyncId*;
android::AChoreographer_getFrameDeadline*;
diff --git a/libs/ui/Transform.cpp b/libs/ui/Transform.cpp
index cd68c1c..b34d906 100644
--- a/libs/ui/Transform.cpp
+++ b/libs/ui/Transform.cpp
@@ -396,6 +396,11 @@
result.mMatrix[1][0] = -b*idet;
result.mMatrix[1][1] = a*idet;
result.mType = mType;
+ if (getOrientation() & ROT_90) {
+ // Recalculate the type if there is a 90-degree rotation component, since the inverse
+ // of ROT_90 is ROT_270 and vice versa.
+ result.mType |= UNKNOWN_TYPE;
+ }
vec2 T(-x, -y);
T = result.transform(T);
diff --git a/libs/ui/tests/Android.bp b/libs/ui/tests/Android.bp
index a87740a..0ee15f2 100644
--- a/libs/ui/tests/Android.bp
+++ b/libs/ui/tests/Android.bp
@@ -27,28 +27,40 @@
name: "Region_test",
shared_libs: ["libui"],
srcs: ["Region_test.cpp"],
- cflags: ["-Wall", "-Werror"],
+ cflags: [
+ "-Wall",
+ "-Werror",
+ ],
}
cc_test {
name: "colorspace_test",
shared_libs: ["libui"],
srcs: ["colorspace_test.cpp"],
- cflags: ["-Wall", "-Werror"],
+ cflags: [
+ "-Wall",
+ "-Werror",
+ ],
}
cc_test {
name: "DisplayId_test",
shared_libs: ["libui"],
srcs: ["DisplayId_test.cpp"],
- cflags: ["-Wall", "-Werror"],
+ cflags: [
+ "-Wall",
+ "-Werror",
+ ],
}
cc_test {
name: "FlattenableHelpers_test",
shared_libs: ["libui"],
srcs: ["FlattenableHelpers_test.cpp"],
- cflags: ["-Wall", "-Werror"],
+ cflags: [
+ "-Wall",
+ "-Werror",
+ ],
}
cc_test {
@@ -68,7 +80,10 @@
"GraphicBufferAllocator_test.cpp",
"mock/MockGrallocAllocator.cpp",
],
- cflags: ["-Wall", "-Werror"],
+ cflags: [
+ "-Wall",
+ "-Werror",
+ ],
}
cc_test {
@@ -83,14 +98,20 @@
"libutils",
],
srcs: ["GraphicBuffer_test.cpp"],
- cflags: ["-Wall", "-Werror"],
+ cflags: [
+ "-Wall",
+ "-Werror",
+ ],
}
// This test has a main method, and requires a separate binary to be built.
cc_test {
name: "GraphicBufferOverBinder_test",
srcs: ["GraphicBufferOverBinder_test.cpp"],
- cflags: ["-Wall", "-Werror"],
+ cflags: [
+ "-Wall",
+ "-Werror",
+ ],
shared_libs: [
"libbinder",
"libgui",
@@ -105,7 +126,10 @@
test_suites: ["device-tests"],
shared_libs: ["libui"],
srcs: ["Rect_test.cpp"],
- cflags: ["-Wall", "-Werror"],
+ cflags: [
+ "-Wall",
+ "-Werror",
+ ],
}
cc_test {
@@ -113,7 +137,10 @@
test_suites: ["device-tests"],
shared_libs: ["libui"],
srcs: ["Size_test.cpp"],
- cflags: ["-Wall", "-Werror"],
+ cflags: [
+ "-Wall",
+ "-Werror",
+ ],
}
cc_test {
@@ -121,6 +148,18 @@
shared_libs: ["libui"],
static_libs: ["libgmock"],
srcs: ["MockFence_test.cpp"],
- cflags: ["-Wall", "-Werror"],
+ cflags: [
+ "-Wall",
+ "-Werror",
+ ],
}
+cc_test {
+ name: "Transform_test",
+ shared_libs: ["libui"],
+ srcs: ["Transform_test.cpp"],
+ cflags: [
+ "-Wall",
+ "-Werror",
+ ],
+}
diff --git a/libs/ui/tests/Transform_test.cpp b/libs/ui/tests/Transform_test.cpp
new file mode 100644
index 0000000..6964284
--- /dev/null
+++ b/libs/ui/tests/Transform_test.cpp
@@ -0,0 +1,49 @@
+/*
+ * Copyright 2021 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 <ui/Transform.h>
+
+#include <gtest/gtest.h>
+
+namespace android::ui {
+
+TEST(TransformTest, inverseRotation_hasCorrectType) {
+ const auto testRotationFlagsForInverse = [](Transform::RotationFlags rotation,
+ Transform::RotationFlags expectedInverse,
+ bool isRotation) {
+ const Transform t(rotation, 0, 0);
+ EXPECT_EQ(t.getOrientation(), rotation);
+ const Transform inverse = t.inverse();
+ EXPECT_EQ(inverse.getOrientation(), expectedInverse);
+
+ if (isRotation) {
+ EXPECT_TRUE(t.getType() & Transform::ROTATE);
+ EXPECT_TRUE(inverse.getType() & Transform::ROTATE);
+ } else {
+ EXPECT_FALSE(t.getType() & Transform::ROTATE);
+ EXPECT_FALSE(inverse.getType() & Transform::ROTATE);
+ }
+ };
+
+ testRotationFlagsForInverse(Transform::ROT_0, Transform::ROT_0, false);
+ testRotationFlagsForInverse(Transform::ROT_90, Transform::ROT_270, true);
+ testRotationFlagsForInverse(Transform::ROT_180, Transform::ROT_180, true);
+ testRotationFlagsForInverse(Transform::ROT_270, Transform::ROT_90, true);
+ testRotationFlagsForInverse(Transform::FLIP_H, Transform::FLIP_H, false);
+ testRotationFlagsForInverse(Transform::FLIP_V, Transform::FLIP_V, false);
+}
+
+} // namespace android::ui