Merge "Add Consumer name in Surface::dequeueBuffer trace"
diff --git a/cmds/dumpstate/dumpstate.cpp b/cmds/dumpstate/dumpstate.cpp
index f759674..6dea91b 100644
--- a/cmds/dumpstate/dumpstate.cpp
+++ b/cmds/dumpstate/dumpstate.cpp
@@ -1508,7 +1508,6 @@
dprintf(out_fd, "========================================================\n");
RunDumpsys("CHECKIN BATTERYSTATS", {"batterystats", "-c"}, out_fd);
- RunDumpsys("CHECKIN MEMINFO", {"meminfo", "--checkin"}, out_fd);
RunDumpsys("CHECKIN NETSTATS", {"netstats", "--checkin"}, out_fd);
RunDumpsys("CHECKIN PROCSTATS", {"procstats", "-c"}, out_fd);
RunDumpsys("CHECKIN USAGESTATS", {"usagestats", "-c"}, out_fd);
diff --git a/cmds/servicemanager/Android.bp b/cmds/servicemanager/Android.bp
index 32922ca..3b753c6 100644
--- a/cmds/servicemanager/Android.bp
+++ b/cmds/servicemanager/Android.bp
@@ -44,6 +44,7 @@
defaults: ["servicemanager_defaults"],
init_rc: ["servicemanager.rc"],
srcs: ["main.cpp"],
+ bootstrap: true,
}
cc_binary {
diff --git a/include/audiomanager/AudioManager.h b/include/audiomanager/AudioManager.h
index 4aa2f60..caf13a0 100644
--- a/include/audiomanager/AudioManager.h
+++ b/include/audiomanager/AudioManager.h
@@ -38,6 +38,7 @@
PLAYER_STATE_PAUSED = 3,
PLAYER_STATE_STOPPED = 4,
PLAYER_UPDATE_DEVICE_ID = 5,
+ PLAYER_UPDATE_PORT_ID = 6,
} player_state_t;
// must be kept in sync with definitions in AudioManager.java
diff --git a/include/audiomanager/IAudioManager.h b/include/audiomanager/IAudioManager.h
index 426e10c..3d531fe 100644
--- a/include/audiomanager/IAudioManager.h
+++ b/include/audiomanager/IAudioManager.h
@@ -17,6 +17,7 @@
#ifndef ANDROID_IAUDIOMANAGER_H
#define ANDROID_IAUDIOMANAGER_H
+#include <audiomanager/AudioManager.h>
#include <utils/Errors.h>
#include <binder/IInterface.h>
#include <hardware/power.h>
@@ -52,7 +53,7 @@
/*oneway*/ virtual status_t playerAttributes(audio_unique_id_t piid, audio_usage_t usage,
audio_content_type_t content)= 0;
/*oneway*/ virtual status_t playerEvent(audio_unique_id_t piid, player_state_t event,
- audio_port_handle_t deviceId) = 0;
+ audio_port_handle_t eventId) = 0;
/*oneway*/ virtual status_t releasePlayer(audio_unique_id_t piid) = 0;
virtual audio_unique_id_t trackRecorder(const sp<IBinder>& recorder) = 0;
/*oneway*/ virtual status_t recorderEvent(audio_unique_id_t riid, recorder_state_t event) = 0;
diff --git a/include/input/PropertyMap.h b/include/input/PropertyMap.h
index 451918b..b1e3f85 100644
--- a/include/input/PropertyMap.h
+++ b/include/input/PropertyMap.h
@@ -18,10 +18,8 @@
#define _UTILS_PROPERTY_MAP_H
#include <android-base/result.h>
-#include <utils/Errors.h>
-#include <utils/KeyedVector.h>
-#include <utils/String8.h>
#include <utils/Tokenizer.h>
+#include <unordered_map>
namespace android {
@@ -58,30 +56,27 @@
/* Adds a property.
* Replaces the property with the same key if it is already present.
*/
- void addProperty(const String8& key, const String8& value);
-
- /* Returns true if the property map contains the specified key. */
- bool hasProperty(const String8& key) const;
+ void addProperty(const std::string& key, const std::string& value);
/* Gets the value of a property and parses it.
* Returns true and sets outValue if the key was found and its value was parsed successfully.
* Otherwise returns false and does not modify outValue. (Also logs a warning.)
*/
- bool tryGetProperty(const String8& key, String8& outValue) const;
- bool tryGetProperty(const String8& key, bool& outValue) const;
- bool tryGetProperty(const String8& key, int32_t& outValue) const;
- bool tryGetProperty(const String8& key, float& outValue) const;
+ bool tryGetProperty(const std::string& key, std::string& outValue) const;
+ bool tryGetProperty(const std::string& key, bool& outValue) const;
+ bool tryGetProperty(const std::string& key, int32_t& outValue) const;
+ bool tryGetProperty(const std::string& key, float& outValue) const;
/* Adds all values from the specified property map. */
void addAll(const PropertyMap* map);
- /* Gets the underlying property map. */
- inline const KeyedVector<String8, String8>& getProperties() const { return mProperties; }
-
/* Loads a property map from a file. */
static android::base::Result<std::unique_ptr<PropertyMap>> load(const char* filename);
private:
+ /* Returns true if the property map contains the specified key. */
+ bool hasProperty(const std::string& key) const;
+
class Parser {
PropertyMap* mMap;
Tokenizer* mTokenizer;
@@ -95,11 +90,11 @@
status_t parseType();
status_t parseKey();
status_t parseKeyProperty();
- status_t parseModifier(const String8& token, int32_t* outMetaState);
+ status_t parseModifier(const std::string& token, int32_t* outMetaState);
status_t parseCharacterLiteral(char16_t* outCharacter);
};
- KeyedVector<String8, String8> mProperties;
+ std::unordered_map<std::string, std::string> mProperties;
};
} // namespace android
diff --git a/libs/binder/BpBinder.cpp b/libs/binder/BpBinder.cpp
index 49fc195..82ebdd7 100644
--- a/libs/binder/BpBinder.cpp
+++ b/libs/binder/BpBinder.cpp
@@ -358,6 +358,14 @@
LOG_ALWAYS_FATAL_IF(recipient == nullptr,
"linkToDeath(): recipient must be non-NULL");
+ if (ProcessState::self()->getThreadPoolMaxTotalThreadCount() == 0) {
+ ALOGW("Linking to death on %s but there are no threads (yet?) listening to incoming "
+ "transactions. See ProcessState::startThreadPool and "
+ "ProcessState::setThreadPoolMaxThreadCount. Generally you should setup the binder "
+ "threadpool before other initialization steps.",
+ String8(getInterfaceDescriptor()).c_str());
+ }
+
{
AutoMutex _l(mLock);
diff --git a/libs/binder/IPCThreadState.cpp b/libs/binder/IPCThreadState.cpp
index d536219..b50cfb3 100644
--- a/libs/binder/IPCThreadState.cpp
+++ b/libs/binder/IPCThreadState.cpp
@@ -972,18 +972,15 @@
freeBuffer);
} else {
err = *reinterpret_cast<const status_t*>(tr.data.ptr.buffer);
- freeBuffer(nullptr,
- reinterpret_cast<const uint8_t*>(tr.data.ptr.buffer),
- tr.data_size,
- reinterpret_cast<const binder_size_t*>(tr.data.ptr.offsets),
- tr.offsets_size/sizeof(binder_size_t));
+ freeBuffer(reinterpret_cast<const uint8_t*>(tr.data.ptr.buffer),
+ tr.data_size,
+ reinterpret_cast<const binder_size_t*>(tr.data.ptr.offsets),
+ tr.offsets_size / sizeof(binder_size_t));
}
} else {
- freeBuffer(nullptr,
- reinterpret_cast<const uint8_t*>(tr.data.ptr.buffer),
- tr.data_size,
- reinterpret_cast<const binder_size_t*>(tr.data.ptr.offsets),
- tr.offsets_size/sizeof(binder_size_t));
+ freeBuffer(reinterpret_cast<const uint8_t*>(tr.data.ptr.buffer), tr.data_size,
+ reinterpret_cast<const binder_size_t*>(tr.data.ptr.offsets),
+ tr.offsets_size / sizeof(binder_size_t));
continue;
}
}
@@ -1473,17 +1470,13 @@
ee.id, ee.command, ee.param);
}
-void IPCThreadState::freeBuffer(Parcel* parcel, const uint8_t* data,
- size_t /*dataSize*/,
- const binder_size_t* /*objects*/,
- size_t /*objectsSize*/)
-{
+void IPCThreadState::freeBuffer(const uint8_t* data, size_t /*dataSize*/,
+ const binder_size_t* /*objects*/, size_t /*objectsSize*/) {
//ALOGI("Freeing parcel %p", &parcel);
IF_LOG_COMMANDS() {
alog << "Writing BC_FREE_BUFFER for " << data << endl;
}
ALOG_ASSERT(data != NULL, "Called with NULL data");
- if (parcel != nullptr) parcel->closeFileDescriptors();
IPCThreadState* state = self();
state->mOut.writeInt32(BC_FREE_BUFFER);
state->mOut.writePointer((uintptr_t)data);
diff --git a/libs/binder/Parcel.cpp b/libs/binder/Parcel.cpp
index 537527e..8b5d118 100644
--- a/libs/binder/Parcel.cpp
+++ b/libs/binder/Parcel.cpp
@@ -2590,6 +2590,22 @@
LOG_ALWAYS_FATAL_IF(session == nullptr);
+ if (objectTableSize != ancillaryFds.size()) {
+ ALOGE("objectTableSize=%zu ancillaryFds.size=%zu", objectTableSize, ancillaryFds.size());
+ relFunc(data, dataSize, nullptr, 0);
+ return BAD_VALUE;
+ }
+ for (size_t i = 0; i < objectTableSize; i++) {
+ uint32_t minObjectEnd;
+ if (__builtin_add_overflow(objectTable[i], sizeof(RpcFields::ObjectType), &minObjectEnd) ||
+ minObjectEnd >= dataSize) {
+ ALOGE("received out of range object position: %" PRIu32 " (parcel size is %zu)",
+ objectTable[i], dataSize);
+ relFunc(data, dataSize, nullptr, 0);
+ return BAD_VALUE;
+ }
+ }
+
freeData();
markForRpc(session);
@@ -2600,12 +2616,6 @@
mDataSize = mDataCapacity = dataSize;
mOwner = relFunc;
- if (objectTableSize != ancillaryFds.size()) {
- ALOGE("objectTableSize=%zu ancillaryFds.size=%zu", objectTableSize, ancillaryFds.size());
- freeData(); // don't leak mData
- return BAD_VALUE;
- }
-
rpcFields->mObjectPositions.reserve(objectTableSize);
for (size_t i = 0; i < objectTableSize; i++) {
rpcFields->mObjectPositions.push_back(objectTable[i]);
@@ -2706,7 +2716,9 @@
LOG_ALLOC("Parcel %p: freeing other owner data", this);
//ALOGI("Freeing data ref of %p (pid=%d)", this, getpid());
auto* kernelFields = maybeKernelFields();
- mOwner(this, mData, mDataSize, kernelFields ? kernelFields->mObjects : nullptr,
+ // Close FDs before freeing, otherwise they will leak for kernel binder.
+ closeFileDescriptors();
+ mOwner(mData, mDataSize, kernelFields ? kernelFields->mObjects : nullptr,
kernelFields ? kernelFields->mObjectsSize : 0);
} else {
LOG_ALLOC("Parcel %p: freeing allocated data", this);
@@ -2891,8 +2903,13 @@
if (objects && kernelFields && kernelFields->mObjects) {
memcpy(objects, kernelFields->mObjects, objectsSize * sizeof(binder_size_t));
}
- //ALOGI("Freeing data ref of %p (pid=%d)", this, getpid());
- mOwner(this, mData, mDataSize, kernelFields ? kernelFields->mObjects : nullptr,
+ // ALOGI("Freeing data ref of %p (pid=%d)", this, getpid());
+ if (kernelFields) {
+ // TODO(b/239222407): This seems wrong. We should only free FDs when
+ // they are in a truncated section of the parcel.
+ closeFileDescriptors();
+ }
+ mOwner(mData, mDataSize, kernelFields ? kernelFields->mObjects : nullptr,
kernelFields ? kernelFields->mObjectsSize : 0);
mOwner = nullptr;
diff --git a/libs/binder/ProcessState.cpp b/libs/binder/ProcessState.cpp
index 7faff47..1f311ac 100644
--- a/libs/binder/ProcessState.cpp
+++ b/libs/binder/ProcessState.cpp
@@ -19,6 +19,7 @@
#include <binder/ProcessState.h>
#include <android-base/result.h>
+#include <android-base/scopeguard.h>
#include <android-base/strings.h>
#include <binder/BpBinder.h>
#include <binder/IPCThreadState.h>
@@ -420,6 +421,9 @@
}
size_t ProcessState::getThreadPoolMaxTotalThreadCount() const {
+ pthread_mutex_lock(&mThreadCountLock);
+ base::ScopeGuard detachGuard = [&]() { pthread_mutex_unlock(&mThreadCountLock); };
+
// may actually be one more than this, if join is called
if (mThreadPoolStarted) {
return mCurrentThreads < mKernelStartedThreads
diff --git a/libs/binder/RpcServer.cpp b/libs/binder/RpcServer.cpp
index 096d5cc..49be4dd 100644
--- a/libs/binder/RpcServer.cpp
+++ b/libs/binder/RpcServer.cpp
@@ -209,9 +209,10 @@
{
RpcMutexLockGuard _l(mLock);
- RpcMaybeThread thread = RpcMaybeThread(&RpcServer::establishConnection,
- sp<RpcServer>::fromExisting(this),
- std::move(clientFd), addr, addrLen);
+ RpcMaybeThread thread =
+ RpcMaybeThread(&RpcServer::establishConnection,
+ sp<RpcServer>::fromExisting(this), std::move(clientFd), addr,
+ addrLen, RpcSession::join);
auto& threadRef = mConnectingThreads[thread.get_id()];
threadRef = std::move(thread);
@@ -294,8 +295,10 @@
return mConnectingThreads.size();
}
-void RpcServer::establishConnection(sp<RpcServer>&& server, base::unique_fd clientFd,
- std::array<uint8_t, kRpcAddressSize> addr, size_t addrLen) {
+void RpcServer::establishConnection(
+ sp<RpcServer>&& server, base::unique_fd clientFd, std::array<uint8_t, kRpcAddressSize> addr,
+ size_t addrLen,
+ std::function<void(sp<RpcSession>&&, RpcSession::PreJoinSetupResult&&)>&& joinFn) {
// mShutdownTrigger can only be cleared once connection threads have joined.
// It must be set before this thread is started
LOG_ALWAYS_FATAL_IF(server->mShutdownTrigger == nullptr);
@@ -478,7 +481,7 @@
// avoid strong cycle
server = nullptr;
- RpcSession::join(std::move(session), std::move(setupResult));
+ joinFn(std::move(session), std::move(setupResult));
}
status_t RpcServer::setupSocketServer(const RpcSocketAddress& addr) {
diff --git a/libs/binder/RpcState.cpp b/libs/binder/RpcState.cpp
index 0ae75cd..d063001 100644
--- a/libs/binder/RpcState.cpp
+++ b/libs/binder/RpcState.cpp
@@ -244,60 +244,63 @@
"New state should be impossible after terminating!");
return;
}
+ mTerminated = true;
if (SHOULD_LOG_RPC_DETAIL) {
ALOGE("RpcState::clear()");
dumpLocked();
}
- // if the destructor of a binder object makes another RPC call, then calling
- // decStrong could deadlock. So, we must hold onto these binders until
- // mNodeMutex is no longer taken.
- std::vector<sp<IBinder>> tempHoldBinder;
-
- mTerminated = true;
+ // invariants
for (auto& [address, node] : mNodeForAddress) {
- sp<IBinder> binder = node.binder.promote();
- LOG_ALWAYS_FATAL_IF(binder == nullptr, "Binder %p expected to be owned.", binder.get());
-
- if (node.sentRef != nullptr) {
- tempHoldBinder.push_back(node.sentRef);
+ bool guaranteedHaveBinder = node.timesSent > 0;
+ if (guaranteedHaveBinder) {
+ LOG_ALWAYS_FATAL_IF(node.sentRef == nullptr,
+ "Binder expected to be owned with address: %" PRIu64 " %s", address,
+ node.toString().c_str());
}
}
- mNodeForAddress.clear();
+ // if the destructor of a binder object makes another RPC call, then calling
+ // decStrong could deadlock. So, we must hold onto these binders until
+ // mNodeMutex is no longer taken.
+ auto temp = std::move(mNodeForAddress);
+ mNodeForAddress.clear(); // RpcState isn't reusable, but for future/explicit
_l.unlock();
- tempHoldBinder.clear(); // explicit
+ temp.clear(); // explicit
}
void RpcState::dumpLocked() {
ALOGE("DUMP OF RpcState %p", this);
ALOGE("DUMP OF RpcState (%zu nodes)", mNodeForAddress.size());
for (const auto& [address, node] : mNodeForAddress) {
- sp<IBinder> binder = node.binder.promote();
-
- const char* desc;
- if (binder) {
- if (binder->remoteBinder()) {
- if (binder->remoteBinder()->isRpcBinder()) {
- desc = "(rpc binder proxy)";
- } else {
- desc = "(binder proxy)";
- }
- } else {
- desc = "(local binder)";
- }
- } else {
- desc = "(null)";
- }
-
- ALOGE("- BINDER NODE: %p times sent:%zu times recd: %zu a: %" PRIu64 " type: %s",
- node.binder.unsafe_get(), node.timesSent, node.timesRecd, address, desc);
+ ALOGE("- address: %" PRIu64 " %s", address, node.toString().c_str());
}
ALOGE("END DUMP OF RpcState");
}
+std::string RpcState::BinderNode::toString() const {
+ sp<IBinder> strongBinder = this->binder.promote();
+
+ const char* desc;
+ if (strongBinder) {
+ if (strongBinder->remoteBinder()) {
+ if (strongBinder->remoteBinder()->isRpcBinder()) {
+ desc = "(rpc binder proxy)";
+ } else {
+ desc = "(binder proxy)";
+ }
+ } else {
+ desc = "(local binder)";
+ }
+ } else {
+ desc = "(not promotable)";
+ }
+
+ return StringPrintf("node{%p times sent: %zu times recd: %zu type: %s}",
+ this->binder.unsafe_get(), this->timesSent, this->timesRecd, desc);
+}
RpcState::CommandData::CommandData(size_t size) : mSize(size) {
// The maximum size for regular binder is 1MB for all concurrent
@@ -581,13 +584,12 @@
return waitForReply(connection, session, reply);
}
-static void cleanup_reply_data(Parcel* p, const uint8_t* data, size_t dataSize,
- const binder_size_t* objects, size_t objectsCount) {
- (void)p;
+static void cleanup_reply_data(const uint8_t* data, size_t dataSize, const binder_size_t* objects,
+ size_t objectsCount) {
delete[] const_cast<uint8_t*>(data);
(void)dataSize;
LOG_ALWAYS_FATAL_IF(objects != nullptr);
- LOG_ALWAYS_FATAL_IF(objectsCount != 0, "%zu objects remaining", objectsCount);
+ (void)objectsCount;
}
status_t RpcState::waitForReply(const sp<RpcSession::RpcConnection>& connection,
@@ -643,14 +645,21 @@
Span<const uint32_t> objectTableSpan;
if (session->getProtocolVersion().value() >=
RPC_WIRE_PROTOCOL_VERSION_RPC_HEADER_FEATURE_EXPLICIT_PARCEL_SIZE) {
- Span<const uint8_t> objectTableBytes = parcelSpan.splitOff(rpcReply.parcelDataSize);
+ std::optional<Span<const uint8_t>> objectTableBytes =
+ parcelSpan.splitOff(rpcReply.parcelDataSize);
+ if (!objectTableBytes.has_value()) {
+ ALOGE("Parcel size larger than available bytes: %" PRId32 " vs %zu. Terminating!",
+ rpcReply.parcelDataSize, parcelSpan.byteSize());
+ (void)session->shutdownAndWait(false);
+ return BAD_VALUE;
+ }
std::optional<Span<const uint32_t>> maybeSpan =
- objectTableBytes.reinterpret<const uint32_t>();
+ objectTableBytes->reinterpret<const uint32_t>();
if (!maybeSpan.has_value()) {
ALOGE("Bad object table size inferred from RpcWireReply. Saw bodySize=%" PRId32
" sizeofHeader=%zu parcelSize=%" PRId32 " objectTableBytesSize=%zu. Terminating!",
command.bodySize, rpcReplyWireSize, rpcReply.parcelDataSize,
- objectTableBytes.size);
+ objectTableBytes->size);
return BAD_VALUE;
}
objectTableSpan = *maybeSpan;
@@ -787,9 +796,8 @@
std::move(ancillaryFds));
}
-static void do_nothing_to_transact_data(Parcel* p, const uint8_t* data, size_t dataSize,
+static void do_nothing_to_transact_data(const uint8_t* data, size_t dataSize,
const binder_size_t* objects, size_t objectsCount) {
- (void)p;
(void)data;
(void)dataSize;
(void)objects;
@@ -893,15 +901,22 @@
Span<const uint32_t> objectTableSpan;
if (session->getProtocolVersion().value() >
RPC_WIRE_PROTOCOL_VERSION_RPC_HEADER_FEATURE_EXPLICIT_PARCEL_SIZE) {
- Span<const uint8_t> objectTableBytes = parcelSpan.splitOff(transaction->parcelDataSize);
+ std::optional<Span<const uint8_t>> objectTableBytes =
+ parcelSpan.splitOff(transaction->parcelDataSize);
+ if (!objectTableBytes.has_value()) {
+ ALOGE("Parcel size (%" PRId32 ") greater than available bytes (%zu). Terminating!",
+ transaction->parcelDataSize, parcelSpan.byteSize());
+ (void)session->shutdownAndWait(false);
+ return BAD_VALUE;
+ }
std::optional<Span<const uint32_t>> maybeSpan =
- objectTableBytes.reinterpret<const uint32_t>();
+ objectTableBytes->reinterpret<const uint32_t>();
if (!maybeSpan.has_value()) {
ALOGE("Bad object table size inferred from RpcWireTransaction. Saw bodySize=%zu "
"sizeofHeader=%zu parcelSize=%" PRId32
" objectTableBytesSize=%zu. Terminating!",
transactionData.size(), sizeof(RpcWireTransaction),
- transaction->parcelDataSize, objectTableBytes.size);
+ transaction->parcelDataSize, objectTableBytes->size);
return BAD_VALUE;
}
objectTableSpan = *maybeSpan;
diff --git a/libs/binder/RpcState.h b/libs/binder/RpcState.h
index 6fb2e4a..892ecd6 100644
--- a/libs/binder/RpcState.h
+++ b/libs/binder/RpcState.h
@@ -258,6 +258,8 @@
//
// (no additional data specific to remote binders)
+
+ std::string toString() const;
};
// checks if there is any reference left to a node and erases it. If erase
diff --git a/libs/binder/Utils.h b/libs/binder/Utils.h
index 7c6d6f1..e04199c 100644
--- a/libs/binder/Utils.h
+++ b/libs/binder/Utils.h
@@ -48,9 +48,11 @@
// Truncates `this` to a length of `offset` and returns a span with the
// remainder.
//
- // Aborts if offset > size.
- Span<T> splitOff(size_t offset) {
- LOG_ALWAYS_FATAL_IF(offset > size);
+ // `std::nullopt` iff offset > size.
+ std::optional<Span<T>> splitOff(size_t offset) {
+ if (offset > size) {
+ return std::nullopt;
+ }
Span<T> rest = {data + offset, size - offset};
size = offset;
return rest;
diff --git a/libs/binder/include/binder/IPCThreadState.h b/libs/binder/include/binder/IPCThreadState.h
index 8ce3bc9..c01e92f 100644
--- a/libs/binder/include/binder/IPCThreadState.h
+++ b/libs/binder/include/binder/IPCThreadState.h
@@ -217,9 +217,8 @@
void clearCaller();
static void threadDestructor(void *st);
- static void freeBuffer(Parcel* parcel,
- const uint8_t* data, size_t dataSize,
- const binder_size_t* objects, size_t objectsSize);
+ static void freeBuffer(const uint8_t* data, size_t dataSize, const binder_size_t* objects,
+ size_t objectsSize);
static void logExtendedError();
const sp<ProcessState> mProcess;
diff --git a/libs/binder/include/binder/Parcel.h b/libs/binder/include/binder/Parcel.h
index 91febbd..5469239 100644
--- a/libs/binder/include/binder/Parcel.h
+++ b/libs/binder/include/binder/Parcel.h
@@ -598,9 +598,9 @@
void print(TextOutput& to, uint32_t flags = 0) const;
private:
- typedef void (*release_func)(Parcel* parcel,
- const uint8_t* data, size_t dataSize,
- const binder_size_t* objects, size_t objectsSize);
+ // `objects` and `objectsSize` always 0 for RPC Parcels.
+ typedef void (*release_func)(const uint8_t* data, size_t dataSize, const binder_size_t* objects,
+ size_t objectsSize);
uintptr_t ipcData() const;
size_t ipcDataSize() const;
diff --git a/libs/binder/include/binder/ProcessState.h b/libs/binder/include/binder/ProcessState.h
index 882dfbf..9679a5f 100644
--- a/libs/binder/include/binder/ProcessState.h
+++ b/libs/binder/include/binder/ProcessState.h
@@ -130,7 +130,7 @@
void* mVMStart;
// Protects thread count and wait variables below.
- pthread_mutex_t mThreadCountLock;
+ mutable pthread_mutex_t mThreadCountLock;
// Broadcast whenever mWaitingForThreads > 0
pthread_cond_t mThreadCountDecrement;
// Number of binder threads current executing a command.
diff --git a/libs/binder/include/binder/RpcServer.h b/libs/binder/include/binder/RpcServer.h
index 9318c27..52bda0e 100644
--- a/libs/binder/include/binder/RpcServer.h
+++ b/libs/binder/include/binder/RpcServer.h
@@ -29,6 +29,7 @@
namespace android {
class FdTrigger;
+class RpcServerTrusty;
class RpcSocketAddress;
/**
@@ -189,6 +190,7 @@
~RpcServer();
private:
+ friend RpcServerTrusty;
friend sp<RpcServer>;
explicit RpcServer(std::unique_ptr<RpcTransportCtx> ctx);
@@ -196,8 +198,10 @@
void onSessionIncomingThreadEnded() override;
static constexpr size_t kRpcAddressSize = 128;
- static void establishConnection(sp<RpcServer>&& server, base::unique_fd clientFd,
- std::array<uint8_t, kRpcAddressSize> addr, size_t addrLen);
+ static void establishConnection(
+ sp<RpcServer>&& server, base::unique_fd clientFd,
+ std::array<uint8_t, kRpcAddressSize> addr, size_t addrLen,
+ std::function<void(sp<RpcSession>&&, RpcSession::PreJoinSetupResult&&)>&& joinFn);
[[nodiscard]] status_t setupSocketServer(const RpcSocketAddress& address);
const std::unique_ptr<RpcTransportCtx> mCtx;
diff --git a/libs/binder/include/binder/RpcSession.h b/libs/binder/include/binder/RpcSession.h
index a2b28db..9d94e00 100644
--- a/libs/binder/include/binder/RpcSession.h
+++ b/libs/binder/include/binder/RpcSession.h
@@ -31,6 +31,7 @@
class Parcel;
class RpcServer;
+class RpcServerTrusty;
class RpcSocketAddress;
class RpcState;
class RpcTransport;
@@ -202,6 +203,7 @@
private:
friend sp<RpcSession>;
friend RpcServer;
+ friend RpcServerTrusty;
friend RpcState;
explicit RpcSession(std::unique_ptr<RpcTransportCtx> ctx);
diff --git a/libs/binder/tests/binderLibTest.cpp b/libs/binder/tests/binderLibTest.cpp
index 4ed3309..e72f39c 100644
--- a/libs/binder/tests/binderLibTest.cpp
+++ b/libs/binder/tests/binderLibTest.cpp
@@ -1310,7 +1310,7 @@
* not exceed 16 (15 Max + pool thread).
*/
std::vector<std::thread> ts;
- for (size_t i = 0; i < kKernelThreads - 1; i++) {
+ for (size_t i = 0; i < kKernelThreads; i++) {
ts.push_back(std::thread([&] {
Parcel local_reply;
EXPECT_THAT(server->transact(BINDER_LIB_TEST_LOCK_UNLOCK, data, &local_reply),
@@ -1318,7 +1318,7 @@
}));
}
- data.writeInt32(1);
+ data.writeInt32(100);
// Give a chance for all threads to be used
EXPECT_THAT(server->transact(BINDER_LIB_TEST_UNLOCK_AFTER_MS, data, &reply), NO_ERROR);
@@ -1329,8 +1329,7 @@
EXPECT_THAT(server->transact(BINDER_LIB_TEST_GET_MAX_THREAD_COUNT, data, &reply),
StatusEq(NO_ERROR));
replyi = reply.readInt32();
- // No more than 16 threads should exist.
- EXPECT_TRUE(replyi == kKernelThreads || replyi == kKernelThreads + 1);
+ EXPECT_EQ(replyi, kKernelThreads + 1);
}
size_t epochMillis() {
diff --git a/libs/binder/tests/binderRpcTest.cpp b/libs/binder/tests/binderRpcTest.cpp
index 4e41d8e..8afb403 100644
--- a/libs/binder/tests/binderRpcTest.cpp
+++ b/libs/binder/tests/binderRpcTest.cpp
@@ -930,7 +930,7 @@
size_t epochMsAfter = epochMillis();
- EXPECT_GT(epochMsAfter, epochMsBefore + kSleepMs * kNumSleeps);
+ EXPECT_GE(epochMsAfter, epochMsBefore + kSleepMs * kNumSleeps);
saturateThreadPool(1 + kNumExtraServerThreads, proc.rootIface);
}
@@ -1029,13 +1029,6 @@
// since this session has an incoming connection w/ a threadpool, we
// need to manually shut it down
EXPECT_TRUE(proc.proc.sessions.at(0).session->shutdownAndWait(true));
-
- proc.proc.host.setCustomExitStatusCheck([](int wstatus) {
- // Flaky. Sometimes gets SIGABRT.
- EXPECT_TRUE((WIFEXITED(wstatus) && WEXITSTATUS(wstatus) == 0) ||
- (WIFSIGNALED(wstatus) && WTERMSIG(wstatus) == SIGABRT))
- << "server process failed: " << WaitStatusToString(wstatus);
- });
proc.expectAlreadyShutdown = true;
}
}
diff --git a/libs/binder/tests/parcel_fuzzer/Android.bp b/libs/binder/tests/parcel_fuzzer/Android.bp
index 2ca6ebd..0210237 100644
--- a/libs/binder/tests/parcel_fuzzer/Android.bp
+++ b/libs/binder/tests/parcel_fuzzer/Android.bp
@@ -7,6 +7,22 @@
default_applicable_licenses: ["frameworks_native_license"],
}
+aidl_interface {
+ name: "binderReadParcelIface",
+ host_supported: true,
+ unstable: true,
+ srcs: [
+ "EmptyParcelable.aidl",
+ "SingleDataParcelable.aidl",
+ "GenericDataParcelable.aidl",
+ ],
+ backend: {
+ java: {
+ enabled: false,
+ },
+ },
+}
+
cc_fuzz {
name: "binder_parcel_fuzzer",
host_supported: true,
@@ -29,6 +45,8 @@
"libcutils",
"libhidlbase",
"liblog",
+ "binderReadParcelIface-cpp",
+ "binderReadParcelIface-ndk",
],
target: {
diff --git a/libs/binder/tests/parcel_fuzzer/EmptyParcelable.aidl b/libs/binder/tests/parcel_fuzzer/EmptyParcelable.aidl
new file mode 100644
index 0000000..96d6223
--- /dev/null
+++ b/libs/binder/tests/parcel_fuzzer/EmptyParcelable.aidl
@@ -0,0 +1,18 @@
+/*
+ * Copyright (C) 2022 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.
+ */
+
+parcelable EmptyParcelable{
+}
\ No newline at end of file
diff --git a/libs/binder/tests/parcel_fuzzer/GenericDataParcelable.aidl b/libs/binder/tests/parcel_fuzzer/GenericDataParcelable.aidl
new file mode 100644
index 0000000..fc2542b
--- /dev/null
+++ b/libs/binder/tests/parcel_fuzzer/GenericDataParcelable.aidl
@@ -0,0 +1,24 @@
+/*
+ * Copyright (C) 2022 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.
+ */
+
+parcelable GenericDataParcelable {
+ int data;
+ float majorVersion;
+ float minorVersion;
+ IBinder binder;
+ ParcelFileDescriptor fileDescriptor;
+ int[] array;
+}
\ No newline at end of file
diff --git a/libs/binder/tests/parcel_fuzzer/SingleDataParcelable.aidl b/libs/binder/tests/parcel_fuzzer/SingleDataParcelable.aidl
new file mode 100644
index 0000000..d62891b
--- /dev/null
+++ b/libs/binder/tests/parcel_fuzzer/SingleDataParcelable.aidl
@@ -0,0 +1,19 @@
+/*
+ * Copyright (C) 2022 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.
+ */
+
+parcelable SingleDataParcelable{
+ int data;
+}
\ No newline at end of file
diff --git a/libs/binder/tests/parcel_fuzzer/binder.cpp b/libs/binder/tests/parcel_fuzzer/binder.cpp
index 7059d30..9dac2c9 100644
--- a/libs/binder/tests/parcel_fuzzer/binder.cpp
+++ b/libs/binder/tests/parcel_fuzzer/binder.cpp
@@ -16,6 +16,9 @@
#define FUZZ_LOG_TAG "binder"
#include "binder.h"
+#include "EmptyParcelable.h"
+#include "GenericDataParcelable.h"
+#include "SingleDataParcelable.h"
#include "util.h"
#include <android-base/hex.h>
@@ -354,6 +357,24 @@
status_t status = p.compareDataInRange(thisOffset, p, otherOffset, length, &result);
FUZZ_LOG() << " status: " << status << " result: " << result;
},
+ [] (const ::android::Parcel& p, FuzzedDataProvider& /*provider*/) {
+ FUZZ_LOG() << "about to call readFromParcel() with status for EmptyParcelable";
+ EmptyParcelable emptyParcelable{};
+ status_t status = emptyParcelable.readFromParcel(&p);
+ FUZZ_LOG() << " status: " << status;
+ },
+ [] (const ::android::Parcel& p , FuzzedDataProvider& /*provider*/) {
+ FUZZ_LOG() << "about to call readFromParcel() with status for SingleDataParcelable";
+ SingleDataParcelable singleDataParcelable;
+ status_t status = singleDataParcelable.readFromParcel(&p);
+ FUZZ_LOG() <<" status: " << status;
+ },
+ [] (const ::android::Parcel& p, FuzzedDataProvider& /*provider*/) {
+ FUZZ_LOG() << "about to call readFromParcel() with status for GenericDataParcelable";
+ GenericDataParcelable genericDataParcelable;
+ status_t status = genericDataParcelable.readFromParcel(&p);
+ FUZZ_LOG() <<" status: " << status;
+ },
};
// clang-format on
#pragma clang diagnostic pop
diff --git a/libs/binder/tests/parcel_fuzzer/binder_ndk.cpp b/libs/binder/tests/parcel_fuzzer/binder_ndk.cpp
index 26d6770..af773a0 100644
--- a/libs/binder/tests/parcel_fuzzer/binder_ndk.cpp
+++ b/libs/binder/tests/parcel_fuzzer/binder_ndk.cpp
@@ -16,6 +16,9 @@
#define FUZZ_LOG_TAG "binder_ndk"
#include "binder_ndk.h"
+#include "aidl/EmptyParcelable.h"
+#include "aidl/GenericDataParcelable.h"
+#include "aidl/SingleDataParcelable.h"
#include <android/binder_parcel_utils.h>
#include <android/binder_parcelable_utils.h>
@@ -177,5 +180,24 @@
PARCEL_READ(std::array<ndk::ScopedFileDescriptor COMMA 3>, ndk::AParcel_readData),
PARCEL_READ(std::array<std::shared_ptr<ISomeInterface> COMMA 3>, ndk::AParcel_readData),
#undef COMMA
+
+ [](const NdkParcelAdapter& p, FuzzedDataProvider& /*provider*/) {
+ FUZZ_LOG() << "about to read parcel using readFromParcel for EmptyParcelable";
+ aidl::EmptyParcelable emptyParcelable;
+ binder_status_t status = emptyParcelable.readFromParcel(p.aParcel());
+ FUZZ_LOG() << "status: " << status;
+ },
+ [](const NdkParcelAdapter& p, FuzzedDataProvider& /*provider*/) {
+ FUZZ_LOG() << "about to read parcel using readFromParcel for SingleDataParcelable";
+ aidl::SingleDataParcelable singleDataParcelable;
+ binder_status_t status = singleDataParcelable.readFromParcel(p.aParcel());
+ FUZZ_LOG() << "status: " << status;
+ },
+ [](const NdkParcelAdapter& p, FuzzedDataProvider& /*provider*/) {
+ FUZZ_LOG() << "about to read parcel using readFromParcel for GenericDataParcelable";
+ aidl::GenericDataParcelable genericDataParcelable;
+ binder_status_t status = genericDataParcelable.readFromParcel(p.aParcel());
+ FUZZ_LOG() << "status: " << status;
+ },
};
// clang-format on
diff --git a/libs/binder/tests/parcel_fuzzer/include_random_parcel/fuzzbinder/random_fd.h b/libs/binder/tests/parcel_fuzzer/include_random_parcel/fuzzbinder/random_fd.h
index 843b6e3..6ea9708 100644
--- a/libs/binder/tests/parcel_fuzzer/include_random_parcel/fuzzbinder/random_fd.h
+++ b/libs/binder/tests/parcel_fuzzer/include_random_parcel/fuzzbinder/random_fd.h
@@ -19,10 +19,14 @@
#include <android-base/unique_fd.h>
#include <fuzzer/FuzzedDataProvider.h>
+#include <vector>
+
namespace android {
// always valid or aborts
// get a random FD for use in fuzzing, of a few different specific types
-base::unique_fd getRandomFd(FuzzedDataProvider* provider);
+//
+// may return multiple FDs (e.g. pipe), but will always return at least one
+std::vector<base::unique_fd> getRandomFds(FuzzedDataProvider* provider);
} // namespace android
diff --git a/libs/binder/tests/parcel_fuzzer/include_random_parcel/fuzzbinder/random_parcel.h b/libs/binder/tests/parcel_fuzzer/include_random_parcel/fuzzbinder/random_parcel.h
index 459fb12..27587a9 100644
--- a/libs/binder/tests/parcel_fuzzer/include_random_parcel/fuzzbinder/random_parcel.h
+++ b/libs/binder/tests/parcel_fuzzer/include_random_parcel/fuzzbinder/random_parcel.h
@@ -33,10 +33,13 @@
/**
* Fill parcel data, including some random binder objects and FDs
*
+ * May insert additional FDs/binders if they own data related to the Parcel (e.g. the other
+ * end of a pipe).
+ *
* p - the Parcel to fill
* provider - takes ownership and completely consumes provider
* writeHeader - optional function to write a specific header once the format of the parcel is
* picked (for instance, to write an interface header)
*/
-void fillRandomParcel(Parcel* p, FuzzedDataProvider&& provider, const RandomParcelOptions& = {});
+void fillRandomParcel(Parcel* p, FuzzedDataProvider&& provider, RandomParcelOptions* options);
} // namespace android
diff --git a/libs/binder/tests/parcel_fuzzer/libbinder_driver.cpp b/libs/binder/tests/parcel_fuzzer/libbinder_driver.cpp
index d5aa353..32494e3 100644
--- a/libs/binder/tests/parcel_fuzzer/libbinder_driver.cpp
+++ b/libs/binder/tests/parcel_fuzzer/libbinder_driver.cpp
@@ -44,7 +44,7 @@
std::vector<uint8_t> subData = provider.ConsumeBytes<uint8_t>(
provider.ConsumeIntegralInRange<size_t>(0, provider.remaining_bytes()));
- fillRandomParcel(&data, FuzzedDataProvider(subData.data(), subData.size()), options);
+ fillRandomParcel(&data, FuzzedDataProvider(subData.data(), subData.size()), &options);
Parcel reply;
(void)target->transact(code, data, &reply, flags);
diff --git a/libs/binder/tests/parcel_fuzzer/main.cpp b/libs/binder/tests/parcel_fuzzer/main.cpp
index 180a177..bef4ab6 100644
--- a/libs/binder/tests/parcel_fuzzer/main.cpp
+++ b/libs/binder/tests/parcel_fuzzer/main.cpp
@@ -35,17 +35,22 @@
#include <sys/time.h>
using android::fillRandomParcel;
+using android::RandomParcelOptions;
using android::sp;
using android::base::HexString;
-void fillRandomParcel(::android::hardware::Parcel* p, FuzzedDataProvider&& provider) {
+void fillRandomParcel(::android::hardware::Parcel* p, FuzzedDataProvider&& provider,
+ RandomParcelOptions* options) {
// TODO: functionality to create random parcels for libhwbinder parcels
+ (void)options;
+
std::vector<uint8_t> input = provider.ConsumeRemainingBytes<uint8_t>();
p->setData(input.data(), input.size());
}
-static void fillRandomParcel(NdkParcelAdapter* p, FuzzedDataProvider&& provider) {
+static void fillRandomParcel(NdkParcelAdapter* p, FuzzedDataProvider&& provider,
+ RandomParcelOptions* options) {
// fill underlying parcel using functions to fill random libbinder parcel
- fillRandomParcel(p->parcel(), std::move(provider));
+ fillRandomParcel(p->parcel(), std::move(provider), options);
}
template <typename P, typename B>
@@ -55,9 +60,11 @@
FUZZ_LOG() << "backend: " << backend;
+ RandomParcelOptions options;
+
P reply;
P data;
- fillRandomParcel(&data, std::move(provider));
+ fillRandomParcel(&data, std::move(provider), &options);
(void)binder->transact(code, data, &reply, flag);
}
@@ -73,8 +80,10 @@
std::vector<uint8_t> instructions = provider.ConsumeBytes<uint8_t>(
provider.ConsumeIntegralInRange<size_t>(0, maxInstructions));
+ RandomParcelOptions options;
+
P p;
- fillRandomParcel(&p, std::move(provider));
+ fillRandomParcel(&p, std::move(provider), &options);
// since we are only using a byte to index
CHECK(reads.size() <= 255) << reads.size();
@@ -103,9 +112,12 @@
std::vector<uint8_t> bytes = provider.ConsumeBytes<uint8_t>(
provider.ConsumeIntegralInRange<size_t>(0, provider.remaining_bytes()));
+ // same options so that FDs and binders could be shared in both Parcels
+ RandomParcelOptions options;
+
P p0, p1;
- fillRandomParcel(&p0, FuzzedDataProvider(bytes.data(), bytes.size()));
- fillRandomParcel(&p1, std::move(provider));
+ fillRandomParcel(&p0, FuzzedDataProvider(bytes.data(), bytes.size()), &options);
+ fillRandomParcel(&p1, std::move(provider), &options);
FUZZ_LOG() << "backend: " << backend;
FUZZ_LOG() << "start: " << start << " len: " << len;
diff --git a/libs/binder/tests/parcel_fuzzer/random_fd.cpp b/libs/binder/tests/parcel_fuzzer/random_fd.cpp
index ab0b7e3..3fcf104 100644
--- a/libs/binder/tests/parcel_fuzzer/random_fd.cpp
+++ b/libs/binder/tests/parcel_fuzzer/random_fd.cpp
@@ -23,13 +23,44 @@
namespace android {
-base::unique_fd getRandomFd(FuzzedDataProvider* provider) {
- int fd = provider->PickValueInArray<std::function<int()>>({
- []() { return ashmem_create_region("binder test region", 1024); },
- []() { return open("/dev/null", O_RDWR); },
+using base::unique_fd;
+
+std::vector<unique_fd> getRandomFds(FuzzedDataProvider* provider) {
+ std::vector<unique_fd> fds = provider->PickValueInArray<
+ std::function<std::vector<unique_fd>()>>({
+ [&]() {
+ std::vector<unique_fd> ret;
+ ret.push_back(unique_fd(
+ ashmem_create_region("binder test region",
+ provider->ConsumeIntegralInRange<size_t>(0, 4096))));
+ return ret;
+ },
+ [&]() {
+ std::vector<unique_fd> ret;
+ ret.push_back(unique_fd(open("/dev/null", O_RDWR)));
+ return ret;
+ },
+ [&]() {
+ int pipefds[2];
+
+ int flags = O_CLOEXEC;
+ if (provider->ConsumeBool()) flags |= O_DIRECT;
+ if (provider->ConsumeBool()) flags |= O_NONBLOCK;
+
+ CHECK_EQ(0, pipe2(pipefds, flags));
+
+ if (provider->ConsumeBool()) std::swap(pipefds[0], pipefds[1]);
+
+ std::vector<unique_fd> ret;
+ ret.push_back(unique_fd(pipefds[0]));
+ ret.push_back(unique_fd(pipefds[1]));
+ return ret;
+ },
})();
- CHECK(fd >= 0);
- return base::unique_fd(fd);
+
+ for (const auto& fd : fds) CHECK(fd.ok()) << fd.get();
+
+ return fds;
}
} // namespace android
diff --git a/libs/binder/tests/parcel_fuzzer/random_parcel.cpp b/libs/binder/tests/parcel_fuzzer/random_parcel.cpp
index 0204f5e..51cb768 100644
--- a/libs/binder/tests/parcel_fuzzer/random_parcel.cpp
+++ b/libs/binder/tests/parcel_fuzzer/random_parcel.cpp
@@ -39,23 +39,24 @@
CHECK(OK == p->write(data.data(), data.size()));
}
-void fillRandomParcel(Parcel* p, FuzzedDataProvider&& provider,
- const RandomParcelOptions& options) {
+void fillRandomParcel(Parcel* p, FuzzedDataProvider&& provider, RandomParcelOptions* options) {
+ CHECK_NE(options, nullptr);
+
if (provider.ConsumeBool()) {
auto session = RpcSession::make(RpcTransportCtxFactoryRaw::make());
CHECK_EQ(OK, session->addNullDebuggingClient());
p->markForRpc(session);
- if (options.writeHeader) {
- options.writeHeader(p, provider);
+ if (options->writeHeader) {
+ options->writeHeader(p, provider);
}
fillRandomParcelData(p, std::move(provider));
return;
}
- if (options.writeHeader) {
- options.writeHeader(p, provider);
+ if (options->writeHeader) {
+ options->writeHeader(p, provider);
}
while (provider.remaining_bytes() > 0) {
@@ -69,15 +70,21 @@
},
// write FD
[&]() {
- if (options.extraFds.size() > 0 && provider.ConsumeBool()) {
- const base::unique_fd& fd = options.extraFds.at(
+ if (options->extraFds.size() > 0 && provider.ConsumeBool()) {
+ const base::unique_fd& fd = options->extraFds.at(
provider.ConsumeIntegralInRange<size_t>(0,
- options.extraFds.size() -
+ options->extraFds.size() -
1));
CHECK(OK == p->writeFileDescriptor(fd.get(), false /*takeOwnership*/));
} else {
- base::unique_fd fd = getRandomFd(&provider);
- CHECK(OK == p->writeFileDescriptor(fd.release(), true /*takeOwnership*/));
+ std::vector<base::unique_fd> fds = getRandomFds(&provider);
+ CHECK(OK ==
+ p->writeFileDescriptor(fds.begin()->release(),
+ true /*takeOwnership*/));
+
+ options->extraFds.insert(options->extraFds.end(),
+ std::make_move_iterator(fds.begin() + 1),
+ std::make_move_iterator(fds.end()));
}
},
// write binder
@@ -98,10 +105,10 @@
return IInterface::asBinder(defaultServiceManager());
},
[&]() -> sp<IBinder> {
- if (options.extraBinders.size() > 0 && provider.ConsumeBool()) {
- return options.extraBinders.at(
+ if (options->extraBinders.size() > 0 && provider.ConsumeBool()) {
+ return options->extraBinders.at(
provider.ConsumeIntegralInRange<
- size_t>(0, options.extraBinders.size() - 1));
+ size_t>(0, options->extraBinders.size() - 1));
} else {
return nullptr;
}
diff --git a/libs/binder/trusty/OS.cpp b/libs/binder/trusty/OS.cpp
new file mode 100644
index 0000000..187add4
--- /dev/null
+++ b/libs/binder/trusty/OS.cpp
@@ -0,0 +1,35 @@
+/*
+ * Copyright (C) 2022 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 <openssl/rand.h>
+
+#include "../OS.h"
+
+using android::base::Result;
+
+namespace android {
+
+Result<void> setNonBlocking(android::base::borrowed_fd fd) {
+ // Trusty IPC syscalls are all non-blocking by default.
+ return {};
+}
+
+status_t getRandomBytes(uint8_t* data, size_t size) {
+ int res = RAND_bytes(data, size);
+ return res == 1 ? OK : UNKNOWN_ERROR;
+}
+
+} // namespace android
diff --git a/libs/binder/trusty/README.md b/libs/binder/trusty/README.md
new file mode 100644
index 0000000..1a273aa
--- /dev/null
+++ b/libs/binder/trusty/README.md
@@ -0,0 +1,39 @@
+# Binder for Trusty
+
+This is the Trusty port of the libbinder library.
+To build it, take the following steps:
+
+* Check out copies of the Trusty and AOSP repositories.
+* Apply the patches from the `trusty_binder` topic on both repositories.
+* Build Trusty normally using `build.py`.
+* Run the sample AIDL test for Trusty:
+ ```shell
+ $ ./build-root/.../run --headless --boot-test com.android.trusty.aidl.test
+ ```
+
+To run the Android-Trusty IPC test, do the following:
+
+* Build AOSP for the `qemu_trusty_arm64-userdebug` target:
+ ```shell
+ $ lunch qemu_trusty_arm64-userdebug
+ $ m
+ ```
+* In the Trusty directory, run the emulator with the newly built Android:
+ ```shell
+ $ ./build-root/.../run --android /path/to/aosp
+ ```
+* Using either `adb` or the shell inside the emulator itself, run the Trusty
+ Binder test as root:
+ ```shell
+ # /data/nativetest64/vendor/trusty_binder_test/trusty_binder_test
+ ```
+
+## Running the AIDL compiler
+For now, you will need to run the AIDL compiler manually to generate the C++
+source code for Trusty clients and services. The general syntax is:
+```shell
+$ aidl --lang=cpp -o <output directory> -h <output header directory> <AIDL files...>
+```
+
+The compiler will emit some `.cpp` files in the output directory and their
+corresponding `.h` files in the header directory.
diff --git a/libs/binder/trusty/RpcServerTrusty.cpp b/libs/binder/trusty/RpcServerTrusty.cpp
new file mode 100644
index 0000000..e8b91e7
--- /dev/null
+++ b/libs/binder/trusty/RpcServerTrusty.cpp
@@ -0,0 +1,142 @@
+/*
+ * Copyright (C) 2022 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 "RpcServerTrusty"
+
+#include <binder/Parcel.h>
+#include <binder/RpcServer.h>
+#include <binder/RpcServerTrusty.h>
+#include <binder/RpcThreads.h>
+#include <binder/RpcTransportTipcTrusty.h>
+#include <log/log.h>
+
+#include "../FdTrigger.h"
+#include "../RpcState.h"
+#include "TrustyStatus.h"
+
+using android::base::unexpected;
+
+namespace android {
+
+android::base::expected<sp<RpcServerTrusty>, int> RpcServerTrusty::make(
+ tipc_hset* handleSet, std::string&& portName, std::shared_ptr<const PortAcl>&& portAcl,
+ size_t msgMaxSize, std::unique_ptr<RpcTransportCtxFactory> rpcTransportCtxFactory) {
+ // Default is without TLS.
+ if (rpcTransportCtxFactory == nullptr)
+ rpcTransportCtxFactory = RpcTransportCtxFactoryTipcTrusty::make();
+ auto ctx = rpcTransportCtxFactory->newServerCtx();
+ if (ctx == nullptr) {
+ return unexpected(ERR_NO_MEMORY);
+ }
+
+ auto srv = sp<RpcServerTrusty>::make(std::move(ctx), std::move(portName), std::move(portAcl),
+ msgMaxSize);
+ if (srv == nullptr) {
+ return unexpected(ERR_NO_MEMORY);
+ }
+
+ int rc = tipc_add_service(handleSet, &srv->mTipcPort, 1, 0, &kTipcOps);
+ if (rc != NO_ERROR) {
+ return unexpected(rc);
+ }
+ return srv;
+}
+
+RpcServerTrusty::RpcServerTrusty(std::unique_ptr<RpcTransportCtx> ctx, std::string&& portName,
+ std::shared_ptr<const PortAcl>&& portAcl, size_t msgMaxSize)
+ : mRpcServer(sp<RpcServer>::make(std::move(ctx))),
+ mPortName(std::move(portName)),
+ mPortAcl(std::move(portAcl)) {
+ mTipcPort.name = mPortName.c_str();
+ mTipcPort.msg_max_size = msgMaxSize;
+ mTipcPort.msg_queue_len = 6; // Three each way
+ mTipcPort.priv = this;
+
+ if (mPortAcl) {
+ // Initialize the array of pointers to uuids.
+ // The pointers in mUuidPtrs should stay valid across moves of
+ // RpcServerTrusty (the addresses of a std::vector's elements
+ // shouldn't change when the vector is moved), but would be invalidated
+ // by a copy which is why we disable the copy constructor and assignment
+ // operator for RpcServerTrusty.
+ auto numUuids = mPortAcl->uuids.size();
+ mUuidPtrs.resize(numUuids);
+ for (size_t i = 0; i < numUuids; i++) {
+ mUuidPtrs[i] = &mPortAcl->uuids[i];
+ }
+
+ // Copy the contents of portAcl into the tipc_port_acl structure that we
+ // pass to tipc_add_service
+ mTipcPortAcl.flags = mPortAcl->flags;
+ mTipcPortAcl.uuid_num = numUuids;
+ mTipcPortAcl.uuids = mUuidPtrs.data();
+ mTipcPortAcl.extra_data = mPortAcl->extraData;
+
+ mTipcPort.acl = &mTipcPortAcl;
+ } else {
+ mTipcPort.acl = nullptr;
+ }
+}
+
+int RpcServerTrusty::handleConnect(const tipc_port* port, handle_t chan, const uuid* peer,
+ void** ctx_p) {
+ auto* server = reinterpret_cast<RpcServerTrusty*>(const_cast<void*>(port->priv));
+ server->mRpcServer->mShutdownTrigger = FdTrigger::make();
+ server->mRpcServer->mConnectingThreads[rpc_this_thread::get_id()] = RpcMaybeThread();
+
+ int rc = NO_ERROR;
+ auto joinFn = [&](sp<RpcSession>&& session, RpcSession::PreJoinSetupResult&& result) {
+ if (result.status != OK) {
+ rc = statusToTrusty(result.status);
+ return;
+ }
+
+ /* Save the session for easy access */
+ *ctx_p = session.get();
+ };
+
+ base::unique_fd clientFd(chan);
+ std::array<uint8_t, RpcServer::kRpcAddressSize> addr;
+ constexpr size_t addrLen = sizeof(*peer);
+ memcpy(addr.data(), peer, addrLen);
+ RpcServer::establishConnection(sp(server->mRpcServer), std::move(clientFd), addr, addrLen,
+ joinFn);
+
+ return rc;
+}
+
+int RpcServerTrusty::handleMessage(const tipc_port* port, handle_t chan, void* ctx) {
+ auto* session = reinterpret_cast<RpcSession*>(ctx);
+ status_t status = session->state()->drainCommands(session->mConnections.mIncoming[0], session,
+ RpcState::CommandType::ANY);
+ if (status != OK) {
+ LOG_RPC_DETAIL("Binder connection thread closing w/ status %s",
+ statusToString(status).c_str());
+ }
+
+ return NO_ERROR;
+}
+
+void RpcServerTrusty::handleDisconnect(const tipc_port* port, handle_t chan, void* ctx) {}
+
+void RpcServerTrusty::handleChannelCleanup(void* ctx) {
+ auto* session = reinterpret_cast<RpcSession*>(ctx);
+ auto& connection = session->mConnections.mIncoming.at(0);
+ LOG_ALWAYS_FATAL_IF(!session->removeIncomingConnection(connection),
+ "bad state: connection object guaranteed to be in list");
+}
+
+} // namespace android
diff --git a/libs/binder/trusty/RpcTransportTipcTrusty.cpp b/libs/binder/trusty/RpcTransportTipcTrusty.cpp
new file mode 100644
index 0000000..e0d80fb
--- /dev/null
+++ b/libs/binder/trusty/RpcTransportTipcTrusty.cpp
@@ -0,0 +1,248 @@
+/*
+ * Copyright (C) 2022 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 "RpcTransportTipcTrusty"
+
+#include <trusty_ipc.h>
+
+#include <binder/RpcSession.h>
+#include <binder/RpcTransportTipcTrusty.h>
+#include <log/log.h>
+
+#include "../FdTrigger.h"
+#include "../RpcState.h"
+#include "TrustyStatus.h"
+
+namespace android {
+
+namespace {
+
+// RpcTransport for Trusty.
+class RpcTransportTipcTrusty : public RpcTransport {
+public:
+ explicit RpcTransportTipcTrusty(android::base::unique_fd socket) : mSocket(std::move(socket)) {}
+ ~RpcTransportTipcTrusty() { releaseMessage(); }
+
+ status_t pollRead() override {
+ auto status = ensureMessage(false);
+ if (status != OK) {
+ return status;
+ }
+ return mHaveMessage ? OK : WOULD_BLOCK;
+ }
+
+ status_t interruptableWriteFully(
+ FdTrigger* fdTrigger, iovec* iovs, int niovs,
+ const std::optional<android::base::function_ref<status_t()>>& altPoll,
+ const std::vector<std::variant<base::unique_fd, base::borrowed_fd>>* ancillaryFds)
+ override {
+ if (niovs < 0) {
+ return BAD_VALUE;
+ }
+
+ size_t size = 0;
+ for (int i = 0; i < niovs; i++) {
+ size += iovs[i].iov_len;
+ }
+
+ ipc_msg_t msg{
+ .num_iov = static_cast<uint32_t>(niovs),
+ .iov = iovs,
+ .num_handles = 0, // TODO: add ancillaryFds
+ .handles = nullptr,
+ };
+ int rc = send_msg(mSocket.get(), &msg);
+ if (rc == ERR_NOT_ENOUGH_BUFFER) {
+ // Peer is blocked, wait until it unblocks.
+ // TODO: when tipc supports a send-unblocked handler,
+ // save the message here in a queue and retry it asynchronously
+ // when the handler gets called by the library
+ uevent uevt;
+ do {
+ rc = ::wait(mSocket.get(), &uevt, INFINITE_TIME);
+ if (rc < 0) {
+ return statusFromTrusty(rc);
+ }
+ if (uevt.event & IPC_HANDLE_POLL_HUP) {
+ return DEAD_OBJECT;
+ }
+ } while (!(uevt.event & IPC_HANDLE_POLL_SEND_UNBLOCKED));
+
+ // Retry the send, it should go through this time because
+ // sending is now unblocked
+ rc = send_msg(mSocket.get(), &msg);
+ }
+ if (rc < 0) {
+ return statusFromTrusty(rc);
+ }
+ LOG_ALWAYS_FATAL_IF(static_cast<size_t>(rc) != size,
+ "Sent the wrong number of bytes %d!=%zu", rc, size);
+
+ return OK;
+ }
+
+ status_t interruptableReadFully(
+ FdTrigger* fdTrigger, iovec* iovs, int niovs,
+ const std::optional<android::base::function_ref<status_t()>>& altPoll,
+ std::vector<std::variant<base::unique_fd, base::borrowed_fd>>* ancillaryFds) override {
+ if (niovs < 0) {
+ return BAD_VALUE;
+ }
+
+ // If iovs has one or more empty vectors at the end and
+ // we somehow advance past all the preceding vectors and
+ // pass some or all of the empty ones to sendmsg/recvmsg,
+ // the call will return processSize == 0. In that case
+ // we should be returning OK but instead return DEAD_OBJECT.
+ // To avoid this problem, we make sure here that the last
+ // vector at iovs[niovs - 1] has a non-zero length.
+ while (niovs > 0 && iovs[niovs - 1].iov_len == 0) {
+ niovs--;
+ }
+ if (niovs == 0) {
+ // The vectors are all empty, so we have nothing to read.
+ return OK;
+ }
+
+ while (true) {
+ auto status = ensureMessage(true);
+ if (status != OK) {
+ return status;
+ }
+
+ ipc_msg_t msg{
+ .num_iov = static_cast<uint32_t>(niovs),
+ .iov = iovs,
+ .num_handles = 0, // TODO: support ancillaryFds
+ .handles = nullptr,
+ };
+ int rc = read_msg(mSocket.get(), mMessageInfo.id, mMessageOffset, &msg);
+ if (rc < 0) {
+ return statusFromTrusty(rc);
+ }
+
+ size_t processSize = static_cast<size_t>(rc);
+ mMessageOffset += processSize;
+ LOG_ALWAYS_FATAL_IF(mMessageOffset > mMessageInfo.len,
+ "Message offset exceeds length %zu/%zu", mMessageOffset,
+ mMessageInfo.len);
+
+ // Release the message if all of it has been read
+ if (mMessageOffset == mMessageInfo.len) {
+ releaseMessage();
+ }
+
+ while (processSize > 0 && niovs > 0) {
+ auto& iov = iovs[0];
+ if (processSize < iov.iov_len) {
+ // Advance the base of the current iovec
+ iov.iov_base = reinterpret_cast<char*>(iov.iov_base) + processSize;
+ iov.iov_len -= processSize;
+ break;
+ }
+
+ // The current iovec was fully written
+ processSize -= iov.iov_len;
+ iovs++;
+ niovs--;
+ }
+ if (niovs == 0) {
+ LOG_ALWAYS_FATAL_IF(processSize > 0,
+ "Reached the end of iovecs "
+ "with %zd bytes remaining",
+ processSize);
+ return OK;
+ }
+ }
+ }
+
+private:
+ status_t ensureMessage(bool wait) {
+ int rc;
+ if (mHaveMessage) {
+ LOG_ALWAYS_FATAL_IF(mMessageOffset >= mMessageInfo.len, "No data left in message");
+ return OK;
+ }
+
+ /* TODO: interruptible wait, maybe with a timeout??? */
+ uevent uevt;
+ rc = ::wait(mSocket.get(), &uevt, wait ? INFINITE_TIME : 0);
+ if (rc < 0) {
+ if (rc == ERR_TIMED_OUT && !wait) {
+ // If we timed out with wait==false, then there's no message
+ return OK;
+ }
+ return statusFromTrusty(rc);
+ }
+ if (!(uevt.event & IPC_HANDLE_POLL_MSG)) {
+ /* No message, terminate here and leave mHaveMessage false */
+ return OK;
+ }
+
+ rc = get_msg(mSocket.get(), &mMessageInfo);
+ if (rc < 0) {
+ return statusFromTrusty(rc);
+ }
+
+ mHaveMessage = true;
+ mMessageOffset = 0;
+ return OK;
+ }
+
+ void releaseMessage() {
+ if (mHaveMessage) {
+ put_msg(mSocket.get(), mMessageInfo.id);
+ mHaveMessage = false;
+ }
+ }
+
+ base::unique_fd mSocket;
+
+ bool mHaveMessage = false;
+ ipc_msg_info mMessageInfo;
+ size_t mMessageOffset;
+};
+
+// RpcTransportCtx for Trusty.
+class RpcTransportCtxTipcTrusty : public RpcTransportCtx {
+public:
+ std::unique_ptr<RpcTransport> newTransport(android::base::unique_fd fd,
+ FdTrigger*) const override {
+ return std::make_unique<RpcTransportTipcTrusty>(std::move(fd));
+ }
+ std::vector<uint8_t> getCertificate(RpcCertificateFormat) const override { return {}; }
+};
+
+} // namespace
+
+std::unique_ptr<RpcTransportCtx> RpcTransportCtxFactoryTipcTrusty::newServerCtx() const {
+ return std::make_unique<RpcTransportCtxTipcTrusty>();
+}
+
+std::unique_ptr<RpcTransportCtx> RpcTransportCtxFactoryTipcTrusty::newClientCtx() const {
+ return std::make_unique<RpcTransportCtxTipcTrusty>();
+}
+
+const char* RpcTransportCtxFactoryTipcTrusty::toCString() const {
+ return "trusty";
+}
+
+std::unique_ptr<RpcTransportCtxFactory> RpcTransportCtxFactoryTipcTrusty::make() {
+ return std::unique_ptr<RpcTransportCtxFactoryTipcTrusty>(
+ new RpcTransportCtxFactoryTipcTrusty());
+}
+
+} // namespace android
diff --git a/libs/binder/trusty/TrustyStatus.cpp b/libs/binder/trusty/TrustyStatus.cpp
new file mode 100644
index 0000000..b1caf61
--- /dev/null
+++ b/libs/binder/trusty/TrustyStatus.cpp
@@ -0,0 +1,107 @@
+/*
+ * Copyright (C) 2022 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 "TrustyStatus.h"
+#include "../RpcState.h"
+
+namespace android {
+
+status_t statusFromTrusty(int rc) {
+ LOG_RPC_DETAIL("Trusty error: %d", rc);
+ switch (rc) {
+ case NO_ERROR:
+ return OK;
+ case ERR_NOT_FOUND:
+ return NAME_NOT_FOUND;
+ case ERR_NOT_READY:
+ // We get this error if we try to perform an IPC operation when the
+ // channel is not ready
+ return INVALID_OPERATION;
+ case ERR_NO_MSG:
+ return WOULD_BLOCK;
+ case ERR_NO_MEMORY:
+ return NO_MEMORY;
+ case ERR_INVALID_ARGS:
+ return BAD_VALUE;
+ case ERR_NOT_ENOUGH_BUFFER:
+ return WOULD_BLOCK;
+ case ERR_TIMED_OUT:
+ return TIMED_OUT;
+ case ERR_ALREADY_EXISTS:
+ return ALREADY_EXISTS;
+ case ERR_CHANNEL_CLOSED:
+ return DEAD_OBJECT;
+ case ERR_NOT_ALLOWED:
+ return INVALID_OPERATION;
+ case ERR_NOT_SUPPORTED:
+ return INVALID_OPERATION;
+ case ERR_TOO_BIG:
+ return BAD_INDEX;
+ case ERR_CMD_UNKNOWN:
+ return UNKNOWN_TRANSACTION;
+ case ERR_BAD_STATE:
+ return INVALID_OPERATION;
+ case ERR_BAD_LEN:
+ return NOT_ENOUGH_DATA;
+ case ERR_BAD_HANDLE:
+ return BAD_VALUE;
+ case ERR_ACCESS_DENIED:
+ return PERMISSION_DENIED;
+ default:
+ return UNKNOWN_ERROR;
+ }
+}
+
+int statusToTrusty(status_t status) {
+ switch (status) {
+ case OK:
+ return NO_ERROR;
+ case NO_MEMORY:
+ return ERR_NO_MEMORY;
+ case INVALID_OPERATION:
+ case BAD_VALUE:
+ case BAD_TYPE:
+ return ERR_NOT_VALID;
+ case NAME_NOT_FOUND:
+ return ERR_NOT_FOUND;
+ case PERMISSION_DENIED:
+ return ERR_ACCESS_DENIED;
+ case NO_INIT:
+ return ERR_NOT_CONFIGURED;
+ case ALREADY_EXISTS:
+ return ERR_ALREADY_EXISTS;
+ case DEAD_OBJECT:
+ return ERR_CHANNEL_CLOSED;
+ case BAD_INDEX:
+ return ERR_TOO_BIG;
+ case NOT_ENOUGH_DATA:
+ return ERR_BAD_LEN;
+ case WOULD_BLOCK:
+ return ERR_NO_MSG;
+ case TIMED_OUT:
+ return ERR_TIMED_OUT;
+ case UNKNOWN_TRANSACTION:
+ return ERR_CMD_UNKNOWN;
+ case FDS_NOT_ALLOWED:
+ return ERR_NOT_SUPPORTED;
+ case UNEXPECTED_NULL:
+ return ERR_NOT_VALID;
+ default:
+ return ERR_GENERIC;
+ }
+}
+
+} // namespace android
diff --git a/libs/binder/trusty/TrustyStatus.h b/libs/binder/trusty/TrustyStatus.h
new file mode 100644
index 0000000..fcb43f8
--- /dev/null
+++ b/libs/binder/trusty/TrustyStatus.h
@@ -0,0 +1,26 @@
+/*
+ * Copyright (C) 2022 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 <uapi/err.h>
+#include <utils/Errors.h>
+
+namespace android {
+
+status_t statusFromTrusty(int rc);
+int statusToTrusty(status_t status);
+
+} // namespace android
diff --git a/libs/binder/trusty/include/binder/RpcServerTrusty.h b/libs/binder/trusty/include/binder/RpcServerTrusty.h
new file mode 100644
index 0000000..e8fc9f9
--- /dev/null
+++ b/libs/binder/trusty/include/binder/RpcServerTrusty.h
@@ -0,0 +1,100 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include <android-base/expected.h>
+#include <android-base/macros.h>
+#include <android-base/unique_fd.h>
+#include <binder/IBinder.h>
+#include <binder/RpcServer.h>
+#include <binder/RpcSession.h>
+#include <binder/RpcTransport.h>
+#include <utils/Errors.h>
+#include <utils/RefBase.h>
+
+#include <map>
+#include <vector>
+
+#include <lib/tipc/tipc_srv.h>
+
+namespace android {
+
+/**
+ * This is the Trusty-specific RPC server code.
+ */
+class RpcServerTrusty final : public virtual RefBase {
+public:
+ // C++ equivalent to tipc_port_acl that uses safe data structures instead of
+ // raw pointers, except for |extraData| which doesn't have a good
+ // equivalent.
+ struct PortAcl {
+ uint32_t flags;
+ std::vector<const uuid> uuids;
+ const void* extraData;
+ };
+
+ /**
+ * Creates an RPC server listening on the given port and adds it to the
+ * Trusty handle set at |handleSet|.
+ *
+ * The caller is responsible for calling tipc_run_event_loop() to start
+ * the TIPC event loop after creating one or more services here.
+ */
+ static android::base::expected<sp<RpcServerTrusty>, int> make(
+ tipc_hset* handleSet, std::string&& portName, std::shared_ptr<const PortAcl>&& portAcl,
+ size_t msgMaxSize,
+ std::unique_ptr<RpcTransportCtxFactory> rpcTransportCtxFactory = nullptr);
+
+ void setProtocolVersion(uint32_t version) { mRpcServer->setProtocolVersion(version); }
+ void setRootObject(const sp<IBinder>& binder) { mRpcServer->setRootObject(binder); }
+ void setRootObjectWeak(const wp<IBinder>& binder) { mRpcServer->setRootObjectWeak(binder); }
+ void setPerSessionRootObject(std::function<sp<IBinder>(const void*, size_t)>&& object) {
+ mRpcServer->setPerSessionRootObject(std::move(object));
+ }
+ sp<IBinder> getRootObject() { return mRpcServer->getRootObject(); }
+
+private:
+ // Both this class and RpcServer have multiple non-copyable fields,
+ // including mPortAcl below which can't be copied because mUuidPtrs
+ // holds pointers into it
+ DISALLOW_COPY_AND_ASSIGN(RpcServerTrusty);
+
+ friend sp<RpcServerTrusty>;
+ explicit RpcServerTrusty(std::unique_ptr<RpcTransportCtx> ctx, std::string&& portName,
+ std::shared_ptr<const PortAcl>&& portAcl, size_t msgMaxSize);
+
+ static int handleConnect(const tipc_port* port, handle_t chan, const uuid* peer, void** ctx_p);
+ static int handleMessage(const tipc_port* port, handle_t chan, void* ctx);
+ static void handleDisconnect(const tipc_port* port, handle_t chan, void* ctx);
+ static void handleChannelCleanup(void* ctx);
+
+ static constexpr tipc_srv_ops kTipcOps = {
+ .on_connect = &handleConnect,
+ .on_message = &handleMessage,
+ .on_disconnect = &handleDisconnect,
+ .on_channel_cleanup = &handleChannelCleanup,
+ };
+
+ sp<RpcServer> mRpcServer;
+ std::string mPortName;
+ std::shared_ptr<const PortAcl> mPortAcl;
+ std::vector<const uuid*> mUuidPtrs;
+ tipc_port_acl mTipcPortAcl;
+ tipc_port mTipcPort;
+};
+
+} // namespace android
diff --git a/libs/binder/trusty/include/binder/RpcTransportTipcTrusty.h b/libs/binder/trusty/include/binder/RpcTransportTipcTrusty.h
new file mode 100644
index 0000000..8eae8c2
--- /dev/null
+++ b/libs/binder/trusty/include/binder/RpcTransportTipcTrusty.h
@@ -0,0 +1,41 @@
+/*
+ * Copyright (C) 2022 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.
+ */
+
+// Wraps the transport layer of RPC. Implementation uses plain sockets.
+// Note: don't use directly. You probably want newServerRpcTransportCtx / newClientRpcTransportCtx.
+
+#pragma once
+
+#include <memory>
+
+#include <binder/RpcTransport.h>
+
+namespace android {
+
+// RpcTransportCtxFactory with TLS disabled.
+class RpcTransportCtxFactoryTipcTrusty : public RpcTransportCtxFactory {
+public:
+ static std::unique_ptr<RpcTransportCtxFactory> make();
+
+ std::unique_ptr<RpcTransportCtx> newServerCtx() const override;
+ std::unique_ptr<RpcTransportCtx> newClientCtx() const override;
+ const char* toCString() const override;
+
+private:
+ RpcTransportCtxFactoryTipcTrusty() = default;
+};
+
+} // namespace android
diff --git a/libs/binder/trusty/include/log/log.h b/libs/binder/trusty/include/log/log.h
new file mode 100644
index 0000000..bf877a3
--- /dev/null
+++ b/libs/binder/trusty/include/log/log.h
@@ -0,0 +1,122 @@
+/*
+ * Copyright (C) 2022 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
+
+#define BINDER_LOG_LEVEL_NONE 0
+#define BINDER_LOG_LEVEL_NORMAL 1
+#define BINDER_LOG_LEVEL_VERBOSE 2
+
+#ifndef BINDER_LOG_LEVEL
+#define BINDER_LOG_LEVEL BINDER_LOG_LEVEL_NORMAL
+#endif // BINDER_LOG_LEVEL
+
+#ifndef TLOG_TAG
+#ifdef LOG_TAG
+#define TLOG_TAG "libbinder-" LOG_TAG
+#else // LOG_TAG
+#define TLOG_TAG "libbinder"
+#endif // LOG_TAG
+#endif // TLOG_TAG
+
+#include <stdlib.h>
+#include <trusty_log.h>
+
+static inline void __ignore_va_args__(...) {}
+
+#if BINDER_LOG_LEVEL >= BINDER_LOG_LEVEL_NORMAL
+#define ALOGD(fmt, ...) TLOGD(fmt "\n", ##__VA_ARGS__)
+#define ALOGI(fmt, ...) TLOGI(fmt "\n", ##__VA_ARGS__)
+#define ALOGW(fmt, ...) TLOGW(fmt "\n", ##__VA_ARGS__)
+#define ALOGE(fmt, ...) TLOGE(fmt "\n", ##__VA_ARGS__)
+#else // BINDER_LOG_LEVEL >= BINDER_LOG_LEVEL_NORMAL
+#define ALOGD(fmt, ...) \
+ while (0) { \
+ __ignore_va_args__(__VA_ARGS__); \
+ }
+#define ALOGI(fmt, ...) \
+ while (0) { \
+ __ignore_va_args__(__VA_ARGS__); \
+ }
+#define ALOGW(fmt, ...) \
+ while (0) { \
+ __ignore_va_args__(__VA_ARGS__); \
+ }
+#define ALOGE(fmt, ...) \
+ while (0) { \
+ __ignore_va_args__(__VA_ARGS__); \
+ }
+#endif // BINDER_LOG_LEVEL >= BINDER_LOG_LEVEL_NORMAL
+
+#if BINDER_LOG_LEVEL >= BINDER_LOG_LEVEL_VERBOSE
+#define IF_ALOGV() if (TLOG_LVL >= TLOG_LVL_INFO)
+#define ALOGV(fmt, ...) TLOGI(fmt "\n", ##__VA_ARGS__)
+#else // BINDER_LOG_LEVEL >= BINDER_LOG_LEVEL_VERBOSE
+#define IF_ALOGV() if (false)
+#define ALOGV(fmt, ...) \
+ while (0) { \
+ __ignore_va_args__(__VA_ARGS__); \
+ }
+#endif // BINDER_LOG_LEVEL >= BINDER_LOG_LEVEL_VERBOSE
+
+#define ALOGI_IF(cond, ...) \
+ do { \
+ if (cond) { \
+ ALOGI(#cond ": " __VA_ARGS__); \
+ } \
+ } while (0)
+#define ALOGE_IF(cond, ...) \
+ do { \
+ if (cond) { \
+ ALOGE(#cond ": " __VA_ARGS__); \
+ } \
+ } while (0)
+#define ALOGW_IF(cond, ...) \
+ do { \
+ if (cond) { \
+ ALOGW(#cond ": " __VA_ARGS__); \
+ } \
+ } while (0)
+
+#define LOG_ALWAYS_FATAL(fmt, ...) \
+ do { \
+ TLOGE("libbinder fatal error: " fmt "\n", ##__VA_ARGS__); \
+ abort(); \
+ } while (0)
+#define LOG_ALWAYS_FATAL_IF(cond, ...) \
+ do { \
+ if (cond) { \
+ LOG_ALWAYS_FATAL(#cond ": " __VA_ARGS__); \
+ } \
+ } while (0)
+#define LOG_FATAL(fmt, ...) \
+ do { \
+ TLOGE("libbinder fatal error: " fmt "\n", ##__VA_ARGS__); \
+ abort(); \
+ } while (0)
+#define LOG_FATAL_IF(cond, ...) \
+ do { \
+ if (cond) { \
+ LOG_FATAL(#cond ": " __VA_ARGS__); \
+ } \
+ } while (0)
+
+#define ALOG_ASSERT(cond, ...) LOG_FATAL_IF(!(cond), ##__VA_ARGS__)
+
+#define android_errorWriteLog(tag, subTag) \
+ do { \
+ TLOGE("android_errorWriteLog: tag:%x subTag:%s\n", tag, subTag); \
+ } while (0)
diff --git a/libs/binder/trusty/logging.cpp b/libs/binder/trusty/logging.cpp
new file mode 100644
index 0000000..fd54744
--- /dev/null
+++ b/libs/binder/trusty/logging.cpp
@@ -0,0 +1,166 @@
+/*
+ * Copyright (C) 2022 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 TLOG_TAG "libbinder"
+
+#include "android-base/logging.h"
+
+#include <trusty_log.h>
+#include <iostream>
+#include <string>
+
+#include <android-base/macros.h>
+#include <android-base/strings.h>
+
+namespace android {
+namespace base {
+
+static const char* GetFileBasename(const char* file) {
+ const char* last_slash = strrchr(file, '/');
+ if (last_slash != nullptr) {
+ return last_slash + 1;
+ }
+ return file;
+}
+
+// This splits the message up line by line, by calling log_function with a pointer to the start of
+// each line and the size up to the newline character. It sends size = -1 for the final line.
+template <typename F, typename... Args>
+static void SplitByLines(const char* msg, const F& log_function, Args&&... args) {
+ const char* newline;
+ while ((newline = strchr(msg, '\n')) != nullptr) {
+ log_function(msg, newline - msg, args...);
+ msg = newline + 1;
+ }
+
+ log_function(msg, -1, args...);
+}
+
+void DefaultAborter(const char* abort_message) {
+ TLOGC("aborting: %s\n", abort_message);
+ abort();
+}
+
+static void TrustyLogLine(const char* msg, int length, android::base::LogSeverity severity,
+ const char* tag) {
+ switch (severity) {
+ case VERBOSE:
+ case DEBUG:
+ TLOGD("%s: %s\n", tag, msg);
+ break;
+ case INFO:
+ TLOGI("%s: %s\n", tag, msg);
+ break;
+ case WARNING:
+ TLOGW("%s: %s\n", tag, msg);
+ break;
+ case ERROR:
+ TLOGE("%s: %s\n", tag, msg);
+ break;
+ case FATAL_WITHOUT_ABORT:
+ case FATAL:
+ TLOGC("%s: %s\n", tag, msg);
+ break;
+ }
+}
+
+void TrustyLogger(android::base::LogId, android::base::LogSeverity severity, const char* tag,
+ const char*, unsigned int, const char* full_message) {
+ SplitByLines(full_message, TrustyLogLine, severity, tag);
+}
+
+// This indirection greatly reduces the stack impact of having lots of
+// checks/logging in a function.
+class LogMessageData {
+public:
+ LogMessageData(const char* file, unsigned int line, LogSeverity severity, const char* tag,
+ int error)
+ : file_(GetFileBasename(file)),
+ line_number_(line),
+ severity_(severity),
+ tag_(tag),
+ error_(error) {}
+
+ const char* GetFile() const { return file_; }
+
+ unsigned int GetLineNumber() const { return line_number_; }
+
+ LogSeverity GetSeverity() const { return severity_; }
+
+ const char* GetTag() const { return tag_; }
+
+ int GetError() const { return error_; }
+
+ std::ostream& GetBuffer() { return buffer_; }
+
+ std::string ToString() const { return buffer_.str(); }
+
+private:
+ std::ostringstream buffer_;
+ const char* const file_;
+ const unsigned int line_number_;
+ const LogSeverity severity_;
+ const char* const tag_;
+ const int error_;
+
+ DISALLOW_COPY_AND_ASSIGN(LogMessageData);
+};
+
+LogMessage::LogMessage(const char* file, unsigned int line, LogId, LogSeverity severity,
+ const char* tag, int error)
+ : LogMessage(file, line, severity, tag, error) {}
+
+LogMessage::LogMessage(const char* file, unsigned int line, LogSeverity severity, const char* tag,
+ int error)
+ : data_(new LogMessageData(file, line, severity, tag, error)) {}
+
+LogMessage::~LogMessage() {
+ // Check severity again. This is duplicate work wrt/ LOG macros, but not LOG_STREAM.
+ if (!WOULD_LOG(data_->GetSeverity())) {
+ return;
+ }
+
+ // Finish constructing the message.
+ if (data_->GetError() != -1) {
+ data_->GetBuffer() << ": " << strerror(data_->GetError());
+ }
+ std::string msg(data_->ToString());
+
+ LogLine(data_->GetFile(), data_->GetLineNumber(), data_->GetSeverity(), data_->GetTag(),
+ msg.c_str());
+
+ // Abort if necessary.
+ if (data_->GetSeverity() == FATAL) {
+ DefaultAborter(msg.c_str());
+ }
+}
+
+std::ostream& LogMessage::stream() {
+ return data_->GetBuffer();
+}
+
+void LogMessage::LogLine(const char* file, unsigned int line, LogSeverity severity, const char* tag,
+ const char* message) {
+ TrustyLogger(DEFAULT, severity, tag ?: "<unknown>", file, line, message);
+}
+
+bool ShouldLog(LogSeverity severity, const char* tag) {
+ // This is controlled by Trusty's log level.
+ return true;
+}
+
+} // namespace base
+} // namespace android
diff --git a/libs/binder/trusty/rules.mk b/libs/binder/trusty/rules.mk
new file mode 100644
index 0000000..cd81a09
--- /dev/null
+++ b/libs/binder/trusty/rules.mk
@@ -0,0 +1,82 @@
+# Copyright (C) 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.
+#
+
+LOCAL_DIR := $(GET_LOCAL_DIR)
+
+MODULE := $(LOCAL_DIR)
+
+LIBBINDER_DIR := frameworks/native/libs/binder
+LIBBASE_DIR := system/libbase
+LIBCUTILS_DIR := system/core/libcutils
+LIBUTILS_DIR := system/core/libutils
+FMTLIB_DIR := external/fmtlib
+
+MODULE_SRCS := \
+ $(LOCAL_DIR)/logging.cpp \
+ $(LOCAL_DIR)/OS.cpp \
+ $(LOCAL_DIR)/RpcServerTrusty.cpp \
+ $(LOCAL_DIR)/RpcTransportTipcTrusty.cpp \
+ $(LOCAL_DIR)/TrustyStatus.cpp \
+ $(LOCAL_DIR)/socket.cpp \
+ $(LIBBINDER_DIR)/Binder.cpp \
+ $(LIBBINDER_DIR)/BpBinder.cpp \
+ $(LIBBINDER_DIR)/FdTrigger.cpp \
+ $(LIBBINDER_DIR)/IInterface.cpp \
+ $(LIBBINDER_DIR)/IResultReceiver.cpp \
+ $(LIBBINDER_DIR)/Parcel.cpp \
+ $(LIBBINDER_DIR)/RpcServer.cpp \
+ $(LIBBINDER_DIR)/RpcSession.cpp \
+ $(LIBBINDER_DIR)/RpcState.cpp \
+ $(LIBBINDER_DIR)/Stability.cpp \
+ $(LIBBINDER_DIR)/Status.cpp \
+ $(LIBBINDER_DIR)/Utils.cpp \
+ $(LIBBASE_DIR)/hex.cpp \
+ $(LIBBASE_DIR)/stringprintf.cpp \
+ $(LIBUTILS_DIR)/Errors.cpp \
+ $(LIBUTILS_DIR)/misc.cpp \
+ $(LIBUTILS_DIR)/RefBase.cpp \
+ $(LIBUTILS_DIR)/StrongPointer.cpp \
+ $(LIBUTILS_DIR)/Unicode.cpp \
+
+# TODO: remove the following when libbinder supports std::string
+# instead of String16 and String8 for Status and descriptors
+MODULE_SRCS += \
+ $(LIBUTILS_DIR)/SharedBuffer.cpp \
+ $(LIBUTILS_DIR)/String16.cpp \
+ $(LIBUTILS_DIR)/String8.cpp \
+
+# TODO: disable dump() transactions to get rid of Vector
+MODULE_SRCS += \
+ $(LIBUTILS_DIR)/VectorImpl.cpp \
+
+MODULE_EXPORT_INCLUDES += \
+ $(LOCAL_DIR)/include \
+ $(LIBBINDER_DIR)/include \
+ $(LIBBASE_DIR)/include \
+ $(LIBCUTILS_DIR)/include \
+ $(LIBUTILS_DIR)/include \
+ $(FMTLIB_DIR)/include \
+
+MODULE_EXPORT_COMPILEFLAGS += \
+ -DBINDER_NO_KERNEL_IPC \
+ -DBINDER_RPC_SINGLE_THREADED \
+ -D__ANDROID_VNDK__ \
+
+MODULE_LIBRARY_DEPS += \
+ trusty/user/base/lib/libstdc++-trusty \
+ trusty/user/base/lib/tipc \
+ external/boringssl \
+
+include make/library.mk
diff --git a/libs/binder/trusty/socket.cpp b/libs/binder/trusty/socket.cpp
new file mode 100644
index 0000000..02df8af
--- /dev/null
+++ b/libs/binder/trusty/socket.cpp
@@ -0,0 +1,29 @@
+/*
+ * Copyright (C) 2022 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 <log/log.h>
+
+// On some versions of clang, RpcServer.cpp refuses to link without accept4
+__attribute__((weak)) extern "C" int accept4(int, void*, void*, int) {
+ LOG_ALWAYS_FATAL("accept4 called on Trusty");
+ return 0;
+}
+
+// Placeholder for poll used by FdTrigger
+__attribute__((weak)) extern "C" int poll(void*, long, int) {
+ LOG_ALWAYS_FATAL("poll called on Trusty");
+ return 0;
+}
diff --git a/libs/gui/Android.bp b/libs/gui/Android.bp
index d098e3a..d7bc500 100644
--- a/libs/gui/Android.bp
+++ b/libs/gui/Android.bp
@@ -193,6 +193,7 @@
"BufferHubConsumer.cpp",
"BufferHubProducer.cpp",
"BufferItemConsumer.cpp",
+ "CompositorTiming.cpp",
"ConsumerBase.cpp",
"CpuConsumer.cpp",
"DebugEGLImageTracker.cpp",
diff --git a/libs/gui/BLASTBufferQueue.cpp b/libs/gui/BLASTBufferQueue.cpp
index 2212e58..3bf2e19 100644
--- a/libs/gui/BLASTBufferQueue.cpp
+++ b/libs/gui/BLASTBufferQueue.cpp
@@ -599,6 +599,7 @@
}
void BLASTBufferQueue::acquireAndReleaseBuffer() {
+ BBQ_TRACE();
BufferItem bufferItem;
status_t status =
mBufferItemConsumer->acquireBuffer(&bufferItem, 0 /* expectedPresent */, false);
@@ -612,6 +613,7 @@
}
void BLASTBufferQueue::flushAndWaitForFreeBuffer(std::unique_lock<std::mutex>& lock) {
+ BBQ_TRACE();
if (!mSyncedFrameNumbers.empty() && mNumFrameAvailable > 0) {
// We are waiting on a previous sync's transaction callback so allow another sync
// transaction to proceed.
@@ -642,8 +644,8 @@
bool waitForTransactionCallback = !mSyncedFrameNumbers.empty();
{
- BBQ_TRACE();
std::unique_lock _lock{mMutex};
+ BBQ_TRACE();
const bool syncTransactionSet = mTransactionReadyCallback != nullptr;
BQA_LOGV("onFrameAvailable-start syncTransactionSet=%s", boolToString(syncTransactionSet));
diff --git a/libs/gui/CompositorTiming.cpp b/libs/gui/CompositorTiming.cpp
new file mode 100644
index 0000000..50f7b25
--- /dev/null
+++ b/libs/gui/CompositorTiming.cpp
@@ -0,0 +1,54 @@
+/*
+ * Copyright 2022 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 "CompositorTiming"
+
+#include <cutils/compiler.h>
+#include <gui/CompositorTiming.h>
+#include <log/log.h>
+
+namespace android::gui {
+
+CompositorTiming::CompositorTiming(nsecs_t vsyncDeadline, nsecs_t vsyncPeriod, nsecs_t vsyncPhase,
+ nsecs_t presentLatency) {
+ if (CC_UNLIKELY(vsyncPeriod <= 0)) {
+ ALOGE("Invalid VSYNC period");
+ return;
+ }
+
+ const nsecs_t idealLatency = [=] {
+ // Modulo rounds toward 0 not INT64_MIN, so treat signs separately.
+ if (vsyncPhase < 0) return -vsyncPhase % vsyncPeriod;
+
+ const nsecs_t latency = (vsyncPeriod - vsyncPhase) % vsyncPeriod;
+ return latency > 0 ? latency : vsyncPeriod;
+ }();
+
+ // Snap the latency to a value that removes scheduling jitter from the composite and present
+ // times, which often have >1ms of jitter. Reducing jitter is important if an app attempts to
+ // extrapolate something like user input to an accurate present time. Snapping also allows an
+ // app to precisely calculate vsyncPhase with (presentLatency % interval).
+ const nsecs_t bias = vsyncPeriod / 2;
+ const nsecs_t extraVsyncs = (presentLatency - idealLatency + bias) / vsyncPeriod;
+ const nsecs_t snappedLatency =
+ extraVsyncs > 0 ? idealLatency + extraVsyncs * vsyncPeriod : idealLatency;
+
+ this->deadline = vsyncDeadline - idealLatency;
+ this->interval = vsyncPeriod;
+ this->presentLatency = snappedLatency;
+}
+
+} // namespace android::gui
diff --git a/libs/gui/fuzzer/Android.bp b/libs/gui/fuzzer/Android.bp
index 952b6a9..f1c529d 100644
--- a/libs/gui/fuzzer/Android.bp
+++ b/libs/gui/fuzzer/Android.bp
@@ -13,6 +13,15 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
+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: "libgui_fuzzer_defaults",
static_libs: [
diff --git a/libs/gui/include/gui/CompositorTiming.h b/libs/gui/include/gui/CompositorTiming.h
new file mode 100644
index 0000000..cb8ca7a
--- /dev/null
+++ b/libs/gui/include/gui/CompositorTiming.h
@@ -0,0 +1,43 @@
+/*
+ * Copyright 2022 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 <utils/Timers.h>
+
+namespace android::gui {
+
+// Expected timing of the next composited frame, based on the timing of the latest frames.
+struct CompositorTiming {
+ static constexpr nsecs_t kDefaultVsyncPeriod = 16'666'667;
+
+ CompositorTiming() = default;
+ CompositorTiming(nsecs_t vsyncDeadline, nsecs_t vsyncPeriod, nsecs_t vsyncPhase,
+ nsecs_t presentLatency);
+
+ // Time point when compositing is expected to start.
+ nsecs_t deadline = 0;
+
+ // Duration between consecutive frames. In other words, the VSYNC period.
+ nsecs_t interval = kDefaultVsyncPeriod;
+
+ // Duration between composite start and present. For missed frames, the extra latency is rounded
+ // to a multiple of the VSYNC period, such that the remainder (presentLatency % interval) always
+ // evaluates to the VSYNC phase offset.
+ nsecs_t presentLatency = kDefaultVsyncPeriod;
+};
+
+} // namespace android::gui
diff --git a/libs/gui/include/gui/FrameTimestamps.h b/libs/gui/include/gui/FrameTimestamps.h
index f73bc3b..c08a9b1 100644
--- a/libs/gui/include/gui/FrameTimestamps.h
+++ b/libs/gui/include/gui/FrameTimestamps.h
@@ -19,6 +19,7 @@
#include <android/gui/FrameEvent.h>
+#include <gui/CompositorTiming.h>
#include <ui/FenceTime.h>
#include <utils/Flattenable.h>
#include <utils/StrongPointer.h>
@@ -33,6 +34,7 @@
struct FrameEvents;
class FrameEventHistoryDelta;
+using gui::CompositorTiming;
using gui::FrameEvent;
// A collection of timestamps corresponding to a single frame.
@@ -83,12 +85,6 @@
std::shared_ptr<FenceTime> releaseFence{FenceTime::NO_FENCE};
};
-struct CompositorTiming {
- nsecs_t deadline{0};
- nsecs_t interval{16666667};
- nsecs_t presentLatency{16666667};
-};
-
// A short history of frames that are synchronized between the consumer and
// producer via deltas.
class FrameEventHistory {
diff --git a/libs/gui/include/gui/TraceUtils.h b/libs/gui/include/gui/TraceUtils.h
index e5d2684..0009615 100644
--- a/libs/gui/include/gui/TraceUtils.h
+++ b/libs/gui/include/gui/TraceUtils.h
@@ -27,6 +27,8 @@
#define ATRACE_FORMAT_BEGIN(fmt, ...) TraceUtils::atraceFormatBegin(fmt, ##__VA_ARGS__)
+#define ATRACE_FORMAT_INSTANT(fmt, ...) TraceUtils::intantFormat(fmt, ##__VA_ARGS__)
+
namespace android {
class TraceUtils {
@@ -50,6 +52,20 @@
ATRACE_BEGIN(buf);
}
+ static void intantFormat(const char* fmt, ...) {
+ if (CC_LIKELY(!ATRACE_ENABLED())) return;
+
+ const int BUFFER_SIZE = 256;
+ va_list ap;
+ char buf[BUFFER_SIZE];
+
+ va_start(ap, fmt);
+ vsnprintf(buf, BUFFER_SIZE, fmt, ap);
+ va_end(ap);
+
+ ATRACE_INSTANT(buf);
+ }
+
}; // class TraceUtils
} /* namespace android */
diff --git a/libs/gui/tests/Android.bp b/libs/gui/tests/Android.bp
index fc68ad2..7b37b25 100644
--- a/libs/gui/tests/Android.bp
+++ b/libs/gui/tests/Android.bp
@@ -24,6 +24,7 @@
"BLASTBufferQueue_test.cpp",
"BufferItemConsumer_test.cpp",
"BufferQueue_test.cpp",
+ "CompositorTiming_test.cpp",
"CpuConsumer_test.cpp",
"EndToEndNativeInputTest.cpp",
"DisplayInfo_test.cpp",
diff --git a/libs/gui/tests/CompositorTiming_test.cpp b/libs/gui/tests/CompositorTiming_test.cpp
new file mode 100644
index 0000000..d8bb21d
--- /dev/null
+++ b/libs/gui/tests/CompositorTiming_test.cpp
@@ -0,0 +1,61 @@
+/*
+ * Copyright 2022 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 <gtest/gtest.h>
+#include <gui/CompositorTiming.h>
+
+namespace android::test {
+namespace {
+
+constexpr nsecs_t kMillisecond = 1'000'000;
+constexpr nsecs_t kVsyncPeriod = 8'333'333;
+constexpr nsecs_t kVsyncPhase = -2'166'667;
+constexpr nsecs_t kIdealLatency = -kVsyncPhase;
+
+} // namespace
+
+TEST(CompositorTimingTest, InvalidVsyncPeriod) {
+ const nsecs_t vsyncDeadline = systemTime();
+ constexpr nsecs_t kInvalidVsyncPeriod = -1;
+
+ const gui::CompositorTiming timing(vsyncDeadline, kInvalidVsyncPeriod, kVsyncPhase,
+ kIdealLatency);
+
+ EXPECT_EQ(timing.deadline, 0);
+ EXPECT_EQ(timing.interval, gui::CompositorTiming::kDefaultVsyncPeriod);
+ EXPECT_EQ(timing.presentLatency, gui::CompositorTiming::kDefaultVsyncPeriod);
+}
+
+TEST(CompositorTimingTest, PresentLatencySnapping) {
+ for (nsecs_t presentDelay = 0, compositeTime = systemTime(); presentDelay < 10 * kVsyncPeriod;
+ presentDelay += kMillisecond, compositeTime += kVsyncPeriod) {
+ const nsecs_t presentLatency = kIdealLatency + presentDelay;
+ const nsecs_t vsyncDeadline = compositeTime + presentLatency + kVsyncPeriod;
+
+ const gui::CompositorTiming timing(vsyncDeadline, kVsyncPeriod, kVsyncPhase,
+ presentLatency);
+
+ EXPECT_EQ(timing.deadline, compositeTime + presentDelay + kVsyncPeriod);
+ EXPECT_EQ(timing.interval, kVsyncPeriod);
+
+ // The presentDelay should be rounded to a multiple of the VSYNC period, such that the
+ // remainder (presentLatency % interval) always evaluates to the VSYNC phase offset.
+ EXPECT_GE(timing.presentLatency, kIdealLatency);
+ EXPECT_EQ(timing.presentLatency % timing.interval, kIdealLatency);
+ }
+}
+
+} // namespace android::test
diff --git a/libs/gui/tests/Surface_test.cpp b/libs/gui/tests/Surface_test.cpp
index 7eff3b3..391a0aa 100644
--- a/libs/gui/tests/Surface_test.cpp
+++ b/libs/gui/tests/Surface_test.cpp
@@ -1087,20 +1087,30 @@
protected:
struct FenceAndFenceTime {
explicit FenceAndFenceTime(FenceToFenceTimeMap& fenceMap)
- : mFence(new Fence),
- mFenceTime(fenceMap.createFenceTimeForTest(mFence)) {}
- sp<Fence> mFence { nullptr };
- std::shared_ptr<FenceTime> mFenceTime { nullptr };
+ : mFenceTime(fenceMap.createFenceTimeForTest(mFence)) {}
+
+ sp<Fence> mFence = sp<Fence>::make();
+ std::shared_ptr<FenceTime> mFenceTime;
};
+ static CompositorTiming makeCompositorTiming(nsecs_t deadline = 1'000'000'000,
+ nsecs_t interval = 16'666'667,
+ nsecs_t presentLatency = 50'000'000) {
+ CompositorTiming timing;
+ timing.deadline = deadline;
+ timing.interval = interval;
+ timing.presentLatency = presentLatency;
+ return timing;
+ }
+
struct RefreshEvents {
RefreshEvents(FenceToFenceTimeMap& fenceMap, nsecs_t refreshStart)
- : mFenceMap(fenceMap),
- kCompositorTiming(
- {refreshStart, refreshStart + 1, refreshStart + 2 }),
- kStartTime(refreshStart + 3),
- kGpuCompositionDoneTime(refreshStart + 4),
- kPresentTime(refreshStart + 5) {}
+ : mFenceMap(fenceMap),
+ kCompositorTiming(
+ makeCompositorTiming(refreshStart, refreshStart + 1, refreshStart + 2)),
+ kStartTime(refreshStart + 3),
+ kGpuCompositionDoneTime(refreshStart + 4),
+ kPresentTime(refreshStart + 5) {}
void signalPostCompositeFences() {
mFenceMap.signalAllForTest(
@@ -1110,8 +1120,8 @@
FenceToFenceTimeMap& mFenceMap;
- FenceAndFenceTime mGpuCompositionDone { mFenceMap };
- FenceAndFenceTime mPresent { mFenceMap };
+ FenceAndFenceTime mGpuCompositionDone{mFenceMap};
+ FenceAndFenceTime mPresent{mFenceMap};
const CompositorTiming kCompositorTiming;
@@ -1377,11 +1387,7 @@
// This test verifies that the frame timestamps are retrieved if explicitly
// enabled via native_window_enable_frame_timestamps.
TEST_F(GetFrameTimestampsTest, EnabledSimple) {
- CompositorTiming initialCompositorTiming {
- 1000000000, // 1s deadline
- 16666667, // 16ms interval
- 50000000, // 50ms present latency
- };
+ const CompositorTiming initialCompositorTiming = makeCompositorTiming();
mCfeh->initializeCompositorTiming(initialCompositorTiming);
enableFrameTimestamps();
@@ -1520,11 +1526,7 @@
// This verifies the compositor timing is updated by refresh events
// and piggy backed on a queue, dequeue, and enabling of timestamps..
TEST_F(GetFrameTimestampsTest, CompositorTimingUpdatesBasic) {
- CompositorTiming initialCompositorTiming {
- 1000000000, // 1s deadline
- 16666667, // 16ms interval
- 50000000, // 50ms present latency
- };
+ const CompositorTiming initialCompositorTiming = makeCompositorTiming();
mCfeh->initializeCompositorTiming(initialCompositorTiming);
enableFrameTimestamps();
@@ -1605,11 +1607,7 @@
// This verifies the compositor deadline properly snaps to the the next
// deadline based on the current time.
TEST_F(GetFrameTimestampsTest, CompositorTimingDeadlineSnaps) {
- CompositorTiming initialCompositorTiming {
- 1000000000, // 1s deadline
- 16666667, // 16ms interval
- 50000000, // 50ms present latency
- };
+ const CompositorTiming initialCompositorTiming = makeCompositorTiming();
mCfeh->initializeCompositorTiming(initialCompositorTiming);
enableFrameTimestamps();
diff --git a/libs/input/Keyboard.cpp b/libs/input/Keyboard.cpp
index c3f5151..3f8467d 100644
--- a/libs/input/Keyboard.cpp
+++ b/libs/input/Keyboard.cpp
@@ -49,25 +49,23 @@
const PropertyMap* deviceConfiguration) {
// Use the configured key layout if available.
if (deviceConfiguration) {
- String8 keyLayoutName;
- if (deviceConfiguration->tryGetProperty(String8("keyboard.layout"),
- keyLayoutName)) {
+ std::string keyLayoutName;
+ if (deviceConfiguration->tryGetProperty("keyboard.layout", keyLayoutName)) {
status_t status = loadKeyLayout(deviceIdentifier, keyLayoutName.c_str());
if (status == NAME_NOT_FOUND) {
ALOGE("Configuration for keyboard device '%s' requested keyboard layout '%s' but "
- "it was not found.",
- deviceIdentifier.name.c_str(), keyLayoutName.string());
+ "it was not found.",
+ deviceIdentifier.name.c_str(), keyLayoutName.c_str());
}
}
- String8 keyCharacterMapName;
- if (deviceConfiguration->tryGetProperty(String8("keyboard.characterMap"),
- keyCharacterMapName)) {
+ std::string keyCharacterMapName;
+ if (deviceConfiguration->tryGetProperty("keyboard.characterMap", keyCharacterMapName)) {
status_t status = loadKeyCharacterMap(deviceIdentifier, keyCharacterMapName.c_str());
if (status == NAME_NOT_FOUND) {
ALOGE("Configuration for keyboard device '%s' requested keyboard character "
- "map '%s' but it was not found.",
- deviceIdentifier.name.c_str(), keyCharacterMapName.string());
+ "map '%s' but it was not found.",
+ deviceIdentifier.name.c_str(), keyCharacterMapName.c_str());
}
}
@@ -165,7 +163,7 @@
return false;
}
bool isSpecialFunction = false;
- config->tryGetProperty(String8("keyboard.specialFunction"), isSpecialFunction);
+ config->tryGetProperty("keyboard.specialFunction", isSpecialFunction);
return isSpecialFunction;
}
@@ -180,8 +178,7 @@
if (deviceConfiguration) {
bool builtIn = false;
- if (deviceConfiguration->tryGetProperty(String8("keyboard.builtIn"), builtIn)
- && builtIn) {
+ if (deviceConfiguration->tryGetProperty("keyboard.builtIn", builtIn) && builtIn) {
return true;
}
}
diff --git a/libs/input/PropertyMap.cpp b/libs/input/PropertyMap.cpp
index a842166..662e568 100644
--- a/libs/input/PropertyMap.cpp
+++ b/libs/input/PropertyMap.cpp
@@ -17,6 +17,7 @@
#define LOG_TAG "PropertyMap"
#include <input/PropertyMap.h>
+#include <log/log.h>
// Enables debug output for the parser.
#define DEBUG_PARSER 0
@@ -39,25 +40,25 @@
mProperties.clear();
}
-void PropertyMap::addProperty(const String8& key, const String8& value) {
- mProperties.add(key, value);
+void PropertyMap::addProperty(const std::string& key, const std::string& value) {
+ mProperties.emplace(key, value);
}
-bool PropertyMap::hasProperty(const String8& key) const {
- return mProperties.indexOfKey(key) >= 0;
+bool PropertyMap::hasProperty(const std::string& key) const {
+ return mProperties.find(key) != mProperties.end();
}
-bool PropertyMap::tryGetProperty(const String8& key, String8& outValue) const {
- ssize_t index = mProperties.indexOfKey(key);
- if (index < 0) {
+bool PropertyMap::tryGetProperty(const std::string& key, std::string& outValue) const {
+ auto it = mProperties.find(key);
+ if (it == mProperties.end()) {
return false;
}
- outValue = mProperties.valueAt(index);
+ outValue = it->second;
return true;
}
-bool PropertyMap::tryGetProperty(const String8& key, bool& outValue) const {
+bool PropertyMap::tryGetProperty(const std::string& key, bool& outValue) const {
int32_t intValue;
if (!tryGetProperty(key, intValue)) {
return false;
@@ -67,34 +68,34 @@
return true;
}
-bool PropertyMap::tryGetProperty(const String8& key, int32_t& outValue) const {
- String8 stringValue;
+bool PropertyMap::tryGetProperty(const std::string& key, int32_t& outValue) const {
+ std::string stringValue;
if (!tryGetProperty(key, stringValue) || stringValue.length() == 0) {
return false;
}
char* end;
- int value = strtol(stringValue.string(), &end, 10);
+ int value = strtol(stringValue.c_str(), &end, 10);
if (*end != '\0') {
- ALOGW("Property key '%s' has invalid value '%s'. Expected an integer.", key.string(),
- stringValue.string());
+ ALOGW("Property key '%s' has invalid value '%s'. Expected an integer.", key.c_str(),
+ stringValue.c_str());
return false;
}
outValue = value;
return true;
}
-bool PropertyMap::tryGetProperty(const String8& key, float& outValue) const {
- String8 stringValue;
+bool PropertyMap::tryGetProperty(const std::string& key, float& outValue) const {
+ std::string stringValue;
if (!tryGetProperty(key, stringValue) || stringValue.length() == 0) {
return false;
}
char* end;
- float value = strtof(stringValue.string(), &end);
+ float value = strtof(stringValue.c_str(), &end);
if (*end != '\0') {
- ALOGW("Property key '%s' has invalid value '%s'. Expected a float.", key.string(),
- stringValue.string());
+ ALOGW("Property key '%s' has invalid value '%s'. Expected a float.", key.c_str(),
+ stringValue.c_str());
return false;
}
outValue = value;
@@ -102,8 +103,8 @@
}
void PropertyMap::addAll(const PropertyMap* map) {
- for (size_t i = 0; i < map->mProperties.size(); i++) {
- mProperties.add(map->mProperties.keyAt(i), map->mProperties.valueAt(i));
+ for (const auto& [key, value] : map->mProperties) {
+ mProperties.emplace(key, value);
}
}
@@ -184,13 +185,13 @@
return BAD_VALUE;
}
- if (mMap->hasProperty(keyToken)) {
+ if (mMap->hasProperty(keyToken.string())) {
ALOGE("%s: Duplicate property value for key '%s'.",
mTokenizer->getLocation().string(), keyToken.string());
return BAD_VALUE;
}
- mMap->addProperty(keyToken, valueToken);
+ mMap->addProperty(keyToken.string(), valueToken.string());
}
mTokenizer->nextLine();
diff --git a/libs/input/PropertyMap_fuzz.cpp b/libs/input/PropertyMap_fuzz.cpp
index afb97a1..d985dc1 100755
--- a/libs/input/PropertyMap_fuzz.cpp
+++ b/libs/input/PropertyMap_fuzz.cpp
@@ -17,32 +17,22 @@
#include "android-base/file.h"
#include "fuzzer/FuzzedDataProvider.h"
#include "input/PropertyMap.h"
-#include "utils/String8.h"
static constexpr int MAX_FILE_SIZE = 256;
static constexpr int MAX_STR_LEN = 2048;
static constexpr int MAX_OPERATIONS = 1000;
-static const std::vector<std::function<void(FuzzedDataProvider*, android::PropertyMap)>>
+static const std::vector<std::function<void(FuzzedDataProvider*, android::PropertyMap&)>>
operations = {
- [](FuzzedDataProvider*, android::PropertyMap propertyMap) -> void {
- propertyMap.getProperties();
- },
- [](FuzzedDataProvider*, android::PropertyMap propertyMap) -> void {
+ [](FuzzedDataProvider*, android::PropertyMap& propertyMap) -> void {
propertyMap.clear();
},
- [](FuzzedDataProvider* dataProvider, android::PropertyMap propertyMap) -> void {
- std::string keyStr = dataProvider->ConsumeRandomLengthString(MAX_STR_LEN);
- android::String8 key = android::String8(keyStr.c_str());
- propertyMap.hasProperty(key);
- },
- [](FuzzedDataProvider* dataProvider, android::PropertyMap propertyMap) -> void {
- std::string keyStr = dataProvider->ConsumeRandomLengthString(MAX_STR_LEN);
- android::String8 key = android::String8(keyStr.c_str());
- android::String8 out;
+ [](FuzzedDataProvider* dataProvider, android::PropertyMap& propertyMap) -> void {
+ std::string key = dataProvider->ConsumeRandomLengthString(MAX_STR_LEN);
+ std::string out;
propertyMap.tryGetProperty(key, out);
},
- [](FuzzedDataProvider* dataProvider, android::PropertyMap /*unused*/) -> void {
+ [](FuzzedDataProvider* dataProvider, android::PropertyMap& /*unused*/) -> void {
TemporaryFile tf;
// Generate file contents
std::string contents = dataProvider->ConsumeRandomLengthString(MAX_FILE_SIZE);
@@ -54,17 +44,15 @@
}
android::PropertyMap::load(tf.path);
},
- [](FuzzedDataProvider* dataProvider, android::PropertyMap propertyMap) -> void {
- std::string keyStr = dataProvider->ConsumeRandomLengthString(MAX_STR_LEN);
- std::string valStr = dataProvider->ConsumeRandomLengthString(MAX_STR_LEN);
- android::String8 key = android::String8(keyStr.c_str());
- android::String8 val = android::String8(valStr.c_str());
+ [](FuzzedDataProvider* dataProvider, android::PropertyMap& propertyMap) -> void {
+ std::string key = dataProvider->ConsumeRandomLengthString(MAX_STR_LEN);
+ std::string val = dataProvider->ConsumeRandomLengthString(MAX_STR_LEN);
propertyMap.addProperty(key, val);
},
};
extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) {
FuzzedDataProvider dataProvider(data, size);
- android::PropertyMap propertyMap = android::PropertyMap();
+ android::PropertyMap propertyMap;
int opsRun = 0;
while (dataProvider.remaining_bytes() > 0 && opsRun++ < MAX_OPERATIONS) {
diff --git a/libs/renderengine/gl/GLESRenderEngine.cpp b/libs/renderengine/gl/GLESRenderEngine.cpp
index 22dd866..6dc01b9 100644
--- a/libs/renderengine/gl/GLESRenderEngine.cpp
+++ b/libs/renderengine/gl/GLESRenderEngine.cpp
@@ -921,7 +921,8 @@
// Finally, we cut the layer into 3 parts, with top and bottom parts having rounded corners
// and the middle part without rounded corners.
- const int32_t radius = ceil(layer.geometry.roundedCornersRadius);
+ const int32_t radius = ceil(
+ (layer.geometry.roundedCornersRadius.x + layer.geometry.roundedCornersRadius.y) / 2.0);
const Rect topRect(bounds.left, bounds.top, bounds.right, bounds.top + radius);
setScissor(topRect);
drawMesh(mesh);
@@ -1266,23 +1267,24 @@
const half3 solidColor = layer.source.solidColor;
const half4 color = half4(solidColor.r, solidColor.g, solidColor.b, layer.alpha);
+ const float radius =
+ (layer.geometry.roundedCornersRadius.x + layer.geometry.roundedCornersRadius.y) /
+ 2.0f;
// Buffer sources will have a black solid color ignored in the shader,
// so in that scenario the solid color passed here is arbitrary.
- setupLayerBlending(usePremultipliedAlpha, isOpaque, disableTexture, color,
- layer.geometry.roundedCornersRadius);
+ setupLayerBlending(usePremultipliedAlpha, isOpaque, disableTexture, color, radius);
if (layer.disableBlending) {
glDisable(GL_BLEND);
}
setSourceDataSpace(layer.sourceDataspace);
if (layer.shadow.length > 0.0f) {
- handleShadow(layer.geometry.boundaries, layer.geometry.roundedCornersRadius,
- layer.shadow);
+ handleShadow(layer.geometry.boundaries, radius, layer.shadow);
}
// We only want to do a special handling for rounded corners when having rounded corners
// is the only reason it needs to turn on blending, otherwise, we handle it like the
// usual way since it needs to turn on blending anyway.
- else if (layer.geometry.roundedCornersRadius > 0.0 && color.a >= 1.0f && isOpaque) {
+ else if (radius > 0.0 && color.a >= 1.0f && isOpaque) {
handleRoundedCorners(display, layer, mesh);
} else {
drawMesh(mesh);
diff --git a/libs/renderengine/include/renderengine/LayerSettings.h b/libs/renderengine/include/renderengine/LayerSettings.h
index 154e526..b3a617c 100644
--- a/libs/renderengine/include/renderengine/LayerSettings.h
+++ b/libs/renderengine/include/renderengine/LayerSettings.h
@@ -87,7 +87,7 @@
// rectangle to figure out how to apply the radius for this layer. The crop rectangle will be
// in local layer coordinate space, so we have to take the layer transform into account when
// walking up the tree.
- float roundedCornersRadius = 0.0;
+ vec2 roundedCornersRadius = vec2(0.0f, 0.0f);
// Rectangle within which corners will be rounded.
FloatRect roundedCornersCrop = FloatRect();
@@ -258,7 +258,8 @@
PrintTo(settings.boundaries, os);
*os << "\n .positionTransform = ";
PrintMatrix(settings.positionTransform, os);
- *os << "\n .roundedCornersRadius = " << settings.roundedCornersRadius;
+ *os << "\n .roundedCornersRadiusX = " << settings.roundedCornersRadius.x;
+ *os << "\n .roundedCornersRadiusY = " << settings.roundedCornersRadius.y;
*os << "\n .roundedCornersCrop = ";
PrintTo(settings.roundedCornersCrop, os);
*os << "\n}";
diff --git a/libs/renderengine/skia/Cache.cpp b/libs/renderengine/skia/Cache.cpp
index f3064f3..c39f0a9 100644
--- a/libs/renderengine/skia/Cache.cpp
+++ b/libs/renderengine/skia/Cache.cpp
@@ -66,7 +66,7 @@
Geometry{
.boundaries = rect,
.roundedCornersCrop = rect,
- .roundedCornersRadius = 50.f,
+ .roundedCornersRadius = {50.f, 50.f},
},
// drawShadow ignores alpha
.shadow =
@@ -87,7 +87,7 @@
Geometry{
.boundaries = smallerRect,
.roundedCornersCrop = rect,
- .roundedCornersRadius = 50.f,
+ .roundedCornersRadius = {50.f, 50.f},
},
.source =
PixelSource{
@@ -148,7 +148,7 @@
// In reduced shader mode, all non-zero round rect radii get the same code path.
for (float roundedCornersRadius : {0.0f, 50.0f}) {
// roundedCornersCrop is always set, but the radius triggers the behavior
- layer.geometry.roundedCornersRadius = roundedCornersRadius;
+ layer.geometry.roundedCornersRadius = {roundedCornersRadius, roundedCornersRadius};
for (bool isOpaque : {true, false}) {
layer.source.buffer.isOpaque = isOpaque;
for (auto alpha : {half(.2f), half(1.0f)}) {
@@ -181,7 +181,7 @@
for (auto transform : {mat4(), kScaleAndTranslate}) {
layer.geometry.positionTransform = transform;
for (float roundedCornersRadius : {0.0f, 50.f}) {
- layer.geometry.roundedCornersRadius = roundedCornersRadius;
+ layer.geometry.roundedCornersRadius = {roundedCornersRadius, roundedCornersRadius};
auto layers = std::vector<LayerSettings>{layer};
renderengine->drawLayers(display, layers, dstTexture, kUseFrameBufferCache,
base::unique_fd());
@@ -238,7 +238,7 @@
.geometry =
Geometry{
.boundaries = rect,
- .roundedCornersRadius = 27, // larger than the 20 above.
+ .roundedCornersRadius = {27.f, 27.f},
.roundedCornersCrop =
FloatRect(0, 0, displayRect.width(), displayRect.height()),
},
@@ -275,9 +275,9 @@
// larger than the layer bounds.
.positionTransform = kFlip,
.boundaries = rect,
- .roundedCornersRadius = 94.2551,
- .roundedCornersCrop = FloatRect(
- -93.75, 0, displayRect.width() + 93.75, displayRect.height()),
+ .roundedCornersRadius = {94.2551f, 94.2551f},
+ .roundedCornersCrop = FloatRect(-93.75, 0, displayRect.width() + 93.75,
+ displayRect.height()),
},
.source = PixelSource{.buffer =
Buffer{
@@ -307,10 +307,11 @@
// the boundaries have to be smaller than the rounded crop so that
// clipRRect is used instead of drawRRect
.boundaries = small,
- .roundedCornersRadius = 50.f,
+ .roundedCornersRadius = {50.f, 50.f},
.roundedCornersCrop = rect,
},
- .source = PixelSource{
+ .source =
+ PixelSource{
.solidColor = half3(0.f, 0.f, 0.f),
},
.sourceDataspace = kDestDataSpace,
diff --git a/libs/renderengine/skia/SkiaGLRenderEngine.cpp b/libs/renderengine/skia/SkiaGLRenderEngine.cpp
index 5187d7b..a0bba59 100644
--- a/libs/renderengine/skia/SkiaGLRenderEngine.cpp
+++ b/libs/renderengine/skia/SkiaGLRenderEngine.cpp
@@ -1307,7 +1307,7 @@
* produce the insected roundRect. If false, the returned state of the radii param is undefined.
*/
static bool intersectionIsRoundRect(const SkRect& bounds, const SkRect& crop,
- const SkRect& insetCrop, float cornerRadius,
+ const SkRect& insetCrop, const vec2& cornerRadius,
SkVector radii[4]) {
const bool leftEqual = bounds.fLeft == crop.fLeft;
const bool topEqual = bounds.fTop == crop.fTop;
@@ -1319,8 +1319,8 @@
// In particular the round rect implementation will scale the value of all corner radii
// if the sum of the radius along any edge is greater than the length of that edge.
// See https://www.w3.org/TR/css-backgrounds-3/#corner-overlap
- const bool requiredWidth = bounds.width() > (cornerRadius * 2);
- const bool requiredHeight = bounds.height() > (cornerRadius * 2);
+ const bool requiredWidth = bounds.width() > (cornerRadius.x * 2);
+ const bool requiredHeight = bounds.height() > (cornerRadius.y * 2);
if (!requiredWidth || !requiredHeight) {
return false;
}
@@ -1329,7 +1329,7 @@
// contained within the cropped shape and does not need rounded.
// compute the UpperLeft corner radius
if (leftEqual && topEqual) {
- radii[0].set(cornerRadius, cornerRadius);
+ radii[0].set(cornerRadius.x, cornerRadius.y);
} else if ((leftEqual && bounds.fTop >= insetCrop.fTop) ||
(topEqual && bounds.fLeft >= insetCrop.fLeft)) {
radii[0].set(0, 0);
@@ -1338,7 +1338,7 @@
}
// compute the UpperRight corner radius
if (rightEqual && topEqual) {
- radii[1].set(cornerRadius, cornerRadius);
+ radii[1].set(cornerRadius.x, cornerRadius.y);
} else if ((rightEqual && bounds.fTop >= insetCrop.fTop) ||
(topEqual && bounds.fRight <= insetCrop.fRight)) {
radii[1].set(0, 0);
@@ -1347,7 +1347,7 @@
}
// compute the BottomRight corner radius
if (rightEqual && bottomEqual) {
- radii[2].set(cornerRadius, cornerRadius);
+ radii[2].set(cornerRadius.x, cornerRadius.y);
} else if ((rightEqual && bounds.fBottom <= insetCrop.fBottom) ||
(bottomEqual && bounds.fRight <= insetCrop.fRight)) {
radii[2].set(0, 0);
@@ -1356,7 +1356,7 @@
}
// compute the BottomLeft corner radius
if (leftEqual && bottomEqual) {
- radii[3].set(cornerRadius, cornerRadius);
+ radii[3].set(cornerRadius.x, cornerRadius.y);
} else if ((leftEqual && bounds.fBottom <= insetCrop.fBottom) ||
(bottomEqual && bounds.fLeft >= insetCrop.fLeft)) {
radii[3].set(0, 0);
@@ -1369,22 +1369,22 @@
inline std::pair<SkRRect, SkRRect> SkiaGLRenderEngine::getBoundsAndClip(const FloatRect& boundsRect,
const FloatRect& cropRect,
- const float cornerRadius) {
+ const vec2& cornerRadius) {
const SkRect bounds = getSkRect(boundsRect);
const SkRect crop = getSkRect(cropRect);
SkRRect clip;
- if (cornerRadius > 0) {
+ if (cornerRadius.x > 0 && cornerRadius.y > 0) {
// it the crop and the bounds are equivalent or there is no crop then we don't need a clip
if (bounds == crop || crop.isEmpty()) {
- return {SkRRect::MakeRectXY(bounds, cornerRadius, cornerRadius), clip};
+ return {SkRRect::MakeRectXY(bounds, cornerRadius.x, cornerRadius.y), clip};
}
// This makes an effort to speed up common, simple bounds + clip combinations by
// converting them to a single RRect draw. It is possible there are other cases
// that can be converted.
if (crop.contains(bounds)) {
- const auto insetCrop = crop.makeInset(cornerRadius, cornerRadius);
+ const auto insetCrop = crop.makeInset(cornerRadius.x, cornerRadius.y);
if (insetCrop.contains(bounds)) {
return {SkRRect::MakeRect(bounds), clip}; // clip is empty - no rounding required
}
@@ -1398,7 +1398,7 @@
}
// we didn't hit any of our fast paths so set the clip to the cropRect
- clip.setRectXY(crop, cornerRadius, cornerRadius);
+ clip.setRectXY(crop, cornerRadius.x, cornerRadius.y);
}
// if we hit this point then we either don't have rounded corners or we are going to rely
diff --git a/libs/renderengine/skia/SkiaGLRenderEngine.h b/libs/renderengine/skia/SkiaGLRenderEngine.h
index a8bc4f7..34f18c1 100644
--- a/libs/renderengine/skia/SkiaGLRenderEngine.h
+++ b/libs/renderengine/skia/SkiaGLRenderEngine.h
@@ -94,7 +94,8 @@
inline SkRect getSkRect(const FloatRect& layer);
inline SkRect getSkRect(const Rect& layer);
inline std::pair<SkRRect, SkRRect> getBoundsAndClip(const FloatRect& bounds,
- const FloatRect& crop, float cornerRadius);
+ const FloatRect& crop,
+ const vec2& cornerRadius);
inline bool layerHasBlur(const LayerSettings& layer, bool colorTransformModifiesAlpha);
inline SkColor getSkColor(const vec4& color);
inline SkM44 getSkM44(const mat4& matrix);
diff --git a/libs/renderengine/tests/RenderEngineTest.cpp b/libs/renderengine/tests/RenderEngineTest.cpp
index 3340857..f289730 100644
--- a/libs/renderengine/tests/RenderEngineTest.cpp
+++ b/libs/renderengine/tests/RenderEngineTest.cpp
@@ -444,7 +444,9 @@
const ubyte4& backgroundColor) {
const Rect casterRect(castingLayer.geometry.boundaries);
Region casterRegion = Region(casterRect);
- const float casterCornerRadius = castingLayer.geometry.roundedCornersRadius;
+ const float casterCornerRadius = (castingLayer.geometry.roundedCornersRadius.x +
+ castingLayer.geometry.roundedCornersRadius.y) /
+ 2.0;
if (casterCornerRadius > 0.0f) {
// ignore the corners if a corner radius is set
Rect cornerRect(casterCornerRadius, casterCornerRadius);
@@ -1129,7 +1131,7 @@
renderengine::LayerSettings layer;
layer.sourceDataspace = ui::Dataspace::V0_SRGB_LINEAR;
layer.geometry.boundaries = fullscreenRect().toFloatRect();
- layer.geometry.roundedCornersRadius = 5.0f;
+ layer.geometry.roundedCornersRadius = {5.0f, 5.0f};
layer.geometry.roundedCornersCrop = fullscreenRect().toFloatRect();
SourceVariant::fillColor(layer, 1.0f, 0.0f, 0.0f, this);
layer.alpha = 1.0f;
@@ -2161,7 +2163,7 @@
casterBounds.offsetBy(shadowLength + 1, shadowLength + 1);
renderengine::LayerSettings castingLayer;
castingLayer.geometry.boundaries = casterBounds.toFloatRect();
- castingLayer.geometry.roundedCornersRadius = 3.0f;
+ castingLayer.geometry.roundedCornersRadius = {3.0f, 3.0f};
castingLayer.geometry.roundedCornersCrop = casterBounds.toFloatRect();
castingLayer.alpha = 1.0f;
renderengine::ShadowSettings settings =
@@ -2249,7 +2251,8 @@
renderengine::LayerSettings redLayer;
redLayer.sourceDataspace = ui::Dataspace::V0_SRGB_LINEAR;
redLayer.geometry.boundaries = fullscreenRect().toFloatRect();
- redLayer.geometry.roundedCornersRadius = 5.0f;
+ redLayer.geometry.roundedCornersRadius = {5.0f, 5.0f};
+
redLayer.geometry.roundedCornersCrop = fullscreenRect().toFloatRect();
// Red background.
redLayer.source.solidColor = half3(1.0f, 0.0f, 0.0f);
@@ -2261,7 +2264,7 @@
renderengine::LayerSettings greenLayer;
greenLayer.sourceDataspace = ui::Dataspace::V0_SRGB_LINEAR;
greenLayer.geometry.boundaries = fullscreenRect().toFloatRect();
- greenLayer.geometry.roundedCornersRadius = 5.0f;
+ greenLayer.geometry.roundedCornersRadius = {5.0f, 5.0f};
// Bottom right corner is not going to be rounded.
greenLayer.geometry.roundedCornersCrop =
Rect(DEFAULT_DISPLAY_WIDTH / 3, DEFAULT_DISPLAY_HEIGHT / 3, DEFAULT_DISPLAY_HEIGHT,
@@ -2298,7 +2301,7 @@
renderengine::LayerSettings redLayer;
redLayer.sourceDataspace = ui::Dataspace::V0_SRGB_LINEAR;
redLayer.geometry.boundaries = fullscreenRect().toFloatRect();
- redLayer.geometry.roundedCornersRadius = 5.0f;
+ redLayer.geometry.roundedCornersRadius = {5.0f, 5.0f};
redLayer.geometry.roundedCornersCrop = fullscreenRect().toFloatRect();
// Red background.
redLayer.source.solidColor = half3(1.0f, 0.0f, 0.0f);
@@ -2343,7 +2346,7 @@
renderengine::LayerSettings redLayer;
redLayer.sourceDataspace = ui::Dataspace::V0_SRGB_LINEAR;
redLayer.geometry.boundaries = FloatRect(0, 0, DEFAULT_DISPLAY_WIDTH, 32);
- redLayer.geometry.roundedCornersRadius = 64;
+ redLayer.geometry.roundedCornersRadius = {64.0f, 64.0f};
redLayer.geometry.roundedCornersCrop = FloatRect(0, 0, DEFAULT_DISPLAY_WIDTH, 128);
// Red background.
redLayer.source.solidColor = half3(1.0f, 0.0f, 0.0f);
@@ -2364,6 +2367,49 @@
expectBufferColor(Point(DEFAULT_DISPLAY_WIDTH / 2, 31), 255, 0, 0, 255);
}
+TEST_P(RenderEngineTest, testRoundedCornersXY) {
+ if (GetParam()->type() != renderengine::RenderEngine::RenderEngineType::SKIA_GL) {
+ GTEST_SKIP();
+ }
+
+ initializeRenderEngine();
+
+ renderengine::DisplaySettings settings;
+ settings.physicalDisplay = fullscreenRect();
+ settings.clip = fullscreenRect();
+ settings.outputDataspace = ui::Dataspace::V0_SRGB_LINEAR;
+
+ std::vector<renderengine::LayerSettings> layers;
+
+ renderengine::LayerSettings redLayer;
+ redLayer.sourceDataspace = ui::Dataspace::V0_SRGB_LINEAR;
+ redLayer.geometry.boundaries = fullscreenRect().toFloatRect();
+ redLayer.geometry.roundedCornersRadius = {5.0f, 20.0f};
+ redLayer.geometry.roundedCornersCrop = fullscreenRect().toFloatRect();
+ // Red background.
+ redLayer.source.solidColor = half3(1.0f, 0.0f, 0.0f);
+ redLayer.alpha = 1.0f;
+
+ layers.push_back(redLayer);
+
+ invokeDraw(settings, layers);
+
+ // Due to roundedCornersRadius, the corners are untouched.
+ expectBufferColor(Point(0, 0), 0, 0, 0, 0);
+ expectBufferColor(Point(DEFAULT_DISPLAY_WIDTH - 1, 0), 0, 0, 0, 0);
+ expectBufferColor(Point(0, DEFAULT_DISPLAY_HEIGHT - 1), 0, 0, 0, 0);
+ expectBufferColor(Point(DEFAULT_DISPLAY_WIDTH - 1, DEFAULT_DISPLAY_HEIGHT - 1), 0, 0, 0, 0);
+
+ // Y-axis draws a larger radius, check that its untouched as well
+ expectBufferColor(Point(0, DEFAULT_DISPLAY_HEIGHT - 5), 0, 0, 0, 0);
+ expectBufferColor(Point(DEFAULT_DISPLAY_WIDTH - 1, DEFAULT_DISPLAY_HEIGHT - 5), 0, 0, 0, 0);
+ expectBufferColor(Point(DEFAULT_DISPLAY_WIDTH - 1, 5), 0, 0, 0, 0);
+ expectBufferColor(Point(0, 5), 0, 0, 0, 0);
+
+ // middle should be red
+ expectBufferColor(Point(DEFAULT_DISPLAY_WIDTH / 2, DEFAULT_DISPLAY_HEIGHT / 2), 255, 0, 0, 255);
+}
+
TEST_P(RenderEngineTest, testClear) {
initializeRenderEngine();
diff --git a/services/audiomanager/IAudioManager.cpp b/services/audiomanager/IAudioManager.cpp
index ae1bb1a..700074a 100644
--- a/services/audiomanager/IAudioManager.cpp
+++ b/services/audiomanager/IAudioManager.cpp
@@ -87,12 +87,12 @@
}
virtual status_t playerEvent(audio_unique_id_t piid, player_state_t event,
- audio_port_handle_t deviceId) {
+ audio_port_handle_t eventId) {
Parcel data, reply;
data.writeInterfaceToken(IAudioManager::getInterfaceDescriptor());
data.writeInt32((int32_t) piid);
data.writeInt32((int32_t) event);
- data.writeInt32((int32_t) deviceId);
+ data.writeInt32((int32_t) eventId);
return remote()->transact(PLAYER_EVENT, data, &reply, IBinder::FLAG_ONEWAY);
}
diff --git a/services/gpuservice/gpuwork/Android.bp b/services/gpuservice/gpuwork/Android.bp
index a9a59bc..e204040 100644
--- a/services/gpuservice/gpuwork/Android.bp
+++ b/services/gpuservice/gpuwork/Android.bp
@@ -55,6 +55,6 @@
],
required: [
"bpfloader",
- "gpu_work.o",
+ "gpuWork.o",
],
}
diff --git a/services/gpuservice/gpuwork/GpuWork.cpp b/services/gpuservice/gpuwork/GpuWork.cpp
index 974ae38..fd70323 100644
--- a/services/gpuservice/gpuwork/GpuWork.cpp
+++ b/services/gpuservice/gpuwork/GpuWork.cpp
@@ -42,7 +42,7 @@
#include <unordered_set>
#include <vector>
-#include "gpuwork/gpu_work.h"
+#include "gpuwork/gpuWork.h"
#define ONE_MS_IN_NS (10000000)
@@ -128,11 +128,11 @@
{
std::lock_guard<std::mutex> lock(mMutex);
- if (!getBpfMap("/sys/fs/bpf/map_gpu_work_gpu_work_map", &mGpuWorkMap)) {
+ if (!getBpfMap("/sys/fs/bpf/map_gpuWork_gpu_work_map", &mGpuWorkMap)) {
return;
}
- if (!getBpfMap("/sys/fs/bpf/map_gpu_work_gpu_work_global_data", &mGpuWorkGlobalDataMap)) {
+ if (!getBpfMap("/sys/fs/bpf/map_gpuWork_gpu_work_global_data", &mGpuWorkGlobalDataMap)) {
return;
}
@@ -140,7 +140,7 @@
}
// Attach the tracepoint.
- if (!attachTracepoint("/sys/fs/bpf/prog_gpu_work_tracepoint_power_gpu_work_period", "power",
+ if (!attachTracepoint("/sys/fs/bpf/prog_gpuWork_tracepoint_power_gpu_work_period", "power",
"gpu_work_period")) {
return;
}
diff --git a/services/gpuservice/gpuwork/bpfprogs/Android.bp b/services/gpuservice/gpuwork/bpfprogs/Android.bp
index b3c4eff..fe45c98 100644
--- a/services/gpuservice/gpuwork/bpfprogs/Android.bp
+++ b/services/gpuservice/gpuwork/bpfprogs/Android.bp
@@ -17,8 +17,8 @@
}
bpf {
- name: "gpu_work.o",
- srcs: ["gpu_work.c"],
+ name: "gpuWork.o",
+ srcs: ["gpuWork.c"],
cflags: [
"-Wall",
"-Werror",
diff --git a/services/gpuservice/gpuwork/bpfprogs/gpu_work.c b/services/gpuservice/gpuwork/bpfprogs/gpuWork.c
similarity index 99%
rename from services/gpuservice/gpuwork/bpfprogs/gpu_work.c
rename to services/gpuservice/gpuwork/bpfprogs/gpuWork.c
index d73fff4..f470189 100644
--- a/services/gpuservice/gpuwork/bpfprogs/gpu_work.c
+++ b/services/gpuservice/gpuwork/bpfprogs/gpuWork.c
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-#include "include/gpuwork/gpu_work.h"
+#include "include/gpuwork/gpuWork.h"
#include <linux/bpf.h>
#include <stddef.h>
diff --git a/services/gpuservice/gpuwork/bpfprogs/include/gpuwork/gpu_work.h b/services/gpuservice/gpuwork/bpfprogs/include/gpuwork/gpuWork.h
similarity index 100%
rename from services/gpuservice/gpuwork/bpfprogs/include/gpuwork/gpu_work.h
rename to services/gpuservice/gpuwork/bpfprogs/include/gpuwork/gpuWork.h
diff --git a/services/gpuservice/gpuwork/include/gpuwork/GpuWork.h b/services/gpuservice/gpuwork/include/gpuwork/GpuWork.h
index acecd56..cece999 100644
--- a/services/gpuservice/gpuwork/include/gpuwork/GpuWork.h
+++ b/services/gpuservice/gpuwork/include/gpuwork/GpuWork.h
@@ -27,7 +27,7 @@
#include <functional>
#include <thread>
-#include "gpuwork/gpu_work.h"
+#include "gpuwork/gpuWork.h"
namespace android {
namespace gpuwork {
diff --git a/services/inputflinger/UnwantedInteractionBlocker.cpp b/services/inputflinger/UnwantedInteractionBlocker.cpp
index 3ee60a9..a8e495f 100644
--- a/services/inputflinger/UnwantedInteractionBlocker.cpp
+++ b/services/inputflinger/UnwantedInteractionBlocker.cpp
@@ -29,6 +29,14 @@
using android::base::StringPrintf;
+/**
+ * This type is declared here to ensure consistency between the instantiated type (used in the
+ * constructor via std::make_unique) and the cast-to type (used in PalmRejector::dump() with
+ * static_cast). Due to the lack of rtti support, dynamic_cast is not available, so this can't be
+ * checked at runtime to avoid undefined behaviour.
+ */
+using PalmFilterImplementation = ::ui::NeuralStylusPalmDetectionFilter;
+
namespace android {
// Category (=namespace) name for the input settings that are applied at boot time
@@ -81,21 +89,6 @@
return MT_TOOL_FINGER;
}
-static std::string dumpDeviceInfo(const AndroidPalmFilterDeviceInfo& info) {
- std::string out;
- out += StringPrintf("max_x = %.2f\n", info.max_x);
- out += StringPrintf("max_y = %.2f\n", info.max_y);
- out += StringPrintf("x_res = %.2f\n", info.x_res);
- out += StringPrintf("y_res = %.2f\n", info.y_res);
- out += StringPrintf("major_radius_res = %.2f\n", info.major_radius_res);
- out += StringPrintf("minor_radius_res = %.2f\n", info.minor_radius_res);
- out += StringPrintf("minor_radius_supported = %s\n",
- info.minor_radius_supported ? "true" : "false");
- out += StringPrintf("touch_major_res = %" PRId32 "\n", info.touch_major_res);
- out += StringPrintf("touch_minor_res = %" PRId32 "\n", info.touch_minor_res);
- return out;
-}
-
static int32_t getActionUpForPointerId(const NotifyMotionArgs& args, int32_t pointerId) {
for (size_t i = 0; i < args.pointerCount; i++) {
if (pointerId == args.pointerProperties[i].id) {
@@ -128,15 +121,6 @@
return AMOTION_EVENT_ACTION_MOVE;
}
-std::string toString(const ::ui::InProgressTouchEvdev& touch) {
- return StringPrintf("x=%.1f, y=%.1f, tracking_id=%i, slot=%zu,"
- " pressure=%.1f, major=%i, minor=%i, "
- "tool_type=%i, altered=%s, was_touching=%s, touching=%s",
- touch.x, touch.y, touch.tracking_id, touch.slot, touch.pressure,
- touch.major, touch.minor, touch.tool_type, toString(touch.altered),
- toString(touch.was_touching), toString(touch.touching));
-}
-
/**
* Remove the data for the provided pointers from the args. The pointers are identified by their
* pointerId, not by the index inside the array.
@@ -428,9 +412,10 @@
dump += "UnwantedInteractionBlocker:\n";
dump += " mPreferStylusOverTouchBlocker:\n";
dump += addLinePrefix(mPreferStylusOverTouchBlocker.dump(), " ");
- dump += StringPrintf(" mEnablePalmRejection: %s\n", toString(mEnablePalmRejection));
+ dump += StringPrintf(" mEnablePalmRejection: %s\n",
+ std::to_string(mEnablePalmRejection).c_str());
dump += StringPrintf(" isPalmRejectionEnabled (flag value): %s\n",
- toString(isPalmRejectionEnabled()));
+ std::to_string(isPalmRejectionEnabled()).c_str());
dump += mPalmRejectors.empty() ? " mPalmRejectors: None\n" : " mPalmRejectors:\n";
for (const auto& [deviceId, palmRejector] : mPalmRejectors) {
dump += StringPrintf(" deviceId = %" PRId32 ":\n", deviceId);
@@ -517,7 +502,7 @@
AndroidPalmRejectionModel()
: ::ui::OneDeviceTrainNeuralStylusPalmDetectionFilterModel(/*default version*/ "",
std::vector<float>()) {
- config_.resample_touch = true;
+ config_.resample_period = ::ui::kResamplePeriod;
}
};
@@ -533,9 +518,8 @@
}
std::unique_ptr<::ui::NeuralStylusPalmDetectionFilterModel> model =
std::make_unique<AndroidPalmRejectionModel>();
- mPalmDetectionFilter =
- std::make_unique<::ui::NeuralStylusPalmDetectionFilter>(mDeviceInfo, std::move(model),
- mSharedPalmState.get());
+ mPalmDetectionFilter = std::make_unique<PalmFilterImplementation>(mDeviceInfo, std::move(model),
+ mSharedPalmState.get());
}
std::vector<::ui::InProgressTouchEvdev> getTouches(const NotifyMotionArgs& args,
@@ -656,12 +640,13 @@
LOG_ALWAYS_FATAL_IF(checkArgs.action == ACTION_UNKNOWN, "%s", checkArgs.dump().c_str());
}
- if (mSuppressedPointerIds != oldSuppressedIds) {
- if (argsWithoutUnwantedPointers.size() != 1 ||
- argsWithoutUnwantedPointers[0].pointerCount != args.pointerCount) {
- ALOGI("Palm detected, removing pointer ids %s from %s",
- dumpSet(mSuppressedPointerIds).c_str(), args.dump().c_str());
- }
+ // Only log if new pointers are getting rejected. That means mSuppressedPointerIds is not a
+ // subset of oldSuppressedIds.
+ if (!std::includes(oldSuppressedIds.begin(), oldSuppressedIds.end(),
+ mSuppressedPointerIds.begin(), mSuppressedPointerIds.end())) {
+ ALOGI("Palm detected, removing pointer ids %s after %" PRId64 "ms from %s",
+ dumpSet(mSuppressedPointerIds).c_str(), ns2ms(args.eventTime - args.downTime),
+ args.dump().c_str());
}
return argsWithoutUnwantedPointers;
@@ -674,11 +659,21 @@
std::string PalmRejector::dump() const {
std::string out;
out += "mDeviceInfo:\n";
- out += addLinePrefix(dumpDeviceInfo(mDeviceInfo), " ");
+ std::stringstream deviceInfo;
+ deviceInfo << mDeviceInfo << ", touch_major_res=" << mDeviceInfo.touch_major_res
+ << ", touch_minor_res=" << mDeviceInfo.touch_minor_res << "\n";
+ out += addLinePrefix(deviceInfo.str(), " ");
out += "mSlotState:\n";
out += addLinePrefix(mSlotState.dump(), " ");
out += "mSuppressedPointerIds: ";
out += dumpSet(mSuppressedPointerIds) + "\n";
+ std::stringstream state;
+ state << *mSharedPalmState;
+ out += "mSharedPalmState: " + state.str() + "\n";
+ std::stringstream filter;
+ filter << static_cast<const PalmFilterImplementation&>(*mPalmDetectionFilter);
+ out += "mPalmDetectionFilter:\n";
+ out += addLinePrefix(filter.str(), " ") + "\n";
return out;
}
diff --git a/services/inputflinger/benchmarks/InputDispatcher_benchmarks.cpp b/services/inputflinger/benchmarks/InputDispatcher_benchmarks.cpp
index ec5841c..c9ea068 100644
--- a/services/inputflinger/benchmarks/InputDispatcher_benchmarks.cpp
+++ b/services/inputflinger/benchmarks/InputDispatcher_benchmarks.cpp
@@ -112,6 +112,8 @@
void notifyDropWindow(const sp<IBinder>&, float x, float y) override {}
+ bool isPerDisplayTouchModeEnabled() override { return false; }
+
InputDispatcherConfiguration mConfig;
};
diff --git a/services/inputflinger/dispatcher/InputDispatcher.cpp b/services/inputflinger/dispatcher/InputDispatcher.cpp
index 625c367..bd55216 100644
--- a/services/inputflinger/dispatcher/InputDispatcher.cpp
+++ b/services/inputflinger/dispatcher/InputDispatcher.cpp
@@ -553,7 +553,8 @@
mWindowTokenWithPointerCapture(nullptr),
mStaleEventTimeout(staleEventTimeout),
mLatencyAggregator(),
- mLatencyTracker(&mLatencyAggregator) {
+ mLatencyTracker(&mLatencyAggregator),
+ kPerDisplayTouchModeEnabled(mPolicy->isPerDisplayTouchModeEnabled()) {
mLooper = new Looper(false);
mReporter = createInputReporter();
@@ -4987,8 +4988,8 @@
mLooper->wake();
}
-bool InputDispatcher::setInTouchMode(bool inTouchMode, int32_t pid, int32_t uid,
- bool hasPermission) {
+bool InputDispatcher::setInTouchMode(bool inTouchMode, int32_t pid, int32_t uid, bool hasPermission,
+ int32_t displayId) {
bool needWake = false;
{
std::scoped_lock lock(mLock);
@@ -4997,8 +4998,9 @@
}
if (DEBUG_TOUCH_MODE) {
ALOGD("Request to change touch mode from %s to %s (calling pid=%d, uid=%d, "
- "hasPermission=%s)",
- toString(mInTouchMode), toString(inTouchMode), pid, uid, toString(hasPermission));
+ "hasPermission=%s, target displayId=%d, perDisplayTouchModeEnabled=%s)",
+ toString(mInTouchMode), toString(inTouchMode), pid, uid, toString(hasPermission),
+ displayId, toString(kPerDisplayTouchModeEnabled));
}
if (!hasPermission) {
if (!focusedWindowIsOwnedByLocked(pid, uid) &&
@@ -5010,7 +5012,7 @@
}
}
- // TODO(b/198499018): Store touch mode per display.
+ // TODO(b/198499018): Store touch mode per display (kPerDisplayTouchModeEnabled)
mInTouchMode = inTouchMode;
auto entry = std::make_unique<TouchModeEntry>(mIdGenerator.nextId(), now(), inTouchMode);
diff --git a/services/inputflinger/dispatcher/InputDispatcher.h b/services/inputflinger/dispatcher/InputDispatcher.h
index da4af48..be619ae 100644
--- a/services/inputflinger/dispatcher/InputDispatcher.h
+++ b/services/inputflinger/dispatcher/InputDispatcher.h
@@ -119,7 +119,8 @@
void setFocusedDisplay(int32_t displayId) override;
void setInputDispatchMode(bool enabled, bool frozen) override;
void setInputFilterEnabled(bool enabled) override;
- bool setInTouchMode(bool inTouchMode, int32_t pid, int32_t uid, bool hasPermission) override;
+ bool setInTouchMode(bool inTouchMode, int32_t pid, int32_t uid, bool hasPermission,
+ int32_t displayId) override;
void setMaximumObscuringOpacityForTouch(float opacity) override;
bool transferTouchFocus(const sp<IBinder>& fromToken, const sp<IBinder>& toToken,
@@ -685,6 +686,9 @@
bool focusedWindowIsOwnedByLocked(int32_t pid, int32_t uid) REQUIRES(mLock);
bool recentWindowsAreOwnedByLocked(int32_t pid, int32_t uid) REQUIRES(mLock);
+ // Per display touch mode enabled
+ const bool kPerDisplayTouchModeEnabled;
+
sp<InputReporterInterface> mReporter;
};
diff --git a/services/inputflinger/dispatcher/include/InputDispatcherInterface.h b/services/inputflinger/dispatcher/include/InputDispatcherInterface.h
index 84d9007..32b3ddb 100644
--- a/services/inputflinger/dispatcher/include/InputDispatcherInterface.h
+++ b/services/inputflinger/dispatcher/include/InputDispatcherInterface.h
@@ -132,7 +132,8 @@
*
* Returns true when changing touch mode state.
*/
- virtual bool setInTouchMode(bool inTouchMode, int32_t pid, int32_t uid, bool hasPermission) = 0;
+ virtual bool setInTouchMode(bool inTouchMode, int32_t pid, int32_t uid, bool hasPermission,
+ int32_t displayId) = 0;
/**
* Sets the maximum allowed obscuring opacity by UID to propagate touches.
diff --git a/services/inputflinger/dispatcher/include/InputDispatcherPolicyInterface.h b/services/inputflinger/dispatcher/include/InputDispatcherPolicyInterface.h
index 6ee05da..7c299b2 100644
--- a/services/inputflinger/dispatcher/include/InputDispatcherPolicyInterface.h
+++ b/services/inputflinger/dispatcher/include/InputDispatcherPolicyInterface.h
@@ -137,6 +137,9 @@
/* Notifies the policy that the drag window has moved over to another window */
virtual void notifyDropWindow(const sp<IBinder>& token, float x, float y) = 0;
+
+ /* If touch mode is enabled per display or global */
+ virtual bool isPerDisplayTouchModeEnabled() = 0;
};
} // namespace android
diff --git a/services/inputflinger/host/InputDriver.cpp b/services/inputflinger/host/InputDriver.cpp
index 2ebdbcf..97d57e4 100644
--- a/services/inputflinger/host/InputDriver.cpp
+++ b/services/inputflinger/host/InputDriver.cpp
@@ -240,19 +240,16 @@
return nullptr;
}
-input_property_t* InputDriver::inputGetDeviceProperty(input_property_map_t* map,
- const char* key) {
- String8 keyString(key);
+input_property_t* InputDriver::inputGetDeviceProperty(input_property_map_t* map, const char* key) {
if (map != nullptr) {
- if (map->propertyMap->hasProperty(keyString)) {
- auto prop = new input_property_t();
- if (!map->propertyMap->tryGetProperty(keyString, prop->value)) {
- delete prop;
- return nullptr;
- }
- prop->key = keyString;
- return prop;
+ std::string value;
+ auto prop = std::make_unique<input_property_t>();
+ if (!map->propertyMap->tryGetProperty(key, value)) {
+ return nullptr;
}
+ prop->key = key;
+ prop->value = value.c_str();
+ return prop.release();
}
return nullptr;
}
diff --git a/services/inputflinger/reader/EventHub.cpp b/services/inputflinger/reader/EventHub.cpp
index fe4d443..a17d2c0 100644
--- a/services/inputflinger/reader/EventHub.cpp
+++ b/services/inputflinger/reader/EventHub.cpp
@@ -502,7 +502,7 @@
bool EventHub::Device::isExternalDeviceLocked() {
if (configuration) {
bool value;
- if (configuration->tryGetProperty(String8("device.internal"), value)) {
+ if (configuration->tryGetProperty("device.internal", value)) {
return !value;
}
}
@@ -512,7 +512,7 @@
bool EventHub::Device::deviceHasMicLocked() {
if (configuration) {
bool value;
- if (configuration->tryGetProperty(String8("audio.mic"), value)) {
+ if (configuration->tryGetProperty("audio.mic", value)) {
return value;
}
}
@@ -2076,10 +2076,9 @@
}
// See if this is a rotary encoder type device.
- String8 deviceType = String8();
- if (device->configuration &&
- device->configuration->tryGetProperty(String8("device.type"), deviceType)) {
- if (!deviceType.compare(String8("rotaryEncoder"))) {
+ std::string deviceType;
+ if (device->configuration && device->configuration->tryGetProperty("device.type", deviceType)) {
+ if (deviceType == "rotaryEncoder") {
device->classes |= InputDeviceClass::ROTARY_ENCODER;
}
}
diff --git a/services/inputflinger/reader/include/EventHub.h b/services/inputflinger/reader/include/EventHub.h
index b2d50fc..79188aa 100644
--- a/services/inputflinger/reader/include/EventHub.h
+++ b/services/inputflinger/reader/include/EventHub.h
@@ -36,7 +36,6 @@
#include <sys/epoll.h>
#include <utils/BitSet.h>
#include <utils/Errors.h>
-#include <utils/KeyedVector.h>
#include <utils/List.h>
#include <utils/Log.h>
#include <utils/Mutex.h>
diff --git a/services/inputflinger/reader/mapper/CursorInputMapper.cpp b/services/inputflinger/reader/mapper/CursorInputMapper.cpp
index 91dc619..baa6007 100644
--- a/services/inputflinger/reader/mapper/CursorInputMapper.cpp
+++ b/services/inputflinger/reader/mapper/CursorInputMapper.cpp
@@ -225,18 +225,17 @@
void CursorInputMapper::configureParameters() {
mParameters.mode = Parameters::Mode::POINTER;
- String8 cursorModeString;
- if (getDeviceContext().getConfiguration().tryGetProperty(String8("cursor.mode"),
- cursorModeString)) {
+ std::string cursorModeString;
+ if (getDeviceContext().getConfiguration().tryGetProperty("cursor.mode", cursorModeString)) {
if (cursorModeString == "navigation") {
mParameters.mode = Parameters::Mode::NAVIGATION;
} else if (cursorModeString != "pointer" && cursorModeString != "default") {
- ALOGW("Invalid value for cursor.mode: '%s'", cursorModeString.string());
+ ALOGW("Invalid value for cursor.mode: '%s'", cursorModeString.c_str());
}
}
mParameters.orientationAware = false;
- getDeviceContext().getConfiguration().tryGetProperty(String8("cursor.orientationAware"),
+ getDeviceContext().getConfiguration().tryGetProperty("cursor.orientationAware",
mParameters.orientationAware);
mParameters.hasAssociatedDisplay = false;
diff --git a/services/inputflinger/reader/mapper/KeyboardInputMapper.cpp b/services/inputflinger/reader/mapper/KeyboardInputMapper.cpp
index 8eb870f..6a406d2 100644
--- a/services/inputflinger/reader/mapper/KeyboardInputMapper.cpp
+++ b/services/inputflinger/reader/mapper/KeyboardInputMapper.cpp
@@ -160,7 +160,7 @@
static void mapStemKey(int32_t keyCode, const PropertyMap& config, char const* property) {
int32_t mapped = 0;
- if (config.tryGetProperty(String8(property), mapped) && mapped > 0) {
+ if (config.tryGetProperty(property, mapped) && mapped > 0) {
for (size_t i = 0; i < stemKeyRotationMapSize; i++) {
if (stemKeyRotationMap[i][0] == keyCode) {
stemKeyRotationMap[i][1] = mapped;
@@ -173,7 +173,7 @@
void KeyboardInputMapper::configureParameters() {
mParameters.orientationAware = false;
const PropertyMap& config = getDeviceContext().getConfiguration();
- config.tryGetProperty(String8("keyboard.orientationAware"), mParameters.orientationAware);
+ config.tryGetProperty("keyboard.orientationAware", mParameters.orientationAware);
if (mParameters.orientationAware) {
mapStemKey(AKEYCODE_STEM_PRIMARY, config, "keyboard.rotated.stem_primary");
@@ -183,10 +183,10 @@
}
mParameters.handlesKeyRepeat = false;
- config.tryGetProperty(String8("keyboard.handlesKeyRepeat"), mParameters.handlesKeyRepeat);
+ config.tryGetProperty("keyboard.handlesKeyRepeat", mParameters.handlesKeyRepeat);
mParameters.doNotWakeByDefault = false;
- config.tryGetProperty(String8("keyboard.doNotWakeByDefault"), mParameters.doNotWakeByDefault);
+ config.tryGetProperty("keyboard.doNotWakeByDefault", mParameters.doNotWakeByDefault);
}
void KeyboardInputMapper::dumpParameters(std::string& dump) {
diff --git a/services/inputflinger/reader/mapper/RotaryEncoderInputMapper.cpp b/services/inputflinger/reader/mapper/RotaryEncoderInputMapper.cpp
index eca25f6..05973f7 100644
--- a/services/inputflinger/reader/mapper/RotaryEncoderInputMapper.cpp
+++ b/services/inputflinger/reader/mapper/RotaryEncoderInputMapper.cpp
@@ -40,10 +40,10 @@
if (mRotaryEncoderScrollAccumulator.haveRelativeVWheel()) {
float res = 0.0f;
- if (!getDeviceContext().getConfiguration().tryGetProperty(String8("device.res"), res)) {
+ if (!getDeviceContext().getConfiguration().tryGetProperty("device.res", res)) {
ALOGW("Rotary Encoder device configuration file didn't specify resolution!\n");
}
- if (!getDeviceContext().getConfiguration().tryGetProperty(String8("device.scalingFactor"),
+ if (!getDeviceContext().getConfiguration().tryGetProperty("device.scalingFactor",
mScalingFactor)) {
ALOGW("Rotary Encoder device configuration file didn't specify scaling factor,"
"default to 1.0!\n");
diff --git a/services/inputflinger/reader/mapper/SensorInputMapper.cpp b/services/inputflinger/reader/mapper/SensorInputMapper.cpp
index b01c2bc..573f99c 100644
--- a/services/inputflinger/reader/mapper/SensorInputMapper.cpp
+++ b/services/inputflinger/reader/mapper/SensorInputMapper.cpp
@@ -64,7 +64,7 @@
template <typename T>
bool SensorInputMapper::tryGetProperty(std::string keyName, T& outValue) {
const auto& config = getDeviceContext().getConfiguration();
- return config.tryGetProperty(String8(keyName.c_str()), outValue);
+ return config.tryGetProperty(keyName, outValue);
}
void SensorInputMapper::parseSensorConfiguration(InputDeviceSensorType sensorType, int32_t absCode,
diff --git a/services/inputflinger/reader/mapper/TouchInputMapper.cpp b/services/inputflinger/reader/mapper/TouchInputMapper.cpp
index f07a3e8..c1111f2 100644
--- a/services/inputflinger/reader/mapper/TouchInputMapper.cpp
+++ b/services/inputflinger/reader/mapper/TouchInputMapper.cpp
@@ -42,6 +42,8 @@
// data.
static constexpr nsecs_t STYLUS_DATA_LATENCY = ms2ns(10);
+// Minimum width between two pointers to determine a gesture as freeform gesture in mm
+static const float MIN_FREEFORM_GESTURE_WIDTH_IN_MILLIMETER = 30;
// --- Static Definitions ---
template <typename T>
@@ -204,30 +206,30 @@
y.fuzz, y.resolution);
}
- if (mOrientedRanges.haveSize) {
- info->addMotionRange(mOrientedRanges.size);
+ if (mOrientedRanges.size) {
+ info->addMotionRange(*mOrientedRanges.size);
}
- if (mOrientedRanges.haveTouchSize) {
- info->addMotionRange(mOrientedRanges.touchMajor);
- info->addMotionRange(mOrientedRanges.touchMinor);
+ if (mOrientedRanges.touchMajor) {
+ info->addMotionRange(*mOrientedRanges.touchMajor);
+ info->addMotionRange(*mOrientedRanges.touchMinor);
}
- if (mOrientedRanges.haveToolSize) {
- info->addMotionRange(mOrientedRanges.toolMajor);
- info->addMotionRange(mOrientedRanges.toolMinor);
+ if (mOrientedRanges.toolMajor) {
+ info->addMotionRange(*mOrientedRanges.toolMajor);
+ info->addMotionRange(*mOrientedRanges.toolMinor);
}
- if (mOrientedRanges.haveOrientation) {
- info->addMotionRange(mOrientedRanges.orientation);
+ if (mOrientedRanges.orientation) {
+ info->addMotionRange(*mOrientedRanges.orientation);
}
- if (mOrientedRanges.haveDistance) {
- info->addMotionRange(mOrientedRanges.distance);
+ if (mOrientedRanges.distance) {
+ info->addMotionRange(*mOrientedRanges.distance);
}
- if (mOrientedRanges.haveTilt) {
- info->addMotionRange(mOrientedRanges.tilt);
+ if (mOrientedRanges.tilt) {
+ info->addMotionRange(*mOrientedRanges.tilt);
}
if (mCursorScrollAccumulator.haveRelativeVWheel()) {
@@ -415,15 +417,15 @@
? Parameters::GestureMode::SINGLE_TOUCH
: Parameters::GestureMode::MULTI_TOUCH;
- String8 gestureModeString;
- if (getDeviceContext().getConfiguration().tryGetProperty(String8("touch.gestureMode"),
+ std::string gestureModeString;
+ if (getDeviceContext().getConfiguration().tryGetProperty("touch.gestureMode",
gestureModeString)) {
if (gestureModeString == "single-touch") {
mParameters.gestureMode = Parameters::GestureMode::SINGLE_TOUCH;
} else if (gestureModeString == "multi-touch") {
mParameters.gestureMode = Parameters::GestureMode::MULTI_TOUCH;
} else if (gestureModeString != "default") {
- ALOGW("Invalid value for touch.gestureMode: '%s'", gestureModeString.string());
+ ALOGW("Invalid value for touch.gestureMode: '%s'", gestureModeString.c_str());
}
}
@@ -445,8 +447,8 @@
mParameters.hasButtonUnderPad = getDeviceContext().hasInputProperty(INPUT_PROP_BUTTONPAD);
- String8 deviceTypeString;
- if (getDeviceContext().getConfiguration().tryGetProperty(String8("touch.deviceType"),
+ std::string deviceTypeString;
+ if (getDeviceContext().getConfiguration().tryGetProperty("touch.deviceType",
deviceTypeString)) {
if (deviceTypeString == "touchScreen") {
mParameters.deviceType = Parameters::DeviceType::TOUCH_SCREEN;
@@ -457,17 +459,17 @@
} else if (deviceTypeString == "pointer") {
mParameters.deviceType = Parameters::DeviceType::POINTER;
} else if (deviceTypeString != "default") {
- ALOGW("Invalid value for touch.deviceType: '%s'", deviceTypeString.string());
+ ALOGW("Invalid value for touch.deviceType: '%s'", deviceTypeString.c_str());
}
}
mParameters.orientationAware = mParameters.deviceType == Parameters::DeviceType::TOUCH_SCREEN;
- getDeviceContext().getConfiguration().tryGetProperty(String8("touch.orientationAware"),
+ getDeviceContext().getConfiguration().tryGetProperty("touch.orientationAware",
mParameters.orientationAware);
mParameters.orientation = Parameters::Orientation::ORIENTATION_0;
- String8 orientationString;
- if (getDeviceContext().getConfiguration().tryGetProperty(String8("touch.orientation"),
+ std::string orientationString;
+ if (getDeviceContext().getConfiguration().tryGetProperty("touch.orientation",
orientationString)) {
if (mParameters.deviceType != Parameters::DeviceType::TOUCH_SCREEN) {
ALOGW("The configuration 'touch.orientation' is only supported for touchscreens.");
@@ -478,7 +480,7 @@
} else if (orientationString == "ORIENTATION_270") {
mParameters.orientation = Parameters::Orientation::ORIENTATION_270;
} else if (orientationString != "ORIENTATION_0") {
- ALOGW("Invalid value for touch.orientation: '%s'", orientationString.string());
+ ALOGW("Invalid value for touch.orientation: '%s'", orientationString.c_str());
}
}
@@ -490,8 +492,8 @@
mParameters.hasAssociatedDisplay = true;
if (mParameters.deviceType == Parameters::DeviceType::TOUCH_SCREEN) {
mParameters.associatedDisplayIsExternal = getDeviceContext().isExternal();
- String8 uniqueDisplayId;
- getDeviceContext().getConfiguration().tryGetProperty(String8("touch.displayId"),
+ std::string uniqueDisplayId;
+ getDeviceContext().getConfiguration().tryGetProperty("touch.displayId",
uniqueDisplayId);
mParameters.uniqueDisplayId = uniqueDisplayId.c_str();
}
@@ -504,7 +506,7 @@
// Normally we don't do this for internal touch screens to prevent them from waking
// up in your pocket but you can enable it using the input device configuration.
mParameters.wake = getDeviceContext().isExternal();
- getDeviceContext().getConfiguration().tryGetProperty(String8("touch.wake"), mParameters.wake);
+ getDeviceContext().getConfiguration().tryGetProperty("touch.wake", mParameters.wake);
}
void TouchInputMapper::dumpParameters(std::string& dump) {
@@ -639,57 +641,58 @@
mSizeScale = 0.0f;
}
- mOrientedRanges.haveTouchSize = true;
- mOrientedRanges.haveToolSize = true;
- mOrientedRanges.haveSize = true;
+ mOrientedRanges.touchMajor = InputDeviceInfo::MotionRange{
+ .axis = AMOTION_EVENT_AXIS_TOUCH_MAJOR,
+ .source = mSource,
+ .min = 0,
+ .max = diagonalSize,
+ .flat = 0,
+ .fuzz = 0,
+ .resolution = 0,
+ };
- mOrientedRanges.touchMajor.axis = AMOTION_EVENT_AXIS_TOUCH_MAJOR;
- mOrientedRanges.touchMajor.source = mSource;
- mOrientedRanges.touchMajor.min = 0;
- mOrientedRanges.touchMajor.max = diagonalSize;
- mOrientedRanges.touchMajor.flat = 0;
- mOrientedRanges.touchMajor.fuzz = 0;
- mOrientedRanges.touchMajor.resolution = 0;
if (mRawPointerAxes.touchMajor.valid) {
mRawPointerAxes.touchMajor.resolution =
clampResolution("touchMajor", mRawPointerAxes.touchMajor.resolution);
- mOrientedRanges.touchMajor.resolution = mRawPointerAxes.touchMajor.resolution;
+ mOrientedRanges.touchMajor->resolution = mRawPointerAxes.touchMajor.resolution;
}
mOrientedRanges.touchMinor = mOrientedRanges.touchMajor;
- mOrientedRanges.touchMinor.axis = AMOTION_EVENT_AXIS_TOUCH_MINOR;
+ mOrientedRanges.touchMinor->axis = AMOTION_EVENT_AXIS_TOUCH_MINOR;
if (mRawPointerAxes.touchMinor.valid) {
mRawPointerAxes.touchMinor.resolution =
clampResolution("touchMinor", mRawPointerAxes.touchMinor.resolution);
- mOrientedRanges.touchMinor.resolution = mRawPointerAxes.touchMinor.resolution;
+ mOrientedRanges.touchMinor->resolution = mRawPointerAxes.touchMinor.resolution;
}
- mOrientedRanges.toolMajor.axis = AMOTION_EVENT_AXIS_TOOL_MAJOR;
- mOrientedRanges.toolMajor.source = mSource;
- mOrientedRanges.toolMajor.min = 0;
- mOrientedRanges.toolMajor.max = diagonalSize;
- mOrientedRanges.toolMajor.flat = 0;
- mOrientedRanges.toolMajor.fuzz = 0;
- mOrientedRanges.toolMajor.resolution = 0;
+ mOrientedRanges.toolMajor = InputDeviceInfo::MotionRange{
+ .axis = AMOTION_EVENT_AXIS_TOOL_MAJOR,
+ .source = mSource,
+ .min = 0,
+ .max = diagonalSize,
+ .flat = 0,
+ .fuzz = 0,
+ .resolution = 0,
+ };
if (mRawPointerAxes.toolMajor.valid) {
mRawPointerAxes.toolMajor.resolution =
clampResolution("toolMajor", mRawPointerAxes.toolMajor.resolution);
- mOrientedRanges.toolMajor.resolution = mRawPointerAxes.toolMajor.resolution;
+ mOrientedRanges.toolMajor->resolution = mRawPointerAxes.toolMajor.resolution;
}
mOrientedRanges.toolMinor = mOrientedRanges.toolMajor;
- mOrientedRanges.toolMinor.axis = AMOTION_EVENT_AXIS_TOOL_MINOR;
+ mOrientedRanges.toolMinor->axis = AMOTION_EVENT_AXIS_TOOL_MINOR;
if (mRawPointerAxes.toolMinor.valid) {
mRawPointerAxes.toolMinor.resolution =
clampResolution("toolMinor", mRawPointerAxes.toolMinor.resolution);
- mOrientedRanges.toolMinor.resolution = mRawPointerAxes.toolMinor.resolution;
+ mOrientedRanges.toolMinor->resolution = mRawPointerAxes.toolMinor.resolution;
}
if (mCalibration.sizeCalibration == Calibration::SizeCalibration::GEOMETRIC) {
- mOrientedRanges.touchMajor.resolution *= mGeometricScale;
- mOrientedRanges.touchMinor.resolution *= mGeometricScale;
- mOrientedRanges.toolMajor.resolution *= mGeometricScale;
- mOrientedRanges.toolMinor.resolution *= mGeometricScale;
+ mOrientedRanges.touchMajor->resolution *= mGeometricScale;
+ mOrientedRanges.touchMinor->resolution *= mGeometricScale;
+ mOrientedRanges.toolMajor->resolution *= mGeometricScale;
+ mOrientedRanges.toolMinor->resolution *= mGeometricScale;
} else {
// Support for other calibrations can be added here.
ALOGW("%s calibration is not supported for size ranges at the moment. "
@@ -697,13 +700,15 @@
ftl::enum_string(mCalibration.sizeCalibration).c_str());
}
- mOrientedRanges.size.axis = AMOTION_EVENT_AXIS_SIZE;
- mOrientedRanges.size.source = mSource;
- mOrientedRanges.size.min = 0;
- mOrientedRanges.size.max = 1.0;
- mOrientedRanges.size.flat = 0;
- mOrientedRanges.size.fuzz = 0;
- mOrientedRanges.size.resolution = 0;
+ mOrientedRanges.size = InputDeviceInfo::MotionRange{
+ .axis = AMOTION_EVENT_AXIS_SIZE,
+ .source = mSource,
+ .min = 0,
+ .max = 1.0,
+ .flat = 0,
+ .fuzz = 0,
+ .resolution = 0,
+ };
}
void TouchInputMapper::initializeOrientedRanges() {
@@ -730,21 +735,23 @@
float pressureMax = 1.0;
if (mCalibration.pressureCalibration == Calibration::PressureCalibration::PHYSICAL ||
mCalibration.pressureCalibration == Calibration::PressureCalibration::AMPLITUDE) {
- if (mCalibration.havePressureScale) {
- mPressureScale = mCalibration.pressureScale;
+ if (mCalibration.pressureScale) {
+ mPressureScale = *mCalibration.pressureScale;
pressureMax = mPressureScale * mRawPointerAxes.pressure.maxValue;
} else if (mRawPointerAxes.pressure.valid && mRawPointerAxes.pressure.maxValue != 0) {
mPressureScale = 1.0f / mRawPointerAxes.pressure.maxValue;
}
}
- mOrientedRanges.pressure.axis = AMOTION_EVENT_AXIS_PRESSURE;
- mOrientedRanges.pressure.source = mSource;
- mOrientedRanges.pressure.min = 0;
- mOrientedRanges.pressure.max = pressureMax;
- mOrientedRanges.pressure.flat = 0;
- mOrientedRanges.pressure.fuzz = 0;
- mOrientedRanges.pressure.resolution = 0;
+ mOrientedRanges.pressure = InputDeviceInfo::MotionRange{
+ .axis = AMOTION_EVENT_AXIS_PRESSURE,
+ .source = mSource,
+ .min = 0,
+ .max = pressureMax,
+ .flat = 0,
+ .fuzz = 0,
+ .resolution = 0,
+ };
// Tilt
mTiltXCenter = 0;
@@ -765,29 +772,30 @@
mTiltYScale = 1.0 / mRawPointerAxes.tiltY.resolution;
}
- mOrientedRanges.haveTilt = true;
-
- mOrientedRanges.tilt.axis = AMOTION_EVENT_AXIS_TILT;
- mOrientedRanges.tilt.source = mSource;
- mOrientedRanges.tilt.min = 0;
- mOrientedRanges.tilt.max = M_PI_2;
- mOrientedRanges.tilt.flat = 0;
- mOrientedRanges.tilt.fuzz = 0;
- mOrientedRanges.tilt.resolution = 0;
+ mOrientedRanges.tilt = InputDeviceInfo::MotionRange{
+ .axis = AMOTION_EVENT_AXIS_TILT,
+ .source = mSource,
+ .min = 0,
+ .max = M_PI_2,
+ .flat = 0,
+ .fuzz = 0,
+ .resolution = 0,
+ };
}
// Orientation
mOrientationScale = 0;
if (mHaveTilt) {
- mOrientedRanges.haveOrientation = true;
+ mOrientedRanges.orientation = InputDeviceInfo::MotionRange{
+ .axis = AMOTION_EVENT_AXIS_ORIENTATION,
+ .source = mSource,
+ .min = -M_PI,
+ .max = M_PI,
+ .flat = 0,
+ .fuzz = 0,
+ .resolution = 0,
+ };
- mOrientedRanges.orientation.axis = AMOTION_EVENT_AXIS_ORIENTATION;
- mOrientedRanges.orientation.source = mSource;
- mOrientedRanges.orientation.min = -M_PI;
- mOrientedRanges.orientation.max = M_PI;
- mOrientedRanges.orientation.flat = 0;
- mOrientedRanges.orientation.fuzz = 0;
- mOrientedRanges.orientation.resolution = 0;
} else if (mCalibration.orientationCalibration != Calibration::OrientationCalibration::NONE) {
if (mCalibration.orientationCalibration ==
Calibration::OrientationCalibration::INTERPOLATED) {
@@ -802,37 +810,34 @@
}
}
- mOrientedRanges.haveOrientation = true;
-
- mOrientedRanges.orientation.axis = AMOTION_EVENT_AXIS_ORIENTATION;
- mOrientedRanges.orientation.source = mSource;
- mOrientedRanges.orientation.min = -M_PI_2;
- mOrientedRanges.orientation.max = M_PI_2;
- mOrientedRanges.orientation.flat = 0;
- mOrientedRanges.orientation.fuzz = 0;
- mOrientedRanges.orientation.resolution = 0;
+ mOrientedRanges.orientation = InputDeviceInfo::MotionRange{
+ .axis = AMOTION_EVENT_AXIS_ORIENTATION,
+ .source = mSource,
+ .min = -M_PI_2,
+ .max = M_PI_2,
+ .flat = 0,
+ .fuzz = 0,
+ .resolution = 0,
+ };
}
// Distance
mDistanceScale = 0;
if (mCalibration.distanceCalibration != Calibration::DistanceCalibration::NONE) {
if (mCalibration.distanceCalibration == Calibration::DistanceCalibration::SCALED) {
- if (mCalibration.haveDistanceScale) {
- mDistanceScale = mCalibration.distanceScale;
- } else {
- mDistanceScale = 1.0f;
- }
+ mDistanceScale = mCalibration.distanceScale.value_or(1.0f);
}
- mOrientedRanges.haveDistance = true;
+ mOrientedRanges.distance = InputDeviceInfo::MotionRange{
- mOrientedRanges.distance.axis = AMOTION_EVENT_AXIS_DISTANCE;
- mOrientedRanges.distance.source = mSource;
- mOrientedRanges.distance.min = mRawPointerAxes.distance.minValue * mDistanceScale;
- mOrientedRanges.distance.max = mRawPointerAxes.distance.maxValue * mDistanceScale;
- mOrientedRanges.distance.flat = 0;
- mOrientedRanges.distance.fuzz = mRawPointerAxes.distance.fuzz * mDistanceScale;
- mOrientedRanges.distance.resolution = 0;
+ .axis = AMOTION_EVENT_AXIS_DISTANCE,
+ .source = mSource,
+ .min = mRawPointerAxes.distance.minValue * mDistanceScale,
+ .max = mRawPointerAxes.distance.maxValue * mDistanceScale,
+ .flat = 0,
+ .fuzz = mRawPointerAxes.distance.fuzz * mDistanceScale,
+ .resolution = 0,
+ };
}
// Compute oriented precision, scales and ranges.
@@ -936,6 +941,11 @@
// Raw width and height in the natural orientation.
const int32_t rawWidth = mRawPointerAxes.getRawWidth();
const int32_t rawHeight = mRawPointerAxes.getRawHeight();
+ const int32_t rawXResolution = mRawPointerAxes.x.resolution;
+ const int32_t rawYResolution = mRawPointerAxes.y.resolution;
+ // Calculate the mean resolution when both x and y resolution are set, otherwise set it to 0.
+ const float rawMeanResolution =
+ (rawXResolution > 0 && rawYResolution > 0) ? (rawXResolution + rawYResolution) / 2 : 0;
const bool viewportChanged = mViewport != *newViewport;
bool skipViewportUpdate = false;
@@ -1094,10 +1104,14 @@
mConfig.pointerGestureZoomSpeedRatio * displayDiagonal / rawDiagonal;
mPointerYZoomScale = mPointerXZoomScale;
- // Max width between pointers to detect a swipe gesture is more than some fraction
- // of the diagonal axis of the touch pad. Touches that are wider than this are
- // translated into freeform gestures.
- mPointerGestureMaxSwipeWidth = mConfig.pointerGestureSwipeMaxWidthRatio * rawDiagonal;
+ // Calculate the min freeform gesture width. It will be 0 when the resolution of any
+ // axis is non positive value.
+ const float minFreeformGestureWidth =
+ rawMeanResolution * MIN_FREEFORM_GESTURE_WIDTH_IN_MILLIMETER;
+
+ mPointerGestureMaxSwipeWidth =
+ std::max(mConfig.pointerGestureSwipeMaxWidthRatio * rawDiagonal,
+ minFreeformGestureWidth);
// Abort current pointer usages because the state has changed.
const nsecs_t readTime = when; // synthetic event
@@ -1192,8 +1206,8 @@
// Size
out.sizeCalibration = Calibration::SizeCalibration::DEFAULT;
- String8 sizeCalibrationString;
- if (in.tryGetProperty(String8("touch.size.calibration"), sizeCalibrationString)) {
+ std::string sizeCalibrationString;
+ if (in.tryGetProperty("touch.size.calibration", sizeCalibrationString)) {
if (sizeCalibrationString == "none") {
out.sizeCalibration = Calibration::SizeCalibration::NONE;
} else if (sizeCalibrationString == "geometric") {
@@ -1205,18 +1219,28 @@
} else if (sizeCalibrationString == "area") {
out.sizeCalibration = Calibration::SizeCalibration::AREA;
} else if (sizeCalibrationString != "default") {
- ALOGW("Invalid value for touch.size.calibration: '%s'", sizeCalibrationString.string());
+ ALOGW("Invalid value for touch.size.calibration: '%s'", sizeCalibrationString.c_str());
}
}
- out.haveSizeScale = in.tryGetProperty(String8("touch.size.scale"), out.sizeScale);
- out.haveSizeBias = in.tryGetProperty(String8("touch.size.bias"), out.sizeBias);
- out.haveSizeIsSummed = in.tryGetProperty(String8("touch.size.isSummed"), out.sizeIsSummed);
+ float sizeScale;
+
+ if (in.tryGetProperty("touch.size.scale", sizeScale)) {
+ out.sizeScale = sizeScale;
+ }
+ float sizeBias;
+ if (in.tryGetProperty("touch.size.bias", sizeBias)) {
+ out.sizeBias = sizeBias;
+ }
+ bool sizeIsSummed;
+ if (in.tryGetProperty("touch.size.isSummed", sizeIsSummed)) {
+ out.sizeIsSummed = sizeIsSummed;
+ }
// Pressure
out.pressureCalibration = Calibration::PressureCalibration::DEFAULT;
- String8 pressureCalibrationString;
- if (in.tryGetProperty(String8("touch.pressure.calibration"), pressureCalibrationString)) {
+ std::string pressureCalibrationString;
+ if (in.tryGetProperty("touch.pressure.calibration", pressureCalibrationString)) {
if (pressureCalibrationString == "none") {
out.pressureCalibration = Calibration::PressureCalibration::NONE;
} else if (pressureCalibrationString == "physical") {
@@ -1225,16 +1249,19 @@
out.pressureCalibration = Calibration::PressureCalibration::AMPLITUDE;
} else if (pressureCalibrationString != "default") {
ALOGW("Invalid value for touch.pressure.calibration: '%s'",
- pressureCalibrationString.string());
+ pressureCalibrationString.c_str());
}
}
- out.havePressureScale = in.tryGetProperty(String8("touch.pressure.scale"), out.pressureScale);
+ float pressureScale;
+ if (in.tryGetProperty("touch.pressure.scale", pressureScale)) {
+ out.pressureScale = pressureScale;
+ }
// Orientation
out.orientationCalibration = Calibration::OrientationCalibration::DEFAULT;
- String8 orientationCalibrationString;
- if (in.tryGetProperty(String8("touch.orientation.calibration"), orientationCalibrationString)) {
+ std::string orientationCalibrationString;
+ if (in.tryGetProperty("touch.orientation.calibration", orientationCalibrationString)) {
if (orientationCalibrationString == "none") {
out.orientationCalibration = Calibration::OrientationCalibration::NONE;
} else if (orientationCalibrationString == "interpolated") {
@@ -1243,36 +1270,39 @@
out.orientationCalibration = Calibration::OrientationCalibration::VECTOR;
} else if (orientationCalibrationString != "default") {
ALOGW("Invalid value for touch.orientation.calibration: '%s'",
- orientationCalibrationString.string());
+ orientationCalibrationString.c_str());
}
}
// Distance
out.distanceCalibration = Calibration::DistanceCalibration::DEFAULT;
- String8 distanceCalibrationString;
- if (in.tryGetProperty(String8("touch.distance.calibration"), distanceCalibrationString)) {
+ std::string distanceCalibrationString;
+ if (in.tryGetProperty("touch.distance.calibration", distanceCalibrationString)) {
if (distanceCalibrationString == "none") {
out.distanceCalibration = Calibration::DistanceCalibration::NONE;
} else if (distanceCalibrationString == "scaled") {
out.distanceCalibration = Calibration::DistanceCalibration::SCALED;
} else if (distanceCalibrationString != "default") {
ALOGW("Invalid value for touch.distance.calibration: '%s'",
- distanceCalibrationString.string());
+ distanceCalibrationString.c_str());
}
}
- out.haveDistanceScale = in.tryGetProperty(String8("touch.distance.scale"), out.distanceScale);
+ float distanceScale;
+ if (in.tryGetProperty("touch.distance.scale", distanceScale)) {
+ out.distanceScale = distanceScale;
+ }
out.coverageCalibration = Calibration::CoverageCalibration::DEFAULT;
- String8 coverageCalibrationString;
- if (in.tryGetProperty(String8("touch.coverage.calibration"), coverageCalibrationString)) {
+ std::string coverageCalibrationString;
+ if (in.tryGetProperty("touch.coverage.calibration", coverageCalibrationString)) {
if (coverageCalibrationString == "none") {
out.coverageCalibration = Calibration::CoverageCalibration::NONE;
} else if (coverageCalibrationString == "box") {
out.coverageCalibration = Calibration::CoverageCalibration::BOX;
} else if (coverageCalibrationString != "default") {
ALOGW("Invalid value for touch.coverage.calibration: '%s'",
- coverageCalibrationString.string());
+ coverageCalibrationString.c_str());
}
}
}
@@ -1326,17 +1356,17 @@
dump += INDENT4 "touch.size.calibration: ";
dump += ftl::enum_string(mCalibration.sizeCalibration) + "\n";
- if (mCalibration.haveSizeScale) {
- dump += StringPrintf(INDENT4 "touch.size.scale: %0.3f\n", mCalibration.sizeScale);
+ if (mCalibration.sizeScale) {
+ dump += StringPrintf(INDENT4 "touch.size.scale: %0.3f\n", *mCalibration.sizeScale);
}
- if (mCalibration.haveSizeBias) {
- dump += StringPrintf(INDENT4 "touch.size.bias: %0.3f\n", mCalibration.sizeBias);
+ if (mCalibration.sizeBias) {
+ dump += StringPrintf(INDENT4 "touch.size.bias: %0.3f\n", *mCalibration.sizeBias);
}
- if (mCalibration.haveSizeIsSummed) {
+ if (mCalibration.sizeIsSummed) {
dump += StringPrintf(INDENT4 "touch.size.isSummed: %s\n",
- toString(mCalibration.sizeIsSummed));
+ toString(*mCalibration.sizeIsSummed));
}
// Pressure
@@ -1354,8 +1384,8 @@
ALOG_ASSERT(false);
}
- if (mCalibration.havePressureScale) {
- dump += StringPrintf(INDENT4 "touch.pressure.scale: %0.3f\n", mCalibration.pressureScale);
+ if (mCalibration.pressureScale) {
+ dump += StringPrintf(INDENT4 "touch.pressure.scale: %0.3f\n", *mCalibration.pressureScale);
}
// Orientation
@@ -1385,8 +1415,8 @@
ALOG_ASSERT(false);
}
- if (mCalibration.haveDistanceScale) {
- dump += StringPrintf(INDENT4 "touch.distance.scale: %0.3f\n", mCalibration.distanceScale);
+ if (mCalibration.distanceScale) {
+ dump += StringPrintf(INDENT4 "touch.distance.scale: %0.3f\n", *mCalibration.distanceScale);
}
switch (mCalibration.coverageCalibration) {
@@ -2175,7 +2205,7 @@
size = 0;
}
- if (mCalibration.haveSizeIsSummed && mCalibration.sizeIsSummed) {
+ if (mCalibration.sizeIsSummed && *mCalibration.sizeIsSummed) {
uint32_t touchingCount = mCurrentRawState.rawPointerData.touchingIdBits.count();
if (touchingCount > 1) {
touchMajor /= touchingCount;
@@ -2201,13 +2231,16 @@
toolMinor = toolMajor;
}
- mCalibration.applySizeScaleAndBias(&touchMajor);
- mCalibration.applySizeScaleAndBias(&touchMinor);
- mCalibration.applySizeScaleAndBias(&toolMajor);
- mCalibration.applySizeScaleAndBias(&toolMinor);
+ mCalibration.applySizeScaleAndBias(touchMajor);
+ mCalibration.applySizeScaleAndBias(touchMinor);
+ mCalibration.applySizeScaleAndBias(toolMajor);
+ mCalibration.applySizeScaleAndBias(toolMinor);
size *= mSizeScale;
break;
- default:
+ case Calibration::SizeCalibration::DEFAULT:
+ LOG_ALWAYS_FATAL("Resolution should not be 'DEFAULT' at this point");
+ break;
+ case Calibration::SizeCalibration::NONE:
touchMajor = 0;
touchMinor = 0;
toolMajor = 0;
@@ -2304,10 +2337,9 @@
bottom = float(mRawPointerAxes.x.maxValue - rawLeft) * mXScale;
top = float(mRawPointerAxes.x.maxValue - rawRight) * mXScale;
orientation -= M_PI_2;
- if (mOrientedRanges.haveOrientation &&
- orientation < mOrientedRanges.orientation.min) {
+ if (mOrientedRanges.orientation && orientation < mOrientedRanges.orientation->min) {
orientation +=
- (mOrientedRanges.orientation.max - mOrientedRanges.orientation.min);
+ (mOrientedRanges.orientation->max - mOrientedRanges.orientation->min);
}
break;
case DISPLAY_ORIENTATION_180:
@@ -2316,10 +2348,9 @@
bottom = float(mRawPointerAxes.y.maxValue - rawTop) * mYScale;
top = float(mRawPointerAxes.y.maxValue - rawBottom) * mYScale;
orientation -= M_PI;
- if (mOrientedRanges.haveOrientation &&
- orientation < mOrientedRanges.orientation.min) {
+ if (mOrientedRanges.orientation && orientation < mOrientedRanges.orientation->min) {
orientation +=
- (mOrientedRanges.orientation.max - mOrientedRanges.orientation.min);
+ (mOrientedRanges.orientation->max - mOrientedRanges.orientation->min);
}
break;
case DISPLAY_ORIENTATION_270:
@@ -2328,10 +2359,9 @@
bottom = float(rawRight - mRawPointerAxes.x.minValue) * mXScale;
top = float(rawLeft - mRawPointerAxes.x.minValue) * mXScale;
orientation += M_PI_2;
- if (mOrientedRanges.haveOrientation &&
- orientation > mOrientedRanges.orientation.max) {
+ if (mOrientedRanges.orientation && orientation > mOrientedRanges.orientation->max) {
orientation -=
- (mOrientedRanges.orientation.max - mOrientedRanges.orientation.min);
+ (mOrientedRanges.orientation->max - mOrientedRanges.orientation->min);
}
break;
default:
diff --git a/services/inputflinger/reader/mapper/TouchInputMapper.h b/services/inputflinger/reader/mapper/TouchInputMapper.h
index 714ad3f..3cb11aa 100644
--- a/services/inputflinger/reader/mapper/TouchInputMapper.h
+++ b/services/inputflinger/reader/mapper/TouchInputMapper.h
@@ -248,12 +248,9 @@
SizeCalibration sizeCalibration;
- bool haveSizeScale;
- float sizeScale;
- bool haveSizeBias;
- float sizeBias;
- bool haveSizeIsSummed;
- bool sizeIsSummed;
+ std::optional<float> sizeScale;
+ std::optional<float> sizeBias;
+ std::optional<bool> sizeIsSummed;
// Pressure
enum class PressureCalibration {
@@ -264,8 +261,7 @@
};
PressureCalibration pressureCalibration;
- bool havePressureScale;
- float pressureScale;
+ std::optional<float> pressureScale;
// Orientation
enum class OrientationCalibration {
@@ -285,8 +281,7 @@
};
DistanceCalibration distanceCalibration;
- bool haveDistanceScale;
- float distanceScale;
+ std::optional<float> distanceScale;
enum class CoverageCalibration {
DEFAULT,
@@ -296,15 +291,15 @@
CoverageCalibration coverageCalibration;
- inline void applySizeScaleAndBias(float* outSize) const {
- if (haveSizeScale) {
- *outSize *= sizeScale;
+ inline void applySizeScaleAndBias(float& outSize) const {
+ if (sizeScale) {
+ outSize *= *sizeScale;
}
- if (haveSizeBias) {
- *outSize += sizeBias;
+ if (sizeBias) {
+ outSize += *sizeBias;
}
- if (*outSize < 0) {
- *outSize = 0;
+ if (outSize < 0) {
+ outSize = 0;
}
}
} mCalibration;
@@ -475,35 +470,29 @@
InputDeviceInfo::MotionRange y;
InputDeviceInfo::MotionRange pressure;
- bool haveSize;
- InputDeviceInfo::MotionRange size;
+ std::optional<InputDeviceInfo::MotionRange> size;
- bool haveTouchSize;
- InputDeviceInfo::MotionRange touchMajor;
- InputDeviceInfo::MotionRange touchMinor;
+ std::optional<InputDeviceInfo::MotionRange> touchMajor;
+ std::optional<InputDeviceInfo::MotionRange> touchMinor;
- bool haveToolSize;
- InputDeviceInfo::MotionRange toolMajor;
- InputDeviceInfo::MotionRange toolMinor;
+ std::optional<InputDeviceInfo::MotionRange> toolMajor;
+ std::optional<InputDeviceInfo::MotionRange> toolMinor;
- bool haveOrientation;
- InputDeviceInfo::MotionRange orientation;
+ std::optional<InputDeviceInfo::MotionRange> orientation;
- bool haveDistance;
- InputDeviceInfo::MotionRange distance;
+ std::optional<InputDeviceInfo::MotionRange> distance;
- bool haveTilt;
- InputDeviceInfo::MotionRange tilt;
-
- OrientedRanges() { clear(); }
+ std::optional<InputDeviceInfo::MotionRange> tilt;
void clear() {
- haveSize = false;
- haveTouchSize = false;
- haveToolSize = false;
- haveOrientation = false;
- haveDistance = false;
- haveTilt = false;
+ size = std::nullopt;
+ touchMajor = std::nullopt;
+ touchMinor = std::nullopt;
+ toolMajor = std::nullopt;
+ toolMinor = std::nullopt;
+ orientation = std::nullopt;
+ distance = std::nullopt;
+ tilt = std::nullopt;
}
} mOrientedRanges;
@@ -527,7 +516,9 @@
float mPointerXZoomScale;
float mPointerYZoomScale;
- // The maximum swipe width.
+ // The maximum swipe width between pointers to detect a swipe gesture
+ // in the number of pixels.Touches that are wider than this are translated
+ // into freeform gestures.
float mPointerGestureMaxSwipeWidth;
struct PointerDistanceHeapElement {
diff --git a/services/inputflinger/tests/InputDispatcher_test.cpp b/services/inputflinger/tests/InputDispatcher_test.cpp
index 70fd25c..8a2dea5 100644
--- a/services/inputflinger/tests/InputDispatcher_test.cpp
+++ b/services/inputflinger/tests/InputDispatcher_test.cpp
@@ -500,6 +500,11 @@
verify(*mFilteredEvent);
mFilteredEvent = nullptr;
}
+
+ bool isPerDisplayTouchModeEnabled() {
+ // TODO(b/198499018): Make this a regular property once per display touch mode is enabled
+ return false;
+ }
};
// --- InputDispatcherTest ---
@@ -3293,7 +3298,7 @@
SCOPED_TRACE("Disable touch mode");
mDispatcher->setInTouchMode(false, windowInfo.ownerPid, windowInfo.ownerUid,
- /* hasPermission */ true);
+ true /*hasPermission*/, ADISPLAY_ID_DEFAULT);
window->consumeTouchModeEvent(false);
window->setFocusable(true);
mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {window}}});
@@ -3307,7 +3312,7 @@
SCOPED_TRACE("Enable touch mode again");
mDispatcher->setInTouchMode(true, windowInfo.ownerPid, windowInfo.ownerUid,
- /* hasPermission */ true);
+ true /*hasPermission*/, ADISPLAY_ID_DEFAULT);
window->consumeTouchModeEvent(true);
window->setFocusable(true);
mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {window}}});
@@ -6715,14 +6720,15 @@
// Set initial touch mode to InputDispatcher::kDefaultInTouchMode.
if (mDispatcher->setInTouchMode(InputDispatcher::kDefaultInTouchMode, WINDOW_PID,
- WINDOW_UID, /* hasPermission */ true)) {
+ WINDOW_UID, true /*hasPermission*/, ADISPLAY_ID_DEFAULT)) {
mWindow->consumeTouchModeEvent(InputDispatcher::kDefaultInTouchMode);
mSecondWindow->consumeTouchModeEvent(InputDispatcher::kDefaultInTouchMode);
}
}
void changeAndVerifyTouchMode(bool inTouchMode, int32_t pid, int32_t uid, bool hasPermission) {
- ASSERT_TRUE(mDispatcher->setInTouchMode(inTouchMode, pid, uid, hasPermission));
+ ASSERT_TRUE(mDispatcher->setInTouchMode(inTouchMode, pid, uid, hasPermission,
+ ADISPLAY_ID_DEFAULT));
mWindow->consumeTouchModeEvent(inTouchMode);
mSecondWindow->consumeTouchModeEvent(inTouchMode);
}
@@ -6731,7 +6737,7 @@
TEST_F(InputDispatcherTouchModeChangedTests, FocusedWindowCanChangeTouchMode) {
const WindowInfo& windowInfo = *mWindow->getInfo();
changeAndVerifyTouchMode(!InputDispatcher::kDefaultInTouchMode, windowInfo.ownerPid,
- windowInfo.ownerUid, /* hasPermission */ false);
+ windowInfo.ownerUid, false /*hasPermission*/);
}
TEST_F(InputDispatcherTouchModeChangedTests, NonFocusedWindowOwnerCannotChangeTouchMode) {
@@ -6740,7 +6746,8 @@
int32_t ownerUid = windowInfo.ownerUid;
mWindow->setOwnerInfo(/* pid */ -1, /* uid */ -1);
ASSERT_FALSE(mDispatcher->setInTouchMode(InputDispatcher::kDefaultInTouchMode, ownerPid,
- ownerUid, /* hasPermission */ false));
+ ownerUid, false /*hasPermission*/,
+ ADISPLAY_ID_DEFAULT));
mWindow->assertNoEvents();
mSecondWindow->assertNoEvents();
}
@@ -6751,14 +6758,14 @@
int32_t ownerUid = windowInfo.ownerUid;
mWindow->setOwnerInfo(/* pid */ -1, /* uid */ -1);
changeAndVerifyTouchMode(!InputDispatcher::kDefaultInTouchMode, ownerPid, ownerUid,
- /* hasPermission */ true);
+ true /*hasPermission*/);
}
TEST_F(InputDispatcherTouchModeChangedTests, EventIsNotGeneratedIfNotChangingTouchMode) {
const WindowInfo& windowInfo = *mWindow->getInfo();
ASSERT_FALSE(mDispatcher->setInTouchMode(InputDispatcher::kDefaultInTouchMode,
windowInfo.ownerPid, windowInfo.ownerUid,
- /* hasPermission */ true));
+ true /*hasPermission*/, ADISPLAY_ID_DEFAULT));
mWindow->assertNoEvents();
mSecondWindow->assertNoEvents();
}
@@ -6777,7 +6784,7 @@
const WindowInfo& windowInfo = *mWindow->getInfo();
ASSERT_TRUE(mDispatcher->setInTouchMode(!InputDispatcher::kDefaultInTouchMode,
windowInfo.ownerPid, windowInfo.ownerUid,
- /* hasPermission= */ false));
+ false /*hasPermission*/, ADISPLAY_ID_DEFAULT));
}
class InputDispatcherSpyWindowTest : public InputDispatcherTest {
diff --git a/services/inputflinger/tests/InputReader_test.cpp b/services/inputflinger/tests/InputReader_test.cpp
index 901b4de..67fcc40 100644
--- a/services/inputflinger/tests/InputReader_test.cpp
+++ b/services/inputflinger/tests/InputReader_test.cpp
@@ -373,8 +373,12 @@
mConfig.defaultPointerDisplayId = pointerDisplayId;
}
+ void setPointerGestureEnabled(bool enabled) { mConfig.pointerGesturesEnabled = enabled; }
+
float getPointerGestureMovementSpeedRatio() { return mConfig.pointerGestureMovementSpeedRatio; }
+ float getPointerGestureZoomSpeedRatio() { return mConfig.pointerGestureZoomSpeedRatio; }
+
void setVelocityControlParams(const VelocityControlParameters& params) {
mConfig.pointerVelocityControlParameters = params;
mConfig.wheelVelocityControlParameters = params;
@@ -546,7 +550,7 @@
enqueueEvent(ARBITRARY_TIME, READ_TIME, 0, EventHubInterface::FINISHED_DEVICE_SCAN, 0, 0);
}
- void addConfigurationProperty(int32_t deviceId, const String8& key, const String8& value) {
+ void addConfigurationProperty(int32_t deviceId, const char* key, const char* value) {
Device* device = getDevice(deviceId);
device->configuration.addProperty(key, value);
}
@@ -2731,7 +2735,7 @@
TEST_F(InputDeviceTest, WhenMappersAreRegistered_DeviceIsNotIgnoredAndForwardsRequestsToMappers) {
// Configuration.
- mFakeEventHub->addConfigurationProperty(EVENTHUB_ID, String8("key"), String8("value"));
+ mFakeEventHub->addConfigurationProperty(EVENTHUB_ID, "key", "value");
FakeInputMapper& mapper1 =
mDevice->addMapper<FakeInputMapper>(EVENTHUB_ID, AINPUT_SOURCE_KEYBOARD);
@@ -2752,10 +2756,10 @@
InputReaderConfiguration config;
mDevice->configure(ARBITRARY_TIME, &config, 0);
- String8 propertyValue;
- ASSERT_TRUE(mDevice->getConfiguration().tryGetProperty(String8("key"), propertyValue))
+ std::string propertyValue;
+ ASSERT_TRUE(mDevice->getConfiguration().tryGetProperty("key", propertyValue))
<< "Device should have read configuration during configuration phase.";
- ASSERT_STREQ("value", propertyValue.string());
+ ASSERT_EQ("value", propertyValue);
ASSERT_NO_FATAL_FAILURE(mapper1.assertConfigureWasCalled());
ASSERT_NO_FATAL_FAILURE(mapper2.assertConfigureWasCalled());
@@ -2953,7 +2957,7 @@
}
void addConfigurationProperty(const char* key, const char* value) {
- mFakeEventHub->addConfigurationProperty(EVENTHUB_ID, String8(key), String8(value));
+ mFakeEventHub->addConfigurationProperty(EVENTHUB_ID, key, value);
}
void configureDevice(uint32_t changes) {
@@ -9464,6 +9468,258 @@
ASSERT_EQ(AINPUT_SOURCE_TOUCHPAD, mapper.getSources());
}
+class MultiTouchPointerModeTest : public MultiTouchInputMapperTest {
+protected:
+ float mPointerMovementScale;
+ float mPointerXZoomScale;
+ void preparePointerMode(int xAxisResolution, int yAxisResolution) {
+ addConfigurationProperty("touch.deviceType", "pointer");
+ std::shared_ptr<FakePointerController> fakePointerController =
+ std::make_shared<FakePointerController>();
+ fakePointerController->setBounds(0, 0, DISPLAY_WIDTH - 1, DISPLAY_HEIGHT - 1);
+ fakePointerController->setPosition(0, 0);
+ fakePointerController->setButtonState(0);
+ prepareDisplay(DISPLAY_ORIENTATION_0);
+
+ prepareAxes(POSITION);
+ prepareAbsoluteAxisResolution(xAxisResolution, yAxisResolution);
+ // In order to enable swipe and freeform gesture in pointer mode, pointer capture
+ // needs to be disabled, and the pointer gesture needs to be enabled.
+ mFakePolicy->setPointerCapture(false);
+ mFakePolicy->setPointerGestureEnabled(true);
+ mFakePolicy->setPointerController(fakePointerController);
+
+ float rawDiagonal = hypotf(RAW_X_MAX - RAW_X_MIN, RAW_Y_MAX - RAW_Y_MIN);
+ float displayDiagonal = hypotf(DISPLAY_WIDTH, DISPLAY_HEIGHT);
+ mPointerMovementScale =
+ mFakePolicy->getPointerGestureMovementSpeedRatio() * displayDiagonal / rawDiagonal;
+ mPointerXZoomScale =
+ mFakePolicy->getPointerGestureZoomSpeedRatio() * displayDiagonal / rawDiagonal;
+ }
+
+ void prepareAbsoluteAxisResolution(int xAxisResolution, int yAxisResolution) {
+ mFakeEventHub->addAbsoluteAxis(EVENTHUB_ID, ABS_MT_POSITION_X, RAW_X_MIN, RAW_X_MAX,
+ /*flat*/ 0,
+ /*fuzz*/ 0, /*resolution*/ xAxisResolution);
+ mFakeEventHub->addAbsoluteAxis(EVENTHUB_ID, ABS_MT_POSITION_Y, RAW_Y_MIN, RAW_Y_MAX,
+ /*flat*/ 0,
+ /*fuzz*/ 0, /*resolution*/ yAxisResolution);
+ }
+};
+
+/**
+ * Two fingers down on a pointer mode touch pad. The width
+ * of the two finger is larger than 1/4 of the touch pack diagnal length. However, it
+ * is smaller than the fixed min physical length 30mm. Two fingers' distance must
+ * be greater than the both value to be freeform gesture, so that after two
+ * fingers start to move downwards, the gesture should be swipe.
+ */
+TEST_F(MultiTouchPointerModeTest, PointerGestureMaxSwipeWidthSwipe) {
+ // The min freeform gesture width is 25units/mm x 30mm = 750
+ // which is greater than fraction of the diagnal length of the touchpad (349).
+ // Thus, MaxSwipWidth is 750.
+ preparePointerMode(25 /*xResolution*/, 25 /*yResolution*/);
+ MultiTouchInputMapper& mapper = addMapperAndConfigure<MultiTouchInputMapper>();
+ NotifyMotionArgs motionArgs;
+
+ // Two fingers down at once.
+ // The two fingers are 450 units apart, expects the current gesture to be PRESS
+ // Pointer's initial position is used the [0,0] coordinate.
+ int32_t x1 = 100, y1 = 125, x2 = 550, y2 = 125;
+
+ processId(mapper, FIRST_TRACKING_ID);
+ processPosition(mapper, x1, y1);
+ processMTSync(mapper);
+ processId(mapper, SECOND_TRACKING_ID);
+ processPosition(mapper, x2, y2);
+ processMTSync(mapper);
+ processSync(mapper);
+
+ ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs));
+ ASSERT_EQ(1U, motionArgs.pointerCount);
+ ASSERT_EQ(AMOTION_EVENT_ACTION_DOWN, motionArgs.action);
+ ASSERT_EQ(AMOTION_EVENT_TOOL_TYPE_FINGER, motionArgs.pointerProperties[0].toolType);
+ ASSERT_NO_FATAL_FAILURE(
+ assertPointerCoords(motionArgs.pointerCoords[0], 0, 0, 1, 0, 0, 0, 0, 0, 0, 0));
+
+ // It should be recognized as a SWIPE gesture when two fingers start to move down,
+ // that there should be 1 pointer.
+ int32_t movingDistance = 200;
+ y1 += movingDistance;
+ y2 += movingDistance;
+
+ processId(mapper, FIRST_TRACKING_ID);
+ processPosition(mapper, x1, y1);
+ processMTSync(mapper);
+ processId(mapper, SECOND_TRACKING_ID);
+ processPosition(mapper, x2, y2);
+ processMTSync(mapper);
+ processSync(mapper);
+
+ ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs));
+ ASSERT_EQ(1U, motionArgs.pointerCount);
+ ASSERT_EQ(AMOTION_EVENT_ACTION_MOVE, motionArgs.action);
+ ASSERT_EQ(AMOTION_EVENT_TOOL_TYPE_FINGER, motionArgs.pointerProperties[0].toolType);
+ ASSERT_NO_FATAL_FAILURE(assertPointerCoords(motionArgs.pointerCoords[0], 0,
+ movingDistance * mPointerMovementScale, 1, 0, 0, 0,
+ 0, 0, 0, 0));
+}
+
+/**
+ * Two fingers down on a pointer mode touch pad. The width of the two finger is larger
+ * than the minimum freeform gesture width, 30mm. However, it is smaller than 1/4 of
+ * the touch pack diagnal length. Two fingers' distance must be greater than the both
+ * value to be freeform gesture, so that after two fingers start to move downwards,
+ * the gesture should be swipe.
+ */
+TEST_F(MultiTouchPointerModeTest, PointerGestureMaxSwipeWidthLowResolutionSwipe) {
+ // The min freeform gesture width is 5units/mm x 30mm = 150
+ // which is greater than fraction of the diagnal length of the touchpad (349).
+ // Thus, MaxSwipWidth is the fraction of the diagnal length, 349.
+ preparePointerMode(5 /*xResolution*/, 5 /*yResolution*/);
+ MultiTouchInputMapper& mapper = addMapperAndConfigure<MultiTouchInputMapper>();
+ NotifyMotionArgs motionArgs;
+
+ // Two fingers down at once.
+ // The two fingers are 250 units apart, expects the current gesture to be PRESS
+ // Pointer's initial position is used the [0,0] coordinate.
+ int32_t x1 = 100, y1 = 125, x2 = 350, y2 = 125;
+
+ processId(mapper, FIRST_TRACKING_ID);
+ processPosition(mapper, x1, y1);
+ processMTSync(mapper);
+ processId(mapper, SECOND_TRACKING_ID);
+ processPosition(mapper, x2, y2);
+ processMTSync(mapper);
+ processSync(mapper);
+
+ ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs));
+ ASSERT_EQ(1U, motionArgs.pointerCount);
+ ASSERT_EQ(AMOTION_EVENT_ACTION_DOWN, motionArgs.action);
+ ASSERT_EQ(AMOTION_EVENT_TOOL_TYPE_FINGER, motionArgs.pointerProperties[0].toolType);
+ ASSERT_NO_FATAL_FAILURE(
+ assertPointerCoords(motionArgs.pointerCoords[0], 0, 0, 1, 0, 0, 0, 0, 0, 0, 0));
+
+ // It should be recognized as a SWIPE gesture when two fingers start to move down,
+ // and there should be 1 pointer.
+ int32_t movingDistance = 200;
+ y1 += movingDistance;
+ y2 += movingDistance;
+
+ processId(mapper, FIRST_TRACKING_ID);
+ processPosition(mapper, x1, y1);
+ processMTSync(mapper);
+ processId(mapper, SECOND_TRACKING_ID);
+ processPosition(mapper, x2, y2);
+ processMTSync(mapper);
+ processSync(mapper);
+
+ ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs));
+ ASSERT_EQ(1U, motionArgs.pointerCount);
+ ASSERT_EQ(AMOTION_EVENT_ACTION_MOVE, motionArgs.action);
+ ASSERT_EQ(AMOTION_EVENT_TOOL_TYPE_FINGER, motionArgs.pointerProperties[0].toolType);
+ // New coordinate is the scaled relative coordinate from the initial coordinate.
+ ASSERT_NO_FATAL_FAILURE(assertPointerCoords(motionArgs.pointerCoords[0], 0,
+ movingDistance * mPointerMovementScale, 1, 0, 0, 0,
+ 0, 0, 0, 0));
+}
+
+/**
+ * Touch the touch pad with two fingers with a distance wider than the minimum freeform
+ * gesture width and 1/4 of the diagnal length of the touchpad. Expect to receive
+ * freeform gestures after two fingers start to move downwards.
+ */
+TEST_F(MultiTouchPointerModeTest, PointerGestureMaxSwipeWidthFreeform) {
+ preparePointerMode(25 /*xResolution*/, 25 /*yResolution*/);
+ MultiTouchInputMapper& mapper = addMapperAndConfigure<MultiTouchInputMapper>();
+
+ NotifyMotionArgs motionArgs;
+
+ // Two fingers down at once. Wider than the max swipe width.
+ // The gesture is expected to be PRESS, then transformed to FREEFORM
+ int32_t x1 = 100, y1 = 125, x2 = 900, y2 = 125;
+
+ processId(mapper, FIRST_TRACKING_ID);
+ processPosition(mapper, x1, y1);
+ processMTSync(mapper);
+ processId(mapper, SECOND_TRACKING_ID);
+ processPosition(mapper, x2, y2);
+ processMTSync(mapper);
+ processSync(mapper);
+
+ ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs));
+ ASSERT_EQ(1U, motionArgs.pointerCount);
+ ASSERT_EQ(AMOTION_EVENT_ACTION_DOWN, motionArgs.action);
+ ASSERT_EQ(AMOTION_EVENT_TOOL_TYPE_FINGER, motionArgs.pointerProperties[0].toolType);
+ // One pointer for PRESS, and its coordinate is used as the origin for pointer coordinates.
+ ASSERT_NO_FATAL_FAILURE(
+ assertPointerCoords(motionArgs.pointerCoords[0], 0, 0, 1, 0, 0, 0, 0, 0, 0, 0));
+
+ int32_t movingDistance = 200;
+
+ // Move two fingers down, expect a cancel event because gesture is changing to freeform,
+ // then two down events for two pointers.
+ y1 += movingDistance;
+ y2 += movingDistance;
+
+ processId(mapper, FIRST_TRACKING_ID);
+ processPosition(mapper, x1, y1);
+ processMTSync(mapper);
+ processId(mapper, SECOND_TRACKING_ID);
+ processPosition(mapper, x2, y2);
+ processMTSync(mapper);
+ processSync(mapper);
+
+ ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs));
+ // The previous PRESS gesture is cancelled, because it is transformed to freeform
+ ASSERT_EQ(1U, motionArgs.pointerCount);
+ ASSERT_EQ(AMOTION_EVENT_ACTION_CANCEL, motionArgs.action);
+ ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs));
+ ASSERT_EQ(AMOTION_EVENT_TOOL_TYPE_FINGER, motionArgs.pointerProperties[0].toolType);
+ ASSERT_EQ(1U, motionArgs.pointerCount);
+ ASSERT_EQ(AMOTION_EVENT_ACTION_DOWN, motionArgs.action);
+ ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs));
+ ASSERT_EQ(AMOTION_EVENT_TOOL_TYPE_FINGER, motionArgs.pointerProperties[0].toolType);
+ ASSERT_EQ(2U, motionArgs.pointerCount);
+ ASSERT_EQ(AMOTION_EVENT_ACTION_POINTER_DOWN, motionArgs.action & AMOTION_EVENT_ACTION_MASK);
+ ASSERT_EQ(AMOTION_EVENT_TOOL_TYPE_FINGER, motionArgs.pointerProperties[0].toolType);
+ // Two pointers' scaled relative coordinates from their initial centroid.
+ // Initial y coordinates are 0 as y1 and y2 have the same value.
+ float cookedX1 = (x1 - x2) / 2 * mPointerXZoomScale;
+ float cookedX2 = (x2 - x1) / 2 * mPointerXZoomScale;
+ // When pointers move, the new coordinates equal to the initial coordinates plus
+ // scaled moving distance.
+ ASSERT_NO_FATAL_FAILURE(assertPointerCoords(motionArgs.pointerCoords[0], cookedX1,
+ movingDistance * mPointerMovementScale, 1, 0, 0, 0,
+ 0, 0, 0, 0));
+ ASSERT_NO_FATAL_FAILURE(assertPointerCoords(motionArgs.pointerCoords[1], cookedX2,
+ movingDistance * mPointerMovementScale, 1, 0, 0, 0,
+ 0, 0, 0, 0));
+
+ // Move two fingers down again, expect one MOVE motion event.
+ y1 += movingDistance;
+ y2 += movingDistance;
+
+ processId(mapper, FIRST_TRACKING_ID);
+ processPosition(mapper, x1, y1);
+ processMTSync(mapper);
+ processId(mapper, SECOND_TRACKING_ID);
+ processPosition(mapper, x2, y2);
+ processMTSync(mapper);
+ processSync(mapper);
+
+ ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs));
+ ASSERT_EQ(2U, motionArgs.pointerCount);
+ ASSERT_EQ(AMOTION_EVENT_ACTION_MOVE, motionArgs.action);
+ ASSERT_EQ(AMOTION_EVENT_TOOL_TYPE_FINGER, motionArgs.pointerProperties[0].toolType);
+ ASSERT_NO_FATAL_FAILURE(assertPointerCoords(motionArgs.pointerCoords[0], cookedX1,
+ movingDistance * 2 * mPointerMovementScale, 1, 0, 0,
+ 0, 0, 0, 0, 0));
+ ASSERT_NO_FATAL_FAILURE(assertPointerCoords(motionArgs.pointerCoords[1], cookedX2,
+ movingDistance * 2 * mPointerMovementScale, 1, 0, 0,
+ 0, 0, 0, 0, 0));
+}
+
// --- JoystickInputMapperTest ---
class JoystickInputMapperTest : public InputMapperTest {
diff --git a/services/inputflinger/tests/UnwantedInteractionBlocker_test.cpp b/services/inputflinger/tests/UnwantedInteractionBlocker_test.cpp
index 7f14b94..8af3871 100644
--- a/services/inputflinger/tests/UnwantedInteractionBlocker_test.cpp
+++ b/services/inputflinger/tests/UnwantedInteractionBlocker_test.cpp
@@ -388,7 +388,7 @@
expected.reported_tool_type = ::ui::EventPointerType::kTouch;
expected.stylus_button = false;
- ASSERT_EQ(expected, touches[0]) << toString(touches[0]);
+ ASSERT_EQ(expected, touches[0]) << touches[0];
}
// --- UnwantedInteractionBlockerTest ---
diff --git a/services/surfaceflinger/Android.bp b/services/surfaceflinger/Android.bp
index 20e86a0..f1457bf 100644
--- a/services/surfaceflinger/Android.bp
+++ b/services/surfaceflinger/Android.bp
@@ -141,8 +141,6 @@
name: "libsurfaceflinger_sources",
srcs: [
"BackgroundExecutor.cpp",
- "BufferLayer.cpp",
- "BufferLayerConsumer.cpp",
"BufferStateLayer.cpp",
"ClientCache.cpp",
"Client.cpp",
@@ -168,10 +166,8 @@
"WindowInfosListenerInvoker.cpp",
"Layer.cpp",
"LayerProtoHelper.cpp",
- "LayerRejecter.cpp",
"LayerRenderArea.cpp",
"LayerVector.cpp",
- "MonitoredProducer.cpp",
"NativeWindowSurface.cpp",
"RefreshRateOverlay.cpp",
"RegionSamplingThread.cpp",
diff --git a/services/surfaceflinger/BufferLayer.cpp b/services/surfaceflinger/BufferLayer.cpp
deleted file mode 100644
index fb15f1d..0000000
--- a/services/surfaceflinger/BufferLayer.cpp
+++ /dev/null
@@ -1,818 +0,0 @@
-/*
- * Copyright (C) 2017 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.
- */
-
-// TODO(b/129481165): remove the #pragma below and fix conversion issues
-#pragma clang diagnostic push
-#pragma clang diagnostic ignored "-Wconversion"
-
-//#define LOG_NDEBUG 0
-#undef LOG_TAG
-#define LOG_TAG "BufferLayer"
-#define ATRACE_TAG ATRACE_TAG_GRAPHICS
-
-#include "BufferLayer.h"
-
-#include <compositionengine/CompositionEngine.h>
-#include <compositionengine/LayerFECompositionState.h>
-#include <compositionengine/OutputLayer.h>
-#include <compositionengine/impl/OutputLayerCompositionState.h>
-#include <cutils/compiler.h>
-#include <cutils/native_handle.h>
-#include <cutils/properties.h>
-#include <gui/BufferItem.h>
-#include <gui/BufferQueue.h>
-#include <gui/GLConsumer.h>
-#include <gui/LayerDebugInfo.h>
-#include <gui/Surface.h>
-#include <renderengine/RenderEngine.h>
-#include <ui/DebugUtils.h>
-#include <utils/Errors.h>
-#include <utils/Log.h>
-#include <utils/NativeHandle.h>
-#include <utils/StopWatch.h>
-#include <utils/Trace.h>
-
-#include <cmath>
-#include <cstdlib>
-#include <mutex>
-#include <sstream>
-
-#include "Colorizer.h"
-#include "DisplayDevice.h"
-#include "FrameTracer/FrameTracer.h"
-#include "LayerRejecter.h"
-#include "TimeStats/TimeStats.h"
-
-namespace android {
-
-using gui::WindowInfo;
-
-static constexpr float defaultMaxLuminance = 1000.0;
-
-BufferLayer::BufferLayer(const LayerCreationArgs& args)
- : Layer(args),
- mTextureName(args.textureName),
- mCompositionState{mFlinger->getCompositionEngine().createLayerFECompositionState()} {
- ALOGV("Creating Layer %s", getDebugName());
-
- mPremultipliedAlpha = !(args.flags & ISurfaceComposerClient::eNonPremultiplied);
-
- mPotentialCursor = args.flags & ISurfaceComposerClient::eCursorWindow;
- mProtectedByApp = args.flags & ISurfaceComposerClient::eProtectedByApp;
-}
-
-BufferLayer::~BufferLayer() {
- if (!isClone()) {
- // The original layer and the clone layer share the same texture. Therefore, only one of
- // the layers, in this case the original layer, needs to handle the deletion. The original
- // layer and the clone should be removed at the same time so there shouldn't be any issue
- // with the clone layer trying to use the deleted texture.
- mFlinger->deleteTextureAsync(mTextureName);
- }
- const int32_t layerId = getSequence();
- mFlinger->mTimeStats->onDestroy(layerId);
- mFlinger->mFrameTracer->onDestroy(layerId);
-}
-
-void BufferLayer::useSurfaceDamage() {
- if (mFlinger->mForceFullDamage) {
- surfaceDamageRegion = Region::INVALID_REGION;
- } else {
- surfaceDamageRegion = mBufferInfo.mSurfaceDamage;
- }
-}
-
-void BufferLayer::useEmptyDamage() {
- surfaceDamageRegion.clear();
-}
-
-bool BufferLayer::isOpaque(const Layer::State& s) const {
- // if we don't have a buffer or sidebandStream yet, we're translucent regardless of the
- // layer's opaque flag.
- if ((mSidebandStream == nullptr) && (mBufferInfo.mBuffer == nullptr)) {
- return false;
- }
-
- // if the layer has the opaque flag, then we're always opaque,
- // otherwise we use the current buffer's format.
- return ((s.flags & layer_state_t::eLayerOpaque) != 0) || getOpacityForFormat(getPixelFormat());
-}
-
-bool BufferLayer::canReceiveInput() const {
- return !isHiddenByPolicy() && (mBufferInfo.mBuffer == nullptr || getAlpha() > 0.0f);
-}
-
-bool BufferLayer::isVisible() const {
- return !isHiddenByPolicy() && getAlpha() > 0.0f &&
- (mBufferInfo.mBuffer != nullptr || mSidebandStream != nullptr);
-}
-
-bool BufferLayer::isFixedSize() const {
- return getEffectiveScalingMode() != NATIVE_WINDOW_SCALING_MODE_FREEZE;
-}
-
-bool BufferLayer::usesSourceCrop() const {
- return true;
-}
-
-static constexpr mat4 inverseOrientation(uint32_t transform) {
- const mat4 flipH(-1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 1, 0, 0, 1);
- const mat4 flipV(1, 0, 0, 0, 0, -1, 0, 0, 0, 0, 1, 0, 0, 1, 0, 1);
- const mat4 rot90(0, 1, 0, 0, -1, 0, 0, 0, 0, 0, 1, 0, 1, 0, 0, 1);
- mat4 tr;
-
- if (transform & NATIVE_WINDOW_TRANSFORM_ROT_90) {
- tr = tr * rot90;
- }
- if (transform & NATIVE_WINDOW_TRANSFORM_FLIP_H) {
- tr = tr * flipH;
- }
- if (transform & NATIVE_WINDOW_TRANSFORM_FLIP_V) {
- tr = tr * flipV;
- }
- return inverse(tr);
-}
-
-std::optional<compositionengine::LayerFE::LayerSettings> BufferLayer::prepareClientComposition(
- compositionengine::LayerFE::ClientCompositionTargetSettings& targetSettings) {
- ATRACE_CALL();
-
- std::optional<compositionengine::LayerFE::LayerSettings> result =
- Layer::prepareClientComposition(targetSettings);
- if (!result) {
- return result;
- }
-
- if (CC_UNLIKELY(mBufferInfo.mBuffer == 0) && mSidebandStream != nullptr) {
- // For surfaceview of tv sideband, there is no activeBuffer
- // in bufferqueue, we need return LayerSettings.
- return result;
- }
- const bool blackOutLayer = (isProtected() && !targetSettings.supportsProtectedContent) ||
- ((isSecure() || isProtected()) && !targetSettings.isSecure);
- const bool bufferCanBeUsedAsHwTexture =
- mBufferInfo.mBuffer->getUsage() & GraphicBuffer::USAGE_HW_TEXTURE;
- compositionengine::LayerFE::LayerSettings& layer = *result;
- if (blackOutLayer || !bufferCanBeUsedAsHwTexture) {
- ALOGE_IF(!bufferCanBeUsedAsHwTexture, "%s is blacked out as buffer is not gpu readable",
- mName.c_str());
- prepareClearClientComposition(layer, true /* blackout */);
- return layer;
- }
-
- const State& s(getDrawingState());
- layer.source.buffer.buffer = mBufferInfo.mBuffer;
- layer.source.buffer.isOpaque = isOpaque(s);
- layer.source.buffer.fence = mBufferInfo.mFence;
- layer.source.buffer.textureName = mTextureName;
- layer.source.buffer.usePremultipliedAlpha = getPremultipledAlpha();
- layer.source.buffer.isY410BT2020 = isHdrY410();
- bool hasSmpte2086 = mBufferInfo.mHdrMetadata.validTypes & HdrMetadata::SMPTE2086;
- bool hasCta861_3 = mBufferInfo.mHdrMetadata.validTypes & HdrMetadata::CTA861_3;
- float maxLuminance = 0.f;
- if (hasSmpte2086 && hasCta861_3) {
- maxLuminance = std::min(mBufferInfo.mHdrMetadata.smpte2086.maxLuminance,
- mBufferInfo.mHdrMetadata.cta8613.maxContentLightLevel);
- } else if (hasSmpte2086) {
- maxLuminance = mBufferInfo.mHdrMetadata.smpte2086.maxLuminance;
- } else if (hasCta861_3) {
- maxLuminance = mBufferInfo.mHdrMetadata.cta8613.maxContentLightLevel;
- } else {
- switch (layer.sourceDataspace & HAL_DATASPACE_TRANSFER_MASK) {
- case HAL_DATASPACE_TRANSFER_ST2084:
- case HAL_DATASPACE_TRANSFER_HLG:
- // Behavior-match previous releases for HDR content
- maxLuminance = defaultMaxLuminance;
- break;
- }
- }
- layer.source.buffer.maxLuminanceNits = maxLuminance;
- layer.frameNumber = mCurrentFrameNumber;
- layer.bufferId = mBufferInfo.mBuffer ? mBufferInfo.mBuffer->getId() : 0;
-
- const bool useFiltering =
- targetSettings.needsFiltering || mNeedsFiltering || bufferNeedsFiltering();
-
- // Query the texture matrix given our current filtering mode.
- float textureMatrix[16];
- getDrawingTransformMatrix(useFiltering, textureMatrix);
-
- if (getTransformToDisplayInverse()) {
- /*
- * the code below applies the primary display's inverse transform to
- * the texture transform
- */
- uint32_t transform = DisplayDevice::getPrimaryDisplayRotationFlags();
- mat4 tr = inverseOrientation(transform);
-
- /**
- * TODO(b/36727915): This is basically a hack.
- *
- * Ensure that regardless of the parent transformation,
- * this buffer is always transformed from native display
- * orientation to display orientation. For example, in the case
- * of a camera where the buffer remains in native orientation,
- * we want the pixels to always be upright.
- */
- sp<Layer> p = mDrawingParent.promote();
- if (p != nullptr) {
- const auto parentTransform = p->getTransform();
- tr = tr * inverseOrientation(parentTransform.getOrientation());
- }
-
- // and finally apply it to the original texture matrix
- const mat4 texTransform(mat4(static_cast<const float*>(textureMatrix)) * tr);
- memcpy(textureMatrix, texTransform.asArray(), sizeof(textureMatrix));
- }
-
- const Rect win{getBounds()};
- float bufferWidth = getBufferSize(s).getWidth();
- float bufferHeight = getBufferSize(s).getHeight();
-
- // BufferStateLayers can have a "buffer size" of [0, 0, -1, -1] when no display frame has
- // been set and there is no parent layer bounds. In that case, the scale is meaningless so
- // ignore them.
- if (!getBufferSize(s).isValid()) {
- bufferWidth = float(win.right) - float(win.left);
- bufferHeight = float(win.bottom) - float(win.top);
- }
-
- const float scaleHeight = (float(win.bottom) - float(win.top)) / bufferHeight;
- const float scaleWidth = (float(win.right) - float(win.left)) / bufferWidth;
- const float translateY = float(win.top) / bufferHeight;
- const float translateX = float(win.left) / bufferWidth;
-
- // Flip y-coordinates because GLConsumer expects OpenGL convention.
- mat4 tr = mat4::translate(vec4(.5, .5, 0, 1)) * mat4::scale(vec4(1, -1, 1, 1)) *
- mat4::translate(vec4(-.5, -.5, 0, 1)) *
- mat4::translate(vec4(translateX, translateY, 0, 1)) *
- mat4::scale(vec4(scaleWidth, scaleHeight, 1.0, 1.0));
-
- layer.source.buffer.useTextureFiltering = useFiltering;
- layer.source.buffer.textureTransform = mat4(static_cast<const float*>(textureMatrix)) * tr;
-
- return layer;
-}
-
-bool BufferLayer::isHdrY410() const {
- // pixel format is HDR Y410 masquerading as RGBA_1010102
- return (mBufferInfo.mDataspace == ui::Dataspace::BT2020_ITU_PQ &&
- mBufferInfo.mApi == NATIVE_WINDOW_API_MEDIA &&
- mBufferInfo.mPixelFormat == HAL_PIXEL_FORMAT_RGBA_1010102);
-}
-
-sp<compositionengine::LayerFE> BufferLayer::getCompositionEngineLayerFE() const {
- return asLayerFE();
-}
-
-compositionengine::LayerFECompositionState* BufferLayer::editCompositionState() {
- return mCompositionState.get();
-}
-
-const compositionengine::LayerFECompositionState* BufferLayer::getCompositionState() const {
- return mCompositionState.get();
-}
-
-void BufferLayer::preparePerFrameCompositionState() {
- Layer::preparePerFrameCompositionState();
-
- // Sideband layers
- auto* compositionState = editCompositionState();
- if (compositionState->sidebandStream.get() && !compositionState->sidebandStreamHasFrame) {
- compositionState->compositionType =
- aidl::android::hardware::graphics::composer3::Composition::SIDEBAND;
- return;
- } else if ((mDrawingState.flags & layer_state_t::eLayerIsDisplayDecoration) != 0) {
- compositionState->compositionType =
- aidl::android::hardware::graphics::composer3::Composition::DISPLAY_DECORATION;
- } else {
- // Normal buffer layers
- compositionState->hdrMetadata = mBufferInfo.mHdrMetadata;
- compositionState->compositionType = mPotentialCursor
- ? aidl::android::hardware::graphics::composer3::Composition::CURSOR
- : aidl::android::hardware::graphics::composer3::Composition::DEVICE;
- }
-
- compositionState->buffer = getBuffer();
- compositionState->bufferSlot = (mBufferInfo.mBufferSlot == BufferQueue::INVALID_BUFFER_SLOT)
- ? 0
- : mBufferInfo.mBufferSlot;
- compositionState->acquireFence = mBufferInfo.mFence;
- compositionState->frameNumber = mBufferInfo.mFrameNumber;
- compositionState->sidebandStreamHasFrame = false;
-}
-
-bool BufferLayer::onPreComposition(nsecs_t) {
- return hasReadyFrame();
-}
-namespace {
-TimeStats::SetFrameRateVote frameRateToSetFrameRateVotePayload(Layer::FrameRate frameRate) {
- using FrameRateCompatibility = TimeStats::SetFrameRateVote::FrameRateCompatibility;
- using Seamlessness = TimeStats::SetFrameRateVote::Seamlessness;
- const auto frameRateCompatibility = [frameRate] {
- switch (frameRate.type) {
- case Layer::FrameRateCompatibility::Default:
- return FrameRateCompatibility::Default;
- case Layer::FrameRateCompatibility::ExactOrMultiple:
- return FrameRateCompatibility::ExactOrMultiple;
- default:
- return FrameRateCompatibility::Undefined;
- }
- }();
-
- const auto seamlessness = [frameRate] {
- switch (frameRate.seamlessness) {
- case scheduler::Seamlessness::OnlySeamless:
- return Seamlessness::ShouldBeSeamless;
- case scheduler::Seamlessness::SeamedAndSeamless:
- return Seamlessness::NotRequired;
- default:
- return Seamlessness::Undefined;
- }
- }();
-
- return TimeStats::SetFrameRateVote{.frameRate = frameRate.rate.getValue(),
- .frameRateCompatibility = frameRateCompatibility,
- .seamlessness = seamlessness};
-}
-} // namespace
-
-void BufferLayer::onPostComposition(const DisplayDevice* display,
- const std::shared_ptr<FenceTime>& glDoneFence,
- const std::shared_ptr<FenceTime>& presentFence,
- const CompositorTiming& compositorTiming) {
- // mFrameLatencyNeeded is true when a new frame was latched for the
- // composition.
- if (!mBufferInfo.mFrameLatencyNeeded) return;
-
- // Update mFrameEventHistory.
- finalizeFrameEventHistory(glDoneFence, compositorTiming);
-
- // Update mFrameTracker.
- nsecs_t desiredPresentTime = mBufferInfo.mDesiredPresentTime;
- mFrameTracker.setDesiredPresentTime(desiredPresentTime);
-
- const int32_t layerId = getSequence();
- mFlinger->mTimeStats->setDesiredTime(layerId, mCurrentFrameNumber, desiredPresentTime);
-
- const auto outputLayer = findOutputLayerForDisplay(display);
- if (outputLayer && outputLayer->requiresClientComposition()) {
- nsecs_t clientCompositionTimestamp = outputLayer->getState().clientCompositionTimestamp;
- mFlinger->mFrameTracer->traceTimestamp(layerId, getCurrentBufferId(), mCurrentFrameNumber,
- clientCompositionTimestamp,
- FrameTracer::FrameEvent::FALLBACK_COMPOSITION);
- // Update the SurfaceFrames in the drawing state
- if (mDrawingState.bufferSurfaceFrameTX) {
- mDrawingState.bufferSurfaceFrameTX->setGpuComposition();
- }
- for (auto& [token, surfaceFrame] : mDrawingState.bufferlessSurfaceFramesTX) {
- surfaceFrame->setGpuComposition();
- }
- }
-
- std::shared_ptr<FenceTime> frameReadyFence = mBufferInfo.mFenceTime;
- if (frameReadyFence->isValid()) {
- mFrameTracker.setFrameReadyFence(std::move(frameReadyFence));
- } else {
- // There was no fence for this frame, so assume that it was ready
- // to be presented at the desired present time.
- mFrameTracker.setFrameReadyTime(desiredPresentTime);
- }
-
- if (display) {
- const Fps refreshRate = display->refreshRateConfigs().getActiveMode()->getFps();
- const std::optional<Fps> renderRate =
- mFlinger->mScheduler->getFrameRateOverride(getOwnerUid());
-
- const auto vote = frameRateToSetFrameRateVotePayload(mDrawingState.frameRate);
- const auto gameMode = getGameMode();
-
- if (presentFence->isValid()) {
- mFlinger->mTimeStats->setPresentFence(layerId, mCurrentFrameNumber, presentFence,
- refreshRate, renderRate, vote, gameMode);
- mFlinger->mFrameTracer->traceFence(layerId, getCurrentBufferId(), mCurrentFrameNumber,
- presentFence,
- FrameTracer::FrameEvent::PRESENT_FENCE);
- mFrameTracker.setActualPresentFence(std::shared_ptr<FenceTime>(presentFence));
- } else if (const auto displayId = PhysicalDisplayId::tryCast(display->getId());
- displayId && mFlinger->getHwComposer().isConnected(*displayId)) {
- // The HWC doesn't support present fences, so use the refresh
- // timestamp instead.
- const nsecs_t actualPresentTime = display->getRefreshTimestamp();
- mFlinger->mTimeStats->setPresentTime(layerId, mCurrentFrameNumber, actualPresentTime,
- refreshRate, renderRate, vote, gameMode);
- mFlinger->mFrameTracer->traceTimestamp(layerId, getCurrentBufferId(),
- mCurrentFrameNumber, actualPresentTime,
- FrameTracer::FrameEvent::PRESENT_FENCE);
- mFrameTracker.setActualPresentTime(actualPresentTime);
- }
- }
-
- mFrameTracker.advanceFrame();
- mBufferInfo.mFrameLatencyNeeded = false;
-}
-
-void BufferLayer::gatherBufferInfo() {
- mBufferInfo.mPixelFormat =
- !mBufferInfo.mBuffer ? PIXEL_FORMAT_NONE : mBufferInfo.mBuffer->getPixelFormat();
- mBufferInfo.mFrameLatencyNeeded = true;
-}
-
-bool BufferLayer::shouldPresentNow(nsecs_t expectedPresentTime) const {
- // If this is not a valid vsync for the layer's uid, return and try again later
- const bool isVsyncValidForUid =
- mFlinger->mScheduler->isVsyncValid(expectedPresentTime, mOwnerUid);
- if (!isVsyncValidForUid) {
- ATRACE_NAME("!isVsyncValidForUid");
- return false;
- }
-
- // AutoRefresh layers and sideband streams should always be presented
- if (getSidebandStreamChanged() || getAutoRefresh()) {
- return true;
- }
-
- // If this layer doesn't have a frame is shouldn't be presented
- if (!hasFrameUpdate()) {
- return false;
- }
-
- // Defer to the derived class to decide whether the next buffer is due for
- // presentation.
- return isBufferDue(expectedPresentTime);
-}
-
-bool BufferLayer::latchBuffer(bool& recomputeVisibleRegions, nsecs_t latchTime,
- nsecs_t expectedPresentTime) {
- ATRACE_CALL();
-
- bool refreshRequired = latchSidebandStream(recomputeVisibleRegions);
-
- if (refreshRequired) {
- return refreshRequired;
- }
-
- // If the head buffer's acquire fence hasn't signaled yet, return and
- // try again later
- if (!fenceHasSignaled()) {
- ATRACE_NAME("!fenceHasSignaled()");
- mFlinger->onLayerUpdate();
- return false;
- }
-
- // Capture the old state of the layer for comparisons later
- const State& s(getDrawingState());
- const bool oldOpacity = isOpaque(s);
-
- BufferInfo oldBufferInfo = mBufferInfo;
-
- status_t err = updateTexImage(recomputeVisibleRegions, latchTime, expectedPresentTime);
- if (err != NO_ERROR) {
- return false;
- }
-
- err = updateActiveBuffer();
- if (err != NO_ERROR) {
- return false;
- }
-
- err = updateFrameNumber();
- if (err != NO_ERROR) {
- return false;
- }
-
- gatherBufferInfo();
-
- if (oldBufferInfo.mBuffer == nullptr) {
- // the first time we receive a buffer, we need to trigger a
- // geometry invalidation.
- recomputeVisibleRegions = true;
- }
-
- if ((mBufferInfo.mCrop != oldBufferInfo.mCrop) ||
- (mBufferInfo.mTransform != oldBufferInfo.mTransform) ||
- (mBufferInfo.mScaleMode != oldBufferInfo.mScaleMode) ||
- (mBufferInfo.mTransformToDisplayInverse != oldBufferInfo.mTransformToDisplayInverse)) {
- recomputeVisibleRegions = true;
- }
-
- if (oldBufferInfo.mBuffer != nullptr) {
- uint32_t bufWidth = mBufferInfo.mBuffer->getWidth();
- uint32_t bufHeight = mBufferInfo.mBuffer->getHeight();
- if (bufWidth != oldBufferInfo.mBuffer->getWidth() ||
- bufHeight != oldBufferInfo.mBuffer->getHeight()) {
- recomputeVisibleRegions = true;
- }
- }
-
- if (oldOpacity != isOpaque(s)) {
- recomputeVisibleRegions = true;
- }
-
- return true;
-}
-
-bool BufferLayer::hasReadyFrame() const {
- return hasFrameUpdate() || getSidebandStreamChanged() || getAutoRefresh();
-}
-
-uint32_t BufferLayer::getEffectiveScalingMode() const {
- return mBufferInfo.mScaleMode;
-}
-
-bool BufferLayer::isProtected() const {
- return (mBufferInfo.mBuffer != nullptr) &&
- (mBufferInfo.mBuffer->getUsage() & GRALLOC_USAGE_PROTECTED);
-}
-
-// As documented in libhardware header, formats in the range
-// 0x100 - 0x1FF are specific to the HAL implementation, and
-// are known to have no alpha channel
-// TODO: move definition for device-specific range into
-// hardware.h, instead of using hard-coded values here.
-#define HARDWARE_IS_DEVICE_FORMAT(f) ((f) >= 0x100 && (f) <= 0x1FF)
-
-bool BufferLayer::getOpacityForFormat(PixelFormat format) {
- if (HARDWARE_IS_DEVICE_FORMAT(format)) {
- return true;
- }
- switch (format) {
- case PIXEL_FORMAT_RGBA_8888:
- case PIXEL_FORMAT_BGRA_8888:
- case PIXEL_FORMAT_RGBA_FP16:
- case PIXEL_FORMAT_RGBA_1010102:
- case PIXEL_FORMAT_R_8:
- return false;
- }
- // in all other case, we have no blending (also for unknown formats)
- return true;
-}
-
-bool BufferLayer::needsFiltering(const DisplayDevice* display) const {
- const auto outputLayer = findOutputLayerForDisplay(display);
- if (outputLayer == nullptr) {
- return false;
- }
-
- // We need filtering if the sourceCrop rectangle size does not match the
- // displayframe rectangle size (not a 1:1 render)
- const auto& compositionState = outputLayer->getState();
- const auto displayFrame = compositionState.displayFrame;
- const auto sourceCrop = compositionState.sourceCrop;
- return sourceCrop.getHeight() != displayFrame.getHeight() ||
- sourceCrop.getWidth() != displayFrame.getWidth();
-}
-
-bool BufferLayer::needsFilteringForScreenshots(const DisplayDevice* display,
- const ui::Transform& inverseParentTransform) const {
- const auto outputLayer = findOutputLayerForDisplay(display);
- if (outputLayer == nullptr) {
- return false;
- }
-
- // We need filtering if the sourceCrop rectangle size does not match the
- // viewport rectangle size (not a 1:1 render)
- const auto& compositionState = outputLayer->getState();
- const ui::Transform& displayTransform = display->getTransform();
- const ui::Transform inverseTransform = inverseParentTransform * displayTransform.inverse();
- // Undo the transformation of the displayFrame so that we're back into
- // layer-stack space.
- const Rect frame = inverseTransform.transform(compositionState.displayFrame);
- const FloatRect sourceCrop = compositionState.sourceCrop;
-
- int32_t frameHeight = frame.getHeight();
- int32_t frameWidth = frame.getWidth();
- // If the display transform had a rotational component then undo the
- // rotation so that the orientation matches the source crop.
- if (displayTransform.getOrientation() & ui::Transform::ROT_90) {
- std::swap(frameHeight, frameWidth);
- }
- return sourceCrop.getHeight() != frameHeight || sourceCrop.getWidth() != frameWidth;
-}
-
-Rect BufferLayer::getBufferSize(const State& s) const {
- // If we have a sideband stream, or we are scaling the buffer then return the layer size since
- // we cannot determine the buffer size.
- if ((s.sidebandStream != nullptr) ||
- (getEffectiveScalingMode() != NATIVE_WINDOW_SCALING_MODE_FREEZE)) {
- return Rect(getActiveWidth(s), getActiveHeight(s));
- }
-
- if (mBufferInfo.mBuffer == nullptr) {
- return Rect::INVALID_RECT;
- }
-
- uint32_t bufWidth = mBufferInfo.mBuffer->getWidth();
- uint32_t bufHeight = mBufferInfo.mBuffer->getHeight();
-
- // Undo any transformations on the buffer and return the result.
- if (mBufferInfo.mTransform & ui::Transform::ROT_90) {
- std::swap(bufWidth, bufHeight);
- }
-
- if (getTransformToDisplayInverse()) {
- uint32_t invTransform = DisplayDevice::getPrimaryDisplayRotationFlags();
- if (invTransform & ui::Transform::ROT_90) {
- std::swap(bufWidth, bufHeight);
- }
- }
-
- return Rect(bufWidth, bufHeight);
-}
-
-FloatRect BufferLayer::computeSourceBounds(const FloatRect& parentBounds) const {
- const State& s(getDrawingState());
-
- // If we have a sideband stream, or we are scaling the buffer then return the layer size since
- // we cannot determine the buffer size.
- if ((s.sidebandStream != nullptr) ||
- (getEffectiveScalingMode() != NATIVE_WINDOW_SCALING_MODE_FREEZE)) {
- return FloatRect(0, 0, getActiveWidth(s), getActiveHeight(s));
- }
-
- if (mBufferInfo.mBuffer == nullptr) {
- return parentBounds;
- }
-
- uint32_t bufWidth = mBufferInfo.mBuffer->getWidth();
- uint32_t bufHeight = mBufferInfo.mBuffer->getHeight();
-
- // Undo any transformations on the buffer and return the result.
- if (mBufferInfo.mTransform & ui::Transform::ROT_90) {
- std::swap(bufWidth, bufHeight);
- }
-
- if (getTransformToDisplayInverse()) {
- uint32_t invTransform = DisplayDevice::getPrimaryDisplayRotationFlags();
- if (invTransform & ui::Transform::ROT_90) {
- std::swap(bufWidth, bufHeight);
- }
- }
-
- return FloatRect(0, 0, bufWidth, bufHeight);
-}
-
-void BufferLayer::latchAndReleaseBuffer() {
- if (hasReadyFrame()) {
- bool ignored = false;
- latchBuffer(ignored, systemTime(), 0 /* expectedPresentTime */);
- }
- releasePendingBuffer(systemTime());
-}
-
-PixelFormat BufferLayer::getPixelFormat() const {
- return mBufferInfo.mPixelFormat;
-}
-
-bool BufferLayer::getTransformToDisplayInverse() const {
- return mBufferInfo.mTransformToDisplayInverse;
-}
-
-Rect BufferLayer::getBufferCrop() const {
- // this is the crop rectangle that applies to the buffer
- // itself (as opposed to the window)
- if (!mBufferInfo.mCrop.isEmpty()) {
- // if the buffer crop is defined, we use that
- return mBufferInfo.mCrop;
- } else if (mBufferInfo.mBuffer != nullptr) {
- // otherwise we use the whole buffer
- return mBufferInfo.mBuffer->getBounds();
- } else {
- // if we don't have a buffer yet, we use an empty/invalid crop
- return Rect();
- }
-}
-
-uint32_t BufferLayer::getBufferTransform() const {
- return mBufferInfo.mTransform;
-}
-
-ui::Dataspace BufferLayer::getDataSpace() const {
- return mBufferInfo.mDataspace;
-}
-
-ui::Dataspace BufferLayer::translateDataspace(ui::Dataspace dataspace) {
- ui::Dataspace updatedDataspace = dataspace;
- // translate legacy dataspaces to modern dataspaces
- switch (dataspace) {
- case ui::Dataspace::SRGB:
- updatedDataspace = ui::Dataspace::V0_SRGB;
- break;
- case ui::Dataspace::SRGB_LINEAR:
- updatedDataspace = ui::Dataspace::V0_SRGB_LINEAR;
- break;
- case ui::Dataspace::JFIF:
- updatedDataspace = ui::Dataspace::V0_JFIF;
- break;
- case ui::Dataspace::BT601_625:
- updatedDataspace = ui::Dataspace::V0_BT601_625;
- break;
- case ui::Dataspace::BT601_525:
- updatedDataspace = ui::Dataspace::V0_BT601_525;
- break;
- case ui::Dataspace::BT709:
- updatedDataspace = ui::Dataspace::V0_BT709;
- break;
- default:
- break;
- }
-
- return updatedDataspace;
-}
-
-sp<GraphicBuffer> BufferLayer::getBuffer() const {
- return mBufferInfo.mBuffer ? mBufferInfo.mBuffer->getBuffer() : nullptr;
-}
-
-void BufferLayer::getDrawingTransformMatrix(bool filteringEnabled, float outMatrix[16]) {
- sp<GraphicBuffer> buffer = getBuffer();
- if (!buffer) {
- ALOGE("Buffer should not be null!");
- return;
- }
- GLConsumer::computeTransformMatrix(outMatrix, buffer->getWidth(), buffer->getHeight(),
- buffer->getPixelFormat(), mBufferInfo.mCrop,
- mBufferInfo.mTransform, filteringEnabled);
-}
-
-void BufferLayer::setInitialValuesForClone(const sp<Layer>& clonedFrom) {
- Layer::setInitialValuesForClone(clonedFrom);
-
- sp<BufferLayer> bufferClonedFrom = static_cast<BufferLayer*>(clonedFrom.get());
- mPremultipliedAlpha = bufferClonedFrom->mPremultipliedAlpha;
- mPotentialCursor = bufferClonedFrom->mPotentialCursor;
- mProtectedByApp = bufferClonedFrom->mProtectedByApp;
-
- updateCloneBufferInfo();
-}
-
-void BufferLayer::updateCloneBufferInfo() {
- if (!isClone() || !isClonedFromAlive()) {
- return;
- }
-
- sp<BufferLayer> clonedFrom = static_cast<BufferLayer*>(getClonedFrom().get());
- mBufferInfo = clonedFrom->mBufferInfo;
- mSidebandStream = clonedFrom->mSidebandStream;
- surfaceDamageRegion = clonedFrom->surfaceDamageRegion;
- mCurrentFrameNumber = clonedFrom->mCurrentFrameNumber.load();
- mPreviousFrameNumber = clonedFrom->mPreviousFrameNumber;
-
- // After buffer info is updated, the drawingState from the real layer needs to be copied into
- // the cloned. This is because some properties of drawingState can change when latchBuffer is
- // called. However, copying the drawingState would also overwrite the cloned layer's relatives
- // and touchableRegionCrop. Therefore, temporarily store the relatives so they can be set in
- // the cloned drawingState again.
- wp<Layer> tmpZOrderRelativeOf = mDrawingState.zOrderRelativeOf;
- SortedVector<wp<Layer>> tmpZOrderRelatives = mDrawingState.zOrderRelatives;
- wp<Layer> tmpTouchableRegionCrop = mDrawingState.touchableRegionCrop;
- WindowInfo tmpInputInfo = mDrawingState.inputInfo;
-
- cloneDrawingState(clonedFrom.get());
-
- mDrawingState.touchableRegionCrop = tmpTouchableRegionCrop;
- mDrawingState.zOrderRelativeOf = tmpZOrderRelativeOf;
- mDrawingState.zOrderRelatives = tmpZOrderRelatives;
- mDrawingState.inputInfo = tmpInputInfo;
-}
-
-void BufferLayer::setTransformHint(ui::Transform::RotationFlags displayTransformHint) {
- mTransformHint = getFixedTransformHint();
- if (mTransformHint == ui::Transform::ROT_INVALID) {
- mTransformHint = displayTransformHint;
- }
-}
-
-bool BufferLayer::bufferNeedsFiltering() const {
- return isFixedSize();
-}
-
-const std::shared_ptr<renderengine::ExternalTexture>& BufferLayer::getExternalTexture() const {
- return mBufferInfo.mBuffer;
-}
-
-} // namespace android
-
-#if defined(__gl_h_)
-#error "don't include gl/gl.h in this file"
-#endif
-
-#if defined(__gl2_h_)
-#error "don't include gl2/gl2.h in this file"
-#endif
-
-// TODO(b/129481165): remove the #pragma below and fix conversion issues
-#pragma clang diagnostic pop // ignored "-Wconversion"
diff --git a/services/surfaceflinger/BufferLayer.h b/services/surfaceflinger/BufferLayer.h
deleted file mode 100644
index 3bb0fb3..0000000
--- a/services/surfaceflinger/BufferLayer.h
+++ /dev/null
@@ -1,222 +0,0 @@
-/*
- * Copyright (C) 2017 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 <sys/types.h>
-#include <cstdint>
-#include <list>
-
-#include <android/gui/ISurfaceComposerClient.h>
-#include <gui/LayerState.h>
-#include <renderengine/Image.h>
-#include <renderengine/Mesh.h>
-#include <renderengine/Texture.h>
-#include <system/window.h> // For NATIVE_WINDOW_SCALING_MODE_FREEZE
-#include <ui/FrameStats.h>
-#include <ui/GraphicBuffer.h>
-#include <ui/PixelFormat.h>
-#include <ui/Region.h>
-#include <utils/RefBase.h>
-#include <utils/String8.h>
-#include <utils/Timers.h>
-
-#include "BufferLayerConsumer.h"
-#include "Client.h"
-#include "DisplayHardware/HWComposer.h"
-#include "FrameTimeline.h"
-#include "FrameTracker.h"
-#include "Layer.h"
-#include "LayerVector.h"
-#include "MonitoredProducer.h"
-#include "SurfaceFlinger.h"
-
-namespace android {
-
-class BufferLayer : public Layer {
-public:
- explicit BufferLayer(const LayerCreationArgs& args);
- virtual ~BufferLayer() override;
-
- // Implements Layer.
- sp<compositionengine::LayerFE> getCompositionEngineLayerFE() const override;
- compositionengine::LayerFECompositionState* editCompositionState() override;
-
- // If we have received a new buffer this frame, we will pass its surface
- // damage down to hardware composer. Otherwise, we must send a region with
- // one empty rect.
- void useSurfaceDamage() override;
- void useEmptyDamage() override;
-
- bool isOpaque(const Layer::State& s) const override;
- bool canReceiveInput() const override;
-
- // isVisible - true if this layer is visible, false otherwise
- bool isVisible() const override;
-
- // isProtected - true if the layer may contain protected content in the
- // GRALLOC_USAGE_PROTECTED sense.
- bool isProtected() const override;
-
- // isFixedSize - true if content has a fixed size
- bool isFixedSize() const override;
-
- bool usesSourceCrop() const override;
-
- bool isHdrY410() const override;
-
- void onPostComposition(const DisplayDevice*, const std::shared_ptr<FenceTime>& glDoneFence,
- const std::shared_ptr<FenceTime>& presentFence,
- const CompositorTiming&) override;
-
- // latchBuffer - called each time the screen is redrawn and returns whether
- // the visible regions need to be recomputed (this is a fairly heavy
- // operation, so this should be set only if needed). Typically this is used
- // to figure out if the content or size of a surface has changed.
- bool latchBuffer(bool& recomputeVisibleRegions, nsecs_t latchTime,
- nsecs_t expectedPresentTime) override;
- bool hasReadyFrame() const override;
-
- // Returns the current scaling mode
- uint32_t getEffectiveScalingMode() const override;
-
- // Calls latchBuffer if the buffer has a frame queued and then releases the buffer.
- // This is used if the buffer is just latched and releases to free up the buffer
- // and will not be shown on screen.
- // Should only be called on the main thread.
- void latchAndReleaseBuffer() override;
-
- bool getTransformToDisplayInverse() const override;
-
- Rect getBufferCrop() const override;
-
- uint32_t getBufferTransform() const override;
-
- ui::Dataspace getDataSpace() const override;
-
- sp<GraphicBuffer> getBuffer() const override;
- const std::shared_ptr<renderengine::ExternalTexture>& getExternalTexture() const override;
-
- ui::Transform::RotationFlags getTransformHint() const override { return mTransformHint; }
-
- // Returns true if the transformed buffer size does not match the layer size and we need
- // to apply filtering.
- virtual bool bufferNeedsFiltering() const;
-
-protected:
- struct BufferInfo {
- nsecs_t mDesiredPresentTime;
- std::shared_ptr<FenceTime> mFenceTime;
- sp<Fence> mFence;
- uint32_t mTransform{0};
- ui::Dataspace mDataspace{ui::Dataspace::UNKNOWN};
- Rect mCrop;
- uint32_t mScaleMode{NATIVE_WINDOW_SCALING_MODE_FREEZE};
- Region mSurfaceDamage;
- HdrMetadata mHdrMetadata;
- int mApi;
- PixelFormat mPixelFormat{PIXEL_FORMAT_NONE};
- bool mTransformToDisplayInverse{false};
-
- std::shared_ptr<renderengine::ExternalTexture> mBuffer;
- uint64_t mFrameNumber;
- int mBufferSlot{BufferQueue::INVALID_BUFFER_SLOT};
-
- bool mFrameLatencyNeeded{false};
- };
-
- BufferInfo mBufferInfo;
- virtual void gatherBufferInfo() = 0;
-
- std::optional<compositionengine::LayerFE::LayerSettings> prepareClientComposition(
- compositionengine::LayerFE::ClientCompositionTargetSettings&) override;
-
- /*
- * compositionengine::LayerFE overrides
- */
- const compositionengine::LayerFECompositionState* getCompositionState() const override;
- bool onPreComposition(nsecs_t) override;
- void preparePerFrameCompositionState() override;
-
- static bool getOpacityForFormat(PixelFormat format);
-
- // from graphics API
- const uint32_t mTextureName;
- ui::Dataspace translateDataspace(ui::Dataspace dataspace);
- void setInitialValuesForClone(const sp<Layer>& clonedFrom);
- void updateCloneBufferInfo() override;
- uint64_t mPreviousFrameNumber = 0;
-
- void setTransformHint(ui::Transform::RotationFlags displayTransformHint) override;
-
- // Transform hint provided to the producer. This must be accessed holding
- // the mStateLock.
- ui::Transform::RotationFlags mTransformHint = ui::Transform::ROT_0;
-
- bool getAutoRefresh() const { return mDrawingState.autoRefresh; }
- bool getSidebandStreamChanged() const { return mSidebandStreamChanged; }
-
- // Returns true if the next buffer should be presented at the expected present time
- bool shouldPresentNow(nsecs_t expectedPresentTime) const;
-
- // Returns true if the next buffer should be presented at the expected present time,
- // overridden by BufferStateLayer and BufferQueueLayer for implementation
- // specific logic
- virtual bool isBufferDue(nsecs_t /*expectedPresentTime*/) const = 0;
-
- std::atomic<bool> mSidebandStreamChanged{false};
-
-private:
- virtual bool fenceHasSignaled() const = 0;
- virtual bool framePresentTimeIsCurrent(nsecs_t expectedPresentTime) const = 0;
-
- // Latch sideband stream and returns true if the dirty region should be updated.
- virtual bool latchSidebandStream(bool& recomputeVisibleRegions) = 0;
-
- virtual bool hasFrameUpdate() const = 0;
-
- virtual status_t updateTexImage(bool& recomputeVisibleRegions, nsecs_t latchTime,
- nsecs_t expectedPresentTime) = 0;
-
- virtual status_t updateActiveBuffer() = 0;
- virtual status_t updateFrameNumber() = 0;
-
- // We generate InputWindowHandles for all non-cursor buffered layers regardless of whether they
- // have an InputChannel. This is to enable the InputDispatcher to do PID based occlusion
- // detection.
- bool needsInputInfo() const override { return !mPotentialCursor; }
-
- // Returns true if this layer requires filtering
- bool needsFiltering(const DisplayDevice*) const override;
- bool needsFilteringForScreenshots(const DisplayDevice*,
- const ui::Transform& inverseParentTransform) const override;
-
- // BufferStateLayers can return Rect::INVALID_RECT if the layer does not have a display frame
- // and its parent layer is not bounded
- Rect getBufferSize(const State& s) const override;
-
- PixelFormat getPixelFormat() const;
-
- // Computes the transform matrix using the setFilteringEnabled to determine whether the
- // transform matrix should be computed for use with bilinear filtering.
- void getDrawingTransformMatrix(bool filteringEnabled, float outMatrix[16]);
-
- std::unique_ptr<compositionengine::LayerFECompositionState> mCompositionState;
-
- FloatRect computeSourceBounds(const FloatRect& parentBounds) const override;
-};
-
-} // namespace android
diff --git a/services/surfaceflinger/BufferLayerConsumer.cpp b/services/surfaceflinger/BufferLayerConsumer.cpp
deleted file mode 100644
index 7361a4f..0000000
--- a/services/surfaceflinger/BufferLayerConsumer.cpp
+++ /dev/null
@@ -1,500 +0,0 @@
-/*
- * Copyright (C) 2010 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.
- */
-
-// TODO(b/129481165): remove the #pragma below and fix conversion issues
-#pragma clang diagnostic push
-#pragma clang diagnostic ignored "-Wconversion"
-
-#undef LOG_TAG
-#define LOG_TAG "BufferLayerConsumer"
-#define ATRACE_TAG ATRACE_TAG_GRAPHICS
-//#define LOG_NDEBUG 0
-
-#include "BufferLayerConsumer.h"
-#include "Layer.h"
-#include "Scheduler/VsyncController.h"
-
-#include <inttypes.h>
-
-#include <cutils/compiler.h>
-
-#include <hardware/hardware.h>
-
-#include <math/mat4.h>
-
-#include <gui/BufferItem.h>
-#include <gui/GLConsumer.h>
-#include <gui/ISurfaceComposer.h>
-#include <gui/SurfaceComposerClient.h>
-#include <private/gui/ComposerService.h>
-#include <renderengine/RenderEngine.h>
-#include <renderengine/impl/ExternalTexture.h>
-#include <utils/Log.h>
-#include <utils/String8.h>
-#include <utils/Trace.h>
-
-namespace android {
-
-// Macros for including the BufferLayerConsumer name in log messages
-#define BLC_LOGV(x, ...) ALOGV("[%s] " x, mName.string(), ##__VA_ARGS__)
-#define BLC_LOGD(x, ...) ALOGD("[%s] " x, mName.string(), ##__VA_ARGS__)
-//#define BLC_LOGI(x, ...) ALOGI("[%s] " x, mName.string(), ##__VA_ARGS__)
-#define BLC_LOGW(x, ...) ALOGW("[%s] " x, mName.string(), ##__VA_ARGS__)
-#define BLC_LOGE(x, ...) ALOGE("[%s] " x, mName.string(), ##__VA_ARGS__)
-
-static const mat4 mtxIdentity;
-
-BufferLayerConsumer::BufferLayerConsumer(const sp<IGraphicBufferConsumer>& bq,
- renderengine::RenderEngine& engine, uint32_t tex,
- Layer* layer)
- : ConsumerBase(bq, false),
- mCurrentCrop(Rect::EMPTY_RECT),
- mCurrentTransform(0),
- mCurrentScalingMode(NATIVE_WINDOW_SCALING_MODE_FREEZE),
- mCurrentFence(Fence::NO_FENCE),
- mCurrentTimestamp(0),
- mCurrentDataSpace(ui::Dataspace::UNKNOWN),
- mCurrentFrameNumber(0),
- mCurrentTransformToDisplayInverse(false),
- mCurrentSurfaceDamage(),
- mCurrentApi(0),
- mDefaultWidth(1),
- mDefaultHeight(1),
- mFilteringEnabled(true),
- mRE(engine),
- mTexName(tex),
- mLayer(layer),
- mCurrentTexture(BufferQueue::INVALID_BUFFER_SLOT) {
- BLC_LOGV("BufferLayerConsumer");
-
- memcpy(mCurrentTransformMatrix, mtxIdentity.asArray(), sizeof(mCurrentTransformMatrix));
-
- mConsumer->setConsumerUsageBits(DEFAULT_USAGE_FLAGS);
-}
-
-status_t BufferLayerConsumer::setDefaultBufferSize(uint32_t w, uint32_t h) {
- Mutex::Autolock lock(mMutex);
- if (mAbandoned) {
- BLC_LOGE("setDefaultBufferSize: BufferLayerConsumer is abandoned!");
- return NO_INIT;
- }
- mDefaultWidth = w;
- mDefaultHeight = h;
- return mConsumer->setDefaultBufferSize(w, h);
-}
-
-void BufferLayerConsumer::setContentsChangedListener(const wp<ContentsChangedListener>& listener) {
- setFrameAvailableListener(listener);
- Mutex::Autolock lock(mMutex);
- mContentsChangedListener = listener;
-}
-
-status_t BufferLayerConsumer::updateTexImage(BufferRejecter* rejecter, nsecs_t expectedPresentTime,
- bool* autoRefresh, bool* queuedBuffer,
- uint64_t maxFrameNumber) {
- ATRACE_CALL();
- BLC_LOGV("updateTexImage");
- Mutex::Autolock lock(mMutex);
-
- if (mAbandoned) {
- BLC_LOGE("updateTexImage: BufferLayerConsumer is abandoned!");
- return NO_INIT;
- }
-
- BufferItem item;
-
- // Acquire the next buffer.
- // In asynchronous mode the list is guaranteed to be one buffer
- // deep, while in synchronous mode we use the oldest buffer.
- status_t err = acquireBufferLocked(&item, expectedPresentTime, maxFrameNumber);
- if (err != NO_ERROR) {
- if (err == BufferQueue::NO_BUFFER_AVAILABLE) {
- err = NO_ERROR;
- } else if (err == BufferQueue::PRESENT_LATER) {
- // return the error, without logging
- } else {
- BLC_LOGE("updateTexImage: acquire failed: %s (%d)", strerror(-err), err);
- }
- return err;
- }
-
- if (autoRefresh) {
- *autoRefresh = item.mAutoRefresh;
- }
-
- if (queuedBuffer) {
- *queuedBuffer = item.mQueuedBuffer;
- }
-
- // We call the rejecter here, in case the caller has a reason to
- // not accept this buffer. This is used by SurfaceFlinger to
- // reject buffers which have the wrong size
- int slot = item.mSlot;
- if (rejecter && rejecter->reject(mSlots[slot].mGraphicBuffer, item)) {
- releaseBufferLocked(slot, mSlots[slot].mGraphicBuffer);
- return BUFFER_REJECTED;
- }
-
- // Release the previous buffer.
- err = updateAndReleaseLocked(item, &mPendingRelease);
- if (err != NO_ERROR) {
- return err;
- }
- return err;
-}
-
-void BufferLayerConsumer::setReleaseFence(const sp<Fence>& fence) {
- if (!fence->isValid()) {
- return;
- }
-
- auto slot = mPendingRelease.isPending ? mPendingRelease.currentTexture : mCurrentTexture;
- if (slot == BufferQueue::INVALID_BUFFER_SLOT) {
- return;
- }
-
- auto buffer = mPendingRelease.isPending ? mPendingRelease.graphicBuffer
- : mCurrentTextureBuffer->getBuffer();
- auto err = addReleaseFence(slot, buffer, fence);
- if (err != OK) {
- BLC_LOGE("setReleaseFence: failed to add the fence: %s (%d)", strerror(-err), err);
- }
-}
-
-bool BufferLayerConsumer::releasePendingBuffer() {
- if (!mPendingRelease.isPending) {
- BLC_LOGV("Pending buffer already released");
- return false;
- }
- BLC_LOGV("Releasing pending buffer");
- Mutex::Autolock lock(mMutex);
- status_t result =
- releaseBufferLocked(mPendingRelease.currentTexture, mPendingRelease.graphicBuffer);
- if (result < NO_ERROR) {
- BLC_LOGE("releasePendingBuffer failed: %s (%d)", strerror(-result), result);
- }
- mPendingRelease = PendingRelease();
- return true;
-}
-
-sp<Fence> BufferLayerConsumer::getPrevFinalReleaseFence() const {
- Mutex::Autolock lock(mMutex);
- return ConsumerBase::mPrevFinalReleaseFence;
-}
-
-status_t BufferLayerConsumer::acquireBufferLocked(BufferItem* item, nsecs_t presentWhen,
- uint64_t maxFrameNumber) {
- status_t err = ConsumerBase::acquireBufferLocked(item, presentWhen, maxFrameNumber);
- if (err != NO_ERROR) {
- return err;
- }
-
- // If item->mGraphicBuffer is not null, this buffer has not been acquired
- // before, so we need to clean up old references.
- if (item->mGraphicBuffer != nullptr) {
- std::lock_guard<std::mutex> lock(mImagesMutex);
- if (mImages[item->mSlot] == nullptr || mImages[item->mSlot]->getBuffer() == nullptr ||
- mImages[item->mSlot]->getBuffer()->getId() != item->mGraphicBuffer->getId()) {
- mImages[item->mSlot] = std::make_shared<
- renderengine::impl::ExternalTexture>(item->mGraphicBuffer, mRE,
- renderengine::impl::ExternalTexture::
- Usage::READABLE);
- }
- }
-
- return NO_ERROR;
-}
-
-status_t BufferLayerConsumer::updateAndReleaseLocked(const BufferItem& item,
- PendingRelease* pendingRelease) {
- status_t err = NO_ERROR;
-
- int slot = item.mSlot;
-
- BLC_LOGV("updateAndRelease: (slot=%d buf=%p) -> (slot=%d buf=%p)", mCurrentTexture,
- (mCurrentTextureBuffer != nullptr && mCurrentTextureBuffer->getBuffer() != nullptr)
- ? mCurrentTextureBuffer->getBuffer()->handle
- : 0,
- slot, mSlots[slot].mGraphicBuffer->handle);
-
- // Hang onto the pointer so that it isn't freed in the call to
- // releaseBufferLocked() if we're in shared buffer mode and both buffers are
- // the same.
-
- std::shared_ptr<renderengine::ExternalTexture> nextTextureBuffer;
- {
- std::lock_guard<std::mutex> lock(mImagesMutex);
- nextTextureBuffer = mImages[slot];
- }
-
- // release old buffer
- if (mCurrentTexture != BufferQueue::INVALID_BUFFER_SLOT) {
- if (pendingRelease == nullptr) {
- status_t status =
- releaseBufferLocked(mCurrentTexture, mCurrentTextureBuffer->getBuffer());
- if (status < NO_ERROR) {
- BLC_LOGE("updateAndRelease: failed to release buffer: %s (%d)", strerror(-status),
- status);
- err = status;
- // keep going, with error raised [?]
- }
- } else {
- pendingRelease->currentTexture = mCurrentTexture;
- pendingRelease->graphicBuffer = mCurrentTextureBuffer->getBuffer();
- pendingRelease->isPending = true;
- }
- }
-
- // Update the BufferLayerConsumer state.
- mCurrentTexture = slot;
- mCurrentTextureBuffer = nextTextureBuffer;
- mCurrentCrop = item.mCrop;
- mCurrentTransform = item.mTransform;
- mCurrentScalingMode = item.mScalingMode;
- mCurrentTimestamp = item.mTimestamp;
- mCurrentDataSpace = static_cast<ui::Dataspace>(item.mDataSpace);
- mCurrentHdrMetadata = item.mHdrMetadata;
- mCurrentFence = item.mFence;
- mCurrentFenceTime = item.mFenceTime;
- mCurrentFrameNumber = item.mFrameNumber;
- mCurrentTransformToDisplayInverse = item.mTransformToDisplayInverse;
- mCurrentSurfaceDamage = item.mSurfaceDamage;
- mCurrentApi = item.mApi;
-
- computeCurrentTransformMatrixLocked();
-
- return err;
-}
-
-void BufferLayerConsumer::getTransformMatrix(float mtx[16]) {
- Mutex::Autolock lock(mMutex);
- memcpy(mtx, mCurrentTransformMatrix, sizeof(mCurrentTransformMatrix));
-}
-
-void BufferLayerConsumer::setFilteringEnabled(bool enabled) {
- Mutex::Autolock lock(mMutex);
- if (mAbandoned) {
- BLC_LOGE("setFilteringEnabled: BufferLayerConsumer is abandoned!");
- return;
- }
- bool needsRecompute = mFilteringEnabled != enabled;
- mFilteringEnabled = enabled;
-
- if (needsRecompute && mCurrentTextureBuffer == nullptr) {
- BLC_LOGD("setFilteringEnabled called with mCurrentTextureBuffer == nullptr");
- }
-
- if (needsRecompute && mCurrentTextureBuffer != nullptr) {
- computeCurrentTransformMatrixLocked();
- }
-}
-
-void BufferLayerConsumer::computeCurrentTransformMatrixLocked() {
- BLC_LOGV("computeCurrentTransformMatrixLocked");
- if (mCurrentTextureBuffer == nullptr || mCurrentTextureBuffer->getBuffer() == nullptr) {
- BLC_LOGD("computeCurrentTransformMatrixLocked: "
- "mCurrentTextureBuffer is nullptr");
- }
- GLConsumer::computeTransformMatrix(mCurrentTransformMatrix,
- mCurrentTextureBuffer == nullptr
- ? nullptr
- : mCurrentTextureBuffer->getBuffer(),
- getCurrentCropLocked(), mCurrentTransform,
- mFilteringEnabled);
-}
-
-nsecs_t BufferLayerConsumer::getTimestamp() {
- BLC_LOGV("getTimestamp");
- Mutex::Autolock lock(mMutex);
- return mCurrentTimestamp;
-}
-
-ui::Dataspace BufferLayerConsumer::getCurrentDataSpace() {
- BLC_LOGV("getCurrentDataSpace");
- Mutex::Autolock lock(mMutex);
- return mCurrentDataSpace;
-}
-
-const HdrMetadata& BufferLayerConsumer::getCurrentHdrMetadata() const {
- BLC_LOGV("getCurrentHdrMetadata");
- Mutex::Autolock lock(mMutex);
- return mCurrentHdrMetadata;
-}
-
-uint64_t BufferLayerConsumer::getFrameNumber() {
- BLC_LOGV("getFrameNumber");
- Mutex::Autolock lock(mMutex);
- return mCurrentFrameNumber;
-}
-
-bool BufferLayerConsumer::getTransformToDisplayInverse() const {
- Mutex::Autolock lock(mMutex);
- return mCurrentTransformToDisplayInverse;
-}
-
-const Region& BufferLayerConsumer::getSurfaceDamage() const {
- return mCurrentSurfaceDamage;
-}
-
-void BufferLayerConsumer::mergeSurfaceDamage(const Region& damage) {
- if (damage.bounds() == Rect::INVALID_RECT ||
- mCurrentSurfaceDamage.bounds() == Rect::INVALID_RECT) {
- mCurrentSurfaceDamage = Region::INVALID_REGION;
- } else {
- mCurrentSurfaceDamage |= damage;
- }
-}
-
-int BufferLayerConsumer::getCurrentApi() const {
- Mutex::Autolock lock(mMutex);
- return mCurrentApi;
-}
-
-std::shared_ptr<renderengine::ExternalTexture> BufferLayerConsumer::getCurrentBuffer(
- int* outSlot, sp<Fence>* outFence) const {
- Mutex::Autolock lock(mMutex);
-
- if (outSlot != nullptr) {
- *outSlot = mCurrentTexture;
- }
-
- if (outFence != nullptr) {
- *outFence = mCurrentFence;
- }
-
- return mCurrentTextureBuffer == nullptr ? nullptr : mCurrentTextureBuffer;
-}
-
-Rect BufferLayerConsumer::getCurrentCrop() const {
- Mutex::Autolock lock(mMutex);
- return getCurrentCropLocked();
-}
-
-Rect BufferLayerConsumer::getCurrentCropLocked() const {
- uint32_t width = mDefaultWidth;
- uint32_t height = mDefaultHeight;
- // If the buffer comes with a rotated bit for 90 (or 270) degrees, switch width/height in order
- // to scale and crop correctly.
- if (mCurrentTransform & NATIVE_WINDOW_TRANSFORM_ROT_90) {
- width = mDefaultHeight;
- height = mDefaultWidth;
- }
-
- return (mCurrentScalingMode == NATIVE_WINDOW_SCALING_MODE_SCALE_CROP)
- ? GLConsumer::scaleDownCrop(mCurrentCrop, width, height)
- : mCurrentCrop;
-}
-
-uint32_t BufferLayerConsumer::getCurrentTransform() const {
- Mutex::Autolock lock(mMutex);
- return mCurrentTransform;
-}
-
-uint32_t BufferLayerConsumer::getCurrentScalingMode() const {
- Mutex::Autolock lock(mMutex);
- return mCurrentScalingMode;
-}
-
-sp<Fence> BufferLayerConsumer::getCurrentFence() const {
- Mutex::Autolock lock(mMutex);
- return mCurrentFence;
-}
-
-std::shared_ptr<FenceTime> BufferLayerConsumer::getCurrentFenceTime() const {
- Mutex::Autolock lock(mMutex);
- return mCurrentFenceTime;
-}
-
-void BufferLayerConsumer::freeBufferLocked(int slotIndex) {
- BLC_LOGV("freeBufferLocked: slotIndex=%d", slotIndex);
- std::lock_guard<std::mutex> lock(mImagesMutex);
- if (slotIndex == mCurrentTexture) {
- mCurrentTexture = BufferQueue::INVALID_BUFFER_SLOT;
- }
- mImages[slotIndex] = nullptr;
- ConsumerBase::freeBufferLocked(slotIndex);
-}
-
-void BufferLayerConsumer::onDisconnect() {
- Mutex::Autolock lock(mMutex);
-
- if (mAbandoned) {
- // Nothing to do if we're already abandoned.
- return;
- }
-
- mLayer->onDisconnect();
-}
-
-void BufferLayerConsumer::onSidebandStreamChanged() {
- [[maybe_unused]] FrameAvailableListener* unsafeFrameAvailableListener = nullptr;
- {
- Mutex::Autolock lock(mFrameAvailableMutex);
- unsafeFrameAvailableListener = mFrameAvailableListener.unsafe_get();
- }
- sp<ContentsChangedListener> listener;
- { // scope for the lock
- Mutex::Autolock lock(mMutex);
- ALOG_ASSERT(unsafeFrameAvailableListener == mContentsChangedListener.unsafe_get());
- listener = mContentsChangedListener.promote();
- }
-
- if (listener != nullptr) {
- listener->onSidebandStreamChanged();
- }
-}
-
-void BufferLayerConsumer::onBufferAvailable(const BufferItem& item) {
- if (item.mGraphicBuffer != nullptr && item.mSlot != BufferQueue::INVALID_BUFFER_SLOT) {
- std::lock_guard<std::mutex> lock(mImagesMutex);
- const std::shared_ptr<renderengine::ExternalTexture>& oldImage = mImages[item.mSlot];
- if (oldImage == nullptr || oldImage->getBuffer() == nullptr ||
- oldImage->getBuffer()->getId() != item.mGraphicBuffer->getId()) {
- mImages[item.mSlot] = std::make_shared<
- renderengine::impl::ExternalTexture>(item.mGraphicBuffer, mRE,
- renderengine::impl::ExternalTexture::
- Usage::READABLE);
- }
- }
-}
-
-void BufferLayerConsumer::abandonLocked() {
- BLC_LOGV("abandonLocked");
- mCurrentTextureBuffer = nullptr;
- for (int i = 0; i < BufferQueue::NUM_BUFFER_SLOTS; i++) {
- std::lock_guard<std::mutex> lock(mImagesMutex);
- mImages[i] = nullptr;
- }
- ConsumerBase::abandonLocked();
-}
-
-status_t BufferLayerConsumer::setConsumerUsageBits(uint64_t usage) {
- return ConsumerBase::setConsumerUsageBits(usage | DEFAULT_USAGE_FLAGS);
-}
-
-void BufferLayerConsumer::dumpLocked(String8& result, const char* prefix) const {
- result.appendFormat("%smTexName=%d mCurrentTexture=%d\n"
- "%smCurrentCrop=[%d,%d,%d,%d] mCurrentTransform=%#x\n",
- prefix, mTexName, mCurrentTexture, prefix, mCurrentCrop.left,
- mCurrentCrop.top, mCurrentCrop.right, mCurrentCrop.bottom,
- mCurrentTransform);
-
- ConsumerBase::dumpLocked(result, prefix);
-}
-}; // namespace android
-
-// TODO(b/129481165): remove the #pragma below and fix conversion issues
-#pragma clang diagnostic pop // ignored "-Wconversion"
diff --git a/services/surfaceflinger/BufferLayerConsumer.h b/services/surfaceflinger/BufferLayerConsumer.h
deleted file mode 100644
index 23ad2a3..0000000
--- a/services/surfaceflinger/BufferLayerConsumer.h
+++ /dev/null
@@ -1,354 +0,0 @@
-/*
- * Copyright (C) 2010 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.
- */
-
-#ifndef ANDROID_BUFFERLAYERCONSUMER_H
-#define ANDROID_BUFFERLAYERCONSUMER_H
-
-#include <android-base/thread_annotations.h>
-#include <gui/BufferQueueDefs.h>
-#include <gui/ConsumerBase.h>
-#include <gui/HdrMetadata.h>
-#include <renderengine/ExternalTexture.h>
-#include <ui/FenceTime.h>
-#include <ui/GraphicBuffer.h>
-#include <ui/GraphicTypes.h>
-#include <ui/Region.h>
-#include <utils/String8.h>
-#include <utils/Vector.h>
-#include <utils/threads.h>
-
-namespace android {
-// ----------------------------------------------------------------------------
-
-class Layer;
-class String8;
-
-namespace renderengine {
-class RenderEngine;
-} // namespace renderengine
-
-/*
- * BufferLayerConsumer consumes buffers of graphics data from a BufferQueue,
- * and makes them available to RenderEngine as a texture.
- *
- * A typical usage pattern is to call updateTexImage() when a new frame is
- * desired. If a new frame is available, the frame is latched. If not, the
- * previous contents are retained. The texture is attached and updated after
- * bindTextureImage() is called.
- *
- * All calls to updateTexImage must be made with RenderEngine being current.
- * The texture is attached to the TEXTURE_EXTERNAL texture target.
- */
-class BufferLayerConsumer : public ConsumerBase {
-public:
- static const status_t BUFFER_REJECTED = UNKNOWN_ERROR + 8;
-
- class BufferRejecter {
- friend class BufferLayerConsumer;
- virtual bool reject(const sp<GraphicBuffer>& buf, const BufferItem& item) = 0;
-
- protected:
- virtual ~BufferRejecter() {}
- };
-
- struct ContentsChangedListener : public FrameAvailableListener {
- virtual void onSidebandStreamChanged() = 0;
- };
-
- // BufferLayerConsumer constructs a new BufferLayerConsumer object. The
- // tex parameter indicates the name of the RenderEngine texture to which
- // images are to be streamed.
- BufferLayerConsumer(const sp<IGraphicBufferConsumer>& bq, renderengine::RenderEngine& engine,
- uint32_t tex, Layer* layer);
-
- // Sets the contents changed listener. This should be used instead of
- // ConsumerBase::setFrameAvailableListener().
- void setContentsChangedListener(const wp<ContentsChangedListener>& listener);
-
- // updateTexImage acquires the most recently queued buffer, and sets the
- // image contents of the target texture to it.
- //
- // This call may only be made while RenderEngine is current.
- //
- // This calls doFenceWait to ensure proper synchronization unless native
- // fence is supported.
- //
- // Unlike the GLConsumer version, this version takes a functor that may be
- // used to reject the newly acquired buffer. It also does not bind the
- // RenderEngine texture until bindTextureImage is called.
- status_t updateTexImage(BufferRejecter* rejecter, nsecs_t expectedPresentTime,
- bool* autoRefresh, bool* queuedBuffer, uint64_t maxFrameNumber);
-
- // setReleaseFence stores a fence that will signal when the current buffer
- // is no longer being read. This fence will be returned to the producer
- // when the current buffer is released by updateTexImage(). Multiple
- // fences can be set for a given buffer; they will be merged into a single
- // union fence.
- void setReleaseFence(const sp<Fence>& fence);
-
- bool releasePendingBuffer();
-
- sp<Fence> getPrevFinalReleaseFence() const;
-
- // See GLConsumer::getTransformMatrix.
- void getTransformMatrix(float mtx[16]);
-
- // getTimestamp retrieves the timestamp associated with the texture image
- // set by the most recent call to updateTexImage.
- //
- // The timestamp is in nanoseconds, and is monotonically increasing. Its
- // other semantics (zero point, etc) are source-dependent and should be
- // documented by the source.
- int64_t getTimestamp();
-
- // getDataSpace retrieves the DataSpace associated with the texture image
- // set by the most recent call to updateTexImage.
- ui::Dataspace getCurrentDataSpace();
-
- // getCurrentHdrMetadata retrieves the HDR metadata associated with the
- // texture image set by the most recent call to updateTexImage.
- const HdrMetadata& getCurrentHdrMetadata() const;
-
- // getFrameNumber retrieves the frame number associated with the texture
- // image set by the most recent call to updateTexImage.
- //
- // The frame number is an incrementing counter set to 0 at the creation of
- // the BufferQueue associated with this consumer.
- uint64_t getFrameNumber();
-
- bool getTransformToDisplayInverse() const;
-
- // must be called from SF main thread
- const Region& getSurfaceDamage() const;
-
- // Merge the given damage region into the current damage region value.
- void mergeSurfaceDamage(const Region& damage);
-
- // getCurrentApi retrieves the API which queues the current buffer.
- int getCurrentApi() const;
-
- // See GLConsumer::setDefaultBufferSize.
- status_t setDefaultBufferSize(uint32_t width, uint32_t height);
-
- // setFilteringEnabled sets whether the transform matrix should be computed
- // for use with bilinear filtering.
- void setFilteringEnabled(bool enabled);
-
- // getCurrentBuffer returns the buffer associated with the current image.
- // When outSlot is not nullptr, the current buffer slot index is also
- // returned. Simiarly, when outFence is not nullptr, the current output
- // fence is returned.
- std::shared_ptr<renderengine::ExternalTexture> getCurrentBuffer(
- int* outSlot = nullptr, sp<Fence>* outFence = nullptr) const;
-
- // getCurrentCrop returns the cropping rectangle of the current buffer.
- Rect getCurrentCrop() const;
-
- // getCurrentTransform returns the transform of the current buffer.
- uint32_t getCurrentTransform() const;
-
- // getCurrentScalingMode returns the scaling mode of the current buffer.
- uint32_t getCurrentScalingMode() const;
-
- // getCurrentFence returns the fence indicating when the current buffer is
- // ready to be read from.
- sp<Fence> getCurrentFence() const;
-
- // getCurrentFence returns the FenceTime indicating when the current
- // buffer is ready to be read from.
- std::shared_ptr<FenceTime> getCurrentFenceTime() const;
-
- // setConsumerUsageBits overrides the ConsumerBase method to OR
- // DEFAULT_USAGE_FLAGS to usage.
- status_t setConsumerUsageBits(uint64_t usage);
- void onBufferAvailable(const BufferItem& item) EXCLUDES(mImagesMutex);
-
-protected:
- // abandonLocked overrides the ConsumerBase method to clear
- // mCurrentTextureImage in addition to the ConsumerBase behavior.
- virtual void abandonLocked() EXCLUDES(mImagesMutex);
-
- // dumpLocked overrides the ConsumerBase method to dump BufferLayerConsumer-
- // specific info in addition to the ConsumerBase behavior.
- virtual void dumpLocked(String8& result, const char* prefix) const;
-
- // See ConsumerBase::acquireBufferLocked
- virtual status_t acquireBufferLocked(BufferItem* item, nsecs_t presentWhen,
- uint64_t maxFrameNumber = 0) override
- EXCLUDES(mImagesMutex);
-
- bool canUseImageCrop(const Rect& crop) const;
-
- struct PendingRelease {
- PendingRelease() : isPending(false), currentTexture(-1), graphicBuffer() {}
-
- bool isPending;
- int currentTexture;
- sp<GraphicBuffer> graphicBuffer;
- };
-
- // This releases the buffer in the slot referenced by mCurrentTexture,
- // then updates state to refer to the BufferItem, which must be a
- // newly-acquired buffer. If pendingRelease is not null, the parameters
- // which would have been passed to releaseBufferLocked upon the successful
- // completion of the method will instead be returned to the caller, so that
- // it may call releaseBufferLocked itself later.
- status_t updateAndReleaseLocked(const BufferItem& item,
- PendingRelease* pendingRelease = nullptr)
- EXCLUDES(mImagesMutex);
-
-private:
- // Utility class for managing GraphicBuffer references into renderengine
- class Image {
- public:
- Image(const sp<GraphicBuffer>& graphicBuffer, renderengine::RenderEngine& engine);
- virtual ~Image();
- const sp<GraphicBuffer>& graphicBuffer() { return mGraphicBuffer; }
-
- private:
- // mGraphicBuffer is the buffer that was used to create this image.
- sp<GraphicBuffer> mGraphicBuffer;
- // Back-reference into renderengine to initiate cleanup.
- renderengine::RenderEngine& mRE;
- DISALLOW_COPY_AND_ASSIGN(Image);
- };
-
- // freeBufferLocked frees up the given buffer slot. If the slot has been
- // initialized this will release the reference to the GraphicBuffer in
- // that slot. Otherwise it has no effect.
- //
- // This method must be called with mMutex locked.
- virtual void freeBufferLocked(int slotIndex) EXCLUDES(mImagesMutex);
-
- // IConsumerListener interface
- void onDisconnect() override;
- void onSidebandStreamChanged() override;
- void addAndGetFrameTimestamps(const NewFrameEventsEntry*, FrameEventHistoryDelta*) override {}
-
- // computeCurrentTransformMatrixLocked computes the transform matrix for the
- // current texture. It uses mCurrentTransform and the current GraphicBuffer
- // to compute this matrix and stores it in mCurrentTransformMatrix.
- // mCurrentTextureImage must not be nullptr.
- void computeCurrentTransformMatrixLocked();
-
- // getCurrentCropLocked returns the cropping rectangle of the current buffer.
- Rect getCurrentCropLocked() const;
-
- // The default consumer usage flags that BufferLayerConsumer always sets on its
- // BufferQueue instance; these will be OR:d with any additional flags passed
- // from the BufferLayerConsumer user. In particular, BufferLayerConsumer will always
- // consume buffers as hardware textures.
- static const uint64_t DEFAULT_USAGE_FLAGS = GraphicBuffer::USAGE_HW_TEXTURE;
-
- // mCurrentTextureBuffer is the buffer containing the current texture. It's
- // possible that this buffer is not associated with any buffer slot, so we
- // must track it separately in order to support the getCurrentBuffer method.
- std::shared_ptr<renderengine::ExternalTexture> mCurrentTextureBuffer;
-
- // mCurrentCrop is the crop rectangle that applies to the current texture.
- // It gets set each time updateTexImage is called.
- Rect mCurrentCrop;
-
- // mCurrentTransform is the transform identifier for the current texture. It
- // gets set each time updateTexImage is called.
- uint32_t mCurrentTransform;
-
- // mCurrentScalingMode is the scaling mode for the current texture. It gets
- // set each time updateTexImage is called.
- uint32_t mCurrentScalingMode;
-
- // mCurrentFence is the fence received from BufferQueue in updateTexImage.
- sp<Fence> mCurrentFence;
-
- // The FenceTime wrapper around mCurrentFence.
- std::shared_ptr<FenceTime> mCurrentFenceTime{FenceTime::NO_FENCE};
-
- // mCurrentTransformMatrix is the transform matrix for the current texture.
- // It gets computed by computeTransformMatrix each time updateTexImage is
- // called.
- float mCurrentTransformMatrix[16];
-
- // mCurrentTimestamp is the timestamp for the current texture. It
- // gets set each time updateTexImage is called.
- int64_t mCurrentTimestamp;
-
- // mCurrentDataSpace is the dataspace for the current texture. It
- // gets set each time updateTexImage is called.
- ui::Dataspace mCurrentDataSpace;
-
- // mCurrentHdrMetadata is the HDR metadata for the current texture. It
- // gets set each time updateTexImage is called.
- HdrMetadata mCurrentHdrMetadata;
-
- // mCurrentFrameNumber is the frame counter for the current texture.
- // It gets set each time updateTexImage is called.
- uint64_t mCurrentFrameNumber;
-
- // Indicates this buffer must be transformed by the inverse transform of the screen
- // it is displayed onto. This is applied after BufferLayerConsumer::mCurrentTransform.
- // This must be set/read from SurfaceFlinger's main thread.
- bool mCurrentTransformToDisplayInverse;
-
- // The portion of this surface that has changed since the previous frame
- Region mCurrentSurfaceDamage;
-
- int mCurrentApi;
-
- uint32_t mDefaultWidth, mDefaultHeight;
-
- // mFilteringEnabled indicates whether the transform matrix is computed for
- // use with bilinear filtering. It defaults to true and is changed by
- // setFilteringEnabled().
- bool mFilteringEnabled;
-
- renderengine::RenderEngine& mRE;
-
- // mTexName is the name of the RenderEngine texture to which streamed
- // images will be bound when bindTexImage is called. It is set at
- // construction time.
- const uint32_t mTexName;
-
- // The layer for this BufferLayerConsumer. Always check mAbandoned before accessing.
- Layer* mLayer GUARDED_BY(mMutex);
-
- wp<ContentsChangedListener> mContentsChangedListener;
-
- // mCurrentTexture is the buffer slot index of the buffer that is currently
- // bound to the RenderEngine texture. It is initialized to INVALID_BUFFER_SLOT,
- // indicating that no buffer slot is currently bound to the texture. Note,
- // however, that a value of INVALID_BUFFER_SLOT does not necessarily mean
- // that no buffer is bound to the texture. A call to setBufferCount will
- // reset mCurrentTexture to INVALID_BUFFER_SLOT.
- int mCurrentTexture;
-
- // Shadow buffer cache for cleaning up renderengine references.
- std::shared_ptr<renderengine::ExternalTexture>
- mImages[BufferQueueDefs::NUM_BUFFER_SLOTS] GUARDED_BY(mImagesMutex);
-
- // Separate mutex guarding the shadow buffer cache.
- // mImagesMutex can be manipulated with binder threads (e.g. onBuffersAllocated)
- // which is contentious enough that we can't just use mMutex.
- mutable std::mutex mImagesMutex;
-
- // A release that is pending on the receipt of a new release fence from
- // presentDisplay
- PendingRelease mPendingRelease;
-};
-
-// ----------------------------------------------------------------------------
-}; // namespace android
-
-#endif // ANDROID_BUFFERLAYERCONSUMER_H
diff --git a/services/surfaceflinger/BufferStateLayer.cpp b/services/surfaceflinger/BufferStateLayer.cpp
index d88d7c9..59e2d18 100644
--- a/services/surfaceflinger/BufferStateLayer.cpp
+++ b/services/surfaceflinger/BufferStateLayer.cpp
@@ -24,37 +24,82 @@
#include <limits>
#include <FrameTimeline/FrameTimeline.h>
+#include <compositionengine/CompositionEngine.h>
#include <compositionengine/LayerFECompositionState.h>
#include <gui/BufferQueue.h>
#include <private/gui/SyncFeatures.h>
#include <renderengine/Image.h>
#include "TunnelModeEnabledReporter.h"
+#include <gui/TraceUtils.h>
#include "EffectLayer.h"
#include "FrameTracer/FrameTracer.h"
#include "TimeStats/TimeStats.h"
#define EARLY_RELEASE_ENABLED false
+#include <compositionengine/LayerFECompositionState.h>
+#include <compositionengine/OutputLayer.h>
+#include <compositionengine/impl/OutputLayerCompositionState.h>
+#include <cutils/compiler.h>
+#include <cutils/native_handle.h>
+#include <cutils/properties.h>
+#include <gui/BufferItem.h>
+#include <gui/BufferQueue.h>
+#include <gui/GLConsumer.h>
+#include <gui/LayerDebugInfo.h>
+#include <gui/Surface.h>
+#include <renderengine/RenderEngine.h>
+#include <ui/DebugUtils.h>
+#include <utils/Errors.h>
+#include <utils/Log.h>
+#include <utils/NativeHandle.h>
+#include <utils/StopWatch.h>
+#include <utils/Trace.h>
+
+#include <cmath>
+#include <cstdlib>
+#include <mutex>
+#include <sstream>
+
+#include "Colorizer.h"
+#include "DisplayDevice.h"
+#include "FrameTracer/FrameTracer.h"
+#include "TimeStats/TimeStats.h"
+
namespace android {
using PresentState = frametimeline::SurfaceFrame::PresentState;
-namespace {
-void callReleaseBufferCallback(const sp<ITransactionCompletedListener>& listener,
- const sp<GraphicBuffer>& buffer, uint64_t framenumber,
- const sp<Fence>& releaseFence,
- uint32_t currentMaxAcquiredBufferCount) {
+using gui::WindowInfo;
+void BufferStateLayer::callReleaseBufferCallback(const sp<ITransactionCompletedListener>& listener,
+ const sp<GraphicBuffer>& buffer,
+ uint64_t framenumber,
+ const sp<Fence>& releaseFence,
+ uint32_t currentMaxAcquiredBufferCount) {
if (!listener) {
return;
}
+ ATRACE_FORMAT_INSTANT("callReleaseBufferCallback %s - %" PRIu64, getDebugName(), framenumber);
listener->onReleaseBuffer({buffer->getId(), framenumber},
releaseFence ? releaseFence : Fence::NO_FENCE,
currentMaxAcquiredBufferCount);
}
+
+namespace {
+static constexpr float defaultMaxLuminance = 1000.0;
} // namespace
BufferStateLayer::BufferStateLayer(const LayerCreationArgs& args)
- : BufferLayer(args), mHwcSlotGenerator(new HwcSlotGenerator()) {
+ : Layer(args),
+ mTextureName(args.textureName),
+ mCompositionState{mFlinger->getCompositionEngine().createLayerFECompositionState()},
+ mHwcSlotGenerator(new HwcSlotGenerator()) {
+ ALOGV("Creating Layer %s", getDebugName());
+
+ mPremultipliedAlpha = !(args.flags & ISurfaceComposerClient::eNonPremultiplied);
+
+ mPotentialCursor = args.flags & ISurfaceComposerClient::eCursorWindow;
+ mProtectedByApp = args.flags & ISurfaceComposerClient::eProtectedByApp;
mDrawingState.dataspace = ui::Dataspace::V0_SRGB;
}
@@ -70,6 +115,16 @@
mFlinger->getMaxAcquiredBufferCountForCurrentRefreshRate(
mOwnerUid));
}
+ if (!isClone()) {
+ // The original layer and the clone layer share the same texture. Therefore, only one of
+ // the layers, in this case the original layer, needs to handle the deletion. The original
+ // layer and the clone should be removed at the same time so there shouldn't be any issue
+ // with the clone layer trying to use the deleted texture.
+ mFlinger->deleteTextureAsync(mTextureName);
+ }
+ const int32_t layerId = getSequence();
+ mFlinger->mTimeStats->onDestroy(layerId);
+ mFlinger->mFrameTracer->onDestroy(layerId);
}
// -----------------------------------------------------------------------
@@ -145,6 +200,8 @@
handle->dequeueReadyTime = dequeueReadyTime;
handle->currentMaxAcquiredBufferCount =
mFlinger->getMaxAcquiredBufferCountForCurrentRefreshRate(mOwnerUid);
+ ATRACE_FORMAT_INSTANT("releasePendingBuffer %s - %" PRIu64, getDebugName(),
+ handle->previousReleaseCallbackId.framenumber);
}
for (auto& handle : mDrawingState.callbackHandles) {
@@ -569,10 +626,6 @@
}
// -----------------------------------------------------------------------
-
-// -----------------------------------------------------------------------
-// Interface implementation for BufferLayer
-// -----------------------------------------------------------------------
bool BufferStateLayer::fenceHasSignaled() const {
if (SurfaceFlinger::enableLatchUnsignaledConfig != LatchUnsignaledConfig::Disabled) {
return true;
@@ -600,7 +653,7 @@
for (const auto& handle : mDrawingState.callbackHandles) {
handle->refreshStartTime = refreshStartTime;
}
- return BufferLayer::onPreComposition(refreshStartTime);
+ return hasReadyFrame();
}
void BufferStateLayer::setAutoRefresh(bool autoRefresh) {
@@ -785,7 +838,9 @@
}
void BufferStateLayer::gatherBufferInfo() {
- BufferLayer::gatherBufferInfo();
+ mBufferInfo.mPixelFormat =
+ !mBufferInfo.mBuffer ? PIXEL_FORMAT_NONE : mBufferInfo.mBuffer->getPixelFormat();
+ mBufferInfo.mFrameLatencyNeeded = true;
const State& s(getDrawingState());
mBufferInfo.mDesiredPresentTime = s.desiredPresentTime;
@@ -1090,4 +1145,619 @@
return true;
}
+void BufferStateLayer::useSurfaceDamage() {
+ if (mFlinger->mForceFullDamage) {
+ surfaceDamageRegion = Region::INVALID_REGION;
+ } else {
+ surfaceDamageRegion = mBufferInfo.mSurfaceDamage;
+ }
+}
+
+void BufferStateLayer::useEmptyDamage() {
+ surfaceDamageRegion.clear();
+}
+
+bool BufferStateLayer::isOpaque(const Layer::State& s) const {
+ // if we don't have a buffer or sidebandStream yet, we're translucent regardless of the
+ // layer's opaque flag.
+ if ((mSidebandStream == nullptr) && (mBufferInfo.mBuffer == nullptr)) {
+ return false;
+ }
+
+ // if the layer has the opaque flag, then we're always opaque,
+ // otherwise we use the current buffer's format.
+ return ((s.flags & layer_state_t::eLayerOpaque) != 0) || getOpacityForFormat(getPixelFormat());
+}
+
+bool BufferStateLayer::canReceiveInput() const {
+ return !isHiddenByPolicy() && (mBufferInfo.mBuffer == nullptr || getAlpha() > 0.0f);
+}
+
+bool BufferStateLayer::isVisible() const {
+ return !isHiddenByPolicy() && getAlpha() > 0.0f &&
+ (mBufferInfo.mBuffer != nullptr || mSidebandStream != nullptr);
+}
+
+bool BufferStateLayer::isFixedSize() const {
+ return true;
+}
+
+bool BufferStateLayer::usesSourceCrop() const {
+ return true;
+}
+
+static constexpr mat4 inverseOrientation(uint32_t transform) {
+ const mat4 flipH(-1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 1, 0, 0, 1);
+ const mat4 flipV(1, 0, 0, 0, 0, -1, 0, 0, 0, 0, 1, 0, 0, 1, 0, 1);
+ const mat4 rot90(0, 1, 0, 0, -1, 0, 0, 0, 0, 0, 1, 0, 1, 0, 0, 1);
+ mat4 tr;
+
+ if (transform & NATIVE_WINDOW_TRANSFORM_ROT_90) {
+ tr = tr * rot90;
+ }
+ if (transform & NATIVE_WINDOW_TRANSFORM_FLIP_H) {
+ tr = tr * flipH;
+ }
+ if (transform & NATIVE_WINDOW_TRANSFORM_FLIP_V) {
+ tr = tr * flipV;
+ }
+ return inverse(tr);
+}
+
+std::optional<compositionengine::LayerFE::LayerSettings> BufferStateLayer::prepareClientComposition(
+ compositionengine::LayerFE::ClientCompositionTargetSettings& targetSettings) {
+ ATRACE_CALL();
+
+ std::optional<compositionengine::LayerFE::LayerSettings> result =
+ Layer::prepareClientComposition(targetSettings);
+ if (!result) {
+ return result;
+ }
+
+ if (CC_UNLIKELY(mBufferInfo.mBuffer == 0) && mSidebandStream != nullptr) {
+ // For surfaceview of tv sideband, there is no activeBuffer
+ // in bufferqueue, we need return LayerSettings.
+ return result;
+ }
+ const bool blackOutLayer = (isProtected() && !targetSettings.supportsProtectedContent) ||
+ ((isSecure() || isProtected()) && !targetSettings.isSecure);
+ const bool bufferCanBeUsedAsHwTexture =
+ mBufferInfo.mBuffer->getUsage() & GraphicBuffer::USAGE_HW_TEXTURE;
+ compositionengine::LayerFE::LayerSettings& layer = *result;
+ if (blackOutLayer || !bufferCanBeUsedAsHwTexture) {
+ ALOGE_IF(!bufferCanBeUsedAsHwTexture, "%s is blacked out as buffer is not gpu readable",
+ mName.c_str());
+ prepareClearClientComposition(layer, true /* blackout */);
+ return layer;
+ }
+
+ const State& s(getDrawingState());
+ layer.source.buffer.buffer = mBufferInfo.mBuffer;
+ layer.source.buffer.isOpaque = isOpaque(s);
+ layer.source.buffer.fence = mBufferInfo.mFence;
+ layer.source.buffer.textureName = mTextureName;
+ layer.source.buffer.usePremultipliedAlpha = getPremultipledAlpha();
+ layer.source.buffer.isY410BT2020 = isHdrY410();
+ bool hasSmpte2086 = mBufferInfo.mHdrMetadata.validTypes & HdrMetadata::SMPTE2086;
+ bool hasCta861_3 = mBufferInfo.mHdrMetadata.validTypes & HdrMetadata::CTA861_3;
+ float maxLuminance = 0.f;
+ if (hasSmpte2086 && hasCta861_3) {
+ maxLuminance = std::min(mBufferInfo.mHdrMetadata.smpte2086.maxLuminance,
+ mBufferInfo.mHdrMetadata.cta8613.maxContentLightLevel);
+ } else if (hasSmpte2086) {
+ maxLuminance = mBufferInfo.mHdrMetadata.smpte2086.maxLuminance;
+ } else if (hasCta861_3) {
+ maxLuminance = mBufferInfo.mHdrMetadata.cta8613.maxContentLightLevel;
+ } else {
+ switch (layer.sourceDataspace & HAL_DATASPACE_TRANSFER_MASK) {
+ case HAL_DATASPACE_TRANSFER_ST2084:
+ case HAL_DATASPACE_TRANSFER_HLG:
+ // Behavior-match previous releases for HDR content
+ maxLuminance = defaultMaxLuminance;
+ break;
+ }
+ }
+ layer.source.buffer.maxLuminanceNits = maxLuminance;
+ layer.frameNumber = mCurrentFrameNumber;
+ layer.bufferId = mBufferInfo.mBuffer ? mBufferInfo.mBuffer->getId() : 0;
+
+ const bool useFiltering =
+ targetSettings.needsFiltering || mNeedsFiltering || bufferNeedsFiltering();
+
+ // Query the texture matrix given our current filtering mode.
+ float textureMatrix[16];
+ getDrawingTransformMatrix(useFiltering, textureMatrix);
+
+ if (getTransformToDisplayInverse()) {
+ /*
+ * the code below applies the primary display's inverse transform to
+ * the texture transform
+ */
+ uint32_t transform = DisplayDevice::getPrimaryDisplayRotationFlags();
+ mat4 tr = inverseOrientation(transform);
+
+ /**
+ * TODO(b/36727915): This is basically a hack.
+ *
+ * Ensure that regardless of the parent transformation,
+ * this buffer is always transformed from native display
+ * orientation to display orientation. For example, in the case
+ * of a camera where the buffer remains in native orientation,
+ * we want the pixels to always be upright.
+ */
+ sp<Layer> p = mDrawingParent.promote();
+ if (p != nullptr) {
+ const auto parentTransform = p->getTransform();
+ tr = tr * inverseOrientation(parentTransform.getOrientation());
+ }
+
+ // and finally apply it to the original texture matrix
+ const mat4 texTransform(mat4(static_cast<const float*>(textureMatrix)) * tr);
+ memcpy(textureMatrix, texTransform.asArray(), sizeof(textureMatrix));
+ }
+
+ const Rect win{getBounds()};
+ float bufferWidth = getBufferSize(s).getWidth();
+ float bufferHeight = getBufferSize(s).getHeight();
+
+ // BufferStateLayers can have a "buffer size" of [0, 0, -1, -1] when no display frame has
+ // been set and there is no parent layer bounds. In that case, the scale is meaningless so
+ // ignore them.
+ if (!getBufferSize(s).isValid()) {
+ bufferWidth = float(win.right) - float(win.left);
+ bufferHeight = float(win.bottom) - float(win.top);
+ }
+
+ const float scaleHeight = (float(win.bottom) - float(win.top)) / bufferHeight;
+ const float scaleWidth = (float(win.right) - float(win.left)) / bufferWidth;
+ const float translateY = float(win.top) / bufferHeight;
+ const float translateX = float(win.left) / bufferWidth;
+
+ // Flip y-coordinates because GLConsumer expects OpenGL convention.
+ mat4 tr = mat4::translate(vec4(.5f, .5f, 0.f, 1.f)) * mat4::scale(vec4(1.f, -1.f, 1.f, 1.f)) *
+ mat4::translate(vec4(-.5f, -.5f, 0.f, 1.f)) *
+ mat4::translate(vec4(translateX, translateY, 0.f, 1.f)) *
+ mat4::scale(vec4(scaleWidth, scaleHeight, 1.0f, 1.0f));
+
+ layer.source.buffer.useTextureFiltering = useFiltering;
+ layer.source.buffer.textureTransform = mat4(static_cast<const float*>(textureMatrix)) * tr;
+
+ return layer;
+}
+
+bool BufferStateLayer::isHdrY410() const {
+ // pixel format is HDR Y410 masquerading as RGBA_1010102
+ return (mBufferInfo.mDataspace == ui::Dataspace::BT2020_ITU_PQ &&
+ mBufferInfo.mApi == NATIVE_WINDOW_API_MEDIA &&
+ mBufferInfo.mPixelFormat == HAL_PIXEL_FORMAT_RGBA_1010102);
+}
+
+sp<compositionengine::LayerFE> BufferStateLayer::getCompositionEngineLayerFE() const {
+ return asLayerFE();
+}
+
+compositionengine::LayerFECompositionState* BufferStateLayer::editCompositionState() {
+ return mCompositionState.get();
+}
+
+const compositionengine::LayerFECompositionState* BufferStateLayer::getCompositionState() const {
+ return mCompositionState.get();
+}
+
+void BufferStateLayer::preparePerFrameCompositionState() {
+ Layer::preparePerFrameCompositionState();
+
+ // Sideband layers
+ auto* compositionState = editCompositionState();
+ if (compositionState->sidebandStream.get() && !compositionState->sidebandStreamHasFrame) {
+ compositionState->compositionType =
+ aidl::android::hardware::graphics::composer3::Composition::SIDEBAND;
+ return;
+ } else if ((mDrawingState.flags & layer_state_t::eLayerIsDisplayDecoration) != 0) {
+ compositionState->compositionType =
+ aidl::android::hardware::graphics::composer3::Composition::DISPLAY_DECORATION;
+ } else {
+ // Normal buffer layers
+ compositionState->hdrMetadata = mBufferInfo.mHdrMetadata;
+ compositionState->compositionType = mPotentialCursor
+ ? aidl::android::hardware::graphics::composer3::Composition::CURSOR
+ : aidl::android::hardware::graphics::composer3::Composition::DEVICE;
+ }
+
+ compositionState->buffer = getBuffer();
+ compositionState->bufferSlot = (mBufferInfo.mBufferSlot == BufferQueue::INVALID_BUFFER_SLOT)
+ ? 0
+ : mBufferInfo.mBufferSlot;
+ compositionState->acquireFence = mBufferInfo.mFence;
+ compositionState->frameNumber = mBufferInfo.mFrameNumber;
+ compositionState->sidebandStreamHasFrame = false;
+}
+
+namespace {
+TimeStats::SetFrameRateVote frameRateToSetFrameRateVotePayload(Layer::FrameRate frameRate) {
+ using FrameRateCompatibility = TimeStats::SetFrameRateVote::FrameRateCompatibility;
+ using Seamlessness = TimeStats::SetFrameRateVote::Seamlessness;
+ const auto frameRateCompatibility = [frameRate] {
+ switch (frameRate.type) {
+ case Layer::FrameRateCompatibility::Default:
+ return FrameRateCompatibility::Default;
+ case Layer::FrameRateCompatibility::ExactOrMultiple:
+ return FrameRateCompatibility::ExactOrMultiple;
+ default:
+ return FrameRateCompatibility::Undefined;
+ }
+ }();
+
+ const auto seamlessness = [frameRate] {
+ switch (frameRate.seamlessness) {
+ case scheduler::Seamlessness::OnlySeamless:
+ return Seamlessness::ShouldBeSeamless;
+ case scheduler::Seamlessness::SeamedAndSeamless:
+ return Seamlessness::NotRequired;
+ default:
+ return Seamlessness::Undefined;
+ }
+ }();
+
+ return TimeStats::SetFrameRateVote{.frameRate = frameRate.rate.getValue(),
+ .frameRateCompatibility = frameRateCompatibility,
+ .seamlessness = seamlessness};
+}
+} // namespace
+
+void BufferStateLayer::onPostComposition(const DisplayDevice* display,
+ const std::shared_ptr<FenceTime>& glDoneFence,
+ const std::shared_ptr<FenceTime>& presentFence,
+ const CompositorTiming& compositorTiming) {
+ // mFrameLatencyNeeded is true when a new frame was latched for the
+ // composition.
+ if (!mBufferInfo.mFrameLatencyNeeded) return;
+
+ // Update mFrameEventHistory.
+ finalizeFrameEventHistory(glDoneFence, compositorTiming);
+
+ // Update mFrameTracker.
+ nsecs_t desiredPresentTime = mBufferInfo.mDesiredPresentTime;
+ mFrameTracker.setDesiredPresentTime(desiredPresentTime);
+
+ const int32_t layerId = getSequence();
+ mFlinger->mTimeStats->setDesiredTime(layerId, mCurrentFrameNumber, desiredPresentTime);
+
+ const auto outputLayer = findOutputLayerForDisplay(display);
+ if (outputLayer && outputLayer->requiresClientComposition()) {
+ nsecs_t clientCompositionTimestamp = outputLayer->getState().clientCompositionTimestamp;
+ mFlinger->mFrameTracer->traceTimestamp(layerId, getCurrentBufferId(), mCurrentFrameNumber,
+ clientCompositionTimestamp,
+ FrameTracer::FrameEvent::FALLBACK_COMPOSITION);
+ // Update the SurfaceFrames in the drawing state
+ if (mDrawingState.bufferSurfaceFrameTX) {
+ mDrawingState.bufferSurfaceFrameTX->setGpuComposition();
+ }
+ for (auto& [token, surfaceFrame] : mDrawingState.bufferlessSurfaceFramesTX) {
+ surfaceFrame->setGpuComposition();
+ }
+ }
+
+ std::shared_ptr<FenceTime> frameReadyFence = mBufferInfo.mFenceTime;
+ if (frameReadyFence->isValid()) {
+ mFrameTracker.setFrameReadyFence(std::move(frameReadyFence));
+ } else {
+ // There was no fence for this frame, so assume that it was ready
+ // to be presented at the desired present time.
+ mFrameTracker.setFrameReadyTime(desiredPresentTime);
+ }
+
+ if (display) {
+ const Fps refreshRate = display->refreshRateConfigs().getActiveMode()->getFps();
+ const std::optional<Fps> renderRate =
+ mFlinger->mScheduler->getFrameRateOverride(getOwnerUid());
+
+ const auto vote = frameRateToSetFrameRateVotePayload(mDrawingState.frameRate);
+ const auto gameMode = getGameMode();
+
+ if (presentFence->isValid()) {
+ mFlinger->mTimeStats->setPresentFence(layerId, mCurrentFrameNumber, presentFence,
+ refreshRate, renderRate, vote, gameMode);
+ mFlinger->mFrameTracer->traceFence(layerId, getCurrentBufferId(), mCurrentFrameNumber,
+ presentFence,
+ FrameTracer::FrameEvent::PRESENT_FENCE);
+ mFrameTracker.setActualPresentFence(std::shared_ptr<FenceTime>(presentFence));
+ } else if (const auto displayId = PhysicalDisplayId::tryCast(display->getId());
+ displayId && mFlinger->getHwComposer().isConnected(*displayId)) {
+ // The HWC doesn't support present fences, so use the refresh
+ // timestamp instead.
+ const nsecs_t actualPresentTime = display->getRefreshTimestamp();
+ mFlinger->mTimeStats->setPresentTime(layerId, mCurrentFrameNumber, actualPresentTime,
+ refreshRate, renderRate, vote, gameMode);
+ mFlinger->mFrameTracer->traceTimestamp(layerId, getCurrentBufferId(),
+ mCurrentFrameNumber, actualPresentTime,
+ FrameTracer::FrameEvent::PRESENT_FENCE);
+ mFrameTracker.setActualPresentTime(actualPresentTime);
+ }
+ }
+
+ mFrameTracker.advanceFrame();
+ mBufferInfo.mFrameLatencyNeeded = false;
+}
+
+bool BufferStateLayer::latchBuffer(bool& recomputeVisibleRegions, nsecs_t latchTime,
+ nsecs_t expectedPresentTime) {
+ ATRACE_FORMAT_INSTANT("latchBuffer %s - %" PRIu64, getDebugName(),
+ getDrawingState().frameNumber);
+
+ bool refreshRequired = latchSidebandStream(recomputeVisibleRegions);
+
+ if (refreshRequired) {
+ return refreshRequired;
+ }
+
+ // If the head buffer's acquire fence hasn't signaled yet, return and
+ // try again later
+ if (!fenceHasSignaled()) {
+ ATRACE_NAME("!fenceHasSignaled()");
+ mFlinger->onLayerUpdate();
+ return false;
+ }
+
+ // Capture the old state of the layer for comparisons later
+ const State& s(getDrawingState());
+ const bool oldOpacity = isOpaque(s);
+
+ BufferInfo oldBufferInfo = mBufferInfo;
+
+ status_t err = updateTexImage(recomputeVisibleRegions, latchTime, expectedPresentTime);
+ if (err != NO_ERROR) {
+ return false;
+ }
+
+ err = updateActiveBuffer();
+ if (err != NO_ERROR) {
+ return false;
+ }
+
+ err = updateFrameNumber();
+ if (err != NO_ERROR) {
+ return false;
+ }
+
+ gatherBufferInfo();
+
+ if (oldBufferInfo.mBuffer == nullptr) {
+ // the first time we receive a buffer, we need to trigger a
+ // geometry invalidation.
+ recomputeVisibleRegions = true;
+ }
+
+ if ((mBufferInfo.mCrop != oldBufferInfo.mCrop) ||
+ (mBufferInfo.mTransform != oldBufferInfo.mTransform) ||
+ (mBufferInfo.mScaleMode != oldBufferInfo.mScaleMode) ||
+ (mBufferInfo.mTransformToDisplayInverse != oldBufferInfo.mTransformToDisplayInverse)) {
+ recomputeVisibleRegions = true;
+ }
+
+ if (oldBufferInfo.mBuffer != nullptr) {
+ uint32_t bufWidth = mBufferInfo.mBuffer->getWidth();
+ uint32_t bufHeight = mBufferInfo.mBuffer->getHeight();
+ if (bufWidth != oldBufferInfo.mBuffer->getWidth() ||
+ bufHeight != oldBufferInfo.mBuffer->getHeight()) {
+ recomputeVisibleRegions = true;
+ }
+ }
+
+ if (oldOpacity != isOpaque(s)) {
+ recomputeVisibleRegions = true;
+ }
+
+ return true;
+}
+
+bool BufferStateLayer::hasReadyFrame() const {
+ return hasFrameUpdate() || getSidebandStreamChanged() || getAutoRefresh();
+}
+
+bool BufferStateLayer::isProtected() const {
+ return (mBufferInfo.mBuffer != nullptr) &&
+ (mBufferInfo.mBuffer->getUsage() & GRALLOC_USAGE_PROTECTED);
+}
+
+// As documented in libhardware header, formats in the range
+// 0x100 - 0x1FF are specific to the HAL implementation, and
+// are known to have no alpha channel
+// TODO: move definition for device-specific range into
+// hardware.h, instead of using hard-coded values here.
+#define HARDWARE_IS_DEVICE_FORMAT(f) ((f) >= 0x100 && (f) <= 0x1FF)
+
+bool BufferStateLayer::getOpacityForFormat(PixelFormat format) {
+ if (HARDWARE_IS_DEVICE_FORMAT(format)) {
+ return true;
+ }
+ switch (format) {
+ case PIXEL_FORMAT_RGBA_8888:
+ case PIXEL_FORMAT_BGRA_8888:
+ case PIXEL_FORMAT_RGBA_FP16:
+ case PIXEL_FORMAT_RGBA_1010102:
+ case PIXEL_FORMAT_R_8:
+ return false;
+ }
+ // in all other case, we have no blending (also for unknown formats)
+ return true;
+}
+
+bool BufferStateLayer::needsFiltering(const DisplayDevice* display) const {
+ const auto outputLayer = findOutputLayerForDisplay(display);
+ if (outputLayer == nullptr) {
+ return false;
+ }
+
+ // We need filtering if the sourceCrop rectangle size does not match the
+ // displayframe rectangle size (not a 1:1 render)
+ const auto& compositionState = outputLayer->getState();
+ const auto displayFrame = compositionState.displayFrame;
+ const auto sourceCrop = compositionState.sourceCrop;
+ return sourceCrop.getHeight() != displayFrame.getHeight() ||
+ sourceCrop.getWidth() != displayFrame.getWidth();
+}
+
+bool BufferStateLayer::needsFilteringForScreenshots(
+ const DisplayDevice* display, const ui::Transform& inverseParentTransform) const {
+ const auto outputLayer = findOutputLayerForDisplay(display);
+ if (outputLayer == nullptr) {
+ return false;
+ }
+
+ // We need filtering if the sourceCrop rectangle size does not match the
+ // viewport rectangle size (not a 1:1 render)
+ const auto& compositionState = outputLayer->getState();
+ const ui::Transform& displayTransform = display->getTransform();
+ const ui::Transform inverseTransform = inverseParentTransform * displayTransform.inverse();
+ // Undo the transformation of the displayFrame so that we're back into
+ // layer-stack space.
+ const Rect frame = inverseTransform.transform(compositionState.displayFrame);
+ const FloatRect sourceCrop = compositionState.sourceCrop;
+
+ int32_t frameHeight = frame.getHeight();
+ int32_t frameWidth = frame.getWidth();
+ // If the display transform had a rotational component then undo the
+ // rotation so that the orientation matches the source crop.
+ if (displayTransform.getOrientation() & ui::Transform::ROT_90) {
+ std::swap(frameHeight, frameWidth);
+ }
+ return sourceCrop.getHeight() != frameHeight || sourceCrop.getWidth() != frameWidth;
+}
+
+void BufferStateLayer::latchAndReleaseBuffer() {
+ if (hasReadyFrame()) {
+ bool ignored = false;
+ latchBuffer(ignored, systemTime(), 0 /* expectedPresentTime */);
+ }
+ releasePendingBuffer(systemTime());
+}
+
+PixelFormat BufferStateLayer::getPixelFormat() const {
+ return mBufferInfo.mPixelFormat;
+}
+
+bool BufferStateLayer::getTransformToDisplayInverse() const {
+ return mBufferInfo.mTransformToDisplayInverse;
+}
+
+Rect BufferStateLayer::getBufferCrop() const {
+ // this is the crop rectangle that applies to the buffer
+ // itself (as opposed to the window)
+ if (!mBufferInfo.mCrop.isEmpty()) {
+ // if the buffer crop is defined, we use that
+ return mBufferInfo.mCrop;
+ } else if (mBufferInfo.mBuffer != nullptr) {
+ // otherwise we use the whole buffer
+ return mBufferInfo.mBuffer->getBounds();
+ } else {
+ // if we don't have a buffer yet, we use an empty/invalid crop
+ return Rect();
+ }
+}
+
+uint32_t BufferStateLayer::getBufferTransform() const {
+ return mBufferInfo.mTransform;
+}
+
+ui::Dataspace BufferStateLayer::getDataSpace() const {
+ return mBufferInfo.mDataspace;
+}
+
+ui::Dataspace BufferStateLayer::translateDataspace(ui::Dataspace dataspace) {
+ ui::Dataspace updatedDataspace = dataspace;
+ // translate legacy dataspaces to modern dataspaces
+ switch (dataspace) {
+ case ui::Dataspace::SRGB:
+ updatedDataspace = ui::Dataspace::V0_SRGB;
+ break;
+ case ui::Dataspace::SRGB_LINEAR:
+ updatedDataspace = ui::Dataspace::V0_SRGB_LINEAR;
+ break;
+ case ui::Dataspace::JFIF:
+ updatedDataspace = ui::Dataspace::V0_JFIF;
+ break;
+ case ui::Dataspace::BT601_625:
+ updatedDataspace = ui::Dataspace::V0_BT601_625;
+ break;
+ case ui::Dataspace::BT601_525:
+ updatedDataspace = ui::Dataspace::V0_BT601_525;
+ break;
+ case ui::Dataspace::BT709:
+ updatedDataspace = ui::Dataspace::V0_BT709;
+ break;
+ default:
+ break;
+ }
+
+ return updatedDataspace;
+}
+
+sp<GraphicBuffer> BufferStateLayer::getBuffer() const {
+ return mBufferInfo.mBuffer ? mBufferInfo.mBuffer->getBuffer() : nullptr;
+}
+
+void BufferStateLayer::getDrawingTransformMatrix(bool filteringEnabled, float outMatrix[16]) {
+ sp<GraphicBuffer> buffer = getBuffer();
+ if (!buffer) {
+ ALOGE("Buffer should not be null!");
+ return;
+ }
+ GLConsumer::computeTransformMatrix(outMatrix, buffer->getWidth(), buffer->getHeight(),
+ buffer->getPixelFormat(), mBufferInfo.mCrop,
+ mBufferInfo.mTransform, filteringEnabled);
+}
+
+void BufferStateLayer::setInitialValuesForClone(const sp<Layer>& clonedFrom) {
+ Layer::setInitialValuesForClone(clonedFrom);
+
+ sp<BufferStateLayer> bufferClonedFrom = static_cast<BufferStateLayer*>(clonedFrom.get());
+ mPremultipliedAlpha = bufferClonedFrom->mPremultipliedAlpha;
+ mPotentialCursor = bufferClonedFrom->mPotentialCursor;
+ mProtectedByApp = bufferClonedFrom->mProtectedByApp;
+
+ updateCloneBufferInfo();
+}
+
+void BufferStateLayer::updateCloneBufferInfo() {
+ if (!isClone() || !isClonedFromAlive()) {
+ return;
+ }
+
+ sp<BufferStateLayer> clonedFrom = static_cast<BufferStateLayer*>(getClonedFrom().get());
+ mBufferInfo = clonedFrom->mBufferInfo;
+ mSidebandStream = clonedFrom->mSidebandStream;
+ surfaceDamageRegion = clonedFrom->surfaceDamageRegion;
+ mCurrentFrameNumber = clonedFrom->mCurrentFrameNumber.load();
+ mPreviousFrameNumber = clonedFrom->mPreviousFrameNumber;
+
+ // After buffer info is updated, the drawingState from the real layer needs to be copied into
+ // the cloned. This is because some properties of drawingState can change when latchBuffer is
+ // called. However, copying the drawingState would also overwrite the cloned layer's relatives
+ // and touchableRegionCrop. Therefore, temporarily store the relatives so they can be set in
+ // the cloned drawingState again.
+ wp<Layer> tmpZOrderRelativeOf = mDrawingState.zOrderRelativeOf;
+ SortedVector<wp<Layer>> tmpZOrderRelatives = mDrawingState.zOrderRelatives;
+ wp<Layer> tmpTouchableRegionCrop = mDrawingState.touchableRegionCrop;
+ WindowInfo tmpInputInfo = mDrawingState.inputInfo;
+
+ cloneDrawingState(clonedFrom.get());
+
+ mDrawingState.touchableRegionCrop = tmpTouchableRegionCrop;
+ mDrawingState.zOrderRelativeOf = tmpZOrderRelativeOf;
+ mDrawingState.zOrderRelatives = tmpZOrderRelatives;
+ mDrawingState.inputInfo = tmpInputInfo;
+}
+
+void BufferStateLayer::setTransformHint(ui::Transform::RotationFlags displayTransformHint) {
+ mTransformHint = getFixedTransformHint();
+ if (mTransformHint == ui::Transform::ROT_INVALID) {
+ mTransformHint = displayTransformHint;
+ }
+}
+
+const std::shared_ptr<renderengine::ExternalTexture>& BufferStateLayer::getExternalTexture() const {
+ return mBufferInfo.mBuffer;
+}
+
} // namespace android
diff --git a/services/surfaceflinger/BufferStateLayer.h b/services/surfaceflinger/BufferStateLayer.h
index 3f0dbe4..df7ae61 100644
--- a/services/surfaceflinger/BufferStateLayer.h
+++ b/services/surfaceflinger/BufferStateLayer.h
@@ -16,27 +16,106 @@
#pragma once
-#include "BufferLayer.h"
-#include "Layer.h"
-
-#include <renderengine/Image.h>
-#include <renderengine/RenderEngine.h>
-#include <system/window.h>
-#include <utils/String8.h>
-
+#include <sys/types.h>
+#include <cstdint>
+#include <list>
#include <stack>
+#include <android/gui/ISurfaceComposerClient.h>
+#include <gui/LayerState.h>
+#include <renderengine/Image.h>
+#include <renderengine/Mesh.h>
+#include <renderengine/RenderEngine.h>
+#include <renderengine/Texture.h>
+#include <system/window.h> // For NATIVE_WINDOW_SCALING_MODE_FREEZE
+#include <ui/FrameStats.h>
+#include <ui/GraphicBuffer.h>
+#include <ui/PixelFormat.h>
+#include <ui/Region.h>
+#include <utils/RefBase.h>
+#include <utils/String8.h>
+#include <utils/Timers.h>
+
+#include "Client.h"
+#include "DisplayHardware/HWComposer.h"
+#include "FrameTimeline.h"
+#include "FrameTracker.h"
+#include "Layer.h"
+#include "LayerVector.h"
+#include "SurfaceFlinger.h"
+
namespace android {
class SlotGenerationTest;
-class BufferStateLayer : public BufferLayer {
+class BufferStateLayer : public Layer {
public:
explicit BufferStateLayer(const LayerCreationArgs&);
~BufferStateLayer() override;
// Implements Layer.
+ sp<compositionengine::LayerFE> getCompositionEngineLayerFE() const override;
+ compositionengine::LayerFECompositionState* editCompositionState() override;
+
+ // If we have received a new buffer this frame, we will pass its surface
+ // damage down to hardware composer. Otherwise, we must send a region with
+ // one empty rect.
+ void useSurfaceDamage() override;
+ void useEmptyDamage() override;
+
+ bool isOpaque(const Layer::State& s) const override;
+ bool canReceiveInput() const override;
+
+ // isVisible - true if this layer is visible, false otherwise
+ bool isVisible() const override;
+
+ // isProtected - true if the layer may contain protected content in the
+ // GRALLOC_USAGE_PROTECTED sense.
+ bool isProtected() const override;
+
+ // isFixedSize - true if content has a fixed size
+ bool isFixedSize() const override;
+
+ bool usesSourceCrop() const override;
+
+ bool isHdrY410() const override;
+
+ void onPostComposition(const DisplayDevice*, const std::shared_ptr<FenceTime>& glDoneFence,
+ const std::shared_ptr<FenceTime>& presentFence,
+ const CompositorTiming&) override;
+
+ // latchBuffer - called each time the screen is redrawn and returns whether
+ // the visible regions need to be recomputed (this is a fairly heavy
+ // operation, so this should be set only if needed). Typically this is used
+ // to figure out if the content or size of a surface has changed.
+ bool latchBuffer(bool& recomputeVisibleRegions, nsecs_t latchTime,
+ nsecs_t expectedPresentTime) override;
+ bool hasReadyFrame() const override;
+
+ // Returns the current scaling mode
+ uint32_t getEffectiveScalingMode() const override;
+
+ // Calls latchBuffer if the buffer has a frame queued and then releases the buffer.
+ // This is used if the buffer is just latched and releases to free up the buffer
+ // and will not be shown on screen.
+ // Should only be called on the main thread.
+ void latchAndReleaseBuffer() override;
+
+ bool getTransformToDisplayInverse() const override;
+
+ Rect getBufferCrop() const override;
+
+ uint32_t getBufferTransform() const override;
+
+ ui::Dataspace getDataSpace() const override;
+
+ sp<GraphicBuffer> getBuffer() const override;
+ const std::shared_ptr<renderengine::ExternalTexture>& getExternalTexture() const override;
+
+ ui::Transform::RotationFlags getTransformHint() const override { return mTransformHint; }
+
+ // Implements Layer.
const char* getType() const override { return "BufferStateLayer"; }
void onLayerDisplayed(ftl::SharedFuture<FenceResult>) override;
@@ -46,7 +125,10 @@
void finalizeFrameEventHistory(const std::shared_ptr<FenceTime>& glDoneFence,
const CompositorTiming& compositorTiming) override;
- bool isBufferDue(nsecs_t /*expectedPresentTime*/) const override { return true; }
+ // Returns true if the next buffer should be presented at the expected present time,
+ // overridden by BufferStateLayer and BufferQueueLayer for implementation
+ // specific logic
+ bool isBufferDue(nsecs_t /*expectedPresentTime*/) const { return true; }
Region getActiveTransparentRegion(const Layer::State& s) const override {
return s.transparentRegionHint;
@@ -73,6 +155,8 @@
bool setSize(uint32_t /*w*/, uint32_t /*h*/) override { return false; }
bool setTransparentRegionHint(const Region& transparent) override;
+ // BufferStateLayers can return Rect::INVALID_RECT if the layer does not have a display frame
+ // and its parent layer is not bounded
Rect getBufferSize(const State& s) const override;
FloatRect computeSourceBounds(const FloatRect& parentBounds) const override;
void setAutoRefresh(bool autoRefresh) override;
@@ -81,48 +165,114 @@
bool setDestinationFrame(const Rect& destinationFrame) override;
bool updateGeometry() override;
- // -----------------------------------------------------------------------
-
- // -----------------------------------------------------------------------
- // Interface implementation for BufferLayer
- // -----------------------------------------------------------------------
- bool fenceHasSignaled() const override;
- bool framePresentTimeIsCurrent(nsecs_t expectedPresentTime) const override;
- bool onPreComposition(nsecs_t refreshStartTime) override;
- uint32_t getEffectiveScalingMode() const override;
+ bool fenceHasSignaled() const;
+ bool framePresentTimeIsCurrent(nsecs_t expectedPresentTime) const;
+ bool onPreComposition(nsecs_t) override;
// See mPendingBufferTransactions
void decrementPendingBufferCount();
std::atomic<int32_t>* getPendingBufferCounter() override { return &mPendingBufferTransactions; }
std::string getPendingBufferCounterName() override { return mBlastTransactionName; }
+ // Returns true if the next buffer should be presented at the expected present time
bool shouldPresentNow(nsecs_t /*expectedPresentTime*/) const override { return true; }
protected:
- void gatherBufferInfo() override;
+ void gatherBufferInfo();
void onSurfaceFrameCreated(const std::shared_ptr<frametimeline::SurfaceFrame>& surfaceFrame);
ui::Transform getInputTransform() const override;
Rect getInputBounds() const override;
+ struct BufferInfo {
+ nsecs_t mDesiredPresentTime;
+ std::shared_ptr<FenceTime> mFenceTime;
+ sp<Fence> mFence;
+ uint32_t mTransform{0};
+ ui::Dataspace mDataspace{ui::Dataspace::UNKNOWN};
+ Rect mCrop;
+ uint32_t mScaleMode{NATIVE_WINDOW_SCALING_MODE_FREEZE};
+ Region mSurfaceDamage;
+ HdrMetadata mHdrMetadata;
+ int mApi;
+ PixelFormat mPixelFormat{PIXEL_FORMAT_NONE};
+ bool mTransformToDisplayInverse{false};
+
+ std::shared_ptr<renderengine::ExternalTexture> mBuffer;
+ uint64_t mFrameNumber;
+ int mBufferSlot{BufferQueue::INVALID_BUFFER_SLOT};
+
+ bool mFrameLatencyNeeded{false};
+ };
+
+ BufferInfo mBufferInfo;
+
+ std::optional<compositionengine::LayerFE::LayerSettings> prepareClientComposition(
+ compositionengine::LayerFE::ClientCompositionTargetSettings&) override;
+
+ /*
+ * compositionengine::LayerFE overrides
+ */
+ const compositionengine::LayerFECompositionState* getCompositionState() const override;
+ void preparePerFrameCompositionState() override;
+
+ static bool getOpacityForFormat(PixelFormat format);
+
+ // from graphics API
+ const uint32_t mTextureName;
+ ui::Dataspace translateDataspace(ui::Dataspace dataspace);
+ void setInitialValuesForClone(const sp<Layer>& clonedFrom);
+ void updateCloneBufferInfo() override;
+ uint64_t mPreviousFrameNumber = 0;
+
+ void setTransformHint(ui::Transform::RotationFlags displayTransformHint) override;
+
+ // Transform hint provided to the producer. This must be accessed holding
+ // the mStateLock.
+ ui::Transform::RotationFlags mTransformHint = ui::Transform::ROT_0;
+
+ bool getAutoRefresh() const { return mDrawingState.autoRefresh; }
+ bool getSidebandStreamChanged() const { return mSidebandStreamChanged; }
+
+ std::atomic<bool> mSidebandStreamChanged{false};
+
private:
friend class SlotGenerationTest;
friend class TransactionFrameTracerTest;
friend class TransactionSurfaceFrameTest;
+ // We generate InputWindowHandles for all non-cursor buffered layers regardless of whether they
+ // have an InputChannel. This is to enable the InputDispatcher to do PID based occlusion
+ // detection.
+ bool needsInputInfo() const override { return !mPotentialCursor; }
+
+ // Returns true if this layer requires filtering
+ bool needsFiltering(const DisplayDevice*) const override;
+ bool needsFilteringForScreenshots(const DisplayDevice*,
+ const ui::Transform& inverseParentTransform) const override;
+
+ PixelFormat getPixelFormat() const;
+
+ // Computes the transform matrix using the setFilteringEnabled to determine whether the
+ // transform matrix should be computed for use with bilinear filtering.
+ void getDrawingTransformMatrix(bool filteringEnabled, float outMatrix[16]);
+
+ std::unique_ptr<compositionengine::LayerFECompositionState> mCompositionState;
+
inline void tracePendingBufferCount(int32_t pendingBuffers);
bool updateFrameEventHistory(const sp<Fence>& acquireFence, nsecs_t postedTime,
nsecs_t requestedPresentTime);
- bool latchSidebandStream(bool& recomputeVisibleRegions) override;
+ // Latch sideband stream and returns true if the dirty region should be updated.
+ bool latchSidebandStream(bool& recomputeVisibleRegions);
- bool hasFrameUpdate() const override;
+ bool hasFrameUpdate() const;
status_t updateTexImage(bool& recomputeVisibleRegions, nsecs_t latchTime,
- nsecs_t expectedPresentTime) override;
+ nsecs_t expectedPresentTime);
- status_t updateActiveBuffer() override;
- status_t updateFrameNumber() override;
+ status_t updateActiveBuffer();
+ status_t updateFrameNumber();
sp<Layer> createClone() override;
@@ -131,10 +281,17 @@
bool willPresentCurrentTransaction() const;
- bool bufferNeedsFiltering() const override;
+ // Returns true if the transformed buffer size does not match the layer size and we need
+ // to apply filtering.
+ bool bufferNeedsFiltering() const;
bool simpleBufferUpdate(const layer_state_t& s) const override;
+ void callReleaseBufferCallback(const sp<ITransactionCompletedListener>& listener,
+ const sp<GraphicBuffer>& buffer, uint64_t framenumber,
+ const sp<Fence>& releaseFence,
+ uint32_t currentMaxAcquiredBufferCount);
+
ReleaseCallbackId mPreviousReleaseCallbackId = ReleaseCallbackId::INVALID_ID;
uint64_t mPreviousReleasedFrameNumber = 0;
diff --git a/services/surfaceflinger/CompositionEngine/src/Output.cpp b/services/surfaceflinger/CompositionEngine/src/Output.cpp
index 509312f..aea6798 100644
--- a/services/surfaceflinger/CompositionEngine/src/Output.cpp
+++ b/services/surfaceflinger/CompositionEngine/src/Output.cpp
@@ -586,8 +586,29 @@
// Remove the transparent area from the visible region
if (!layerFEState->isOpaque) {
if (tr.preserveRects()) {
- // transform the transparent region
- transparentRegion = tr.transform(layerFEState->transparentRegionHint);
+ // Clip the transparent region to geomLayerBounds first
+ // The transparent region may be influenced by applications, for
+ // instance, by overriding ViewGroup#gatherTransparentRegion with a
+ // custom view. Once the layer stack -> display mapping is known, we
+ // must guard against very wrong inputs to prevent underflow or
+ // overflow errors. We do this here by constraining the transparent
+ // region to be within the pre-transform layer bounds, since the
+ // layer bounds are expected to play nicely with the full
+ // transform.
+ const Region clippedTransparentRegionHint =
+ layerFEState->transparentRegionHint.intersect(
+ Rect(layerFEState->geomLayerBounds));
+
+ if (clippedTransparentRegionHint.isEmpty()) {
+ if (!layerFEState->transparentRegionHint.isEmpty()) {
+ ALOGD("Layer: %s had an out of bounds transparent region",
+ layerFE->getDebugName());
+ layerFEState->transparentRegionHint.dump("transparentRegionHint");
+ }
+ transparentRegion.clear();
+ } else {
+ transparentRegion = tr.transform(clippedTransparentRegionHint);
+ }
} else {
// transformation too complex, can't do the
// transparent region optimization.
diff --git a/services/surfaceflinger/CompositionEngine/tests/OutputTest.cpp b/services/surfaceflinger/CompositionEngine/tests/OutputTest.cpp
index 505f94e..cf12890 100644
--- a/services/surfaceflinger/CompositionEngine/tests/OutputTest.cpp
+++ b/services/surfaceflinger/CompositionEngine/tests/OutputTest.cpp
@@ -1505,6 +1505,8 @@
static const Region kTransparentRegionHint;
static const Region kTransparentRegionHintTwo;
static const Region kTransparentRegionHintTwo90Rotation;
+ static const Region kTransparentRegionHintNegative;
+ static const Region kTransparentRegionHintNegativeIntersectsBounds;
StrictMock<OutputPartialMock> mOutput;
LayerFESet mGeomSnapshots;
@@ -1528,6 +1530,10 @@
Region(Rect(25, 20, 50, 75));
const Region OutputEnsureOutputLayerIfVisibleTest::kTransparentRegionHintTwo90Rotation =
Region(Rect(125, 25, 180, 50));
+const Region OutputEnsureOutputLayerIfVisibleTest::kTransparentRegionHintNegative =
+ Region(Rect(INT32_MIN, INT32_MIN, INT32_MIN + 100, INT32_MIN + 200));
+const Region OutputEnsureOutputLayerIfVisibleTest::kTransparentRegionHintNegativeIntersectsBounds =
+ Region(Rect(INT32_MIN, INT32_MIN, 100, 100));
TEST_F(OutputEnsureOutputLayerIfVisibleTest, performsGeomLatchBeforeCheckingIfLayerIncluded) {
EXPECT_CALL(mOutput, includesLayer(sp<LayerFE>(mLayer.layerFE))).WillOnce(Return(false));
@@ -1997,6 +2003,41 @@
RegionEq(kTransparentRegionHintTwo90Rotation));
}
+TEST_F(OutputEnsureOutputLayerIfVisibleTest, transparentRegionExcludesOutputLayer) {
+ mLayer.layerFEState.isOpaque = false;
+ mLayer.layerFEState.contentDirty = true;
+ mLayer.layerFEState.geomLayerBounds = kFullBoundsNoRotation.bounds().toFloatRect();
+ mLayer.layerFEState.transparentRegionHint = kFullBoundsNoRotation;
+
+ EXPECT_CALL(mOutput, ensureOutputLayer(_, _)).Times(0);
+}
+
+TEST_F(OutputEnsureOutputLayerIfVisibleTest, transparentRegionIgnoredWhenOutsideBounds) {
+ mLayer.layerFEState.isOpaque = false;
+ mLayer.layerFEState.contentDirty = true;
+ mLayer.layerFEState.geomLayerBounds = kFullBoundsNoRotation.bounds().toFloatRect();
+ mLayer.layerFEState.transparentRegionHint = kTransparentRegionHintNegative;
+
+ EXPECT_CALL(mOutput, ensureOutputLayer(_, _)).Times(0);
+}
+
+TEST_F(OutputEnsureOutputLayerIfVisibleTest, transparentRegionClipsWhenOutsideBounds) {
+ mLayer.layerFEState.isOpaque = false;
+ mLayer.layerFEState.contentDirty = true;
+ mLayer.layerFEState.compositionType =
+ aidl::android::hardware::graphics::composer3::Composition::DISPLAY_DECORATION;
+ mLayer.layerFEState.transparentRegionHint = kTransparentRegionHintNegativeIntersectsBounds;
+
+ EXPECT_CALL(mOutput, getOutputLayerCount()).WillOnce(Return(0u));
+ EXPECT_CALL(mOutput, ensureOutputLayer(Eq(std::nullopt), Eq(mLayer.layerFE)))
+ .WillOnce(Return(&mLayer.outputLayer));
+ ensureOutputLayerIfVisible();
+
+ // Check that the blocking region clips an out-of-bounds transparent region.
+ EXPECT_THAT(mLayer.outputLayerState.outputSpaceBlockingRegionHint,
+ RegionEq(kTransparentRegionHint));
+}
+
/*
* Output::present()
*/
diff --git a/services/surfaceflinger/Layer.cpp b/services/surfaceflinger/Layer.cpp
index 6e9dad1..ba3cb51 100644
--- a/services/surfaceflinger/Layer.cpp
+++ b/services/surfaceflinger/Layer.cpp
@@ -62,7 +62,6 @@
#include <mutex>
#include <sstream>
-#include "BufferLayer.h"
#include "Colorizer.h"
#include "DisplayDevice.h"
#include "DisplayHardware/HWComposer.h"
@@ -70,8 +69,6 @@
#include "FrameTimeline.h"
#include "FrameTracer/FrameTracer.h"
#include "LayerProtoHelper.h"
-#include "LayerRejecter.h"
-#include "MonitoredProducer.h"
#include "SurfaceFlinger.h"
#include "TimeStats/TimeStats.h"
#include "TunnelModeEnabledReporter.h"
@@ -156,9 +153,9 @@
mDrawingState.color.g = -1.0_hf;
mDrawingState.color.b = -1.0_hf;
}
- CompositorTiming compositorTiming;
- args.flinger->getCompositorTiming(&compositorTiming);
- mFrameTracker.setDisplayRefreshPeriod(compositorTiming.interval);
+
+ mFrameTracker.setDisplayRefreshPeriod(
+ args.flinger->mScheduler->getVsyncPeriodFromRefreshRateConfigs());
mCallingPid = args.callingPid;
mCallingUid = args.callingUid;
@@ -410,7 +407,7 @@
const auto& drawingState{getDrawingState()};
const auto alpha = static_cast<float>(getAlpha());
const bool opaque = isOpaque(drawingState);
- const bool usesRoundedCorners = getRoundedCornerState().radius != 0.f;
+ const bool usesRoundedCorners = hasRoundedCorners();
auto blendMode = Hwc2::IComposerClient::BlendMode::NONE;
if (!opaque || alpha != 1.0f) {
@@ -486,7 +483,7 @@
compositionState->hasProtectedContent = isProtected();
compositionState->dimmingEnabled = isDimmingEnabled();
- const bool usesRoundedCorners = getRoundedCornerState().radius != 0.f;
+ const bool usesRoundedCorners = hasRoundedCorners();
compositionState->isOpaque =
isOpaque(drawingState) && !usesRoundedCorners && getAlpha() == 1.0_hf;
@@ -1968,27 +1965,22 @@
const auto& parent = mDrawingParent.promote();
if (parent != nullptr) {
parentSettings = parent->getRoundedCornerState();
- if (parentSettings.radius > 0) {
+ if (parentSettings.hasRoundedCorners()) {
ui::Transform t = getActiveTransform(getDrawingState());
t = t.inverse();
parentSettings.cropRect = t.transform(parentSettings.cropRect);
- // The rounded corners shader only accepts 1 corner radius for performance reasons,
- // but a transform matrix can define horizontal and vertical scales.
- // Let's take the average between both of them and pass into the shader, practically we
- // never do this type of transformation on windows anyway.
- auto scaleX = sqrtf(t[0][0] * t[0][0] + t[0][1] * t[0][1]);
- auto scaleY = sqrtf(t[1][0] * t[1][0] + t[1][1] * t[1][1]);
- parentSettings.radius *= (scaleX + scaleY) / 2.0f;
+ parentSettings.radius.x *= t.getScaleX();
+ parentSettings.radius.y *= t.getScaleY();
}
}
// Get layer settings
Rect layerCropRect = getCroppedBufferSize(getDrawingState());
- const float radius = getDrawingState().cornerRadius;
+ const vec2 radius(getDrawingState().cornerRadius, getDrawingState().cornerRadius);
RoundedCornerState layerSettings(layerCropRect.toFloatRect(), radius);
- const bool layerSettingsValid = layerSettings.radius > 0 && layerCropRect.isValid();
+ const bool layerSettingsValid = layerSettings.hasRoundedCorners() && layerCropRect.isValid();
- if (layerSettingsValid && parentSettings.radius > 0) {
+ if (layerSettingsValid && parentSettings.hasRoundedCorners()) {
// If the parent and the layer have rounded corner settings, use the parent settings if the
// parent crop is entirely inside the layer crop.
// This has limitations and cause rendering artifacts. See b/200300845 for correct fix.
@@ -2002,7 +1994,7 @@
}
} else if (layerSettingsValid) {
return layerSettings;
- } else if (parentSettings.radius > 0) {
+ } else if (parentSettings.hasRoundedCorners()) {
return parentSettings;
}
return {};
@@ -2123,7 +2115,8 @@
layerInfo->set_effective_scaling_mode(getEffectiveScalingMode());
layerInfo->set_requested_corner_radius(getDrawingState().cornerRadius);
- layerInfo->set_corner_radius(getRoundedCornerState().radius);
+ layerInfo->set_corner_radius(
+ (getRoundedCornerState().radius.x + getRoundedCornerState().radius.y) / 2.0);
layerInfo->set_background_blur_radius(getBackgroundBlurRadius());
layerInfo->set_is_trusted_overlay(isTrustedOverlay());
LayerProtoHelper::writeToProtoDeprecated(transform, layerInfo->mutable_transform());
diff --git a/services/surfaceflinger/Layer.h b/services/surfaceflinger/Layer.h
index fb9b663..b809c8a 100644
--- a/services/surfaceflinger/Layer.h
+++ b/services/surfaceflinger/Layer.h
@@ -53,7 +53,6 @@
#include "DisplayHardware/HWComposer.h"
#include "FrameTracker.h"
#include "LayerVector.h"
-#include "MonitoredProducer.h"
#include "RenderArea.h"
#include "Scheduler/LayerInfo.h"
#include "SurfaceFlinger.h"
@@ -137,13 +136,14 @@
struct RoundedCornerState {
RoundedCornerState() = default;
- RoundedCornerState(FloatRect cropRect, float radius)
+ RoundedCornerState(const FloatRect& cropRect, const vec2& radius)
: cropRect(cropRect), radius(radius) {}
// Rounded rectangle in local layer coordinate space.
FloatRect cropRect = FloatRect();
// Radius of the rounded rectangle.
- float radius = 0.0f;
+ vec2 radius;
+ bool hasRoundedCorners() const { return radius.x > 0.0f && radius.y > 0.0f; }
};
using FrameRate = scheduler::LayerInfo::FrameRate;
@@ -606,7 +606,7 @@
// corner crop does not intersect with its own rounded corner crop.
virtual RoundedCornerState getRoundedCornerState() const;
- bool hasRoundedCorners() const override { return getRoundedCornerState().radius > .0f; }
+ bool hasRoundedCorners() const override { return getRoundedCornerState().hasRoundedCorners(); }
virtual PixelFormat getPixelFormat() const { return PIXEL_FORMAT_NONE; }
/**
diff --git a/services/surfaceflinger/LayerRejecter.cpp b/services/surfaceflinger/LayerRejecter.cpp
deleted file mode 100644
index 1c0263b..0000000
--- a/services/surfaceflinger/LayerRejecter.cpp
+++ /dev/null
@@ -1,143 +0,0 @@
-/*
- * Copyright (C) 2011 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.
- */
-
-// TODO(b/129481165): remove the #pragma below and fix conversion issues
-#pragma clang diagnostic push
-#pragma clang diagnostic ignored "-Wconversion"
-
-#include "LayerRejecter.h"
-
-#include <gui/BufferItem.h>
-#include <system/window.h>
-
-#define DEBUG_RESIZE 0
-
-namespace android {
-
-LayerRejecter::LayerRejecter(Layer::State& front, Layer::State& current,
- bool& recomputeVisibleRegions, bool stickySet, const std::string& name,
- bool transformToDisplayInverse)
- : mFront(front),
- mCurrent(current),
- mRecomputeVisibleRegions(recomputeVisibleRegions),
- mStickyTransformSet(stickySet),
- mName(name),
- mTransformToDisplayInverse(transformToDisplayInverse) {}
-
-bool LayerRejecter::reject(const sp<GraphicBuffer>& buf, const BufferItem& item) {
- if (buf == nullptr) {
- return false;
- }
-
- uint32_t bufWidth = buf->getWidth();
- uint32_t bufHeight = buf->getHeight();
-
- // check that we received a buffer of the right size
- // (Take the buffer's orientation into account)
- if (item.mTransform & ui::Transform::ROT_90) {
- std::swap(bufWidth, bufHeight);
- }
-
- if (mTransformToDisplayInverse) {
- uint32_t invTransform = DisplayDevice::getPrimaryDisplayRotationFlags();
- if (invTransform & ui::Transform::ROT_90) {
- std::swap(bufWidth, bufHeight);
- }
- }
-
- int actualScalingMode = item.mScalingMode;
- bool isFixedSize = actualScalingMode != NATIVE_WINDOW_SCALING_MODE_FREEZE;
- if (mFront.active_legacy != mFront.requested_legacy) {
- if (isFixedSize ||
- (bufWidth == mFront.requested_legacy.w && bufHeight == mFront.requested_legacy.h)) {
- // Here we pretend the transaction happened by updating the
- // current and drawing states. Drawing state is only accessed
- // in this thread, no need to have it locked
- mFront.active_legacy = mFront.requested_legacy;
-
- // We also need to update the current state so that
- // we don't end-up overwriting the drawing state with
- // this stale current state during the next transaction
- //
- // NOTE: We don't need to hold the transaction lock here
- // because State::active_legacy is only accessed from this thread.
- mCurrent.active_legacy = mFront.active_legacy;
- mCurrent.modified = true;
-
- // recompute visible region
- mRecomputeVisibleRegions = true;
-
- if (mFront.crop != mFront.requestedCrop) {
- mFront.crop = mFront.requestedCrop;
- mCurrent.crop = mFront.requestedCrop;
- mRecomputeVisibleRegions = true;
- }
- }
-
- ALOGD_IF(DEBUG_RESIZE,
- "[%s] latchBuffer/reject: buffer (%ux%u, tr=%02x), scalingMode=%d\n"
- " drawing={ active_legacy ={ wh={%4u,%4u} crop={%4d,%4d,%4d,%4d} "
- "(%4d,%4d) "
- "}\n"
- " requested_legacy={ wh={%4u,%4u} }}\n",
- mName.c_str(), bufWidth, bufHeight, item.mTransform, item.mScalingMode,
- mFront.active_legacy.w, mFront.active_legacy.h, mFront.crop.left, mFront.crop.top,
- mFront.crop.right, mFront.crop.bottom, mFront.crop.getWidth(),
- mFront.crop.getHeight(), mFront.requested_legacy.w, mFront.requested_legacy.h);
- }
-
- if (!isFixedSize && !mStickyTransformSet) {
- if (mFront.active_legacy.w != bufWidth || mFront.active_legacy.h != bufHeight) {
- // reject this buffer
- ALOGE("[%s] rejecting buffer: "
- "bufWidth=%d, bufHeight=%d, front.active_legacy.{w=%d, h=%d}",
- mName.c_str(), bufWidth, bufHeight, mFront.active_legacy.w,
- mFront.active_legacy.h);
- return true;
- }
- }
-
- // if the transparent region has changed (this test is
- // conservative, but that's fine, worst case we're doing
- // a bit of extra work), we latch the new one and we
- // trigger a visible-region recompute.
- //
- // We latch the transparent region here, instead of above where we latch
- // the rest of the geometry because it is only content but not necessarily
- // resize dependent.
- if (!mFront.activeTransparentRegion_legacy.hasSameRects(
- mFront.requestedTransparentRegion_legacy)) {
- mFront.activeTransparentRegion_legacy = mFront.requestedTransparentRegion_legacy;
-
- // We also need to update the current state so that
- // we don't end-up overwriting the drawing state with
- // this stale current state during the next transaction
- //
- // NOTE: We don't need to hold the transaction lock here
- // because State::active_legacy is only accessed from this thread.
- mCurrent.activeTransparentRegion_legacy = mFront.activeTransparentRegion_legacy;
-
- // recompute visible region
- mRecomputeVisibleRegions = true;
- }
-
- return false;
-}
-
-} // namespace android
-
-// TODO(b/129481165): remove the #pragma below and fix conversion issues
-#pragma clang diagnostic pop // ignored "-Wconversion"
diff --git a/services/surfaceflinger/LayerRejecter.h b/services/surfaceflinger/LayerRejecter.h
deleted file mode 100644
index 4981f45..0000000
--- a/services/surfaceflinger/LayerRejecter.h
+++ /dev/null
@@ -1,41 +0,0 @@
-/*
- * Copyright (C) 2007 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 "Layer.h"
-#include "BufferLayerConsumer.h"
-
-namespace android {
-
-class LayerRejecter : public BufferLayerConsumer::BufferRejecter {
-public:
- LayerRejecter(Layer::State& front, Layer::State& current, bool& recomputeVisibleRegions,
- bool stickySet, const std::string& name,
- bool transformToDisplayInverse);
-
- virtual bool reject(const sp<GraphicBuffer>&, const BufferItem&);
-
-private:
- Layer::State& mFront;
- Layer::State& mCurrent;
- bool& mRecomputeVisibleRegions;
- const bool mStickyTransformSet;
- const std::string& mName;
- const bool mTransformToDisplayInverse;
-};
-
-} // namespace android
diff --git a/services/surfaceflinger/MonitoredProducer.cpp b/services/surfaceflinger/MonitoredProducer.cpp
deleted file mode 100644
index df76f50..0000000
--- a/services/surfaceflinger/MonitoredProducer.cpp
+++ /dev/null
@@ -1,170 +0,0 @@
-/*
- * Copyright 2014 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.
- */
-
-// TODO(b/129481165): remove the #pragma below and fix conversion issues
-#pragma clang diagnostic push
-#pragma clang diagnostic ignored "-Wconversion"
-
-#include "MonitoredProducer.h"
-#include "Layer.h"
-#include "SurfaceFlinger.h"
-
-#include "Scheduler/MessageQueue.h"
-
-namespace android {
-
-MonitoredProducer::MonitoredProducer(const sp<IGraphicBufferProducer>& producer,
- const sp<SurfaceFlinger>& flinger,
- const wp<Layer>& layer) :
- mProducer(producer),
- mFlinger(flinger),
- mLayer(layer) {}
-
-MonitoredProducer::~MonitoredProducer() {}
-
-status_t MonitoredProducer::requestBuffer(int slot, sp<GraphicBuffer>* buf) {
- return mProducer->requestBuffer(slot, buf);
-}
-
-status_t MonitoredProducer::setMaxDequeuedBufferCount(
- int maxDequeuedBuffers) {
- return mProducer->setMaxDequeuedBufferCount(maxDequeuedBuffers);
-}
-
-status_t MonitoredProducer::setAsyncMode(bool async) {
- return mProducer->setAsyncMode(async);
-}
-
-status_t MonitoredProducer::dequeueBuffer(int* slot, sp<Fence>* fence, uint32_t w, uint32_t h,
- PixelFormat format, uint64_t usage,
- uint64_t* outBufferAge,
- FrameEventHistoryDelta* outTimestamps) {
- return mProducer->dequeueBuffer(slot, fence, w, h, format, usage, outBufferAge, outTimestamps);
-}
-
-status_t MonitoredProducer::detachBuffer(int slot) {
- return mProducer->detachBuffer(slot);
-}
-
-status_t MonitoredProducer::detachNextBuffer(sp<GraphicBuffer>* outBuffer,
- sp<Fence>* outFence) {
- return mProducer->detachNextBuffer(outBuffer, outFence);
-}
-
-status_t MonitoredProducer::attachBuffer(int* outSlot,
- const sp<GraphicBuffer>& buffer) {
- return mProducer->attachBuffer(outSlot, buffer);
-}
-
-status_t MonitoredProducer::queueBuffer(int slot, const QueueBufferInput& input,
- QueueBufferOutput* output) {
- return mProducer->queueBuffer(slot, input, output);
-}
-
-status_t MonitoredProducer::cancelBuffer(int slot, const sp<Fence>& fence) {
- return mProducer->cancelBuffer(slot, fence);
-}
-
-int MonitoredProducer::query(int what, int* value) {
- return mProducer->query(what, value);
-}
-
-status_t MonitoredProducer::connect(const sp<IProducerListener>& listener,
- int api, bool producerControlledByApp, QueueBufferOutput* output) {
- return mProducer->connect(listener, api, producerControlledByApp, output);
-}
-
-status_t MonitoredProducer::disconnect(int api, DisconnectMode mode) {
- return mProducer->disconnect(api, mode);
-}
-
-status_t MonitoredProducer::setSidebandStream(const sp<NativeHandle>& stream) {
- return mProducer->setSidebandStream(stream);
-}
-
-void MonitoredProducer::allocateBuffers(uint32_t width, uint32_t height,
- PixelFormat format, uint64_t usage) {
- mProducer->allocateBuffers(width, height, format, usage);
-}
-
-status_t MonitoredProducer::allowAllocation(bool allow) {
- return mProducer->allowAllocation(allow);
-}
-
-status_t MonitoredProducer::setGenerationNumber(uint32_t generationNumber) {
- return mProducer->setGenerationNumber(generationNumber);
-}
-
-String8 MonitoredProducer::getConsumerName() const {
- return mProducer->getConsumerName();
-}
-
-status_t MonitoredProducer::setSharedBufferMode(bool sharedBufferMode) {
- return mProducer->setSharedBufferMode(sharedBufferMode);
-}
-
-status_t MonitoredProducer::setAutoRefresh(bool autoRefresh) {
- return mProducer->setAutoRefresh(autoRefresh);
-}
-
-status_t MonitoredProducer::setDequeueTimeout(nsecs_t timeout) {
- return mProducer->setDequeueTimeout(timeout);
-}
-
-status_t MonitoredProducer::setLegacyBufferDrop(bool drop) {
- return mProducer->setLegacyBufferDrop(drop);
-}
-
-status_t MonitoredProducer::getLastQueuedBuffer(sp<GraphicBuffer>* outBuffer,
- sp<Fence>* outFence, float outTransformMatrix[16]) {
- return mProducer->getLastQueuedBuffer(outBuffer, outFence,
- outTransformMatrix);
-}
-
-status_t MonitoredProducer::getLastQueuedBuffer(sp<GraphicBuffer>* outBuffer, sp<Fence>* outFence,
- Rect* outRect, uint32_t* outTransform) {
- return mProducer->getLastQueuedBuffer(outBuffer, outFence, outRect, outTransform);
-}
-
-void MonitoredProducer::getFrameTimestamps(FrameEventHistoryDelta* outDelta) {
- mProducer->getFrameTimestamps(outDelta);
-}
-
-status_t MonitoredProducer::getUniqueId(uint64_t* outId) const {
- return mProducer->getUniqueId(outId);
-}
-
-status_t MonitoredProducer::getConsumerUsage(uint64_t* outUsage) const {
- return mProducer->getConsumerUsage(outUsage);
-}
-
-status_t MonitoredProducer::setAutoPrerotation(bool autoPrerotation) {
- return mProducer->setAutoPrerotation(autoPrerotation);
-}
-
-IBinder* MonitoredProducer::onAsBinder() {
- return this;
-}
-
-sp<Layer> MonitoredProducer::getLayer() const {
- return mLayer.promote();
-}
-
-// ---------------------------------------------------------------------------
-}; // namespace android
-
-// TODO(b/129481165): remove the #pragma below and fix conversion issues
-#pragma clang diagnostic pop // ignored "-Wconversion"
diff --git a/services/surfaceflinger/MonitoredProducer.h b/services/surfaceflinger/MonitoredProducer.h
deleted file mode 100644
index 3778277..0000000
--- a/services/surfaceflinger/MonitoredProducer.h
+++ /dev/null
@@ -1,89 +0,0 @@
-/*
- * Copyright 2014 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.
- */
-
-#ifndef ANDROID_MONITORED_PRODUCER_H
-#define ANDROID_MONITORED_PRODUCER_H
-
-#include <gui/IGraphicBufferProducer.h>
-
-namespace android {
-
-class IProducerListener;
-class NativeHandle;
-class SurfaceFlinger;
-class Layer;
-
-// MonitoredProducer wraps an IGraphicBufferProducer so that SurfaceFlinger will
-// be notified upon its destruction
-class MonitoredProducer : public BnGraphicBufferProducer {
-public:
- MonitoredProducer(const sp<IGraphicBufferProducer>& producer,
- const sp<SurfaceFlinger>& flinger,
- const wp<Layer>& layer);
- virtual ~MonitoredProducer();
-
- // From IGraphicBufferProducer
- virtual status_t requestBuffer(int slot, sp<GraphicBuffer>* buf);
- virtual status_t setMaxDequeuedBufferCount(int maxDequeuedBuffers);
- virtual status_t setAsyncMode(bool async);
- virtual status_t dequeueBuffer(int* slot, sp<Fence>* fence, uint32_t w, uint32_t h,
- PixelFormat format, uint64_t usage, uint64_t* outBufferAge,
- FrameEventHistoryDelta* outTimestamps);
- virtual status_t detachBuffer(int slot);
- virtual status_t detachNextBuffer(sp<GraphicBuffer>* outBuffer,
- sp<Fence>* outFence);
- virtual status_t attachBuffer(int* outSlot,
- const sp<GraphicBuffer>& buffer);
- virtual status_t queueBuffer(int slot, const QueueBufferInput& input,
- QueueBufferOutput* output);
- virtual status_t cancelBuffer(int slot, const sp<Fence>& fence);
- virtual int query(int what, int* value);
- virtual status_t connect(const sp<IProducerListener>& token, int api,
- bool producerControlledByApp, QueueBufferOutput* output);
- virtual status_t disconnect(int api, DisconnectMode mode);
- virtual status_t setSidebandStream(const sp<NativeHandle>& stream);
- virtual void allocateBuffers(uint32_t width, uint32_t height,
- PixelFormat format, uint64_t usage);
- virtual status_t allowAllocation(bool allow);
- virtual status_t setGenerationNumber(uint32_t generationNumber);
- virtual String8 getConsumerName() const override;
- virtual status_t setDequeueTimeout(nsecs_t timeout) override;
- virtual status_t setLegacyBufferDrop(bool drop) override;
- virtual status_t getLastQueuedBuffer(sp<GraphicBuffer>* outBuffer,
- sp<Fence>* outFence, float outTransformMatrix[16]) override;
- virtual status_t getLastQueuedBuffer(sp<GraphicBuffer>* outBuffer, sp<Fence>* outFence,
- Rect* outRect, uint32_t* outTransform) override;
- virtual IBinder* onAsBinder();
- virtual status_t setSharedBufferMode(bool sharedBufferMode) override;
- virtual status_t setAutoRefresh(bool autoRefresh) override;
- virtual void getFrameTimestamps(FrameEventHistoryDelta *outDelta) override;
- virtual status_t getUniqueId(uint64_t* outId) const override;
- virtual status_t getConsumerUsage(uint64_t* outUsage) const override;
- virtual status_t setAutoPrerotation(bool autoPrerotation) override;
-
- // The Layer which created this producer, and on which queued Buffer's will be displayed.
- sp<Layer> getLayer() const;
-
-private:
- sp<IGraphicBufferProducer> mProducer;
- sp<SurfaceFlinger> mFlinger;
- // The Layer which created this producer, and on which queued Buffer's will be displayed.
- wp<Layer> mLayer;
-};
-
-}; // namespace android
-
-#endif // ANDROID_MONITORED_PRODUCER_H
diff --git a/services/surfaceflinger/RefreshRateOverlay.cpp b/services/surfaceflinger/RefreshRateOverlay.cpp
index d4435c2..a9180d4 100644
--- a/services/surfaceflinger/RefreshRateOverlay.cpp
+++ b/services/surfaceflinger/RefreshRateOverlay.cpp
@@ -16,9 +16,10 @@
#include <algorithm>
-#include "RefreshRateOverlay.h"
+#include "BackgroundExecutor.h"
#include "Client.h"
#include "Layer.h"
+#include "RefreshRateOverlay.h"
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wconversion"
@@ -56,6 +57,14 @@
} // namespace
+SurfaceControlHolder::~SurfaceControlHolder() {
+ // Hand the sp<SurfaceControl> to the helper thread to release the last
+ // reference. This makes sure that the SurfaceControl is destructed without
+ // SurfaceFlinger::mStateLock held.
+ BackgroundExecutor::getInstance().sendCallbacks(
+ {[sc = std::move(mSurfaceControl)]() mutable { sc.clear(); }});
+}
+
void RefreshRateOverlay::SevenSegmentDrawer::drawSegment(Segment segment, int left, SkColor color,
SkCanvas& canvas) {
const SkRect rect = [&]() {
@@ -210,21 +219,27 @@
return buffers;
}
+std::unique_ptr<SurfaceControlHolder> createSurfaceControlHolder() {
+ sp<SurfaceControl> surfaceControl =
+ SurfaceComposerClient::getDefault()
+ ->createSurface(String8("RefreshRateOverlay"), kBufferWidth, kBufferHeight,
+ PIXEL_FORMAT_RGBA_8888,
+ ISurfaceComposerClient::eFXSurfaceBufferState);
+ return std::make_unique<SurfaceControlHolder>(std::move(surfaceControl));
+}
+
RefreshRateOverlay::RefreshRateOverlay(FpsRange fpsRange, bool showSpinner)
: mFpsRange(fpsRange),
mShowSpinner(showSpinner),
- mSurfaceControl(SurfaceComposerClient::getDefault()
- ->createSurface(String8("RefreshRateOverlay"), kBufferWidth,
- kBufferHeight, PIXEL_FORMAT_RGBA_8888,
- ISurfaceComposerClient::eFXSurfaceBufferState)) {
+ mSurfaceControl(createSurfaceControlHolder()) {
if (!mSurfaceControl) {
ALOGE("%s: Failed to create buffer state layer", __func__);
return;
}
- createTransaction(mSurfaceControl)
- .setLayer(mSurfaceControl, INT32_MAX - 2)
- .setTrustedOverlay(mSurfaceControl, true)
+ createTransaction(mSurfaceControl->get())
+ .setLayer(mSurfaceControl->get(), INT32_MAX - 2)
+ .setTrustedOverlay(mSurfaceControl->get(), true)
.apply();
}
@@ -233,7 +248,7 @@
if (!mSurfaceControl) return kNoBuffers;
const auto transformHint =
- static_cast<ui::Transform::RotationFlags>(mSurfaceControl->getTransformHint());
+ static_cast<ui::Transform::RotationFlags>(mSurfaceControl->get()->getTransformHint());
// Tell SurfaceFlinger about the pre-rotation on the buffer.
const auto transform = [&] {
@@ -247,7 +262,9 @@
}
}();
- createTransaction(mSurfaceControl).setTransform(mSurfaceControl, transform).apply();
+ createTransaction(mSurfaceControl->get())
+ .setTransform(mSurfaceControl->get(), transform)
+ .apply();
BufferCache::const_iterator it = mBufferCache.find({fps.getIntValue(), transformHint});
if (it == mBufferCache.end()) {
@@ -289,21 +306,21 @@
Rect frame((3 * width) >> 4, height >> 5);
frame.offsetBy(width >> 5, height >> 4);
- createTransaction(mSurfaceControl)
- .setMatrix(mSurfaceControl, frame.getWidth() / static_cast<float>(kBufferWidth), 0, 0,
- frame.getHeight() / static_cast<float>(kBufferHeight))
- .setPosition(mSurfaceControl, frame.left, frame.top)
+ createTransaction(mSurfaceControl->get())
+ .setMatrix(mSurfaceControl->get(), frame.getWidth() / static_cast<float>(kBufferWidth),
+ 0, 0, frame.getHeight() / static_cast<float>(kBufferHeight))
+ .setPosition(mSurfaceControl->get(), frame.left, frame.top)
.apply();
}
void RefreshRateOverlay::setLayerStack(ui::LayerStack stack) {
- createTransaction(mSurfaceControl).setLayerStack(mSurfaceControl, stack).apply();
+ createTransaction(mSurfaceControl->get()).setLayerStack(mSurfaceControl->get(), stack).apply();
}
void RefreshRateOverlay::changeRefreshRate(Fps fps) {
mCurrentFps = fps;
const auto buffer = getOrCreateBuffers(fps)[mFrame];
- createTransaction(mSurfaceControl).setBuffer(mSurfaceControl, buffer).apply();
+ createTransaction(mSurfaceControl->get()).setBuffer(mSurfaceControl->get(), buffer).apply();
}
void RefreshRateOverlay::animate() {
@@ -312,7 +329,7 @@
const auto& buffers = getOrCreateBuffers(*mCurrentFps);
mFrame = (mFrame + 1) % buffers.size();
const auto buffer = buffers[mFrame];
- createTransaction(mSurfaceControl).setBuffer(mSurfaceControl, buffer).apply();
+ createTransaction(mSurfaceControl->get()).setBuffer(mSurfaceControl->get(), buffer).apply();
}
} // namespace android
diff --git a/services/surfaceflinger/RefreshRateOverlay.h b/services/surfaceflinger/RefreshRateOverlay.h
index a465a36..a2966e6 100644
--- a/services/surfaceflinger/RefreshRateOverlay.h
+++ b/services/surfaceflinger/RefreshRateOverlay.h
@@ -33,6 +33,20 @@
class GraphicBuffer;
class SurfaceControl;
+class SurfaceFlinger;
+
+// Helper class to delete the SurfaceControl on a helper thread as
+// SurfaceControl assumes its destruction happens without SurfaceFlinger::mStateLock held.
+class SurfaceControlHolder {
+public:
+ explicit SurfaceControlHolder(sp<SurfaceControl> sc) : mSurfaceControl(std::move(sc)){};
+ ~SurfaceControlHolder();
+
+ const sp<SurfaceControl>& get() const { return mSurfaceControl; }
+
+private:
+ sp<SurfaceControl> mSurfaceControl;
+};
class RefreshRateOverlay {
public:
@@ -75,7 +89,7 @@
const FpsRange mFpsRange; // For color interpolation.
const bool mShowSpinner;
- const sp<SurfaceControl> mSurfaceControl;
+ const std::unique_ptr<SurfaceControlHolder> mSurfaceControl;
};
} // namespace android
diff --git a/services/surfaceflinger/Scheduler/Scheduler.cpp b/services/surfaceflinger/Scheduler/Scheduler.cpp
index 3181a7f..a3d4e8d 100644
--- a/services/surfaceflinger/Scheduler/Scheduler.cpp
+++ b/services/surfaceflinger/Scheduler/Scheduler.cpp
@@ -28,7 +28,6 @@
#include <ftl/fake_guard.h>
#include <gui/WindowInfo.h>
#include <system/window.h>
-#include <ui/DisplayStatInfo.h>
#include <utils/Timers.h>
#include <utils/Trace.h>
@@ -172,8 +171,7 @@
impl::EventThread::GetVsyncPeriodFunction Scheduler::makeGetVsyncPeriodFunction() const {
return [this](uid_t uid) {
const Fps refreshRate = holdRefreshRateConfigs()->getActiveMode()->getFps();
- const auto currentPeriod =
- mVsyncSchedule->getTracker().currentPeriod() ?: refreshRate.getPeriodNsecs();
+ const nsecs_t currentPeriod = mVsyncSchedule->period().ns() ?: refreshRate.getPeriodNsecs();
const auto frameRate = getFrameRateOverride(uid);
if (!frameRate.has_value()) {
@@ -361,12 +359,6 @@
thread->setDuration(workDuration, readyDuration);
}
-DisplayStatInfo Scheduler::getDisplayStatInfo(nsecs_t now) {
- const auto vsyncTime = mVsyncSchedule->getTracker().nextAnticipatedVSyncTimeFrom(now);
- const auto vsyncPeriod = mVsyncSchedule->getTracker().currentPeriod();
- return DisplayStatInfo{.vsyncTime = vsyncTime, .vsyncPeriod = vsyncPeriod};
-}
-
ConnectionHandle Scheduler::enableVSyncInjection(bool enable) {
if (mInjectVSyncs == enable) {
return {};
@@ -767,11 +759,4 @@
mFrameRateOverrideMappings.setPreferredRefreshRateForUid(frameRateOverride);
}
-std::chrono::steady_clock::time_point Scheduler::getPreviousVsyncFrom(
- nsecs_t expectedPresentTime) const {
- const auto presentTime = std::chrono::nanoseconds(expectedPresentTime);
- const auto vsyncPeriod = std::chrono::nanoseconds(mVsyncSchedule->getTracker().currentPeriod());
- return std::chrono::steady_clock::time_point(presentTime - vsyncPeriod);
-}
-
} // namespace android::scheduler
diff --git a/services/surfaceflinger/Scheduler/Scheduler.h b/services/surfaceflinger/Scheduler/Scheduler.h
index 7f76d1e..f800eeb 100644
--- a/services/surfaceflinger/Scheduler/Scheduler.h
+++ b/services/surfaceflinger/Scheduler/Scheduler.h
@@ -32,9 +32,8 @@
#include <ui/GraphicTypes.h>
#pragma clang diagnostic pop // ignored "-Wconversion -Wextra"
-#include <ui/DisplayStatInfo.h>
-
#include <scheduler/Features.h>
+#include <scheduler/Time.h>
#include "EventThread.h"
#include "FrameRateOverrideMappings.h"
@@ -150,8 +149,6 @@
void setDuration(ConnectionHandle, std::chrono::nanoseconds workDuration,
std::chrono::nanoseconds readyDuration);
- DisplayStatInfo getDisplayStatInfo(nsecs_t now);
-
// Returns injector handle if injection has toggled, or an invalid handle otherwise.
ConnectionHandle enableVSyncInjection(bool enable);
// Returns false if injection is disabled.
@@ -191,14 +188,12 @@
void setDisplayPowerMode(hal::PowerMode powerMode);
- VSyncDispatch& getVsyncDispatch() { return mVsyncSchedule->getDispatch(); }
+ VsyncSchedule& getVsyncSchedule() { return *mVsyncSchedule; }
// Returns true if a given vsync timestamp is considered valid vsync
// for a given uid
bool isVsyncValid(nsecs_t expectedVsyncTimestamp, uid_t uid) const;
- std::chrono::steady_clock::time_point getPreviousVsyncFrom(nsecs_t expectedPresentTime) const;
-
void dump(std::string&) const;
void dump(ConnectionHandle, std::string&) const;
void dumpVsync(std::string&) const;
diff --git a/services/surfaceflinger/Scheduler/VsyncSchedule.cpp b/services/surfaceflinger/Scheduler/VsyncSchedule.cpp
index 3a918a1..95bc31f 100644
--- a/services/surfaceflinger/Scheduler/VsyncSchedule.cpp
+++ b/services/surfaceflinger/Scheduler/VsyncSchedule.cpp
@@ -68,6 +68,14 @@
VsyncSchedule::VsyncSchedule(VsyncSchedule&&) = default;
VsyncSchedule::~VsyncSchedule() = default;
+Period VsyncSchedule::period() const {
+ return Period::fromNs(mTracker->currentPeriod());
+}
+
+TimePoint VsyncSchedule::vsyncDeadlineAfter(TimePoint timePoint) const {
+ return TimePoint::fromNs(mTracker->nextAnticipatedVSyncTimeFrom(timePoint.ns()));
+}
+
void VsyncSchedule::dump(std::string& out) const {
out.append("VsyncController:\n");
mController->dump(out);
diff --git a/services/surfaceflinger/Scheduler/VsyncSchedule.h b/services/surfaceflinger/Scheduler/VsyncSchedule.h
index 0d9b114..8c17409 100644
--- a/services/surfaceflinger/Scheduler/VsyncSchedule.h
+++ b/services/surfaceflinger/Scheduler/VsyncSchedule.h
@@ -20,6 +20,7 @@
#include <string>
#include <scheduler/Features.h>
+#include <scheduler/Time.h>
namespace android::scheduler {
@@ -38,6 +39,9 @@
VsyncSchedule(VsyncSchedule&&);
~VsyncSchedule();
+ Period period() const;
+ TimePoint vsyncDeadlineAfter(TimePoint) const;
+
// TODO(b/185535769): Hide behind API.
const VsyncTracker& getTracker() const { return *mTracker; }
VsyncTracker& getTracker() { return *mTracker; }
diff --git a/services/surfaceflinger/Scheduler/include/scheduler/Time.h b/services/surfaceflinger/Scheduler/include/scheduler/Time.h
new file mode 100644
index 0000000..6fa548e
--- /dev/null
+++ b/services/surfaceflinger/Scheduler/include/scheduler/Time.h
@@ -0,0 +1,67 @@
+/*
+ * Copyright 2022 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 <chrono>
+
+#include <utils/Timers.h>
+
+namespace android {
+namespace scheduler {
+
+// TODO(b/185535769): Pull Clock.h to libscheduler to reuse this.
+using SchedulerClock = std::chrono::high_resolution_clock;
+static_assert(SchedulerClock::is_steady);
+
+} // namespace scheduler
+
+struct Duration;
+
+struct TimePoint : scheduler::SchedulerClock::time_point {
+ explicit TimePoint(const Duration&);
+
+ // Implicit conversion from std::chrono counterpart.
+ constexpr TimePoint(scheduler::SchedulerClock::time_point p)
+ : scheduler::SchedulerClock::time_point(p) {}
+
+ static TimePoint fromNs(nsecs_t);
+
+ nsecs_t ns() const;
+};
+
+struct Duration : TimePoint::duration {
+ // Implicit conversion from std::chrono counterpart.
+ constexpr Duration(TimePoint::duration d) : TimePoint::duration(d) {}
+
+ static Duration fromNs(nsecs_t ns) { return {std::chrono::nanoseconds(ns)}; }
+
+ nsecs_t ns() const { return std::chrono::nanoseconds(*this).count(); }
+};
+
+using Period = Duration;
+
+inline TimePoint::TimePoint(const Duration& d) : scheduler::SchedulerClock::time_point(d) {}
+
+inline TimePoint TimePoint::fromNs(nsecs_t ns) {
+ return TimePoint(Duration::fromNs(ns));
+}
+
+inline nsecs_t TimePoint::ns() const {
+ return Duration(time_since_epoch()).ns();
+}
+
+} // namespace android
diff --git a/services/surfaceflinger/SurfaceFlinger.cpp b/services/surfaceflinger/SurfaceFlinger.cpp
index ab5bb77..3874371 100644
--- a/services/surfaceflinger/SurfaceFlinger.cpp
+++ b/services/surfaceflinger/SurfaceFlinger.cpp
@@ -105,7 +105,6 @@
#include <ui/DisplayIdentification.h>
#include "BackgroundExecutor.h"
-#include "BufferLayer.h"
#include "BufferStateLayer.h"
#include "Client.h"
#include "Colorizer.h"
@@ -129,7 +128,6 @@
#include "LayerProtoHelper.h"
#include "LayerRenderArea.h"
#include "LayerVector.h"
-#include "MonitoredProducer.h"
#include "MutexUtils.h"
#include "NativeWindowSurface.h"
#include "RefreshRateOverlay.h"
@@ -1055,12 +1053,14 @@
return NO_ERROR;
}
-status_t SurfaceFlinger::getDisplayStats(const sp<IBinder>&, DisplayStatInfo* stats) {
- if (!stats) {
+status_t SurfaceFlinger::getDisplayStats(const sp<IBinder>&, DisplayStatInfo* outStats) {
+ if (!outStats) {
return BAD_VALUE;
}
- *stats = mScheduler->getDisplayStatInfo(systemTime());
+ const auto& schedule = mScheduler->getVsyncSchedule();
+ outStats->vsyncTime = schedule.vsyncDeadlineAfter(scheduler::SchedulerClock::now()).ns();
+ outStats->vsyncPeriod = schedule.period().ns();
return NO_ERROR;
}
@@ -1569,12 +1569,10 @@
status_t SurfaceFlinger::injectVSync(nsecs_t when) {
Mutex::Autolock lock(mStateLock);
- const DisplayStatInfo stats = mScheduler->getDisplayStatInfo(when);
- const auto expectedPresent = calculateExpectedPresentTime(stats);
- return mScheduler->injectVSync(when, /*expectedVSyncTime=*/expectedPresent,
- /*deadlineTimestamp=*/expectedPresent)
- ? NO_ERROR
- : BAD_VALUE;
+ const nsecs_t expectedPresentTime = calculateExpectedPresentTime(when).ns();
+ const nsecs_t deadlineTimestamp = expectedPresentTime;
+ return mScheduler->injectVSync(when, expectedPresentTime, deadlineTimestamp) ? NO_ERROR
+ : BAD_VALUE;
}
status_t SurfaceFlinger::getLayerDebugInfo(std::vector<gui::LayerDebugInfo>* outLayers) {
@@ -1886,11 +1884,6 @@
}
}
-void SurfaceFlinger::getCompositorTiming(CompositorTiming* compositorTiming) {
- std::lock_guard<std::mutex> lock(getBE().mCompositorTimingLock);
- *compositorTiming = getBE().mCompositorTiming;
-}
-
void SurfaceFlinger::onComposerHalHotplug(hal::HWDisplayId hwcDisplayId,
hal::Connection connection) {
const bool connected = connection == hal::Connection::CONNECTED;
@@ -1951,18 +1944,15 @@
}));
}
-SurfaceFlinger::FenceWithFenceTime SurfaceFlinger::previousFrameFence() {
- const auto now = systemTime();
- const auto vsyncPeriod = mScheduler->getDisplayStatInfo(now).vsyncPeriod;
- const bool expectedPresentTimeIsTheNextVsync = mExpectedPresentTime - now <= vsyncPeriod;
- return expectedPresentTimeIsTheNextVsync ? mPreviousPresentFences[0]
- : mPreviousPresentFences[1];
+auto SurfaceFlinger::getPreviousPresentFence(nsecs_t frameTime, Period vsyncPeriod)
+ -> const FenceTimePtr& {
+ const bool isTwoVsyncsAhead = mExpectedPresentTime - frameTime > vsyncPeriod.ns();
+ const size_t i = static_cast<size_t>(isTwoVsyncsAhead);
+ return mPreviousPresentFences[i].fenceTime;
}
-bool SurfaceFlinger::previousFramePending(int graceTimeMs) {
+bool SurfaceFlinger::isFencePending(const FenceTimePtr& fence, int graceTimeMs) {
ATRACE_CALL();
- const std::shared_ptr<FenceTime>& fence = previousFrameFence().fenceTime;
-
if (fence == FenceTime::NO_FENCE) {
return false;
}
@@ -1973,37 +1963,32 @@
return status == -ETIME;
}
-nsecs_t SurfaceFlinger::previousFramePresentTime() {
- const std::shared_ptr<FenceTime>& fence = previousFrameFence().fenceTime;
+TimePoint SurfaceFlinger::calculateExpectedPresentTime(nsecs_t frameTime) const {
+ const auto& schedule = mScheduler->getVsyncSchedule();
- if (fence == FenceTime::NO_FENCE) {
- return Fence::SIGNAL_TIME_INVALID;
+ const TimePoint vsyncDeadline = schedule.vsyncDeadlineAfter(TimePoint::fromNs(frameTime));
+ if (mVsyncModulator->getVsyncConfig().sfOffset > 0) {
+ return vsyncDeadline;
}
- return fence->getSignalTime();
-}
-
-nsecs_t SurfaceFlinger::calculateExpectedPresentTime(DisplayStatInfo stats) const {
- // Inflate the expected present time if we're targetting the next vsync.
- return mVsyncModulator->getVsyncConfig().sfOffset > 0 ? stats.vsyncTime
- : stats.vsyncTime + stats.vsyncPeriod;
+ // Inflate the expected present time if we're targeting the next vsync.
+ return vsyncDeadline + schedule.period();
}
bool SurfaceFlinger::commit(nsecs_t frameTime, int64_t vsyncId, nsecs_t expectedVsyncTime)
FTL_FAKE_GUARD(kMainThreadContext) {
- // calculate the expected present time once and use the cached
- // value throughout this frame to make sure all layers are
- // seeing this same value.
- if (expectedVsyncTime >= frameTime) {
- mExpectedPresentTime = expectedVsyncTime;
- } else {
- const DisplayStatInfo stats = mScheduler->getDisplayStatInfo(frameTime);
- mExpectedPresentTime = calculateExpectedPresentTime(stats);
- }
-
+ // The expectedVsyncTime, which was predicted when this frame was scheduled, is normally in the
+ // future relative to frameTime, but may not be for delayed frames. Adjust mExpectedPresentTime
+ // accordingly, but not mScheduledPresentTime.
const nsecs_t lastScheduledPresentTime = mScheduledPresentTime;
mScheduledPresentTime = expectedVsyncTime;
+ // Calculate the expected present time once and use the cached value throughout this frame to
+ // make sure all layers are seeing this same value.
+ mExpectedPresentTime = expectedVsyncTime >= frameTime
+ ? expectedVsyncTime
+ : calculateExpectedPresentTime(frameTime).ns();
+
const auto vsyncIn = [&] {
if (!ATRACE_ENABLED()) return 0.f;
return (mExpectedPresentTime - systemTime()) / 1e6f;
@@ -2011,6 +1996,9 @@
ATRACE_FORMAT("%s %" PRId64 " vsyncIn %.2fms%s", __func__, vsyncId, vsyncIn,
mExpectedPresentTime == expectedVsyncTime ? "" : " (adjusted)");
+ const Period vsyncPeriod = mScheduler->getVsyncSchedule().period();
+ const FenceTimePtr& previousPresentFence = getPreviousPresentFence(frameTime, vsyncPeriod);
+
// When Backpressure propagation is enabled we want to give a small grace period
// for the present fence to fire instead of just giving up on this frame to handle cases
// where present fence is just about to get signaled.
@@ -2019,7 +2007,8 @@
// Pending frames may trigger backpressure propagation.
const TracedOrdinal<bool> framePending = {"PrevFramePending",
- previousFramePending(graceTimeForPresentFenceMs)};
+ isFencePending(previousPresentFence,
+ graceTimeForPresentFenceMs)};
// Frame missed counts for metrics tracking.
// A frame is missed if the prior frame is still pending. If no longer pending,
@@ -2029,9 +2018,8 @@
// Add some slop to correct for drift. This should generally be
// smaller than a typical frame duration, but should not be so small
// that it reports reasonable drift as a missed frame.
- const DisplayStatInfo stats = mScheduler->getDisplayStatInfo(systemTime());
- const nsecs_t frameMissedSlop = stats.vsyncPeriod / 2;
- const nsecs_t previousPresentTime = previousFramePresentTime();
+ const nsecs_t frameMissedSlop = vsyncPeriod.ns() / 2;
+ const nsecs_t previousPresentTime = previousPresentFence->getSignalTime();
const TracedOrdinal<bool> frameMissed = {"PrevFrameMissed",
framePending ||
(previousPresentTime >= 0 &&
@@ -2118,7 +2106,7 @@
// Composite if transactions were committed, or if requested by HWC.
bool mustComposite = mMustComposite.exchange(false);
{
- mFrameTimeline->setSfWakeUp(vsyncId, frameTime, Fps::fromPeriodNsecs(stats.vsyncPeriod));
+ mFrameTimeline->setSfWakeUp(vsyncId, frameTime, Fps::fromPeriodNsecs(vsyncPeriod.ns()));
bool needsTraversal = false;
if (clearTransactionFlags(eTransactionFlushNeeded)) {
@@ -2239,7 +2227,9 @@
}
const auto expectedPresentTime = mExpectedPresentTime.load();
- const auto prevVsyncTime = mScheduler->getPreviousVsyncFrom(expectedPresentTime);
+ const auto prevVsyncTime =
+ TimePoint::fromNs(expectedPresentTime) - mScheduler->getVsyncSchedule().period();
+
const auto hwcMinWorkDuration = mVsyncConfiguration->getCurrentConfigs().hwcMinWorkDuration;
refreshArgs.earliestPresentTime = prevVsyncTime - hwcMinWorkDuration;
refreshArgs.previousPresentFence = mPreviousPresentFences[0].fenceTime;
@@ -2326,11 +2316,11 @@
mLayersPendingRefresh.clear();
}
-void SurfaceFlinger::updateCompositorTiming(const DisplayStatInfo& stats, nsecs_t compositeTime,
- std::shared_ptr<FenceTime>& presentFenceTime) {
+nsecs_t SurfaceFlinger::trackPresentLatency(nsecs_t compositeTime,
+ std::shared_ptr<FenceTime> presentFenceTime) {
// Update queue of past composite+present times and determine the
// most recently known composite to present latency.
- getBE().mCompositePresentTimes.push({compositeTime, presentFenceTime});
+ getBE().mCompositePresentTimes.push({compositeTime, std::move(presentFenceTime)});
nsecs_t compositeToPresentLatency = -1;
while (!getBE().mCompositePresentTimes.empty()) {
SurfaceFlingerBE::CompositePresentTime& cpt = getBE().mCompositePresentTimes.front();
@@ -2349,41 +2339,7 @@
getBE().mCompositePresentTimes.pop();
}
- setCompositorTimingSnapped(stats, compositeToPresentLatency);
-}
-
-void SurfaceFlinger::setCompositorTimingSnapped(const DisplayStatInfo& stats,
- nsecs_t compositeToPresentLatency) {
- // Avoid division by 0 by defaulting to 60Hz
- const auto vsyncPeriod = stats.vsyncPeriod ?: (60_Hz).getPeriodNsecs();
-
- // Integer division and modulo round toward 0 not -inf, so we need to
- // treat negative and positive offsets differently.
- nsecs_t idealLatency = (mVsyncConfiguration->getCurrentConfigs().late.sfOffset > 0)
- ? (vsyncPeriod -
- (mVsyncConfiguration->getCurrentConfigs().late.sfOffset % vsyncPeriod))
- : ((-mVsyncConfiguration->getCurrentConfigs().late.sfOffset) % vsyncPeriod);
-
- // Just in case mVsyncConfiguration->getCurrentConfigs().late.sf == -vsyncInterval.
- if (idealLatency <= 0) {
- idealLatency = vsyncPeriod;
- }
-
- // Snap the latency to a value that removes scheduling jitter from the
- // composition and present times, which often have >1ms of jitter.
- // Reducing jitter is important if an app attempts to extrapolate
- // something (such as user input) to an accurate diasplay time.
- // Snapping also allows an app to precisely calculate
- // mVsyncConfiguration->getCurrentConfigs().late.sf with (presentLatency % interval).
- const nsecs_t bias = vsyncPeriod / 2;
- const int64_t extraVsyncs = ((compositeToPresentLatency - idealLatency + bias) / vsyncPeriod);
- const nsecs_t snappedCompositeToPresentLatency =
- (extraVsyncs > 0) ? idealLatency + (extraVsyncs * vsyncPeriod) : idealLatency;
-
- std::lock_guard<std::mutex> lock(getBE().mCompositorTimingLock);
- getBE().mCompositorTiming.deadline = stats.vsyncTime - idealLatency;
- getBE().mCompositorTiming.interval = vsyncPeriod;
- getBE().mCompositorTiming.presentLatency = snappedCompositeToPresentLatency;
+ return compositeToPresentLatency;
}
bool SurfaceFlinger::isHdrLayer(Layer* layer) const {
@@ -2470,18 +2426,20 @@
mFrameTimeline->setSfPresent(/* sfPresentTime */ now, mPreviousPresentFences[0].fenceTime,
glCompositionDoneFenceTime);
- const DisplayStatInfo stats = mScheduler->getDisplayStatInfo(now);
-
// We use the CompositionEngine::getLastFrameRefreshTimestamp() which might
// be sampled a little later than when we started doing work for this frame,
- // but that should be okay since updateCompositorTiming has snapping logic.
- updateCompositorTiming(stats, mCompositionEngine->getLastFrameRefreshTimestamp(),
- mPreviousPresentFences[0].fenceTime);
- CompositorTiming compositorTiming;
- {
- std::lock_guard<std::mutex> lock(getBE().mCompositorTimingLock);
- compositorTiming = getBE().mCompositorTiming;
- }
+ // but that should be okay since CompositorTiming has snapping logic.
+ const nsecs_t compositeTime = mCompositionEngine->getLastFrameRefreshTimestamp();
+ const nsecs_t presentLatency =
+ trackPresentLatency(compositeTime, mPreviousPresentFences[0].fenceTime);
+
+ const auto& schedule = mScheduler->getVsyncSchedule();
+ const TimePoint vsyncDeadline = schedule.vsyncDeadlineAfter(TimePoint::fromNs(now));
+ const Period vsyncPeriod = schedule.period();
+ const nsecs_t vsyncPhase = mVsyncConfiguration->getCurrentConfigs().late.sfOffset;
+
+ const CompositorTiming compositorTiming(vsyncDeadline.ns(), vsyncPeriod.ns(), vsyncPhase,
+ presentLatency);
for (const auto& layer: mLayersWithQueuedFrames) {
layer->onPostComposition(display, glCompositionDoneFenceTime,
@@ -2593,7 +2551,7 @@
mHasPoweredOff = false;
} else {
nsecs_t elapsedTime = currentTime - getBE().mLastSwapTime;
- size_t numPeriods = static_cast<size_t>(elapsedTime / stats.vsyncPeriod);
+ const size_t numPeriods = static_cast<size_t>(elapsedTime / vsyncPeriod.ns());
if (numPeriods < SurfaceFlingerBE::NUM_BUCKETS - 1) {
getBE().mFrameBuckets[numPeriods] += elapsedTime;
} else {
@@ -3470,8 +3428,8 @@
mInterceptor->saveVSyncEvent(timestamp);
});
- mScheduler->initVsync(mScheduler->getVsyncDispatch(), *mFrameTimeline->getTokenManager(),
- configs.late.sfWorkDuration);
+ mScheduler->initVsync(mScheduler->getVsyncSchedule().getDispatch(),
+ *mFrameTimeline->getTokenManager(), configs.late.sfWorkDuration);
mRegionSamplingThread =
new RegionSamplingThread(*this, RegionSamplingThread::EnvironmentTimingTunables());
@@ -3941,10 +3899,10 @@
bool SurfaceFlinger::frameIsEarly(nsecs_t expectedPresentTime, int64_t vsyncId) const {
// The amount of time SF can delay a frame if it is considered early based
// on the VsyncModulator::VsyncConfig::appWorkDuration
- constexpr static std::chrono::nanoseconds kEarlyLatchMaxThreshold = 100ms;
+ constexpr std::chrono::nanoseconds kEarlyLatchMaxThreshold = 100ms;
- const auto currentVsyncPeriod = mScheduler->getDisplayStatInfo(systemTime()).vsyncPeriod;
- const auto earlyLatchVsyncThreshold = currentVsyncPeriod / 2;
+ const nsecs_t currentVsyncPeriod = mScheduler->getVsyncSchedule().period().ns();
+ const nsecs_t earlyLatchVsyncThreshold = currentVsyncPeriod / 2;
const auto prediction = mFrameTimeline->getTokenManager()->getPredictionsForToken(vsyncId);
if (!prediction.has_value()) {
@@ -4847,10 +4805,6 @@
const nsecs_t vsyncPeriod = display->refreshRateConfigs().getActiveMode()->getVsyncPeriod();
mAnimFrameTracker.setDisplayRefreshPeriod(vsyncPeriod);
mActiveDisplayTransformHint = display->getTransformHint();
- // Use phase of 0 since phase is not known.
- // Use latency of 0, which will snap to the ideal latency.
- DisplayStatInfo stats{0 /* vsyncTime */, vsyncPeriod};
- setCompositorTimingSnapped(stats, 0);
}
void SurfaceFlinger::initializeDisplays() {
@@ -6632,24 +6586,21 @@
});
if (captureListener) {
- // TODO: The future returned by std::async blocks the main thread. Return a chain of
- // futures to the Binder thread instead.
- std::async([=]() mutable {
- ATRACE_NAME("captureListener is nonnull!");
- auto fenceResult = renderFuture.get();
- // TODO(b/232535621): Change ScreenCaptureResults to store a FenceResult.
- captureResults.result = fenceStatus(fenceResult);
- captureResults.fence = std::move(fenceResult).value_or(Fence::NO_FENCE);
- captureListener->onScreenCaptureCompleted(captureResults);
- });
+ // Defer blocking on renderFuture back to the Binder thread.
+ return ftl::Future(std::move(renderFuture))
+ .then([captureListener, captureResults = std::move(captureResults)](
+ FenceResult fenceResult) mutable -> FenceResult {
+ // TODO(b/232535621): Change ScreenCaptureResults to store a FenceResult.
+ captureResults.result = fenceStatus(fenceResult);
+ captureResults.fence = std::move(fenceResult).value_or(Fence::NO_FENCE);
+ captureListener->onScreenCaptureCompleted(captureResults);
+ return base::unexpected(NO_ERROR);
+ })
+ .share();
}
return renderFuture;
});
- if (captureListener) {
- return ftl::yield<FenceResult>(base::unexpected(NO_ERROR)).share();
- }
-
// Flatten nested futures.
auto chain = ftl::Future(std::move(future)).then([](ftl::SharedFuture<FenceResult> future) {
return future;
diff --git a/services/surfaceflinger/SurfaceFlinger.h b/services/surfaceflinger/SurfaceFlinger.h
index ed6c8ce..7d23c3c 100644
--- a/services/surfaceflinger/SurfaceFlinger.h
+++ b/services/surfaceflinger/SurfaceFlinger.h
@@ -31,6 +31,7 @@
#include <ftl/future.h>
#include <ftl/small_map.h>
#include <gui/BufferQueue.h>
+#include <gui/CompositorTiming.h>
#include <gui/FrameTimestamps.h>
#include <gui/ISurfaceComposer.h>
#include <gui/ITransactionCompletedListener.h>
@@ -168,10 +169,6 @@
using DisplayColorSetting = compositionengine::OutputColorSetting;
struct SurfaceFlingerBE {
- // protected by mCompositorTimingLock;
- mutable std::mutex mCompositorTimingLock;
- CompositorTiming mCompositorTiming;
-
// Only accessed from the main thread.
struct CompositePresentTime {
nsecs_t composite = -1;
@@ -377,7 +374,6 @@
friend class FpsReporter;
friend class TunnelModeEnabledReporter;
friend class Layer;
- friend class MonitoredProducer;
friend class RefreshRateOverlay;
friend class RegionSamplingThread;
friend class LayerRenderArea;
@@ -958,11 +954,9 @@
* Compositing
*/
void postComposition();
- void getCompositorTiming(CompositorTiming* compositorTiming);
- void updateCompositorTiming(const DisplayStatInfo& stats, nsecs_t compositeTime,
- std::shared_ptr<FenceTime>& presentFenceTime);
- void setCompositorTimingSnapped(const DisplayStatInfo& stats,
- nsecs_t compositeToPresentLatency);
+
+ // Returns the composite-to-present latency of the latest presented frame.
+ nsecs_t trackPresentLatency(nsecs_t compositeTime, std::shared_ptr<FenceTime> presentFenceTime);
void postFrame() REQUIRES(kMainThreadContext);
@@ -997,31 +991,17 @@
getHwComposer().setVsyncEnabled(id, enabled);
}
- struct FenceWithFenceTime {
- sp<Fence> fence = Fence::NO_FENCE;
- std::shared_ptr<FenceTime> fenceTime = FenceTime::NO_FENCE;
- };
+ using FenceTimePtr = std::shared_ptr<FenceTime>;
- // Gets the fence for the previous frame.
- // Must be called on the main thread.
- FenceWithFenceTime previousFrameFence();
+ const FenceTimePtr& getPreviousPresentFence(nsecs_t frameTime, Period)
+ REQUIRES(kMainThreadContext);
- // Whether the previous frame has not yet been presented to the display.
- // If graceTimeMs is positive, this method waits for at most the provided
- // grace period before reporting if the frame missed.
- // Must be called on the main thread.
- bool previousFramePending(int graceTimeMs = 0);
-
- // Returns the previous time that the frame was presented. If the frame has
- // not been presented yet, then returns Fence::SIGNAL_TIME_PENDING. If there
- // is no pending frame, then returns Fence::SIGNAL_TIME_INVALID.
- // Must be called on the main thread.
- nsecs_t previousFramePresentTime();
+ // Blocks the thread waiting for up to graceTimeMs in case the fence is about to signal.
+ static bool isFencePending(const FenceTimePtr&, int graceTimeMs);
// Calculates the expected present time for this frame. For negative offsets, performs a
// correction using the predicted vsync for the next frame instead.
-
- nsecs_t calculateExpectedPresentTime(DisplayStatInfo) const;
+ TimePoint calculateExpectedPresentTime(nsecs_t frameTime) const;
/*
* Display identification
@@ -1209,7 +1189,7 @@
std::unordered_set<sp<Layer>, SpHash<Layer>> mLayersWithQueuedFrames;
// Tracks layers that need to update a display's dirty region.
std::vector<sp<Layer>> mLayersPendingRefresh;
- std::array<FenceWithFenceTime, 2> mPreviousPresentFences;
+
// True if in the previous frame at least one layer was composed via the GPU.
bool mHadClientComposition = false;
// True if in the previous frame at least one layer was composed via HW Composer.
@@ -1346,6 +1326,12 @@
std::unique_ptr<scheduler::RefreshRateStats> mRefreshRateStats;
+ struct FenceWithFenceTime {
+ sp<Fence> fence = Fence::NO_FENCE;
+ FenceTimePtr fenceTime = FenceTime::NO_FENCE;
+ };
+ std::array<FenceWithFenceTime, 2> mPreviousPresentFences;
+
std::atomic<nsecs_t> mExpectedPresentTime = 0;
nsecs_t mScheduledPresentTime = 0;
hal::Vsync mHWCVsyncPendingState = hal::Vsync::DISABLE;
diff --git a/services/surfaceflinger/SurfaceFlingerDefaultFactory.cpp b/services/surfaceflinger/SurfaceFlingerDefaultFactory.cpp
index 39a5d0f..2399a39 100644
--- a/services/surfaceflinger/SurfaceFlingerDefaultFactory.cpp
+++ b/services/surfaceflinger/SurfaceFlingerDefaultFactory.cpp
@@ -22,14 +22,12 @@
#include <cutils/properties.h>
#include <ui/GraphicBuffer.h>
-#include "BufferLayerConsumer.h"
#include "BufferStateLayer.h"
#include "ContainerLayer.h"
#include "DisplayDevice.h"
#include "EffectLayer.h"
#include "FrameTracer/FrameTracer.h"
#include "Layer.h"
-#include "MonitoredProducer.h"
#include "NativeWindowSurface.h"
#include "StartPropertySetThread.h"
#include "SurfaceFlingerDefaultFactory.h"
@@ -83,18 +81,6 @@
BufferQueue::createBufferQueue(outProducer, outConsumer, consumerIsSurfaceFlinger);
}
-sp<IGraphicBufferProducer> DefaultFactory::createMonitoredProducer(
- const sp<IGraphicBufferProducer>& producer, const sp<SurfaceFlinger>& flinger,
- const wp<Layer>& layer) {
- return new MonitoredProducer(producer, flinger, layer);
-}
-
-sp<BufferLayerConsumer> DefaultFactory::createBufferLayerConsumer(
- const sp<IGraphicBufferConsumer>& consumer, renderengine::RenderEngine& renderEngine,
- uint32_t textureName, Layer* layer) {
- return new BufferLayerConsumer(consumer, renderEngine, textureName, layer);
-}
-
std::unique_ptr<surfaceflinger::NativeWindowSurface> DefaultFactory::createNativeWindowSurface(
const sp<IGraphicBufferProducer>& producer) {
return surfaceflinger::impl::createNativeWindowSurface(producer);
diff --git a/services/surfaceflinger/SurfaceFlingerDefaultFactory.h b/services/surfaceflinger/SurfaceFlingerDefaultFactory.h
index 173ca81..4000e09 100644
--- a/services/surfaceflinger/SurfaceFlingerDefaultFactory.h
+++ b/services/surfaceflinger/SurfaceFlingerDefaultFactory.h
@@ -38,12 +38,6 @@
void createBufferQueue(sp<IGraphicBufferProducer>* outProducer,
sp<IGraphicBufferConsumer>* outConsumer,
bool consumerIsSurfaceFlinger) override;
- sp<IGraphicBufferProducer> createMonitoredProducer(const sp<IGraphicBufferProducer>&,
- const sp<SurfaceFlinger>&,
- const wp<Layer>&) override;
- sp<BufferLayerConsumer> createBufferLayerConsumer(const sp<IGraphicBufferConsumer>&,
- renderengine::RenderEngine&, uint32_t tex,
- Layer*) override;
std::unique_ptr<surfaceflinger::NativeWindowSurface> createNativeWindowSurface(
const sp<IGraphicBufferProducer>&) override;
std::unique_ptr<compositionengine::CompositionEngine> createCompositionEngine() override;
diff --git a/services/surfaceflinger/SurfaceFlingerFactory.h b/services/surfaceflinger/SurfaceFlingerFactory.h
index e117e96..77a75c4 100644
--- a/services/surfaceflinger/SurfaceFlingerFactory.h
+++ b/services/surfaceflinger/SurfaceFlingerFactory.h
@@ -85,12 +85,6 @@
virtual void createBufferQueue(sp<IGraphicBufferProducer>* outProducer,
sp<IGraphicBufferConsumer>* outConsumer,
bool consumerIsSurfaceFlinger) = 0;
- virtual sp<IGraphicBufferProducer> createMonitoredProducer(const sp<IGraphicBufferProducer>&,
- const sp<SurfaceFlinger>&,
- const wp<Layer>&) = 0;
- virtual sp<BufferLayerConsumer> createBufferLayerConsumer(const sp<IGraphicBufferConsumer>&,
- renderengine::RenderEngine&,
- uint32_t tex, Layer*) = 0;
virtual std::unique_ptr<surfaceflinger::NativeWindowSurface> createNativeWindowSurface(
const sp<IGraphicBufferProducer>&) = 0;
diff --git a/services/surfaceflinger/Tracing/tools/LayerTraceGenerator.cpp b/services/surfaceflinger/Tracing/tools/LayerTraceGenerator.cpp
index 5e20b74..c4b1b0f 100644
--- a/services/surfaceflinger/Tracing/tools/LayerTraceGenerator.cpp
+++ b/services/surfaceflinger/Tracing/tools/LayerTraceGenerator.cpp
@@ -71,19 +71,6 @@
sp<IGraphicBufferConsumer>* /* outConsumer */,
bool /* consumerIsSurfaceFlinger */) override {}
- sp<IGraphicBufferProducer> createMonitoredProducer(
- const sp<IGraphicBufferProducer>& /* producer */,
- const sp<SurfaceFlinger>& /* flinger */, const wp<Layer>& /* layer */) override {
- return nullptr;
- }
-
- sp<BufferLayerConsumer> createBufferLayerConsumer(
- const sp<IGraphicBufferConsumer>& /* consumer */,
- renderengine::RenderEngine& /* renderEngine */, uint32_t /* textureName */,
- Layer* /* layer */) override {
- return nullptr;
- }
-
std::unique_ptr<surfaceflinger::NativeWindowSurface> createNativeWindowSurface(
const sp<IGraphicBufferProducer>& /* producer */) override {
return nullptr;
diff --git a/services/surfaceflinger/WindowInfosListenerInvoker.cpp b/services/surfaceflinger/WindowInfosListenerInvoker.cpp
index cc33001..4a8f157 100644
--- a/services/surfaceflinger/WindowInfosListenerInvoker.cpp
+++ b/services/surfaceflinger/WindowInfosListenerInvoker.cpp
@@ -111,7 +111,7 @@
auto status = listener->onWindowInfosChanged(windowInfos, displayInfos,
windowInfosReportedListener);
- if (!status.isOk()) {
+ if (windowInfosReportedListener && !status.isOk()) {
windowInfosReportedListener->onWindowInfosReported();
}
}
diff --git a/services/surfaceflinger/fuzzer/surfaceflinger_fuzzers_utils.h b/services/surfaceflinger/fuzzer/surfaceflinger_fuzzers_utils.h
index 456a498..3338da0 100644
--- a/services/surfaceflinger/fuzzer/surfaceflinger_fuzzers_utils.h
+++ b/services/surfaceflinger/fuzzer/surfaceflinger_fuzzers_utils.h
@@ -333,18 +333,6 @@
mCreateBufferQueue(outProducer, outConsumer, consumerIsSurfaceFlinger);
}
- sp<IGraphicBufferProducer> createMonitoredProducer(const sp<IGraphicBufferProducer> &producer,
- const sp<SurfaceFlinger> &flinger,
- const wp<Layer> &layer) override {
- return new MonitoredProducer(producer, flinger, layer);
- }
-
- sp<BufferLayerConsumer> createBufferLayerConsumer(const sp<IGraphicBufferConsumer> &consumer,
- renderengine::RenderEngine &renderEngine,
- uint32_t textureName, Layer *layer) override {
- return new BufferLayerConsumer(consumer, renderEngine, textureName, layer);
- }
-
std::unique_ptr<surfaceflinger::NativeWindowSurface> createNativeWindowSurface(
const sp<IGraphicBufferProducer> &producer) override {
if (!mCreateNativeWindowSurface) return nullptr;
@@ -535,16 +523,6 @@
mFlinger->setVsyncConfig(vsyncConfig, fdp->ConsumeIntegral<nsecs_t>());
}
- void updateCompositorTiming(FuzzedDataProvider *fdp) {
- std::shared_ptr<FenceTime> presentFenceTime = FenceTime::NO_FENCE;
- mFlinger->updateCompositorTiming({}, fdp->ConsumeIntegral<nsecs_t>(), presentFenceTime);
- }
-
- void getCompositorTiming() {
- CompositorTiming compositorTiming;
- mFlinger->getCompositorTiming(&compositorTiming);
- }
-
sp<IBinder> fuzzBoot(FuzzedDataProvider *fdp) {
mFlinger->callingThreadHasUnscopedSurfaceFlingerAccess(fdp->ConsumeBool());
const sp<Client> client = new Client(mFlinger);
@@ -639,11 +617,8 @@
mFlinger->postComposition();
- getCompositorTiming();
+ mFlinger->trackPresentLatency(mFdp.ConsumeIntegral<nsecs_t>(), FenceTime::NO_FENCE);
- updateCompositorTiming(&mFdp);
-
- mFlinger->setCompositorTimingSnapped({}, mFdp.ConsumeIntegral<nsecs_t>());
FTL_FAKE_GUARD(kMainThreadContext, mFlinger->postFrame());
mFlinger->calculateExpectedPresentTime({});
diff --git a/services/surfaceflinger/fuzzer/surfaceflinger_layer_fuzzer.cpp b/services/surfaceflinger/fuzzer/surfaceflinger_layer_fuzzer.cpp
index 4aef017..4d90b1e 100644
--- a/services/surfaceflinger/fuzzer/surfaceflinger_layer_fuzzer.cpp
+++ b/services/surfaceflinger/fuzzer/surfaceflinger_layer_fuzzer.cpp
@@ -18,9 +18,7 @@
#include <Client.h>
#include <DisplayDevice.h>
#include <EffectLayer.h>
-#include <LayerRejecter.h>
#include <LayerRenderArea.h>
-#include <MonitoredProducer.h>
#include <ftl/future.h>
#include <fuzzer/FuzzedDataProvider.h>
#include <gui/IProducerListener.h>
@@ -117,17 +115,18 @@
sp<Fence> fence = sp<Fence>::make();
const std::shared_ptr<FenceTime> fenceTime = std::make_shared<FenceTime>(fence);
- const CompositorTiming compositor = {mFdp.ConsumeIntegral<int64_t>(),
- mFdp.ConsumeIntegral<int64_t>(),
- mFdp.ConsumeIntegral<int64_t>()};
+ const CompositorTiming compositorTiming(mFdp.ConsumeIntegral<int64_t>(),
+ mFdp.ConsumeIntegral<int64_t>(),
+ mFdp.ConsumeIntegral<int64_t>(),
+ mFdp.ConsumeIntegral<int64_t>());
layer->onLayerDisplayed(ftl::yield<FenceResult>(fence).share());
layer->onLayerDisplayed(
ftl::yield<FenceResult>(base::unexpected(mFdp.ConsumeIntegral<status_t>())).share());
layer->releasePendingBuffer(mFdp.ConsumeIntegral<int64_t>());
- layer->finalizeFrameEventHistory(fenceTime, compositor);
- layer->onPostComposition(nullptr, fenceTime, fenceTime, compositor);
+ layer->finalizeFrameEventHistory(fenceTime, compositorTiming);
+ layer->onPostComposition(nullptr, fenceTime, fenceTime, compositorTiming);
layer->isBufferDue(mFdp.ConsumeIntegral<int64_t>());
layer->setTransform(mFdp.ConsumeIntegral<uint32_t>());
diff --git a/services/surfaceflinger/tests/unittests/CompositionTest.cpp b/services/surfaceflinger/tests/unittests/CompositionTest.cpp
index 19eaa19..f6ebfe5 100644
--- a/services/surfaceflinger/tests/unittests/CompositionTest.cpp
+++ b/services/surfaceflinger/tests/unittests/CompositionTest.cpp
@@ -629,7 +629,8 @@
EXPECT_EQ(false, layer.source.buffer.isY410BT2020);
EXPECT_EQ(true, layer.source.buffer.usePremultipliedAlpha);
EXPECT_EQ(false, layer.source.buffer.isOpaque);
- EXPECT_EQ(0.0, layer.geometry.roundedCornersRadius);
+ EXPECT_EQ(0.0, layer.geometry.roundedCornersRadius.x);
+ EXPECT_EQ(0.0, layer.geometry.roundedCornersRadius.y);
EXPECT_EQ(ui::Dataspace::UNKNOWN, layer.sourceDataspace);
EXPECT_EQ(LayerProperties::COLOR[3], layer.alpha);
return resultFuture;
@@ -679,7 +680,8 @@
EXPECT_EQ(half3(LayerProperties::COLOR[0], LayerProperties::COLOR[1],
LayerProperties::COLOR[2]),
layer.source.solidColor);
- EXPECT_EQ(0.0, layer.geometry.roundedCornersRadius);
+ EXPECT_EQ(0.0, layer.geometry.roundedCornersRadius.x);
+ EXPECT_EQ(0.0, layer.geometry.roundedCornersRadius.y);
EXPECT_EQ(ui::Dataspace::UNKNOWN, layer.sourceDataspace);
EXPECT_EQ(LayerProperties::COLOR[3], layer.alpha);
return resultFuture;
@@ -757,7 +759,8 @@
const renderengine::LayerSettings layer = layerSettings.back();
EXPECT_THAT(layer.source.buffer.buffer, IsNull());
EXPECT_EQ(half3(0.0f, 0.0f, 0.0f), layer.source.solidColor);
- EXPECT_EQ(0.0, layer.geometry.roundedCornersRadius);
+ EXPECT_EQ(0.0, layer.geometry.roundedCornersRadius.x);
+ EXPECT_EQ(0.0, layer.geometry.roundedCornersRadius.y);
EXPECT_EQ(ui::Dataspace::UNKNOWN, layer.sourceDataspace);
EXPECT_EQ(1.0f, layer.alpha);
return resultFuture;
diff --git a/services/surfaceflinger/tests/unittests/SurfaceFlinger_OnInitializeDisplaysTest.cpp b/services/surfaceflinger/tests/unittests/SurfaceFlinger_OnInitializeDisplaysTest.cpp
index 37cf05e..3d576f4 100644
--- a/services/surfaceflinger/tests/unittests/SurfaceFlinger_OnInitializeDisplaysTest.cpp
+++ b/services/surfaceflinger/tests/unittests/SurfaceFlinger_OnInitializeDisplaysTest.cpp
@@ -85,12 +85,6 @@
// The display transaction needed flag should be set.
EXPECT_TRUE(hasTransactionFlagSet(eDisplayTransactionNeeded));
-
- // The compositor timing should be set to default values
- const auto& compositorTiming = mFlinger.getCompositorTiming();
- EXPECT_EQ(-DEFAULT_VSYNC_PERIOD, compositorTiming.deadline);
- EXPECT_EQ(DEFAULT_VSYNC_PERIOD, compositorTiming.interval);
- EXPECT_EQ(DEFAULT_VSYNC_PERIOD, compositorTiming.presentLatency);
}
} // namespace
diff --git a/services/surfaceflinger/tests/unittests/TestableSurfaceFlinger.h b/services/surfaceflinger/tests/unittests/TestableSurfaceFlinger.h
index 3a05e2f..60285f9 100644
--- a/services/surfaceflinger/tests/unittests/TestableSurfaceFlinger.h
+++ b/services/surfaceflinger/tests/unittests/TestableSurfaceFlinger.h
@@ -111,18 +111,6 @@
mCreateBufferQueue(outProducer, outConsumer, consumerIsSurfaceFlinger);
}
- sp<IGraphicBufferProducer> createMonitoredProducer(const sp<IGraphicBufferProducer>& producer,
- const sp<SurfaceFlinger>& flinger,
- const wp<Layer>& layer) override {
- return new MonitoredProducer(producer, flinger, layer);
- }
-
- sp<BufferLayerConsumer> createBufferLayerConsumer(const sp<IGraphicBufferConsumer>& consumer,
- renderengine::RenderEngine& renderEngine,
- uint32_t textureName, Layer* layer) override {
- return new BufferLayerConsumer(consumer, renderEngine, textureName, layer);
- }
-
std::unique_ptr<surfaceflinger::NativeWindowSurface> createNativeWindowSurface(
const sp<IGraphicBufferProducer>& producer) override {
if (!mCreateNativeWindowSurface) return nullptr;
@@ -501,8 +489,6 @@
}
auto& getCompositionEngine() const { return mFlinger->getCompositionEngine(); }
- const auto& getCompositorTiming() const { return mFlinger->getBE().mCompositorTiming; }
-
mock::FrameTracer* getFrameTracer() const {
return static_cast<mock::FrameTracer*>(mFlinger->mFrameTracer.get());
}
diff --git a/vulkan/libvulkan/swapchain.cpp b/vulkan/libvulkan/swapchain.cpp
index eb669c0..8e46442 100644
--- a/vulkan/libvulkan/swapchain.cpp
+++ b/vulkan/libvulkan/swapchain.cpp
@@ -789,12 +789,16 @@
if (AHardwareBuffer_isSupported(&desc)) {
all_formats.emplace_back(VkSurfaceFormatKHR{
VK_FORMAT_R5G6B5_UNORM_PACK16, VK_COLOR_SPACE_SRGB_NONLINEAR_KHR});
+ all_formats.emplace_back(VkSurfaceFormatKHR{
+ VK_FORMAT_R5G6B5_UNORM_PACK16, VK_COLOR_SPACE_PASS_THROUGH_EXT});
}
desc.format = AHARDWAREBUFFER_FORMAT_R16G16B16A16_FLOAT;
if (AHardwareBuffer_isSupported(&desc)) {
all_formats.emplace_back(VkSurfaceFormatKHR{
VK_FORMAT_R16G16B16A16_SFLOAT, VK_COLOR_SPACE_SRGB_NONLINEAR_KHR});
+ all_formats.emplace_back(VkSurfaceFormatKHR{
+ VK_FORMAT_R16G16B16A16_SFLOAT, VK_COLOR_SPACE_PASS_THROUGH_EXT});
if (wide_color_support) {
all_formats.emplace_back(
VkSurfaceFormatKHR{VK_FORMAT_R16G16B16A16_SFLOAT,
@@ -810,6 +814,9 @@
all_formats.emplace_back(
VkSurfaceFormatKHR{VK_FORMAT_A2B10G10R10_UNORM_PACK32,
VK_COLOR_SPACE_SRGB_NONLINEAR_KHR});
+ all_formats.emplace_back(
+ VkSurfaceFormatKHR{VK_FORMAT_A2B10G10R10_UNORM_PACK32,
+ VK_COLOR_SPACE_PASS_THROUGH_EXT});
if (wide_color_support) {
all_formats.emplace_back(
VkSurfaceFormatKHR{VK_FORMAT_A2B10G10R10_UNORM_PACK32,