Merge "Addressed NDK API feedback" into main
diff --git a/cmds/bugreport/OWNERS b/cmds/bugreport/OWNERS
index 5f56531..41bfd26 100644
--- a/cmds/bugreport/OWNERS
+++ b/cmds/bugreport/OWNERS
@@ -1,5 +1,5 @@
set noparent
-gavincorkery@google.com
+ronish@google.com
nandana@google.com
jsharkey@android.com
diff --git a/cmds/bugreportz/OWNERS b/cmds/bugreportz/OWNERS
index 5f56531..41bfd26 100644
--- a/cmds/bugreportz/OWNERS
+++ b/cmds/bugreportz/OWNERS
@@ -1,5 +1,5 @@
set noparent
-gavincorkery@google.com
+ronish@google.com
nandana@google.com
jsharkey@android.com
diff --git a/cmds/dumpstate/OWNERS b/cmds/dumpstate/OWNERS
index ab81ecf..c24bf39 100644
--- a/cmds/dumpstate/OWNERS
+++ b/cmds/dumpstate/OWNERS
@@ -1,6 +1,6 @@
set noparent
-gavincorkery@google.com
+ronish@google.com
nandana@google.com
jsharkey@android.com
smoreland@google.com
\ No newline at end of file
diff --git a/cmds/dumpsys/OWNERS b/cmds/dumpsys/OWNERS
index 97a63ca..03143ae 100644
--- a/cmds/dumpsys/OWNERS
+++ b/cmds/dumpsys/OWNERS
@@ -1,6 +1,6 @@
set noparent
-gavincorkery@google.com
+ronish@google.com
nandana@google.com
jsharkey@android.com
diff --git a/cmds/lshal/ListCommand.cpp b/cmds/lshal/ListCommand.cpp
index 870e8eb..0c1feb8 100644
--- a/cmds/lshal/ListCommand.cpp
+++ b/cmds/lshal/ListCommand.cpp
@@ -731,16 +731,17 @@
[hashChain](const auto& ret) { *hashChain = std::move(ret); });
if (!hashRet.isOk()) {
handleError(TRANSACTION_ERROR, "getHashChain failed: " + hashRet.description());
+ break; // skip getHashChain
}
if (static_cast<size_t>(hashIndex) >= hashChain->size()) {
handleError(BAD_IMPL,
"interfaceChain indicates position " + std::to_string(hashIndex) +
" but getHashChain returns " + std::to_string(hashChain->size()) +
" hashes");
- } else {
- auto&& hashArray = (*hashChain)[hashIndex];
- entry->hash = android::base::HexString(hashArray.data(), hashArray.size());
+ break; // skip getHashChain
}
+ auto&& hashArray = (*hashChain)[hashIndex];
+ entry->hash = android::base::HexString(hashArray.data(), hashArray.size());
} while (0);
if (status == OK) {
entry->serviceStatus = ServiceStatus::ALIVE;
diff --git a/libs/binder/ndk/ibinder.cpp b/libs/binder/ndk/ibinder.cpp
index bf7a0ba..e2ede3f 100644
--- a/libs/binder/ndk/ibinder.cpp
+++ b/libs/binder/ndk/ibinder.cpp
@@ -258,11 +258,24 @@
}
}
+void ABBinder::addDeathRecipient(const ::android::sp<AIBinder_DeathRecipient>& /* recipient */,
+ void* /* cookie */) {
+ LOG_ALWAYS_FATAL("Should not reach this. Can't linkToDeath local binders.");
+}
+
ABpBinder::ABpBinder(const ::android::sp<::android::IBinder>& binder)
: AIBinder(nullptr /*clazz*/), mRemote(binder) {
LOG_ALWAYS_FATAL_IF(binder == nullptr, "binder == nullptr");
}
-ABpBinder::~ABpBinder() {}
+
+ABpBinder::~ABpBinder() {
+ for (auto& recip : mDeathRecipients) {
+ sp<AIBinder_DeathRecipient> strongRecip = recip.recipient.promote();
+ if (strongRecip) {
+ strongRecip->pruneThisTransferEntry(getBinder(), recip.cookie);
+ }
+ }
+}
sp<AIBinder> ABpBinder::lookupOrCreateFromBinder(const ::android::sp<::android::IBinder>& binder) {
if (binder == nullptr) {
@@ -301,6 +314,12 @@
return ret;
}
+void ABpBinder::addDeathRecipient(const ::android::sp<AIBinder_DeathRecipient>& recipient,
+ void* cookie) {
+ std::lock_guard<std::mutex> l(mDeathRecipientsMutex);
+ mDeathRecipients.emplace_back(recipient, cookie);
+}
+
struct AIBinder_Weak {
wp<AIBinder> binder;
};
@@ -426,6 +445,17 @@
LOG_ALWAYS_FATAL_IF(onDied == nullptr, "onDied == nullptr");
}
+void AIBinder_DeathRecipient::pruneThisTransferEntry(const sp<IBinder>& who, void* cookie) {
+ std::lock_guard<std::mutex> l(mDeathRecipientsMutex);
+ mDeathRecipients.erase(std::remove_if(mDeathRecipients.begin(), mDeathRecipients.end(),
+ [&](const sp<TransferDeathRecipient>& tdr) {
+ auto tdrWho = tdr->getWho();
+ return tdrWho != nullptr && tdrWho.promote() == who &&
+ cookie == tdr->getCookie();
+ }),
+ mDeathRecipients.end());
+}
+
void AIBinder_DeathRecipient::pruneDeadTransferEntriesLocked() {
mDeathRecipients.erase(std::remove_if(mDeathRecipients.begin(), mDeathRecipients.end(),
[](const sp<TransferDeathRecipient>& tdr) {
@@ -554,8 +584,11 @@
return STATUS_UNEXPECTED_NULL;
}
- // returns binder_status_t
- return recipient->linkToDeath(binder->getBinder(), cookie);
+ binder_status_t ret = recipient->linkToDeath(binder->getBinder(), cookie);
+ if (ret == STATUS_OK) {
+ binder->addDeathRecipient(recipient, cookie);
+ }
+ return ret;
}
binder_status_t AIBinder_unlinkToDeath(AIBinder* binder, AIBinder_DeathRecipient* recipient,
diff --git a/libs/binder/ndk/ibinder_internal.h b/libs/binder/ndk/ibinder_internal.h
index 9d5368f..f5b738c 100644
--- a/libs/binder/ndk/ibinder_internal.h
+++ b/libs/binder/ndk/ibinder_internal.h
@@ -51,6 +51,8 @@
::android::sp<::android::IBinder> binder = const_cast<AIBinder*>(this)->getBinder();
return binder->remoteBinder() != nullptr;
}
+ virtual void addDeathRecipient(const ::android::sp<AIBinder_DeathRecipient>& recipient,
+ void* cookie) = 0;
private:
// AIBinder instance is instance of this class for a local object. In order to transact on a
@@ -78,6 +80,8 @@
::android::status_t dump(int fd, const ::android::Vector<::android::String16>& args) override;
::android::status_t onTransact(uint32_t code, const ::android::Parcel& data,
::android::Parcel* reply, binder_flags_t flags) override;
+ void addDeathRecipient(const ::android::sp<AIBinder_DeathRecipient>& /* recipient */,
+ void* /* cookie */) override;
private:
ABBinder(const AIBinder_Class* clazz, void* userData);
@@ -106,12 +110,20 @@
bool isServiceFuzzing() const { return mServiceFuzzing; }
void setServiceFuzzing() { mServiceFuzzing = true; }
+ void addDeathRecipient(const ::android::sp<AIBinder_DeathRecipient>& recipient,
+ void* cookie) override;
private:
friend android::sp<ABpBinder>;
explicit ABpBinder(const ::android::sp<::android::IBinder>& binder);
::android::sp<::android::IBinder> mRemote;
bool mServiceFuzzing = false;
+ struct DeathRecipientInfo {
+ android::wp<AIBinder_DeathRecipient> recipient;
+ void* cookie;
+ };
+ std::mutex mDeathRecipientsMutex;
+ std::vector<DeathRecipientInfo> mDeathRecipients;
};
struct AIBinder_Class {
@@ -183,6 +195,7 @@
binder_status_t linkToDeath(const ::android::sp<::android::IBinder>&, void* cookie);
binder_status_t unlinkToDeath(const ::android::sp<::android::IBinder>& binder, void* cookie);
void setOnUnlinked(AIBinder_DeathRecipient_onBinderUnlinked onUnlinked);
+ void pruneThisTransferEntry(const ::android::sp<::android::IBinder>&, void* cookie);
private:
// When the user of this API deletes a Bp object but not the death recipient, the
diff --git a/libs/binder/ndk/include_cpp/android/persistable_bundle_aidl.h b/libs/binder/ndk/include_cpp/android/persistable_bundle_aidl.h
index 3aacbe9..d570eab 100644
--- a/libs/binder/ndk/include_cpp/android/persistable_bundle_aidl.h
+++ b/libs/binder/ndk/include_cpp/android/persistable_bundle_aidl.h
@@ -22,14 +22,20 @@
#include <set>
#include <sstream>
-namespace aidl::android::os {
-
+// Include llndk-versioning.h only for vendor build as it is not available for NDK headers.
#if defined(__ANDROID_VENDOR__)
-#define AT_LEAST_V_OR_202404 constexpr(__ANDROID_VENDOR_API__ >= 202404)
-#else
-// TODO(b/322384429) switch this to __ANDROID_API_V__ when V is finalized
-#define AT_LEAST_V_OR_202404 (__builtin_available(android __ANDROID_API_FUTURE__, *))
+#include <android/llndk-versioning.h>
+#else // __ANDROID_VENDOR__
+#if defined(API_LEVEL_AT_LEAST)
+// Redefine API_LEVEL_AT_LEAST here to replace the version to __ANDROID_API_FUTURE__ as a workaround
+#undef API_LEVEL_AT_LEAST
#endif
+// TODO(b/322384429) switch this __ANDROID_API_FUTURE__ to sdk_api_level when V is finalized
+#define API_LEVEL_AT_LEAST(sdk_api_level, vendor_api_level) \
+ (__builtin_available(android __ANDROID_API_FUTURE__, *))
+#endif // __ANDROID_VENDOR__
+
+namespace aidl::android::os {
/**
* Wrapper class that enables interop with AIDL NDK generation
@@ -39,7 +45,7 @@
class PersistableBundle {
public:
PersistableBundle() noexcept {
- if AT_LEAST_V_OR_202404 {
+ if API_LEVEL_AT_LEAST(__ANDROID_API_V__, 202404) {
mPBundle = APersistableBundle_new();
}
}
@@ -49,13 +55,13 @@
PersistableBundle(PersistableBundle&& other) noexcept : mPBundle(other.release()) {}
// duplicates, does not take ownership of the APersistableBundle*
PersistableBundle(const PersistableBundle& other) {
- if AT_LEAST_V_OR_202404 {
+ if API_LEVEL_AT_LEAST(__ANDROID_API_V__, 202404) {
mPBundle = APersistableBundle_dup(other.mPBundle);
}
}
// duplicates, does not take ownership of the APersistableBundle*
PersistableBundle& operator=(const PersistableBundle& other) {
- if AT_LEAST_V_OR_202404 {
+ if API_LEVEL_AT_LEAST(__ANDROID_API_V__, 202404) {
mPBundle = APersistableBundle_dup(other.mPBundle);
}
return *this;
@@ -65,7 +71,7 @@
binder_status_t readFromParcel(const AParcel* _Nonnull parcel) {
reset();
- if AT_LEAST_V_OR_202404 {
+ if API_LEVEL_AT_LEAST(__ANDROID_API_V__, 202404) {
return APersistableBundle_readFromParcel(parcel, &mPBundle);
} else {
return STATUS_INVALID_OPERATION;
@@ -76,7 +82,7 @@
if (!mPBundle) {
return STATUS_BAD_VALUE;
}
- if AT_LEAST_V_OR_202404 {
+ if API_LEVEL_AT_LEAST(__ANDROID_API_V__, 202404) {
return APersistableBundle_writeToParcel(mPBundle, parcel);
} else {
return STATUS_INVALID_OPERATION;
@@ -91,7 +97,7 @@
*/
void reset(APersistableBundle* _Nullable pBundle = nullptr) noexcept {
if (mPBundle) {
- if AT_LEAST_V_OR_202404 {
+ if API_LEVEL_AT_LEAST(__ANDROID_API_V__, 202404) {
APersistableBundle_delete(mPBundle);
}
mPBundle = nullptr;
@@ -104,7 +110,7 @@
* what should be used to check for equality.
*/
bool deepEquals(const PersistableBundle& rhs) const {
- if AT_LEAST_V_OR_202404 {
+ if API_LEVEL_AT_LEAST(__ANDROID_API_V__, 202404) {
return APersistableBundle_isEqual(get(), rhs.get());
} else {
return false;
@@ -143,7 +149,7 @@
inline std::string toString() const {
if (!mPBundle) {
return "<PersistableBundle: null>";
- } else if AT_LEAST_V_OR_202404 {
+ } else if API_LEVEL_AT_LEAST(__ANDROID_API_V__, 202404) {
std::ostringstream os;
os << "<PersistableBundle: ";
os << "size: " << std::to_string(APersistableBundle_size(mPBundle));
@@ -154,7 +160,7 @@
}
int32_t size() const {
- if AT_LEAST_V_OR_202404 {
+ if API_LEVEL_AT_LEAST(__ANDROID_API_V__, 202404) {
return APersistableBundle_size(mPBundle);
} else {
return 0;
@@ -162,7 +168,7 @@
}
int32_t erase(const std::string& key) {
- if AT_LEAST_V_OR_202404 {
+ if API_LEVEL_AT_LEAST(__ANDROID_API_V__, 202404) {
return APersistableBundle_erase(mPBundle, key.c_str());
} else {
return 0;
@@ -170,37 +176,37 @@
}
void putBoolean(const std::string& key, bool val) {
- if AT_LEAST_V_OR_202404 {
+ if API_LEVEL_AT_LEAST(__ANDROID_API_V__, 202404) {
APersistableBundle_putBoolean(mPBundle, key.c_str(), val);
}
}
void putInt(const std::string& key, int32_t val) {
- if AT_LEAST_V_OR_202404 {
+ if API_LEVEL_AT_LEAST(__ANDROID_API_V__, 202404) {
APersistableBundle_putInt(mPBundle, key.c_str(), val);
}
}
void putLong(const std::string& key, int64_t val) {
- if AT_LEAST_V_OR_202404 {
+ if API_LEVEL_AT_LEAST(__ANDROID_API_V__, 202404) {
APersistableBundle_putLong(mPBundle, key.c_str(), val);
}
}
void putDouble(const std::string& key, double val) {
- if AT_LEAST_V_OR_202404 {
+ if API_LEVEL_AT_LEAST(__ANDROID_API_V__, 202404) {
APersistableBundle_putDouble(mPBundle, key.c_str(), val);
}
}
void putString(const std::string& key, const std::string& val) {
- if AT_LEAST_V_OR_202404 {
+ if API_LEVEL_AT_LEAST(__ANDROID_API_V__, 202404) {
APersistableBundle_putString(mPBundle, key.c_str(), val.c_str());
}
}
void putBooleanVector(const std::string& key, const std::vector<bool>& vec) {
- if AT_LEAST_V_OR_202404 {
+ if API_LEVEL_AT_LEAST(__ANDROID_API_V__, 202404) {
// std::vector<bool> has no ::data().
int32_t num = vec.size();
if (num > 0) {
@@ -217,7 +223,7 @@
}
void putIntVector(const std::string& key, const std::vector<int32_t>& vec) {
- if AT_LEAST_V_OR_202404 {
+ if API_LEVEL_AT_LEAST(__ANDROID_API_V__, 202404) {
int32_t num = vec.size();
if (num > 0) {
APersistableBundle_putIntVector(mPBundle, key.c_str(), vec.data(), num);
@@ -225,7 +231,7 @@
}
}
void putLongVector(const std::string& key, const std::vector<int64_t>& vec) {
- if AT_LEAST_V_OR_202404 {
+ if API_LEVEL_AT_LEAST(__ANDROID_API_V__, 202404) {
int32_t num = vec.size();
if (num > 0) {
APersistableBundle_putLongVector(mPBundle, key.c_str(), vec.data(), num);
@@ -233,7 +239,7 @@
}
}
void putDoubleVector(const std::string& key, const std::vector<double>& vec) {
- if AT_LEAST_V_OR_202404 {
+ if API_LEVEL_AT_LEAST(__ANDROID_API_V__, 202404) {
int32_t num = vec.size();
if (num > 0) {
APersistableBundle_putDoubleVector(mPBundle, key.c_str(), vec.data(), num);
@@ -241,7 +247,7 @@
}
}
void putStringVector(const std::string& key, const std::vector<std::string>& vec) {
- if AT_LEAST_V_OR_202404 {
+ if API_LEVEL_AT_LEAST(__ANDROID_API_V__, 202404) {
int32_t num = vec.size();
if (num > 0) {
char** inVec = (char**)malloc(num * sizeof(char*));
@@ -256,13 +262,13 @@
}
}
void putPersistableBundle(const std::string& key, const PersistableBundle& pBundle) {
- if AT_LEAST_V_OR_202404 {
+ if API_LEVEL_AT_LEAST(__ANDROID_API_V__, 202404) {
APersistableBundle_putPersistableBundle(mPBundle, key.c_str(), pBundle.mPBundle);
}
}
bool getBoolean(const std::string& key, bool* _Nonnull val) {
- if AT_LEAST_V_OR_202404 {
+ if API_LEVEL_AT_LEAST(__ANDROID_API_V__, 202404) {
return APersistableBundle_getBoolean(mPBundle, key.c_str(), val);
} else {
return false;
@@ -270,7 +276,7 @@
}
bool getInt(const std::string& key, int32_t* _Nonnull val) {
- if AT_LEAST_V_OR_202404 {
+ if API_LEVEL_AT_LEAST(__ANDROID_API_V__, 202404) {
return APersistableBundle_getInt(mPBundle, key.c_str(), val);
} else {
return false;
@@ -278,7 +284,7 @@
}
bool getLong(const std::string& key, int64_t* _Nonnull val) {
- if AT_LEAST_V_OR_202404 {
+ if API_LEVEL_AT_LEAST(__ANDROID_API_V__, 202404) {
return APersistableBundle_getLong(mPBundle, key.c_str(), val);
} else {
return false;
@@ -286,7 +292,7 @@
}
bool getDouble(const std::string& key, double* _Nonnull val) {
- if AT_LEAST_V_OR_202404 {
+ if API_LEVEL_AT_LEAST(__ANDROID_API_V__, 202404) {
return APersistableBundle_getDouble(mPBundle, key.c_str(), val);
} else {
return false;
@@ -298,7 +304,7 @@
}
bool getString(const std::string& key, std::string* _Nonnull val) {
- if AT_LEAST_V_OR_202404 {
+ if API_LEVEL_AT_LEAST(__ANDROID_API_V__, 202404) {
char* outString = nullptr;
bool ret = APersistableBundle_getString(mPBundle, key.c_str(), &outString,
&stringAllocator, nullptr);
@@ -316,7 +322,7 @@
const char* _Nonnull, T* _Nullable, int32_t),
const APersistableBundle* _Nonnull pBundle, const char* _Nonnull key,
std::vector<T>* _Nonnull vec) {
- if AT_LEAST_V_OR_202404 {
+ if API_LEVEL_AT_LEAST(__ANDROID_API_V__, 202404) {
int32_t bytes = 0;
// call first with nullptr to get required size in bytes
bytes = getVec(pBundle, key, nullptr, 0);
@@ -338,28 +344,28 @@
}
bool getBooleanVector(const std::string& key, std::vector<bool>* _Nonnull vec) {
- if AT_LEAST_V_OR_202404 {
+ if API_LEVEL_AT_LEAST(__ANDROID_API_V__, 202404) {
return getVecInternal<bool>(&APersistableBundle_getBooleanVector, mPBundle, key.c_str(),
vec);
}
return false;
}
bool getIntVector(const std::string& key, std::vector<int32_t>* _Nonnull vec) {
- if AT_LEAST_V_OR_202404 {
+ if API_LEVEL_AT_LEAST(__ANDROID_API_V__, 202404) {
return getVecInternal<int32_t>(&APersistableBundle_getIntVector, mPBundle, key.c_str(),
vec);
}
return false;
}
bool getLongVector(const std::string& key, std::vector<int64_t>* _Nonnull vec) {
- if AT_LEAST_V_OR_202404 {
+ if API_LEVEL_AT_LEAST(__ANDROID_API_V__, 202404) {
return getVecInternal<int64_t>(&APersistableBundle_getLongVector, mPBundle, key.c_str(),
vec);
}
return false;
}
bool getDoubleVector(const std::string& key, std::vector<double>* _Nonnull vec) {
- if AT_LEAST_V_OR_202404 {
+ if API_LEVEL_AT_LEAST(__ANDROID_API_V__, 202404) {
return getVecInternal<double>(&APersistableBundle_getDoubleVector, mPBundle,
key.c_str(), vec);
}
@@ -384,7 +390,7 @@
}
bool getStringVector(const std::string& key, std::vector<std::string>* _Nonnull vec) {
- if AT_LEAST_V_OR_202404 {
+ if API_LEVEL_AT_LEAST(__ANDROID_API_V__, 202404) {
int32_t bytes = APersistableBundle_getStringVector(mPBundle, key.c_str(), nullptr, 0,
&stringAllocator, nullptr);
if (bytes > 0) {
@@ -401,7 +407,7 @@
}
bool getPersistableBundle(const std::string& key, PersistableBundle* _Nonnull val) {
- if AT_LEAST_V_OR_202404 {
+ if API_LEVEL_AT_LEAST(__ANDROID_API_V__, 202404) {
APersistableBundle* bundle = nullptr;
bool ret = APersistableBundle_getPersistableBundle(mPBundle, key.c_str(), &bundle);
if (ret) {
@@ -433,77 +439,77 @@
}
std::set<std::string> getBooleanKeys() {
- if AT_LEAST_V_OR_202404 {
+ if API_LEVEL_AT_LEAST(__ANDROID_API_V__, 202404) {
return getKeys(&APersistableBundle_getBooleanKeys, mPBundle);
} else {
return {};
}
}
std::set<std::string> getIntKeys() {
- if AT_LEAST_V_OR_202404 {
+ if API_LEVEL_AT_LEAST(__ANDROID_API_V__, 202404) {
return getKeys(&APersistableBundle_getIntKeys, mPBundle);
} else {
return {};
}
}
std::set<std::string> getLongKeys() {
- if AT_LEAST_V_OR_202404 {
+ if API_LEVEL_AT_LEAST(__ANDROID_API_V__, 202404) {
return getKeys(&APersistableBundle_getLongKeys, mPBundle);
} else {
return {};
}
}
std::set<std::string> getDoubleKeys() {
- if AT_LEAST_V_OR_202404 {
+ if API_LEVEL_AT_LEAST(__ANDROID_API_V__, 202404) {
return getKeys(&APersistableBundle_getDoubleKeys, mPBundle);
} else {
return {};
}
}
std::set<std::string> getStringKeys() {
- if AT_LEAST_V_OR_202404 {
+ if API_LEVEL_AT_LEAST(__ANDROID_API_V__, 202404) {
return getKeys(&APersistableBundle_getStringKeys, mPBundle);
} else {
return {};
}
}
std::set<std::string> getBooleanVectorKeys() {
- if AT_LEAST_V_OR_202404 {
+ if API_LEVEL_AT_LEAST(__ANDROID_API_V__, 202404) {
return getKeys(&APersistableBundle_getBooleanVectorKeys, mPBundle);
} else {
return {};
}
}
std::set<std::string> getIntVectorKeys() {
- if AT_LEAST_V_OR_202404 {
+ if API_LEVEL_AT_LEAST(__ANDROID_API_V__, 202404) {
return getKeys(&APersistableBundle_getIntVectorKeys, mPBundle);
} else {
return {};
}
}
std::set<std::string> getLongVectorKeys() {
- if AT_LEAST_V_OR_202404 {
+ if API_LEVEL_AT_LEAST(__ANDROID_API_V__, 202404) {
return getKeys(&APersistableBundle_getLongVectorKeys, mPBundle);
} else {
return {};
}
}
std::set<std::string> getDoubleVectorKeys() {
- if AT_LEAST_V_OR_202404 {
+ if API_LEVEL_AT_LEAST(__ANDROID_API_V__, 202404) {
return getKeys(&APersistableBundle_getDoubleVectorKeys, mPBundle);
} else {
return {};
}
}
std::set<std::string> getStringVectorKeys() {
- if AT_LEAST_V_OR_202404 {
+ if API_LEVEL_AT_LEAST(__ANDROID_API_V__, 202404) {
return getKeys(&APersistableBundle_getStringVectorKeys, mPBundle);
} else {
return {};
}
}
std::set<std::string> getPersistableBundleKeys() {
- if AT_LEAST_V_OR_202404 {
+ if API_LEVEL_AT_LEAST(__ANDROID_API_V__, 202404) {
return getKeys(&APersistableBundle_getPersistableBundleKeys, mPBundle);
} else {
return {};
diff --git a/libs/binder/ndk/include_ndk/android/persistable_bundle.h b/libs/binder/ndk/include_ndk/android/persistable_bundle.h
index 1247e8e..42ae15a 100644
--- a/libs/binder/ndk/include_ndk/android/persistable_bundle.h
+++ b/libs/binder/ndk/include_ndk/android/persistable_bundle.h
@@ -20,8 +20,10 @@
#if defined(__ANDROID_VENDOR__)
#include <android/llndk-versioning.h>
#else
-#define __INTRODUCED_IN_LLNDK(x)
+#if !defined(__INTRODUCED_IN_LLNDK)
+#define __INTRODUCED_IN_LLNDK(level) __attribute__((annotate("introduced_in_llndk=" #level)))
#endif
+#endif // __ANDROID_VENDOR__
#include <stdbool.h>
#include <stdint.h>
#include <sys/cdefs.h>
diff --git a/libs/binder/ndk/include_platform/android/binder_manager.h b/libs/binder/ndk/include_platform/android/binder_manager.h
index a905dff..52edae4 100644
--- a/libs/binder/ndk/include_platform/android/binder_manager.h
+++ b/libs/binder/ndk/include_platform/android/binder_manager.h
@@ -18,6 +18,7 @@
#include <android/binder_ibinder.h>
#include <android/binder_status.h>
+#include <android/llndk-versioning.h>
#include <sys/cdefs.h>
__BEGIN_DECLS
@@ -252,9 +253,8 @@
* \return the result of dlopen of the specified HAL
*/
void* AServiceManager_openDeclaredPassthroughHal(const char* interface, const char* instance,
- int flag)
- // TODO(b/302113279) use __INTRODUCED_LLNDK for vendor variants
- __INTRODUCED_IN(__ANDROID_API_V__);
+ int flag) __INTRODUCED_IN(__ANDROID_API_V__)
+ __INTRODUCED_IN_LLNDK(202404);
/**
* Prevent lazy services without client from shutting down their process
diff --git a/libs/binder/ndk/tests/iface.cpp b/libs/binder/ndk/tests/iface.cpp
index 3ee36cd..ca92727 100644
--- a/libs/binder/ndk/tests/iface.cpp
+++ b/libs/binder/ndk/tests/iface.cpp
@@ -25,6 +25,7 @@
const char* IFoo::kSomeInstanceName = "libbinder_ndk-test-IFoo";
const char* IFoo::kInstanceNameToDieFor = "libbinder_ndk-test-IFoo-to-die";
+const char* IFoo::kInstanceNameToDieFor2 = "libbinder_ndk-test-IFoo-to-die2";
const char* IFoo::kIFooDescriptor = "my-special-IFoo-class";
struct IFoo_Class_Data {
diff --git a/libs/binder/ndk/tests/include/iface/iface.h b/libs/binder/ndk/tests/include/iface/iface.h
index 0a562f0..0cdd50b 100644
--- a/libs/binder/ndk/tests/include/iface/iface.h
+++ b/libs/binder/ndk/tests/include/iface/iface.h
@@ -27,6 +27,7 @@
public:
static const char* kSomeInstanceName;
static const char* kInstanceNameToDieFor;
+ static const char* kInstanceNameToDieFor2;
static const char* kIFooDescriptor;
static AIBinder_Class* kClass;
diff --git a/libs/binder/ndk/tests/libbinder_ndk_unit_test.cpp b/libs/binder/ndk/tests/libbinder_ndk_unit_test.cpp
index 966ec95..ce63b82 100644
--- a/libs/binder/ndk/tests/libbinder_ndk_unit_test.cpp
+++ b/libs/binder/ndk/tests/libbinder_ndk_unit_test.cpp
@@ -536,6 +536,7 @@
bool deathReceived = false;
std::function<void(void)> onDeath = [&] {
+ std::unique_lock<std::mutex> lockDeath(deathMutex);
std::cerr << "Binder died (as requested)." << std::endl;
deathReceived = true;
deathCv.notify_one();
@@ -547,6 +548,7 @@
bool wasDeathReceivedFirst = false;
std::function<void(void)> onUnlink = [&] {
+ std::unique_lock<std::mutex> lockUnlink(unlinkMutex);
std::cerr << "Binder unlinked (as requested)." << std::endl;
wasDeathReceivedFirst = deathReceived;
unlinkReceived = true;
@@ -560,7 +562,6 @@
EXPECT_EQ(STATUS_OK, AIBinder_linkToDeath(binder, recipient, static_cast<void*>(cookie)));
- // the binder driver should return this if the service dies during the transaction
EXPECT_EQ(STATUS_DEAD_OBJECT, foo->die());
foo = nullptr;
@@ -579,6 +580,123 @@
binder = nullptr;
}
+TEST(NdkBinder, DeathRecipientDropBinderNoDeath) {
+ using namespace std::chrono_literals;
+
+ std::mutex deathMutex;
+ std::condition_variable deathCv;
+ bool deathReceived = false;
+
+ std::function<void(void)> onDeath = [&] {
+ std::unique_lock<std::mutex> lockDeath(deathMutex);
+ std::cerr << "Binder died (as requested)." << std::endl;
+ deathReceived = true;
+ deathCv.notify_one();
+ };
+
+ std::mutex unlinkMutex;
+ std::condition_variable unlinkCv;
+ bool unlinkReceived = false;
+ bool wasDeathReceivedFirst = false;
+
+ std::function<void(void)> onUnlink = [&] {
+ std::unique_lock<std::mutex> lockUnlink(unlinkMutex);
+ std::cerr << "Binder unlinked (as requested)." << std::endl;
+ wasDeathReceivedFirst = deathReceived;
+ unlinkReceived = true;
+ unlinkCv.notify_one();
+ };
+
+ // keep the death recipient around
+ ndk::ScopedAIBinder_DeathRecipient recipient(AIBinder_DeathRecipient_new(LambdaOnDeath));
+ AIBinder_DeathRecipient_setOnUnlinked(recipient.get(), LambdaOnUnlink);
+
+ {
+ AIBinder* binder;
+ sp<IFoo> foo = IFoo::getService(IFoo::kInstanceNameToDieFor2, &binder);
+ ASSERT_NE(nullptr, foo.get());
+ ASSERT_NE(nullptr, binder);
+
+ DeathRecipientCookie* cookie = new DeathRecipientCookie{&onDeath, &onUnlink};
+
+ EXPECT_EQ(STATUS_OK,
+ AIBinder_linkToDeath(binder, recipient.get(), static_cast<void*>(cookie)));
+ // let the sp<IFoo> and AIBinder fall out of scope
+ AIBinder_decStrong(binder);
+ binder = nullptr;
+ }
+
+ {
+ std::unique_lock<std::mutex> lockDeath(deathMutex);
+ EXPECT_FALSE(deathCv.wait_for(lockDeath, 100ms, [&] { return deathReceived; }));
+ EXPECT_FALSE(deathReceived);
+ }
+
+ {
+ std::unique_lock<std::mutex> lockUnlink(unlinkMutex);
+ EXPECT_TRUE(deathCv.wait_for(lockUnlink, 1s, [&] { return unlinkReceived; }));
+ EXPECT_TRUE(unlinkReceived);
+ EXPECT_FALSE(wasDeathReceivedFirst);
+ }
+}
+
+TEST(NdkBinder, DeathRecipientDropBinderOnDied) {
+ using namespace std::chrono_literals;
+
+ std::mutex deathMutex;
+ std::condition_variable deathCv;
+ bool deathReceived = false;
+
+ sp<IFoo> foo;
+ AIBinder* binder;
+ std::function<void(void)> onDeath = [&] {
+ std::unique_lock<std::mutex> lockDeath(deathMutex);
+ std::cerr << "Binder died (as requested)." << std::endl;
+ deathReceived = true;
+ AIBinder_decStrong(binder);
+ binder = nullptr;
+ deathCv.notify_one();
+ };
+
+ std::mutex unlinkMutex;
+ std::condition_variable unlinkCv;
+ bool unlinkReceived = false;
+ bool wasDeathReceivedFirst = false;
+
+ std::function<void(void)> onUnlink = [&] {
+ std::unique_lock<std::mutex> lockUnlink(unlinkMutex);
+ std::cerr << "Binder unlinked (as requested)." << std::endl;
+ wasDeathReceivedFirst = deathReceived;
+ unlinkReceived = true;
+ unlinkCv.notify_one();
+ };
+
+ ndk::ScopedAIBinder_DeathRecipient recipient(AIBinder_DeathRecipient_new(LambdaOnDeath));
+ AIBinder_DeathRecipient_setOnUnlinked(recipient.get(), LambdaOnUnlink);
+
+ foo = IFoo::getService(IFoo::kInstanceNameToDieFor2, &binder);
+ ASSERT_NE(nullptr, foo.get());
+ ASSERT_NE(nullptr, binder);
+
+ DeathRecipientCookie* cookie = new DeathRecipientCookie{&onDeath, &onUnlink};
+ EXPECT_EQ(STATUS_OK, AIBinder_linkToDeath(binder, recipient.get(), static_cast<void*>(cookie)));
+
+ EXPECT_EQ(STATUS_DEAD_OBJECT, foo->die());
+
+ {
+ std::unique_lock<std::mutex> lockDeath(deathMutex);
+ EXPECT_TRUE(deathCv.wait_for(lockDeath, 1s, [&] { return deathReceived; }));
+ EXPECT_TRUE(deathReceived);
+ }
+
+ {
+ std::unique_lock<std::mutex> lockUnlink(unlinkMutex);
+ EXPECT_TRUE(deathCv.wait_for(lockUnlink, 100ms, [&] { return unlinkReceived; }));
+ EXPECT_TRUE(unlinkReceived);
+ EXPECT_TRUE(wasDeathReceivedFirst);
+ }
+}
+
TEST(NdkBinder, RetrieveNonNdkService) {
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wdeprecated-declarations"
@@ -958,6 +1076,10 @@
}
if (fork() == 0) {
prctl(PR_SET_PDEATHSIG, SIGHUP);
+ return manualThreadPoolService(IFoo::kInstanceNameToDieFor2);
+ }
+ if (fork() == 0) {
+ prctl(PR_SET_PDEATHSIG, SIGHUP);
return manualPollingService(IFoo::kSomeInstanceName);
}
if (fork() == 0) {
diff --git a/libs/gui/WindowInfo.cpp b/libs/gui/WindowInfo.cpp
index 86bf0ee..ad0d99d 100644
--- a/libs/gui/WindowInfo.cpp
+++ b/libs/gui/WindowInfo.cpp
@@ -73,14 +73,6 @@
touchableRegion.orSelf(region);
}
-bool WindowInfo::touchableRegionContainsPoint(int32_t x, int32_t y) const {
- return touchableRegion.contains(x, y);
-}
-
-bool WindowInfo::frameContainsPoint(int32_t x, int32_t y) const {
- return x >= frame.left && x < frame.right && y >= frame.top && y < frame.bottom;
-}
-
bool WindowInfo::supportsSplitTouch() const {
return !inputConfig.test(InputConfig::PREVENT_SPLITTING);
}
diff --git a/libs/gui/include/gui/WindowInfo.h b/libs/gui/include/gui/WindowInfo.h
index 32d60be..2d1b51a 100644
--- a/libs/gui/include/gui/WindowInfo.h
+++ b/libs/gui/include/gui/WindowInfo.h
@@ -254,10 +254,6 @@
void addTouchableRegion(const Rect& region);
- bool touchableRegionContainsPoint(int32_t x, int32_t y) const;
-
- bool frameContainsPoint(int32_t x, int32_t y) const;
-
bool supportsSplitTouch() const;
bool isSpy() const;
diff --git a/libs/input/Android.bp b/libs/input/Android.bp
index 3278c23..5e38bdc 100644
--- a/libs/input/Android.bp
+++ b/libs/input/Android.bp
@@ -248,6 +248,7 @@
"libcutils",
"liblog",
"libPlatformProperties",
+ "libstatslog",
"libtinyxml2",
"libutils",
"libz", // needed by libkernelconfigs
@@ -288,17 +289,6 @@
target: {
android: {
- export_shared_lib_headers: ["libbinder"],
-
- shared_libs: [
- "libutils",
- "libbinder",
- // Stats logging library and its dependencies.
- "libstatslog_libinput",
- "libstatsbootstrap",
- "android.os.statsbootstrap_aidl-cpp",
- ],
-
required: [
"motion_predictor_model_prebuilt",
"motion_predictor_model_config",
@@ -313,43 +303,6 @@
},
}
-// Use bootstrap version of stats logging library.
-// libinput is a bootstrap process (starts early in the boot process), and thus can't use the normal
-// `libstatslog` because that requires `libstatssocket`, which is only available later in the boot.
-cc_library {
- name: "libstatslog_libinput",
- generated_sources: ["statslog_libinput.cpp"],
- generated_headers: ["statslog_libinput.h"],
- export_generated_headers: ["statslog_libinput.h"],
- shared_libs: [
- "libbinder",
- "libstatsbootstrap",
- "libutils",
- "android.os.statsbootstrap_aidl-cpp",
- ],
-}
-
-genrule {
- name: "statslog_libinput.h",
- tools: ["stats-log-api-gen"],
- cmd: "$(location stats-log-api-gen) --header $(genDir)/statslog_libinput.h --module libinput" +
- " --namespace android,stats,libinput --bootstrap",
- out: [
- "statslog_libinput.h",
- ],
-}
-
-genrule {
- name: "statslog_libinput.cpp",
- tools: ["stats-log-api-gen"],
- cmd: "$(location stats-log-api-gen) --cpp $(genDir)/statslog_libinput.cpp --module libinput" +
- " --namespace android,stats,libinput --importHeader statslog_libinput.h" +
- " --bootstrap",
- out: [
- "statslog_libinput.cpp",
- ],
-}
-
cc_defaults {
name: "libinput_fuzz_defaults",
cpp_std: "c++20",
diff --git a/libs/input/MotionPredictorMetricsManager.cpp b/libs/input/MotionPredictorMetricsManager.cpp
index 6872af2..149a36e 100644
--- a/libs/input/MotionPredictorMetricsManager.cpp
+++ b/libs/input/MotionPredictorMetricsManager.cpp
@@ -21,14 +21,11 @@
#include <algorithm>
#include <android-base/logging.h>
+#include <statslog.h>
#include "Eigen/Core"
#include "Eigen/Geometry"
-#ifdef __ANDROID__
-#include <statslog_libinput.h>
-#endif
-
namespace android {
namespace {
@@ -48,22 +45,18 @@
void MotionPredictorMetricsManager::defaultReportAtomFunction(
const MotionPredictorMetricsManager::AtomFields& atomFields) {
- // Call stats_write logging function only on Android targets (not supported on host).
-#ifdef __ANDROID__
- android::stats::libinput::
- stats_write(android::stats::libinput::STYLUS_PREDICTION_METRICS_REPORTED,
- /*stylus_vendor_id=*/0,
- /*stylus_product_id=*/0,
- atomFields.deltaTimeBucketMilliseconds,
- atomFields.alongTrajectoryErrorMeanMillipixels,
- atomFields.alongTrajectoryErrorStdMillipixels,
- atomFields.offTrajectoryRmseMillipixels,
- atomFields.pressureRmseMilliunits,
- atomFields.highVelocityAlongTrajectoryRmse,
- atomFields.highVelocityOffTrajectoryRmse,
- atomFields.scaleInvariantAlongTrajectoryRmse,
- atomFields.scaleInvariantOffTrajectoryRmse);
-#endif
+ android::util::stats_write(android::util::STYLUS_PREDICTION_METRICS_REPORTED,
+ /*stylus_vendor_id=*/0,
+ /*stylus_product_id=*/0,
+ atomFields.deltaTimeBucketMilliseconds,
+ atomFields.alongTrajectoryErrorMeanMillipixels,
+ atomFields.alongTrajectoryErrorStdMillipixels,
+ atomFields.offTrajectoryRmseMillipixels,
+ atomFields.pressureRmseMilliunits,
+ atomFields.highVelocityAlongTrajectoryRmse,
+ atomFields.highVelocityOffTrajectoryRmse,
+ atomFields.scaleInvariantAlongTrajectoryRmse,
+ atomFields.scaleInvariantOffTrajectoryRmse);
}
MotionPredictorMetricsManager::MotionPredictorMetricsManager(
diff --git a/libs/input/tests/Android.bp b/libs/input/tests/Android.bp
index e67a65a..1144f4d 100644
--- a/libs/input/tests/Android.bp
+++ b/libs/input/tests/Android.bp
@@ -64,6 +64,7 @@
"libcutils",
"liblog",
"libPlatformProperties",
+ "libstatslog",
"libtinyxml2",
"libutils",
"server_configurable_flags",
@@ -82,14 +83,6 @@
address: true,
},
},
- android: {
- static_libs: [
- // Stats logging library and its dependencies.
- "libstatslog_libinput",
- "libstatsbootstrap",
- "android.os.statsbootstrap_aidl-cpp",
- ],
- },
},
}
diff --git a/libs/renderengine/Android.bp b/libs/renderengine/Android.bp
index 39902b1..5cd0ce5 100644
--- a/libs/renderengine/Android.bp
+++ b/libs/renderengine/Android.bp
@@ -88,6 +88,7 @@
"skia/SkiaGLRenderEngine.cpp",
"skia/SkiaVkRenderEngine.cpp",
"skia/VulkanInterface.cpp",
+ "skia/compat/GaneshBackendTexture.cpp",
"skia/compat/GaneshGpuContext.cpp",
"skia/debug/CaptureTimer.cpp",
"skia/debug/CommonPool.cpp",
diff --git a/libs/renderengine/skia/AutoBackendTexture.cpp b/libs/renderengine/skia/AutoBackendTexture.cpp
index 02e7337..8aeef9f 100644
--- a/libs/renderengine/skia/AutoBackendTexture.cpp
+++ b/libs/renderengine/skia/AutoBackendTexture.cpp
@@ -20,71 +20,21 @@
#define LOG_TAG "RenderEngine"
#define ATRACE_TAG ATRACE_TAG_GRAPHICS
-#include <SkImage.h>
-#include <android/hardware_buffer.h>
-#include <include/gpu/ganesh/SkImageGanesh.h>
-#include <include/gpu/ganesh/SkSurfaceGanesh.h>
-#include <include/gpu/ganesh/gl/GrGLBackendSurface.h>
-#include <include/gpu/ganesh/vk/GrVkBackendSurface.h>
-#include <include/gpu/vk/GrVkTypes.h>
-#include "ColorSpaces.h"
-#include "log/log_main.h"
-#include "utils/Trace.h"
+#include <include/core/SkImage.h>
+#include <include/core/SkSurface.h>
+
+#include "compat/SkiaBackendTexture.h"
+
+#include <log/log_main.h>
+#include <utils/Trace.h>
namespace android {
namespace renderengine {
namespace skia {
-AutoBackendTexture::AutoBackendTexture(SkiaGpuContext* context, AHardwareBuffer* buffer,
- bool isOutputBuffer, CleanupManager& cleanupMgr)
- : mGrContext(context->grDirectContext()),
- mCleanupMgr(cleanupMgr),
- mIsOutputBuffer(isOutputBuffer) {
- ATRACE_CALL();
-
- AHardwareBuffer_Desc desc;
- AHardwareBuffer_describe(buffer, &desc);
- bool createProtectedImage = 0 != (desc.usage & AHARDWAREBUFFER_USAGE_PROTECTED_CONTENT);
- GrBackendFormat backendFormat;
-
- GrBackendApi backend = mGrContext->backend();
- if (backend == GrBackendApi::kOpenGL) {
- backendFormat =
- GrAHardwareBufferUtils::GetGLBackendFormat(mGrContext.get(), desc.format, false);
- mBackendTexture =
- GrAHardwareBufferUtils::MakeGLBackendTexture(mGrContext.get(), buffer, desc.width,
- desc.height, &mDeleteProc,
- &mUpdateProc, &mImageCtx,
- createProtectedImage, backendFormat,
- isOutputBuffer);
- } else if (backend == GrBackendApi::kVulkan) {
- backendFormat = GrAHardwareBufferUtils::GetVulkanBackendFormat(mGrContext.get(), buffer,
- desc.format, false);
- mBackendTexture =
- GrAHardwareBufferUtils::MakeVulkanBackendTexture(mGrContext.get(), buffer,
- desc.width, desc.height,
- &mDeleteProc, &mUpdateProc,
- &mImageCtx, createProtectedImage,
- backendFormat, isOutputBuffer);
- } else {
- LOG_ALWAYS_FATAL("Unexpected backend %u", static_cast<unsigned>(backend));
- }
-
- mColorType = GrAHardwareBufferUtils::GetSkColorTypeFromBufferFormat(desc.format);
- if (!mBackendTexture.isValid() || !desc.width || !desc.height) {
- LOG_ALWAYS_FATAL("Failed to create a valid texture. [%p]:[%d,%d] isProtected:%d "
- "isWriteable:%d format:%d",
- this, desc.width, desc.height, createProtectedImage, isOutputBuffer,
- desc.format);
- }
-}
-
-AutoBackendTexture::~AutoBackendTexture() {
- if (mBackendTexture.isValid()) {
- mDeleteProc(mImageCtx);
- mBackendTexture = {};
- }
-}
+AutoBackendTexture::AutoBackendTexture(std::unique_ptr<SkiaBackendTexture> backendTexture,
+ CleanupManager& cleanupMgr)
+ : mCleanupMgr(cleanupMgr), mBackendTexture(std::move(backendTexture)) {}
void AutoBackendTexture::unref(bool releaseLocalResources) {
if (releaseLocalResources) {
@@ -112,93 +62,32 @@
textureRelease->unref(false);
}
-void logFatalTexture(const char* msg, const GrBackendTexture& tex, ui::Dataspace dataspace,
- SkColorType colorType) {
- switch (tex.backend()) {
- case GrBackendApi::kOpenGL: {
- GrGLTextureInfo textureInfo;
- bool retrievedTextureInfo = GrBackendTextures::GetGLTextureInfo(tex, &textureInfo);
- LOG_ALWAYS_FATAL("%s isTextureValid:%d dataspace:%d"
- "\n\tGrBackendTexture: (%i x %i) hasMipmaps: %i isProtected: %i "
- "texType: %i\n\t\tGrGLTextureInfo: success: %i fTarget: %u fFormat: %u"
- " colorType %i",
- msg, tex.isValid(), static_cast<int32_t>(dataspace), tex.width(),
- tex.height(), tex.hasMipmaps(), tex.isProtected(),
- static_cast<int>(tex.textureType()), retrievedTextureInfo,
- textureInfo.fTarget, textureInfo.fFormat, colorType);
- break;
- }
- case GrBackendApi::kVulkan: {
- GrVkImageInfo imageInfo;
- bool retrievedImageInfo = GrBackendTextures::GetVkImageInfo(tex, &imageInfo);
- LOG_ALWAYS_FATAL("%s isTextureValid:%d dataspace:%d"
- "\n\tGrBackendTexture: (%i x %i) hasMipmaps: %i isProtected: %i "
- "texType: %i\n\t\tVkImageInfo: success: %i fFormat: %i "
- "fSampleCount: %u fLevelCount: %u colorType %i",
- msg, tex.isValid(), static_cast<int32_t>(dataspace), tex.width(),
- tex.height(), tex.hasMipmaps(), tex.isProtected(),
- static_cast<int>(tex.textureType()), retrievedImageInfo,
- imageInfo.fFormat, imageInfo.fSampleCount, imageInfo.fLevelCount,
- colorType);
- break;
- }
- default:
- LOG_ALWAYS_FATAL("%s Unexpected backend %u", msg, static_cast<unsigned>(tex.backend()));
- break;
- }
-}
-
sk_sp<SkImage> AutoBackendTexture::makeImage(ui::Dataspace dataspace, SkAlphaType alphaType) {
ATRACE_CALL();
- if (mBackendTexture.isValid()) {
- mUpdateProc(mImageCtx, mGrContext.get());
- }
-
- auto colorType = mColorType;
- if (alphaType == kOpaque_SkAlphaType) {
- if (colorType == kRGBA_8888_SkColorType) {
- colorType = kRGB_888x_SkColorType;
- }
- }
-
- sk_sp<SkImage> image =
- SkImages::BorrowTextureFrom(mGrContext.get(), mBackendTexture, kTopLeft_GrSurfaceOrigin,
- colorType, alphaType, toSkColorSpace(dataspace),
- releaseImageProc, this);
- if (image.get()) {
- // The following ref will be counteracted by releaseProc, when SkImage is discarded.
- ref();
- }
+ sk_sp<SkImage> image = mBackendTexture->makeImage(alphaType, dataspace, releaseImageProc, this);
+ // The following ref will be counteracted by releaseProc, when SkImage is discarded.
+ ref();
mImage = image;
mDataspace = dataspace;
- if (!mImage) {
- logFatalTexture("Unable to generate SkImage.", mBackendTexture, dataspace, colorType);
- }
return mImage;
}
sk_sp<SkSurface> AutoBackendTexture::getOrCreateSurface(ui::Dataspace dataspace) {
ATRACE_CALL();
- LOG_ALWAYS_FATAL_IF(!mIsOutputBuffer, "You can't generate a SkSurface for a read-only texture");
+ LOG_ALWAYS_FATAL_IF(!mBackendTexture->isOutputBuffer(),
+ "You can't generate an SkSurface for a read-only texture");
if (!mSurface.get() || mDataspace != dataspace) {
sk_sp<SkSurface> surface =
- SkSurfaces::WrapBackendTexture(mGrContext.get(), mBackendTexture,
- kTopLeft_GrSurfaceOrigin, 0, mColorType,
- toSkColorSpace(dataspace), nullptr,
- releaseSurfaceProc, this);
- if (surface.get()) {
- // The following ref will be counteracted by releaseProc, when SkSurface is discarded.
- ref();
- }
+ mBackendTexture->makeSurface(dataspace, releaseSurfaceProc, this);
+ // The following ref will be counteracted by releaseProc, when SkSurface is discarded.
+ ref();
+
mSurface = surface;
}
mDataspace = dataspace;
- if (!mSurface) {
- logFatalTexture("Unable to generate SkSurface.", mBackendTexture, dataspace, mColorType);
- }
return mSurface;
}
diff --git a/libs/renderengine/skia/AutoBackendTexture.h b/libs/renderengine/skia/AutoBackendTexture.h
index 1d5b565..74daf47 100644
--- a/libs/renderengine/skia/AutoBackendTexture.h
+++ b/libs/renderengine/skia/AutoBackendTexture.h
@@ -16,7 +16,6 @@
#pragma once
-#include <GrAHardwareBufferUtils.h>
#include <GrDirectContext.h>
#include <SkImage.h>
#include <SkSurface.h>
@@ -24,9 +23,9 @@
#include <ui/GraphicTypes.h>
#include "android-base/macros.h"
-#include "compat/SkiaGpuContext.h"
+#include "compat/SkiaBackendTexture.h"
-#include <mutex>
+#include <memory>
#include <vector>
namespace android {
@@ -81,9 +80,8 @@
// of shared ownership with Skia objects, so we wrap it here instead.
class LocalRef {
public:
- LocalRef(SkiaGpuContext* context, AHardwareBuffer* buffer, bool isOutputBuffer,
- CleanupManager& cleanupMgr) {
- mTexture = new AutoBackendTexture(context, buffer, isOutputBuffer, cleanupMgr);
+ LocalRef(std::unique_ptr<SkiaBackendTexture> backendTexture, CleanupManager& cleanupMgr) {
+ mTexture = new AutoBackendTexture(std::move(backendTexture), cleanupMgr);
mTexture->ref();
}
@@ -105,7 +103,7 @@
return mTexture->getOrCreateSurface(dataspace);
}
- SkColorType colorType() const { return mTexture->mColorType; }
+ SkColorType colorType() const { return mTexture->mBackendTexture->internalColorType(); }
DISALLOW_COPY_AND_ASSIGN(LocalRef);
@@ -116,12 +114,13 @@
private:
DISALLOW_COPY_AND_ASSIGN(AutoBackendTexture);
- // Creates a GrBackendTexture whose contents come from the provided buffer.
- AutoBackendTexture(SkiaGpuContext* context, AHardwareBuffer* buffer, bool isOutputBuffer,
+ // Creates an AutoBackendTexture to manage the lifecycle of a given SkiaBackendTexture, which is
+ // in turn backed by an underlying backend-specific texture type.
+ AutoBackendTexture(std::unique_ptr<SkiaBackendTexture> backendTexture,
CleanupManager& cleanupMgr);
// The only way to invoke dtor is with unref, when mUsageCount is 0.
- ~AutoBackendTexture();
+ ~AutoBackendTexture() = default;
void ref() { mUsageCount++; }
@@ -137,24 +136,16 @@
// Makes a new SkSurface from the texture content, if needed.
sk_sp<SkSurface> getOrCreateSurface(ui::Dataspace dataspace);
- GrBackendTexture mBackendTexture;
- GrAHardwareBufferUtils::DeleteImageProc mDeleteProc;
- GrAHardwareBufferUtils::UpdateImageProc mUpdateProc;
- GrAHardwareBufferUtils::TexImageCtx mImageCtx;
-
- // TODO: b/293371537 - Graphite abstractions for ABT.
- const sk_sp<GrDirectContext> mGrContext = nullptr;
CleanupManager& mCleanupMgr;
static void releaseSurfaceProc(SkSurface::ReleaseContext releaseContext);
static void releaseImageProc(SkImages::ReleaseContext releaseContext);
+ std::unique_ptr<SkiaBackendTexture> mBackendTexture;
int mUsageCount = 0;
- const bool mIsOutputBuffer;
sk_sp<SkImage> mImage = nullptr;
sk_sp<SkSurface> mSurface = nullptr;
ui::Dataspace mDataspace = ui::Dataspace::UNKNOWN;
- SkColorType mColorType = kUnknown_SkColorType;
};
} // namespace skia
diff --git a/libs/renderengine/skia/SkiaRenderEngine.cpp b/libs/renderengine/skia/SkiaRenderEngine.cpp
index 27daeba..9e8fe68 100644
--- a/libs/renderengine/skia/SkiaRenderEngine.cpp
+++ b/libs/renderengine/skia/SkiaRenderEngine.cpp
@@ -80,6 +80,7 @@
#include "filters/KawaseBlurFilter.h"
#include "filters/LinearEffect.h"
#include "log/log_main.h"
+#include "skia/compat/SkiaBackendTexture.h"
#include "skia/debug/SkiaCapture.h"
#include "skia/debug/SkiaMemoryReporter.h"
#include "skia/filters/StretchShaderFactory.h"
@@ -417,9 +418,11 @@
if (FlagManager::getInstance().renderable_buffer_usage()) {
isRenderable = buffer->getUsage() & GRALLOC_USAGE_HW_RENDER;
}
- std::shared_ptr<AutoBackendTexture::LocalRef> imageTextureRef =
- std::make_shared<AutoBackendTexture::LocalRef>(context, buffer->toAHardwareBuffer(),
- isRenderable, mTextureCleanupMgr);
+ std::unique_ptr<SkiaBackendTexture> backendTexture =
+ context->makeBackendTexture(buffer->toAHardwareBuffer(), isRenderable);
+ auto imageTextureRef =
+ std::make_shared<AutoBackendTexture::LocalRef>(std::move(backendTexture),
+ mTextureCleanupMgr);
cache.insert({buffer->getId(), imageTextureRef});
}
}
@@ -470,9 +473,10 @@
return it->second;
}
}
- return std::make_shared<AutoBackendTexture::LocalRef>(getActiveContext(),
- buffer->toAHardwareBuffer(),
- isOutputBuffer, mTextureCleanupMgr);
+ std::unique_ptr<SkiaBackendTexture> backendTexture =
+ getActiveContext()->makeBackendTexture(buffer->toAHardwareBuffer(), isOutputBuffer);
+ return std::make_shared<AutoBackendTexture::LocalRef>(std::move(backendTexture),
+ mTextureCleanupMgr);
}
bool SkiaRenderEngine::canSkipPostRenderCleanup() const {
diff --git a/libs/renderengine/skia/compat/GaneshBackendTexture.cpp b/libs/renderengine/skia/compat/GaneshBackendTexture.cpp
new file mode 100644
index 0000000..d246466
--- /dev/null
+++ b/libs/renderengine/skia/compat/GaneshBackendTexture.cpp
@@ -0,0 +1,161 @@
+/*
+ * Copyright 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "GaneshBackendTexture.h"
+
+#undef LOG_TAG
+#define LOG_TAG "RenderEngine"
+#define ATRACE_TAG ATRACE_TAG_GRAPHICS
+
+#include <include/core/SkImage.h>
+#include <include/gpu/GrDirectContext.h>
+#include <include/gpu/ganesh/SkImageGanesh.h>
+#include <include/gpu/ganesh/SkSurfaceGanesh.h>
+#include <include/gpu/ganesh/gl/GrGLBackendSurface.h>
+#include <include/gpu/ganesh/vk/GrVkBackendSurface.h>
+#include <include/gpu/vk/GrVkTypes.h>
+
+#include "skia/ColorSpaces.h"
+#include "skia/compat/SkiaBackendTexture.h"
+
+#include <android/hardware_buffer.h>
+#include <log/log_main.h>
+#include <utils/Trace.h>
+
+namespace android::renderengine::skia {
+
+GaneshBackendTexture::GaneshBackendTexture(sk_sp<GrDirectContext> grContext,
+ AHardwareBuffer* buffer, bool isOutputBuffer)
+ : SkiaBackendTexture(buffer, isOutputBuffer), mGrContext(grContext) {
+ ATRACE_CALL();
+ AHardwareBuffer_Desc desc;
+ AHardwareBuffer_describe(buffer, &desc);
+ const bool createProtectedImage = 0 != (desc.usage & AHARDWAREBUFFER_USAGE_PROTECTED_CONTENT);
+
+ GrBackendFormat backendFormat;
+ const GrBackendApi graphicsApi = grContext->backend();
+ if (graphicsApi == GrBackendApi::kOpenGL) {
+ backendFormat =
+ GrAHardwareBufferUtils::GetGLBackendFormat(grContext.get(), desc.format, false);
+ mBackendTexture =
+ GrAHardwareBufferUtils::MakeGLBackendTexture(grContext.get(), buffer, desc.width,
+ desc.height, &mDeleteProc,
+ &mUpdateProc, &mImageCtx,
+ createProtectedImage, backendFormat,
+ isOutputBuffer);
+ } else if (graphicsApi == GrBackendApi::kVulkan) {
+ backendFormat = GrAHardwareBufferUtils::GetVulkanBackendFormat(grContext.get(), buffer,
+ desc.format, false);
+ mBackendTexture =
+ GrAHardwareBufferUtils::MakeVulkanBackendTexture(grContext.get(), buffer,
+ desc.width, desc.height,
+ &mDeleteProc, &mUpdateProc,
+ &mImageCtx, createProtectedImage,
+ backendFormat, isOutputBuffer);
+ } else {
+ LOG_ALWAYS_FATAL("Unexpected graphics API %u", static_cast<unsigned>(graphicsApi));
+ }
+
+ if (!mBackendTexture.isValid() || !desc.width || !desc.height) {
+ LOG_ALWAYS_FATAL("Failed to create a valid texture. [%p]:[%d,%d] isProtected:%d "
+ "isWriteable:%d format:%d",
+ this, desc.width, desc.height, createProtectedImage, isOutputBuffer,
+ desc.format);
+ }
+}
+
+GaneshBackendTexture::~GaneshBackendTexture() {
+ if (mBackendTexture.isValid()) {
+ mDeleteProc(mImageCtx);
+ mBackendTexture = {};
+ }
+}
+
+sk_sp<SkImage> GaneshBackendTexture::makeImage(SkAlphaType alphaType, ui::Dataspace dataspace,
+ TextureReleaseProc releaseImageProc,
+ ReleaseContext releaseContext) {
+ if (mBackendTexture.isValid()) {
+ mUpdateProc(mImageCtx, mGrContext.get());
+ }
+
+ const SkColorType colorType = colorTypeForImage(alphaType);
+ sk_sp<SkImage> image =
+ SkImages::BorrowTextureFrom(mGrContext.get(), mBackendTexture, kTopLeft_GrSurfaceOrigin,
+ colorType, alphaType, toSkColorSpace(dataspace),
+ releaseImageProc, releaseContext);
+ if (!image) {
+ logFatalTexture("Unable to generate SkImage.", dataspace, colorType);
+ }
+ return image;
+}
+
+sk_sp<SkSurface> GaneshBackendTexture::makeSurface(ui::Dataspace dataspace,
+ TextureReleaseProc releaseSurfaceProc,
+ ReleaseContext releaseContext) {
+ const SkColorType colorType = internalColorType();
+ sk_sp<SkSurface> surface =
+ SkSurfaces::WrapBackendTexture(mGrContext.get(), mBackendTexture,
+ kTopLeft_GrSurfaceOrigin, 0, colorType,
+ toSkColorSpace(dataspace), nullptr, releaseSurfaceProc,
+ releaseContext);
+ if (!surface) {
+ logFatalTexture("Unable to generate SkSurface.", dataspace, colorType);
+ }
+ return surface;
+}
+
+void GaneshBackendTexture::logFatalTexture(const char* msg, ui::Dataspace dataspace,
+ SkColorType colorType) {
+ switch (mBackendTexture.backend()) {
+ case GrBackendApi::kOpenGL: {
+ GrGLTextureInfo textureInfo;
+ bool retrievedTextureInfo =
+ GrBackendTextures::GetGLTextureInfo(mBackendTexture, &textureInfo);
+ LOG_ALWAYS_FATAL("%s isTextureValid:%d dataspace:%d"
+ "\n\tGrBackendTexture: (%i x %i) hasMipmaps: %i isProtected: %i "
+ "texType: %i\n\t\tGrGLTextureInfo: success: %i fTarget: %u fFormat: %u"
+ " colorType %i",
+ msg, mBackendTexture.isValid(), static_cast<int32_t>(dataspace),
+ mBackendTexture.width(), mBackendTexture.height(),
+ mBackendTexture.hasMipmaps(), mBackendTexture.isProtected(),
+ static_cast<int>(mBackendTexture.textureType()), retrievedTextureInfo,
+ textureInfo.fTarget, textureInfo.fFormat, colorType);
+ break;
+ }
+ case GrBackendApi::kVulkan: {
+ GrVkImageInfo imageInfo;
+ bool retrievedImageInfo =
+ GrBackendTextures::GetVkImageInfo(mBackendTexture, &imageInfo);
+ LOG_ALWAYS_FATAL("%s isTextureValid:%d dataspace:%d"
+ "\n\tGrBackendTexture: (%i x %i) hasMipmaps: %i isProtected: %i "
+ "texType: %i\n\t\tVkImageInfo: success: %i fFormat: %i "
+ "fSampleCount: %u fLevelCount: %u colorType %i",
+ msg, mBackendTexture.isValid(), static_cast<int32_t>(dataspace),
+ mBackendTexture.width(), mBackendTexture.height(),
+ mBackendTexture.hasMipmaps(), mBackendTexture.isProtected(),
+ static_cast<int>(mBackendTexture.textureType()), retrievedImageInfo,
+ imageInfo.fFormat, imageInfo.fSampleCount, imageInfo.fLevelCount,
+ colorType);
+ break;
+ }
+ default:
+ LOG_ALWAYS_FATAL("%s Unexpected backend %u", msg,
+ static_cast<unsigned>(mBackendTexture.backend()));
+ break;
+ }
+}
+
+} // namespace android::renderengine::skia
diff --git a/libs/renderengine/skia/compat/GaneshBackendTexture.h b/libs/renderengine/skia/compat/GaneshBackendTexture.h
new file mode 100644
index 0000000..5cf8647
--- /dev/null
+++ b/libs/renderengine/skia/compat/GaneshBackendTexture.h
@@ -0,0 +1,57 @@
+/*
+ * Copyright 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include "SkiaBackendTexture.h"
+#include "ui/GraphicTypes.h"
+
+#include <include/android/GrAHardwareBufferUtils.h>
+#include <include/core/SkColorSpace.h>
+#include <include/gpu/GrDirectContext.h>
+
+#include <android-base/macros.h>
+
+namespace android::renderengine::skia {
+
+class GaneshBackendTexture : public SkiaBackendTexture {
+public:
+ // Creates an internal GrBackendTexture whose contents come from the provided buffer.
+ GaneshBackendTexture(sk_sp<GrDirectContext> grContext, AHardwareBuffer* buffer,
+ bool isOutputBuffer);
+
+ ~GaneshBackendTexture() override;
+
+ sk_sp<SkImage> makeImage(SkAlphaType alphaType, ui::Dataspace dataspace,
+ TextureReleaseProc releaseImageProc,
+ ReleaseContext releaseContext) override;
+
+ sk_sp<SkSurface> makeSurface(ui::Dataspace dataspace, TextureReleaseProc releaseSurfaceProc,
+ ReleaseContext releaseContext) override;
+
+private:
+ DISALLOW_COPY_AND_ASSIGN(GaneshBackendTexture);
+
+ void logFatalTexture(const char* msg, ui::Dataspace dataspace, SkColorType colorType);
+
+ const sk_sp<GrDirectContext> mGrContext;
+ GrBackendTexture mBackendTexture;
+ GrAHardwareBufferUtils::DeleteImageProc mDeleteProc;
+ GrAHardwareBufferUtils::UpdateImageProc mUpdateProc;
+ GrAHardwareBufferUtils::TexImageCtx mImageCtx;
+};
+
+} // namespace android::renderengine::skia
diff --git a/libs/renderengine/skia/compat/GaneshGpuContext.cpp b/libs/renderengine/skia/compat/GaneshGpuContext.cpp
index 51c6a6c..87f00e4 100644
--- a/libs/renderengine/skia/compat/GaneshGpuContext.cpp
+++ b/libs/renderengine/skia/compat/GaneshGpuContext.cpp
@@ -27,8 +27,13 @@
#include <include/gpu/gl/GrGLInterface.h>
#include <include/gpu/vk/GrVkBackendContext.h>
+#include "../AutoBackendTexture.h"
+#include "GaneshBackendTexture.h"
+#include "skia/compat/SkiaBackendTexture.h"
+
#include <android-base/macros.h>
#include <log/log_main.h>
+#include <memory>
namespace android::renderengine::skia {
@@ -66,6 +71,11 @@
return mGrContext;
}
+std::unique_ptr<SkiaBackendTexture> GaneshGpuContext::makeBackendTexture(AHardwareBuffer* buffer,
+ bool isOutputBuffer) {
+ return std::make_unique<GaneshBackendTexture>(mGrContext, buffer, isOutputBuffer);
+}
+
sk_sp<SkSurface> GaneshGpuContext::createRenderTarget(SkImageInfo imageInfo) {
constexpr int kSampleCount = 1; // enable AA
constexpr SkSurfaceProps* kProps = nullptr;
diff --git a/libs/renderengine/skia/compat/GaneshGpuContext.h b/libs/renderengine/skia/compat/GaneshGpuContext.h
index 59001ec..ec0162d 100644
--- a/libs/renderengine/skia/compat/GaneshGpuContext.h
+++ b/libs/renderengine/skia/compat/GaneshGpuContext.h
@@ -29,6 +29,9 @@
sk_sp<GrDirectContext> grDirectContext() override;
+ std::unique_ptr<SkiaBackendTexture> makeBackendTexture(AHardwareBuffer* buffer,
+ bool isOutputBuffer) override;
+
sk_sp<SkSurface> createRenderTarget(SkImageInfo imageInfo) override;
size_t getMaxRenderTargetSize() const override;
diff --git a/libs/renderengine/skia/compat/SkiaBackendTexture.h b/libs/renderengine/skia/compat/SkiaBackendTexture.h
new file mode 100644
index 0000000..09877a5
--- /dev/null
+++ b/libs/renderengine/skia/compat/SkiaBackendTexture.h
@@ -0,0 +1,85 @@
+/*
+ * Copyright 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include <include/android/GrAHardwareBufferUtils.h>
+#include <include/core/SkColorSpace.h>
+#include <include/gpu/GrDirectContext.h>
+
+#include <android/hardware_buffer.h>
+#include <ui/GraphicTypes.h>
+
+namespace android::renderengine::skia {
+
+/**
+ * Abstraction over a Skia backend-specific texture type.
+ *
+ * This class does not do any lifecycle management, and should typically be wrapped in an
+ * AutoBackendTexture::LocalRef. Typically created via SkiaGpuContext::makeBackendTexture(...).
+ */
+class SkiaBackendTexture {
+public:
+ SkiaBackendTexture(AHardwareBuffer* buffer, bool isOutputBuffer)
+ : mIsOutputBuffer(isOutputBuffer) {
+ AHardwareBuffer_Desc desc;
+ AHardwareBuffer_describe(buffer, &desc);
+
+ mColorType = GrAHardwareBufferUtils::GetSkColorTypeFromBufferFormat(desc.format);
+ }
+ virtual ~SkiaBackendTexture() = default;
+
+ // These two definitions mirror Skia's own types used for texture release callbacks, which are
+ // re-declared multiple times between context-specific implementation headers for Ganesh vs.
+ // Graphite, and within the context of SkImages vs. SkSurfaces. Our own re-declaration allows us
+ // to not pull in any implementation-specific headers here.
+ using ReleaseContext = void*;
+ using TextureReleaseProc = void (*)(ReleaseContext);
+
+ // Guaranteed to be non-null (crashes otherwise). An opaque alphaType may coerce the internal
+ // color type to RBGX.
+ virtual sk_sp<SkImage> makeImage(SkAlphaType alphaType, ui::Dataspace dataspace,
+ TextureReleaseProc releaseImageProc,
+ ReleaseContext releaseContext) = 0;
+
+ // Guaranteed to be non-null (crashes otherwise).
+ virtual sk_sp<SkSurface> makeSurface(ui::Dataspace dataspace,
+ TextureReleaseProc releaseSurfaceProc,
+ ReleaseContext releaseContext) = 0;
+
+ bool isOutputBuffer() const { return mIsOutputBuffer; }
+
+ SkColorType internalColorType() const { return mColorType; }
+
+protected:
+ // Strip alpha channel from rawColorType if alphaType is opaque (note: only works for RGBA_8888)
+ SkColorType colorTypeForImage(SkAlphaType alphaType) const {
+ if (alphaType == kOpaque_SkAlphaType) {
+ // TODO: b/40043126 - Support RGBX SkColorType for F16 and support it and 101010x as a
+ // source
+ if (internalColorType() == kRGBA_8888_SkColorType) {
+ return kRGB_888x_SkColorType;
+ }
+ }
+ return internalColorType();
+ }
+
+private:
+ const bool mIsOutputBuffer;
+ SkColorType mColorType = kUnknown_SkColorType;
+};
+
+} // namespace android::renderengine::skia
diff --git a/libs/renderengine/skia/compat/SkiaGpuContext.h b/libs/renderengine/skia/compat/SkiaGpuContext.h
index ba167f3..ddf1642 100644
--- a/libs/renderengine/skia/compat/SkiaGpuContext.h
+++ b/libs/renderengine/skia/compat/SkiaGpuContext.h
@@ -24,8 +24,12 @@
#include <include/gpu/gl/GrGLInterface.h>
#include <include/gpu/vk/GrVkBackendContext.h>
+#include "SkiaBackendTexture.h"
+
#include <log/log.h>
+#include <memory>
+
namespace android::renderengine::skia {
/**
@@ -52,6 +56,9 @@
LOG_ALWAYS_FATAL("grDirectContext() called on a non-Ganesh instance of SkiaGpuContext!");
}
+ virtual std::unique_ptr<SkiaBackendTexture> makeBackendTexture(AHardwareBuffer* buffer,
+ bool isOutputBuffer) = 0;
+
/**
* Notes:
* - The surface doesn't count against Skia's caching budgets.
diff --git a/libs/sensor/OWNERS b/libs/sensor/OWNERS
index 90c2330..7347ac7 100644
--- a/libs/sensor/OWNERS
+++ b/libs/sensor/OWNERS
@@ -1,3 +1 @@
-arthuri@google.com
bduddie@google.com
-stange@google.com
diff --git a/libs/tracing_perfetto/include/tracing_perfetto.h b/libs/tracing_perfetto/include/tracing_perfetto.h
index 4e3c83f..2c1c2a4 100644
--- a/libs/tracing_perfetto/include/tracing_perfetto.h
+++ b/libs/tracing_perfetto/include/tracing_perfetto.h
@@ -46,7 +46,7 @@
Result traceCounter(uint64_t category, const char* name, int64_t value);
-uint64_t getEnabledCategories();
+bool isTagEnabled(uint64_t category);
} // namespace tracing_perfetto
diff --git a/libs/tracing_perfetto/tracing_perfetto.cpp b/libs/tracing_perfetto/tracing_perfetto.cpp
index 19d1eb6..6f716ee 100644
--- a/libs/tracing_perfetto/tracing_perfetto.cpp
+++ b/libs/tracing_perfetto/tracing_perfetto.cpp
@@ -130,12 +130,13 @@
}
}
-uint64_t getEnabledCategories() {
- if (internal::isPerfettoRegistered()) {
- // TODO(b/303199244): Return only enabled categories and not all registered ones
- return internal::getDefaultCategories();
+bool isTagEnabled(uint64_t category) {
+ struct PerfettoTeCategory* perfettoTeCategory =
+ internal::toPerfettoCategory(category);
+ if (perfettoTeCategory != nullptr) {
+ return true;
} else {
- return atrace_get_enabled_tags();
+ return (atrace_get_enabled_tags() & category) != 0;
}
}
diff --git a/libs/ui/Gralloc5.cpp b/libs/ui/Gralloc5.cpp
index 11064ae..f14a5cf 100644
--- a/libs/ui/Gralloc5.cpp
+++ b/libs/ui/Gralloc5.cpp
@@ -23,6 +23,7 @@
#include <aidlcommonsupport/NativeHandle.h>
#include <android/binder_manager.h>
#include <android/hardware/graphics/mapper/utils/IMapperMetadataTypes.h>
+#include <android/llndk-versioning.h>
#include <binder/IPCThreadState.h>
#include <dlfcn.h>
#include <ui/FatVector.h>
@@ -91,8 +92,7 @@
void* so = nullptr;
// TODO(b/322384429) switch this to __ANDROID_API_V__ when V is finalized
- // TODO(b/302113279) use __ANDROID_VENDOR_API__ for vendor variant
- if (__builtin_available(android __ANDROID_API_FUTURE__, *)) {
+ if API_LEVEL_AT_LEAST(__ANDROID_API_FUTURE__, 202404) {
so = AServiceManager_openDeclaredPassthroughHal("mapper", mapperSuffix.c_str(),
RTLD_LOCAL | RTLD_NOW);
} else {
diff --git a/services/inputflinger/PointerChoreographer.cpp b/services/inputflinger/PointerChoreographer.cpp
index c333814..1aa1077 100644
--- a/services/inputflinger/PointerChoreographer.cpp
+++ b/services/inputflinger/PointerChoreographer.cpp
@@ -37,6 +37,11 @@
args.pointerProperties[0].toolType == ToolType::FINGER;
}
+bool isFromDrawingTablet(const NotifyMotionArgs& args) {
+ return isFromSource(args.source, AINPUT_SOURCE_MOUSE | AINPUT_SOURCE_STYLUS) &&
+ isStylusToolType(args.pointerProperties[0].toolType);
+}
+
bool isHoverAction(int32_t action) {
return action == AMOTION_EVENT_ACTION_HOVER_ENTER ||
action == AMOTION_EVENT_ACTION_HOVER_MOVE || action == AMOTION_EVENT_ACTION_HOVER_EXIT;
@@ -46,6 +51,13 @@
return isStylusEvent(args.source, args.pointerProperties) && isHoverAction(args.action);
}
+bool isMouseOrTouchpad(uint32_t sources) {
+ // Check if this is a mouse or touchpad, but not a drawing tablet.
+ return isFromSource(sources, AINPUT_SOURCE_MOUSE_RELATIVE) ||
+ (isFromSource(sources, AINPUT_SOURCE_MOUSE) &&
+ !isFromSource(sources, AINPUT_SOURCE_STYLUS));
+}
+
inline void notifyPointerDisplayChange(std::optional<std::tuple<int32_t, FloatPoint>> change,
PointerChoreographerPolicyInterface& policy) {
if (!change) {
@@ -55,6 +67,18 @@
policy.notifyPointerDisplayIdChanged(displayId, cursorPosition);
}
+void setIconForController(const std::variant<std::unique_ptr<SpriteIcon>, PointerIconStyle>& icon,
+ PointerControllerInterface& controller) {
+ if (std::holds_alternative<std::unique_ptr<SpriteIcon>>(icon)) {
+ if (std::get<std::unique_ptr<SpriteIcon>>(icon) == nullptr) {
+ LOG(FATAL) << "SpriteIcon should not be null";
+ }
+ controller.setCustomPointerIcon(*std::get<std::unique_ptr<SpriteIcon>>(icon));
+ } else {
+ controller.updatePointerIcon(std::get<PointerIconStyle>(icon));
+ }
+}
+
} // namespace
// --- PointerChoreographer ---
@@ -107,6 +131,8 @@
return processMouseEventLocked(args);
} else if (isFromTouchpad(args)) {
return processTouchpadEventLocked(args);
+ } else if (isFromDrawingTablet(args)) {
+ processDrawingTabletEventLocked(args);
} else if (mStylusPointerIconEnabled && isStylusHoverEvent(args)) {
processStylusHoverEventLocked(args);
} else if (isFromSource(args.source, AINPUT_SOURCE_TOUCHSCREEN)) {
@@ -189,6 +215,36 @@
return newArgs;
}
+void PointerChoreographer::processDrawingTabletEventLocked(const android::NotifyMotionArgs& args) {
+ if (args.displayId == ADISPLAY_ID_NONE) {
+ return;
+ }
+
+ if (args.getPointerCount() != 1) {
+ LOG(WARNING) << "Only drawing tablet events with a single pointer are currently supported: "
+ << args.dump();
+ }
+
+ // Use a mouse pointer controller for drawing tablets, or create one if it doesn't exist.
+ auto [it, _] = mDrawingTabletPointersByDevice.try_emplace(args.deviceId,
+ getMouseControllerConstructor(
+ args.displayId));
+
+ PointerControllerInterface& pc = *it->second;
+
+ const float x = args.pointerCoords[0].getAxisValue(AMOTION_EVENT_AXIS_X);
+ const float y = args.pointerCoords[0].getAxisValue(AMOTION_EVENT_AXIS_Y);
+ pc.setPosition(x, y);
+ if (args.action == AMOTION_EVENT_ACTION_HOVER_EXIT) {
+ // TODO(b/315815559): Do not fade and reset the icon if the hover exit will be followed
+ // immediately by a DOWN event.
+ pc.fade(PointerControllerInterface::Transition::IMMEDIATE);
+ pc.updatePointerIcon(PointerIconStyle::TYPE_NOT_SPECIFIED);
+ } else if (canUnfadeOnDisplay(args.displayId)) {
+ pc.unfade(PointerControllerInterface::Transition::IMMEDIATE);
+ }
+}
+
/**
* When screen is touched, fade the mouse pointer on that display. We only call fade for
* ACTION_DOWN events.This would allow both mouse and touch to be used at the same time if the
@@ -255,6 +311,8 @@
const float y = args.pointerCoords[0].getAxisValue(AMOTION_EVENT_AXIS_Y);
pc.setPosition(x, y);
if (args.action == AMOTION_EVENT_ACTION_HOVER_EXIT) {
+ // TODO(b/315815559): Do not fade and reset the icon if the hover exit will be followed
+ // immediately by a DOWN event.
pc.fade(PointerControllerInterface::Transition::IMMEDIATE);
pc.updatePointerIcon(PointerIconStyle::TYPE_NOT_SPECIFIED);
} else if (canUnfadeOnDisplay(args.displayId)) {
@@ -284,6 +342,7 @@
std::scoped_lock _l(mLock);
mTouchPointersByDevice.erase(args.deviceId);
mStylusPointersByDevice.erase(args.deviceId);
+ mDrawingTabletPointersByDevice.erase(args.deviceId);
}
void PointerChoreographer::notifyPointerCaptureChanged(
@@ -320,6 +379,11 @@
std::string pointerControllerDump = addLinePrefix(stylusPointerController->dump(), INDENT);
dump += INDENT + std::to_string(deviceId) + " : " + pointerControllerDump;
}
+ dump += INDENT "DrawingTabletControllers:\n";
+ for (const auto& [deviceId, drawingTabletController] : mDrawingTabletPointersByDevice) {
+ std::string pointerControllerDump = addLinePrefix(drawingTabletController->dump(), INDENT);
+ dump += INDENT + std::to_string(deviceId) + " : " + pointerControllerDump;
+ }
dump += "\n";
}
@@ -361,13 +425,13 @@
std::set<int32_t /*displayId*/> mouseDisplaysToKeep;
std::set<DeviceId> touchDevicesToKeep;
std::set<DeviceId> stylusDevicesToKeep;
+ std::set<DeviceId> drawingTabletDevicesToKeep;
// Mark the displayIds or deviceIds of PointerControllers currently needed, and create
// new PointerControllers if necessary.
for (const auto& info : mInputDeviceInfos) {
const uint32_t sources = info.getSources();
- if (isFromSource(sources, AINPUT_SOURCE_MOUSE) ||
- isFromSource(sources, AINPUT_SOURCE_MOUSE_RELATIVE)) {
+ if (isMouseOrTouchpad(sources)) {
const int32_t displayId = getTargetMouseDisplayLocked(info.getAssociatedDisplayId());
mouseDisplaysToKeep.insert(displayId);
// For mice, show the cursor immediately when the device is first connected or
@@ -388,6 +452,10 @@
info.getAssociatedDisplayId() != ADISPLAY_ID_NONE) {
stylusDevicesToKeep.insert(info.getId());
}
+ if (isFromSource(sources, AINPUT_SOURCE_STYLUS | AINPUT_SOURCE_MOUSE) &&
+ info.getAssociatedDisplayId() != ADISPLAY_ID_NONE) {
+ drawingTabletDevicesToKeep.insert(info.getId());
+ }
}
// Remove PointerControllers no longer needed.
@@ -400,6 +468,9 @@
std::erase_if(mStylusPointersByDevice, [&stylusDevicesToKeep](const auto& pair) {
return stylusDevicesToKeep.find(pair.first) == stylusDevicesToKeep.end();
});
+ std::erase_if(mDrawingTabletPointersByDevice, [&drawingTabletDevicesToKeep](const auto& pair) {
+ return drawingTabletDevicesToKeep.find(pair.first) == drawingTabletDevicesToKeep.end();
+ });
std::erase_if(mMouseDevices, [&](DeviceId id) REQUIRES(mLock) {
return std::find_if(mInputDeviceInfos.begin(), mInputDeviceInfos.end(),
[id](const auto& info) { return info.getId() == id; }) ==
@@ -460,6 +531,12 @@
stylusPointerController->setDisplayViewport(viewport);
}
}
+ for (const auto& [deviceId, drawingTabletController] : mDrawingTabletPointersByDevice) {
+ const InputDeviceInfo* info = findInputDeviceLocked(deviceId);
+ if (info && info->getAssociatedDisplayId() == displayId) {
+ drawingTabletController->setDisplayViewport(viewport);
+ }
+ }
}
mViewports = viewports;
pointerDisplayChange = calculatePointerDisplayChangeToNotify();
@@ -533,42 +610,35 @@
return false;
}
const uint32_t sources = info->getSources();
- const auto stylusPointerIt = mStylusPointersByDevice.find(deviceId);
- if (isFromSource(sources, AINPUT_SOURCE_STYLUS) &&
- stylusPointerIt != mStylusPointersByDevice.end()) {
- if (std::holds_alternative<std::unique_ptr<SpriteIcon>>(icon)) {
- if (std::get<std::unique_ptr<SpriteIcon>>(icon) == nullptr) {
- LOG(FATAL) << "SpriteIcon should not be null";
- }
- stylusPointerIt->second->setCustomPointerIcon(
- *std::get<std::unique_ptr<SpriteIcon>>(icon));
- } else {
- stylusPointerIt->second->updatePointerIcon(std::get<PointerIconStyle>(icon));
+ if (isFromSource(sources, AINPUT_SOURCE_STYLUS | AINPUT_SOURCE_MOUSE)) {
+ auto it = mDrawingTabletPointersByDevice.find(deviceId);
+ if (it != mDrawingTabletPointersByDevice.end()) {
+ setIconForController(icon, *it->second);
+ return true;
}
- } else if (isFromSource(sources, AINPUT_SOURCE_MOUSE)) {
- if (const auto mousePointerIt = mMousePointersByDisplay.find(displayId);
- mousePointerIt != mMousePointersByDisplay.end()) {
- if (std::holds_alternative<std::unique_ptr<SpriteIcon>>(icon)) {
- if (std::get<std::unique_ptr<SpriteIcon>>(icon) == nullptr) {
- LOG(FATAL) << "SpriteIcon should not be null";
- }
- mousePointerIt->second->setCustomPointerIcon(
- *std::get<std::unique_ptr<SpriteIcon>>(icon));
- } else {
- mousePointerIt->second->updatePointerIcon(std::get<PointerIconStyle>(icon));
- }
+ }
+ if (isFromSource(sources, AINPUT_SOURCE_STYLUS)) {
+ auto it = mStylusPointersByDevice.find(deviceId);
+ if (it != mStylusPointersByDevice.end()) {
+ setIconForController(icon, *it->second);
+ return true;
+ }
+ }
+ if (isFromSource(sources, AINPUT_SOURCE_MOUSE)) {
+ auto it = mMousePointersByDisplay.find(displayId);
+ if (it != mMousePointersByDisplay.end()) {
+ setIconForController(icon, *it->second);
+ return true;
} else {
LOG(WARNING) << "No mouse pointer controller found for display " << displayId
<< ", device " << deviceId << ".";
return false;
}
- } else {
- LOG(WARNING) << "Cannot set pointer icon for display " << displayId << ", device "
- << deviceId << ".";
- return false;
}
- return true;
+ LOG(WARNING) << "Cannot set pointer icon for display " << displayId << ", device " << deviceId
+ << ".";
+ return false;
}
void PointerChoreographer::setPointerIconVisibility(int32_t displayId, bool visible) {
diff --git a/services/inputflinger/PointerChoreographer.h b/services/inputflinger/PointerChoreographer.h
index db1488b..a3c210e 100644
--- a/services/inputflinger/PointerChoreographer.h
+++ b/services/inputflinger/PointerChoreographer.h
@@ -123,6 +123,7 @@
NotifyMotionArgs processMotion(const NotifyMotionArgs& args);
NotifyMotionArgs processMouseEventLocked(const NotifyMotionArgs& args) REQUIRES(mLock);
NotifyMotionArgs processTouchpadEventLocked(const NotifyMotionArgs& args) REQUIRES(mLock);
+ void processDrawingTabletEventLocked(const NotifyMotionArgs& args) REQUIRES(mLock);
void processTouchscreenAndStylusEventLocked(const NotifyMotionArgs& args) REQUIRES(mLock);
void processStylusHoverEventLocked(const NotifyMotionArgs& args) REQUIRES(mLock);
void processDeviceReset(const NotifyDeviceResetArgs& args);
@@ -144,6 +145,8 @@
GUARDED_BY(mLock);
std::map<DeviceId, std::shared_ptr<PointerControllerInterface>> mStylusPointersByDevice
GUARDED_BY(mLock);
+ std::map<DeviceId, std::shared_ptr<PointerControllerInterface>> mDrawingTabletPointersByDevice
+ GUARDED_BY(mLock);
int32_t mDefaultMouseDisplayId GUARDED_BY(mLock);
int32_t mNotifiedPointerDisplayId GUARDED_BY(mLock);
diff --git a/services/inputflinger/dispatcher/InputDispatcher.cpp b/services/inputflinger/dispatcher/InputDispatcher.cpp
index dc220fe..2df86a0 100644
--- a/services/inputflinger/dispatcher/InputDispatcher.cpp
+++ b/services/inputflinger/dispatcher/InputDispatcher.cpp
@@ -88,12 +88,13 @@
}
// Create the input tracing backend that writes to perfetto from a single thread.
-std::unique_ptr<trace::InputTracingBackendInterface> createInputTracingBackendIfEnabled() {
+std::unique_ptr<trace::InputTracingBackendInterface> createInputTracingBackendIfEnabled(
+ trace::impl::PerfettoBackend::GetPackageUid getPackageUid) {
if (!isInputTracingEnabled()) {
return nullptr;
}
return std::make_unique<trace::impl::ThreadedBackend<trace::impl::PerfettoBackend>>(
- trace::impl::PerfettoBackend());
+ trace::impl::PerfettoBackend(getPackageUid));
}
template <class Entry>
@@ -598,6 +599,18 @@
return true;
}
+// Returns true if the given window's frame can occlude pointer events at the given display
+// location.
+bool windowOccludesTouchAt(const WindowInfo& windowInfo, int displayId, float x, float y,
+ const ui::Transform& displayTransform) {
+ if (windowInfo.displayId != displayId) {
+ return false;
+ }
+ const auto frame = displayTransform.transform(windowInfo.frame);
+ const auto p = floor(displayTransform.transform(x, y));
+ return p.x >= frame.left && p.x < frame.right && p.y >= frame.top && p.y < frame.bottom;
+}
+
bool isPointerFromStylus(const MotionEntry& entry, int32_t pointerIndex) {
return isFromSource(entry.source, AINPUT_SOURCE_STYLUS) &&
isStylusToolType(entry.pointerProperties[pointerIndex].toolType);
@@ -891,7 +904,9 @@
// --- InputDispatcher ---
InputDispatcher::InputDispatcher(InputDispatcherPolicyInterface& policy)
- : InputDispatcher(policy, createInputTracingBackendIfEnabled()) {}
+ : InputDispatcher(policy, createInputTracingBackendIfEnabled([&policy](std::string pkg) {
+ return policy.getPackageUid(pkg);
+ })) {}
InputDispatcher::InputDispatcher(InputDispatcherPolicyInterface& policy,
std::unique_ptr<trace::InputTracingBackendInterface> traceBackend)
@@ -3105,7 +3120,7 @@
* If neither of those is true, then it means the touch can be allowed.
*/
InputDispatcher::TouchOcclusionInfo InputDispatcher::computeTouchOcclusionInfoLocked(
- const sp<WindowInfoHandle>& windowHandle, int32_t x, int32_t y) const {
+ const sp<WindowInfoHandle>& windowHandle, float x, float y) const {
const WindowInfo* windowInfo = windowHandle->getInfo();
int32_t displayId = windowInfo->displayId;
const std::vector<sp<WindowInfoHandle>>& windowHandles = getWindowHandlesLocked(displayId);
@@ -3119,7 +3134,8 @@
break; // All future windows are below us. Exit early.
}
const WindowInfo* otherInfo = otherHandle->getInfo();
- if (canBeObscuredBy(windowHandle, otherHandle) && otherInfo->frameContainsPoint(x, y) &&
+ if (canBeObscuredBy(windowHandle, otherHandle) &&
+ windowOccludesTouchAt(*otherInfo, displayId, x, y, getTransformLocked(displayId)) &&
!haveSameApplicationToken(windowInfo, otherInfo)) {
if (DEBUG_TOUCH_OCCLUSION) {
info.debugInfo.push_back(
@@ -3189,7 +3205,7 @@
}
bool InputDispatcher::isWindowObscuredAtPointLocked(const sp<WindowInfoHandle>& windowHandle,
- int32_t x, int32_t y) const {
+ float x, float y) const {
int32_t displayId = windowHandle->getInfo()->displayId;
const std::vector<sp<WindowInfoHandle>>& windowHandles = getWindowHandlesLocked(displayId);
for (const sp<WindowInfoHandle>& otherHandle : windowHandles) {
@@ -3198,7 +3214,7 @@
}
const WindowInfo* otherInfo = otherHandle->getInfo();
if (canBeObscuredBy(windowHandle, otherHandle) &&
- otherInfo->frameContainsPoint(x, y)) {
+ windowOccludesTouchAt(*otherInfo, displayId, x, y, getTransformLocked(displayId))) {
return true;
}
}
diff --git a/services/inputflinger/dispatcher/InputDispatcher.h b/services/inputflinger/dispatcher/InputDispatcher.h
index 13571b3..edca63e 100644
--- a/services/inputflinger/dispatcher/InputDispatcher.h
+++ b/services/inputflinger/dispatcher/InputDispatcher.h
@@ -566,11 +566,11 @@
};
TouchOcclusionInfo computeTouchOcclusionInfoLocked(
- const sp<android::gui::WindowInfoHandle>& windowHandle, int32_t x, int32_t y) const
+ const sp<android::gui::WindowInfoHandle>& windowHandle, float x, float y) const
REQUIRES(mLock);
bool isTouchTrustedLocked(const TouchOcclusionInfo& occlusionInfo) const REQUIRES(mLock);
bool isWindowObscuredAtPointLocked(const sp<android::gui::WindowInfoHandle>& windowHandle,
- int32_t x, int32_t y) const REQUIRES(mLock);
+ float x, float y) const REQUIRES(mLock);
bool isWindowObscuredLocked(const sp<android::gui::WindowInfoHandle>& windowHandle) const
REQUIRES(mLock);
std::string dumpWindowForTouchOcclusion(const android::gui::WindowInfo* info,
diff --git a/services/inputflinger/dispatcher/include/InputDispatcherPolicyInterface.h b/services/inputflinger/dispatcher/include/InputDispatcherPolicyInterface.h
index 62c2b02..91a3e3f 100644
--- a/services/inputflinger/dispatcher/include/InputDispatcherPolicyInterface.h
+++ b/services/inputflinger/dispatcher/include/InputDispatcherPolicyInterface.h
@@ -163,6 +163,9 @@
virtual void notifyDeviceInteraction(DeviceId deviceId, nsecs_t timestamp,
const std::set<gui::Uid>& uids) = 0;
+ /* Get the UID associated with the given package. */
+ virtual gui::Uid getPackageUid(std::string package) = 0;
+
private:
// Additional key latency in case a connection is still processing some motion events.
// This will help with the case when a user touched a button that opens a new window,
diff --git a/services/inputflinger/dispatcher/trace/AndroidInputEventProtoConverter.cpp b/services/inputflinger/dispatcher/trace/AndroidInputEventProtoConverter.cpp
index cee741c..c431fb7 100644
--- a/services/inputflinger/dispatcher/trace/AndroidInputEventProtoConverter.cpp
+++ b/services/inputflinger/dispatcher/trace/AndroidInputEventProtoConverter.cpp
@@ -21,8 +21,26 @@
namespace android::inputdispatcher::trace {
+namespace {
+
+using namespace ftl::flag_operators;
+
+// The trace config to use for maximal tracing.
+const impl::TraceConfig CONFIG_TRACE_ALL{
+ .flags = impl::TraceFlag::TRACE_DISPATCHER_INPUT_EVENTS |
+ impl::TraceFlag::TRACE_DISPATCHER_WINDOW_DISPATCH,
+ .rules = {impl::TraceRule{.level = impl::TraceLevel::TRACE_LEVEL_COMPLETE,
+ .matchAllPackages = {},
+ .matchAnyPackages = {},
+ .matchSecure{},
+ .matchImeConnectionActive = {}}},
+};
+
+} // namespace
+
void AndroidInputEventProtoConverter::toProtoMotionEvent(const TracedMotionEvent& event,
- proto::AndroidMotionEvent& outProto) {
+ proto::AndroidMotionEvent& outProto,
+ bool isRedacted) {
outProto.set_event_id(event.id);
outProto.set_event_time_nanos(event.eventTime);
outProto.set_down_time_nanos(event.downTime);
@@ -31,11 +49,15 @@
outProto.set_device_id(event.deviceId);
outProto.set_display_id(event.displayId);
outProto.set_classification(static_cast<int32_t>(event.classification));
- outProto.set_cursor_position_x(event.xCursorPosition);
- outProto.set_cursor_position_y(event.yCursorPosition);
outProto.set_flags(event.flags);
outProto.set_policy_flags(event.policyFlags);
+ if (!isRedacted) {
+ outProto.set_cursor_position_x(event.xCursorPosition);
+ outProto.set_cursor_position_y(event.yCursorPosition);
+ outProto.set_meta_state(event.metaState);
+ }
+
for (uint32_t i = 0; i < event.pointerProperties.size(); i++) {
auto* pointer = outProto.add_pointer();
@@ -49,13 +71,17 @@
const auto axis = bits.clearFirstMarkedBit();
auto axisEntry = pointer->add_axis_value();
axisEntry->set_axis(axis);
- axisEntry->set_value(coords.values[axisIndex]);
+
+ if (!isRedacted) {
+ axisEntry->set_value(coords.values[axisIndex]);
+ }
}
}
}
void AndroidInputEventProtoConverter::toProtoKeyEvent(const TracedKeyEvent& event,
- proto::AndroidKeyEvent& outProto) {
+ proto::AndroidKeyEvent& outProto,
+ bool isRedacted) {
outProto.set_event_id(event.id);
outProto.set_event_time_nanos(event.eventTime);
outProto.set_down_time_nanos(event.downTime);
@@ -63,21 +89,28 @@
outProto.set_action(event.action);
outProto.set_device_id(event.deviceId);
outProto.set_display_id(event.displayId);
- outProto.set_key_code(event.keyCode);
- outProto.set_scan_code(event.scanCode);
- outProto.set_meta_state(event.metaState);
outProto.set_repeat_count(event.repeatCount);
outProto.set_flags(event.flags);
outProto.set_policy_flags(event.policyFlags);
+
+ if (!isRedacted) {
+ outProto.set_key_code(event.keyCode);
+ outProto.set_scan_code(event.scanCode);
+ outProto.set_meta_state(event.metaState);
+ }
}
void AndroidInputEventProtoConverter::toProtoWindowDispatchEvent(
- const WindowDispatchArgs& args, proto::AndroidWindowInputDispatchEvent& outProto) {
+ const WindowDispatchArgs& args, proto::AndroidWindowInputDispatchEvent& outProto,
+ bool isRedacted) {
std::visit([&](auto entry) { outProto.set_event_id(entry.id); }, args.eventEntry);
outProto.set_vsync_id(args.vsyncId);
outProto.set_window_id(args.windowId);
outProto.set_resolved_flags(args.resolvedFlags);
+ if (isRedacted) {
+ return;
+ }
if (auto* motion = std::get_if<TracedMotionEvent>(&args.eventEntry); motion != nullptr) {
for (size_t i = 0; i < motion->pointerProperties.size(); i++) {
auto* pointerProto = outProto.add_dispatched_pointer();
@@ -105,4 +138,65 @@
}
}
+impl::TraceConfig AndroidInputEventProtoConverter::parseConfig(
+ proto::AndroidInputEventConfig::Decoder& protoConfig) {
+ if (protoConfig.has_mode() &&
+ protoConfig.mode() == proto::AndroidInputEventConfig::TRACE_MODE_TRACE_ALL) {
+ // User has requested the preset for maximal tracing
+ return CONFIG_TRACE_ALL;
+ }
+
+ impl::TraceConfig config;
+
+ // Parse trace flags
+ if (protoConfig.has_trace_dispatcher_input_events() &&
+ protoConfig.trace_dispatcher_input_events()) {
+ config.flags |= impl::TraceFlag::TRACE_DISPATCHER_INPUT_EVENTS;
+ }
+ if (protoConfig.has_trace_dispatcher_window_dispatch() &&
+ protoConfig.trace_dispatcher_window_dispatch()) {
+ config.flags |= impl::TraceFlag::TRACE_DISPATCHER_WINDOW_DISPATCH;
+ }
+
+ // Parse trace rules
+ auto rulesIt = protoConfig.rules();
+ while (rulesIt) {
+ proto::AndroidInputEventConfig::TraceRule::Decoder protoRule{rulesIt->as_bytes()};
+ config.rules.emplace_back();
+ auto& rule = config.rules.back();
+
+ rule.level = protoRule.has_trace_level()
+ ? static_cast<impl::TraceLevel>(protoRule.trace_level())
+ : impl::TraceLevel::TRACE_LEVEL_NONE;
+
+ if (protoRule.has_match_all_packages()) {
+ auto pkgIt = protoRule.match_all_packages();
+ while (pkgIt) {
+ rule.matchAllPackages.emplace_back(pkgIt->as_std_string());
+ pkgIt++;
+ }
+ }
+
+ if (protoRule.has_match_any_packages()) {
+ auto pkgIt = protoRule.match_any_packages();
+ while (pkgIt) {
+ rule.matchAnyPackages.emplace_back(pkgIt->as_std_string());
+ pkgIt++;
+ }
+ }
+
+ if (protoRule.has_match_secure()) {
+ rule.matchSecure = protoRule.match_secure();
+ }
+
+ if (protoRule.has_match_ime_connection_active()) {
+ rule.matchImeConnectionActive = protoRule.match_ime_connection_active();
+ }
+
+ rulesIt++;
+ }
+
+ return config;
+}
+
} // namespace android::inputdispatcher::trace
diff --git a/services/inputflinger/dispatcher/trace/AndroidInputEventProtoConverter.h b/services/inputflinger/dispatcher/trace/AndroidInputEventProtoConverter.h
index ab5f9ca..887913f 100644
--- a/services/inputflinger/dispatcher/trace/AndroidInputEventProtoConverter.h
+++ b/services/inputflinger/dispatcher/trace/AndroidInputEventProtoConverter.h
@@ -16,9 +16,11 @@
#pragma once
+#include <perfetto/config/android/android_input_event_config.pbzero.h>
#include <perfetto/trace/android/android_input_event.pbzero.h>
#include "InputTracingBackendInterface.h"
+#include "InputTracingPerfettoBackendConfig.h"
namespace proto = perfetto::protos::pbzero;
@@ -30,10 +32,14 @@
class AndroidInputEventProtoConverter {
public:
static void toProtoMotionEvent(const TracedMotionEvent& event,
- proto::AndroidMotionEvent& outProto);
- static void toProtoKeyEvent(const TracedKeyEvent& event, proto::AndroidKeyEvent& outProto);
+ proto::AndroidMotionEvent& outProto, bool isRedacted);
+ static void toProtoKeyEvent(const TracedKeyEvent& event, proto::AndroidKeyEvent& outProto,
+ bool isRedacted);
static void toProtoWindowDispatchEvent(const WindowDispatchArgs&,
- proto::AndroidWindowInputDispatchEvent& outProto);
+ proto::AndroidWindowInputDispatchEvent& outProto,
+ bool isRedacted);
+
+ static impl::TraceConfig parseConfig(proto::AndroidInputEventConfig::Decoder& protoConfig);
};
} // namespace android::inputdispatcher::trace
diff --git a/services/inputflinger/dispatcher/trace/InputTracer.cpp b/services/inputflinger/dispatcher/trace/InputTracer.cpp
index 47e27be..55ed5c6 100644
--- a/services/inputflinger/dispatcher/trace/InputTracer.cpp
+++ b/services/inputflinger/dispatcher/trace/InputTracer.cpp
@@ -19,6 +19,7 @@
#include "InputTracer.h"
#include <android-base/logging.h>
+#include <private/android_filesystem_config.h>
namespace android::inputdispatcher::trace::impl {
@@ -30,7 +31,7 @@
using V::operator()...;
};
-TracedEvent createTracedEvent(const MotionEntry& e) {
+TracedEvent createTracedEvent(const MotionEntry& e, EventType type) {
return TracedMotionEvent{e.id,
e.eventTime,
e.policyFlags,
@@ -50,13 +51,14 @@
e.yCursorPosition,
e.downTime,
e.pointerProperties,
- e.pointerCoords};
+ e.pointerCoords,
+ type};
}
-TracedEvent createTracedEvent(const KeyEntry& e) {
+TracedEvent createTracedEvent(const KeyEntry& e, EventType type) {
return TracedKeyEvent{e.id, e.eventTime, e.policyFlags, e.deviceId, e.source,
e.displayId, e.action, e.keyCode, e.scanCode, e.metaState,
- e.downTime, e.flags, e.repeatCount};
+ e.downTime, e.flags, e.repeatCount, type};
}
void writeEventToBackend(const TracedEvent& event, const TracedEventArgs args,
@@ -70,6 +72,24 @@
return std::visit([](const auto& event) { return event.id; }, v);
}
+// Helper class to extract relevant information from InputTarget.
+struct InputTargetInfo {
+ gui::Uid uid;
+ bool isSecureWindow;
+};
+
+InputTargetInfo getTargetInfo(const InputTarget& target) {
+ if (target.windowHandle == nullptr) {
+ if (!target.connection->monitor) {
+ LOG(FATAL) << __func__ << ": Window is not set for non-monitor target";
+ }
+ // This is a global monitor, assume its target is the system.
+ return {.uid = gui::Uid{AID_SYSTEM}, .isSecureWindow = false};
+ }
+ return {target.windowHandle->getInfo()->ownerUid,
+ target.windowHandle->getInfo()->layoutParamsFlags.test(gui::WindowInfo::Flag::SECURE)};
+}
+
} // namespace
// --- InputTracer ---
@@ -83,10 +103,10 @@
if (entry.type == EventEntry::Type::MOTION) {
const auto& motion = static_cast<const MotionEntry&>(entry);
- eventState->events.emplace_back(createTracedEvent(motion));
+ eventState->events.emplace_back(createTracedEvent(motion, EventType::INBOUND));
} else if (entry.type == EventEntry::Type::KEY) {
const auto& key = static_cast<const KeyEntry&>(entry);
- eventState->events.emplace_back(createTracedEvent(key));
+ eventState->events.emplace_back(createTracedEvent(key, EventType::INBOUND));
} else {
LOG(FATAL) << "Cannot trace EventEntry of type: " << ftl::enum_string(entry.type);
}
@@ -103,19 +123,25 @@
void InputTracer::dispatchToTargetHint(const EventTrackerInterface& cookie,
const InputTarget& target) {
auto& eventState = getState(cookie);
+ const InputTargetInfo& targetInfo = getTargetInfo(target);
if (eventState->isEventProcessingComplete) {
- // TODO(b/210460522): Disallow adding new targets after eventProcessingComplete() is called.
+ // Disallow adding new targets after eventProcessingComplete() is called.
+ if (eventState->targets.find(targetInfo.uid) == eventState->targets.end()) {
+ LOG(FATAL) << __func__ << ": Cannot add new target after eventProcessingComplete";
+ }
return;
}
if (isDerivedCookie(cookie)) {
- // TODO(b/210460522): Disallow adding new targets from a derived cookie.
+ // Disallow adding new targets from a derived cookie.
+ if (eventState->targets.find(targetInfo.uid) == eventState->targets.end()) {
+ LOG(FATAL) << __func__ << ": Cannot add new target from a derived cookie";
+ }
return;
}
- if (target.windowHandle != nullptr) {
- eventState->isSecure |= target.windowHandle->getInfo()->layoutParamsFlags.test(
- gui::WindowInfo::Flag::SECURE);
- // TODO(b/210460522): Set events as sensitive when the IME connection is active.
- }
+
+ eventState->targets.emplace(targetInfo.uid);
+ eventState->isSecure |= targetInfo.isSecureWindow;
+ // TODO(b/210460522): Set events as sensitive when the IME connection is active.
}
void InputTracer::eventProcessingComplete(const EventTrackerInterface& cookie) {
@@ -138,10 +164,10 @@
if (entry.type == EventEntry::Type::MOTION) {
const auto& motion = static_cast<const MotionEntry&>(entry);
- eventState->events.emplace_back(createTracedEvent(motion));
+ eventState->events.emplace_back(createTracedEvent(motion, EventType::SYNTHESIZED));
} else if (entry.type == EventEntry::Type::KEY) {
const auto& key = static_cast<const KeyEntry&>(entry);
- eventState->events.emplace_back(createTracedEvent(key));
+ eventState->events.emplace_back(createTracedEvent(key, EventType::SYNTHESIZED));
} else {
LOG(FATAL) << "Cannot trace EventEntry of type: " << ftl::enum_string(entry.type);
}
@@ -150,8 +176,10 @@
// It is possible for a derived event to be dispatched some time after the original event
// is dispatched, such as in the case of key fallback events. To account for these cases,
// derived events can be traced after the processing is complete for the original event.
- const TracedEventArgs traceArgs{.isSecure = eventState->isSecure};
- writeEventToBackend(eventState->events.back(), traceArgs, *mBackend);
+ const auto& event = eventState->events.back();
+ const TracedEventArgs traceArgs{.isSecure = eventState->isSecure,
+ .targets = eventState->targets};
+ writeEventToBackend(event, std::move(traceArgs), *mBackend);
}
return std::make_unique<EventTrackerImpl>(std::move(eventState), /*isDerived=*/true);
}
@@ -160,38 +188,34 @@
const EventTrackerInterface& cookie) {
auto& eventState = getState(cookie);
const EventEntry& entry = *dispatchEntry.eventEntry;
+ const int32_t eventId = entry.id;
// TODO(b/328618922): Remove resolved key repeats after making repeatCount non-mutable.
// The KeyEntry's repeatCount is mutable and can be modified after an event is initially traced,
// so we need to find the repeatCount at the time of dispatching to trace it accurately.
int32_t resolvedKeyRepeatCount = 0;
-
- TracedEvent traced;
- if (entry.type == EventEntry::Type::MOTION) {
- const auto& motion = static_cast<const MotionEntry&>(entry);
- traced = createTracedEvent(motion);
- } else if (entry.type == EventEntry::Type::KEY) {
- const auto& key = static_cast<const KeyEntry&>(entry);
- resolvedKeyRepeatCount = key.repeatCount;
- traced = createTracedEvent(key);
- } else {
- LOG(FATAL) << "Cannot trace EventEntry of type: " << ftl::enum_string(entry.type);
+ if (entry.type == EventEntry::Type::KEY) {
+ resolvedKeyRepeatCount = static_cast<const KeyEntry&>(entry).repeatCount;
}
auto tracedEventIt =
std::find_if(eventState->events.begin(), eventState->events.end(),
- [&traced](const auto& event) { return getId(traced) == getId(event); });
+ [eventId](const auto& event) { return eventId == getId(event); });
if (tracedEventIt == eventState->events.end()) {
LOG(FATAL)
<< __func__
<< ": Failed to find a previously traced event that matches the dispatched event";
}
+ if (eventState->targets.count(dispatchEntry.targetUid) == 0) {
+ LOG(FATAL) << __func__ << ": Event is being dispatched to UID that it is not targeting";
+ }
+
// The vsyncId only has meaning if the event is targeting a window.
const int32_t windowId = dispatchEntry.windowId.value_or(0);
const int32_t vsyncId = dispatchEntry.windowId.has_value() ? dispatchEntry.vsyncId : 0;
// TODO(b/210460522): Pass HMAC into traceEventDispatch.
- const WindowDispatchArgs windowDispatchArgs{std::move(traced),
+ const WindowDispatchArgs windowDispatchArgs{*tracedEventIt,
dispatchEntry.deliveryTime,
dispatchEntry.resolvedFlags,
dispatchEntry.targetUid,
@@ -202,8 +226,9 @@
/*hmac=*/{},
resolvedKeyRepeatCount};
if (eventState->isEventProcessingComplete) {
- mBackend->traceWindowDispatch(std::move(windowDispatchArgs),
- TracedEventArgs{.isSecure = eventState->isSecure});
+ const TracedEventArgs traceArgs{.isSecure = eventState->isSecure,
+ .targets = eventState->targets};
+ mBackend->traceWindowDispatch(std::move(windowDispatchArgs), std::move(traceArgs));
} else {
eventState->pendingDispatchArgs.emplace_back(std::move(windowDispatchArgs));
}
@@ -222,13 +247,24 @@
void InputTracer::EventState::onEventProcessingComplete() {
// Write all of the events known so far to the trace.
- const TracedEventArgs traceArgs{.isSecure = isSecure};
for (const auto& event : events) {
+ const TracedEventArgs traceArgs{.isSecure = isSecure, .targets = targets};
writeEventToBackend(event, traceArgs, *tracer.mBackend);
}
// Write all pending dispatch args to the trace.
for (const auto& windowDispatchArgs : pendingDispatchArgs) {
- tracer.mBackend->traceWindowDispatch(windowDispatchArgs, traceArgs);
+ auto tracedEventIt =
+ std::find_if(events.begin(), events.end(),
+ [id = getId(windowDispatchArgs.eventEntry)](const auto& event) {
+ return id == getId(event);
+ });
+ if (tracedEventIt == events.end()) {
+ LOG(FATAL) << __func__
+ << ": Failed to find a previously traced event that matches the dispatched "
+ "event";
+ }
+ const TracedEventArgs traceArgs{.isSecure = isSecure, .targets = targets};
+ tracer.mBackend->traceWindowDispatch(windowDispatchArgs, std::move(traceArgs));
}
pendingDispatchArgs.clear();
diff --git a/services/inputflinger/dispatcher/trace/InputTracer.h b/services/inputflinger/dispatcher/trace/InputTracer.h
index 4ef6ca6..717bc1f 100644
--- a/services/inputflinger/dispatcher/trace/InputTracer.h
+++ b/services/inputflinger/dispatcher/trace/InputTracer.h
@@ -66,6 +66,8 @@
std::vector<const WindowDispatchArgs> pendingDispatchArgs;
// True if the event is targeting at least one secure window;
bool isSecure{false};
+ // The list of all possible UIDs that this event could be targeting.
+ std::set<gui::Uid> targets;
};
// Get the event state associated with a tracking cookie.
diff --git a/services/inputflinger/dispatcher/trace/InputTracingBackendInterface.h b/services/inputflinger/dispatcher/trace/InputTracingBackendInterface.h
index 6eef12e..3ff7fab 100644
--- a/services/inputflinger/dispatcher/trace/InputTracingBackendInterface.h
+++ b/services/inputflinger/dispatcher/trace/InputTracingBackendInterface.h
@@ -21,12 +21,27 @@
#include <ui/Transform.h>
#include <array>
+#include <set>
#include <variant>
#include <vector>
namespace android::inputdispatcher::trace {
/**
+ * Describes the type of this event being traced, with respect to InputDispatcher.
+ */
+enum class EventType {
+ // This is an event that was reported through the InputListener interface or was injected.
+ INBOUND,
+ // This is an event that was synthesized within InputDispatcher; either being derived
+ // from an inbound event (e.g. a split motion event), or synthesized completely
+ // (e.g. a CANCEL event generated when the inbound stream is not canceled).
+ SYNTHESIZED,
+
+ ftl_last = SYNTHESIZED,
+};
+
+/**
* A representation of an Android KeyEvent used by the tracing backend.
*/
struct TracedKeyEvent {
@@ -43,6 +58,7 @@
nsecs_t downTime;
int32_t flags;
int32_t repeatCount;
+ EventType eventType;
};
/**
@@ -69,6 +85,7 @@
nsecs_t downTime;
std::vector<PointerProperties> pointerProperties;
std::vector<PointerCoords> pointerCoords;
+ EventType eventType;
};
/** A representation of a traced input event. */
@@ -78,6 +95,8 @@
struct TracedEventArgs {
// True if the event is targeting at least one secure window.
bool isSecure;
+ // The list of possible UIDs that this event could be targeting.
+ std::set<gui::Uid> targets;
};
/** Additional information about an input event being dispatched to a window. */
diff --git a/services/inputflinger/dispatcher/trace/InputTracingPerfettoBackend.cpp b/services/inputflinger/dispatcher/trace/InputTracingPerfettoBackend.cpp
index 8ef9ca5..b76bec3 100644
--- a/services/inputflinger/dispatcher/trace/InputTracingPerfettoBackend.cpp
+++ b/services/inputflinger/dispatcher/trace/InputTracingPerfettoBackend.cpp
@@ -22,6 +22,7 @@
#include <android-base/logging.h>
#include <perfetto/trace/android/android_input_event.pbzero.h>
+#include <private/android_filesystem_config.h>
namespace android::inputdispatcher::trace::impl {
@@ -29,24 +30,125 @@
constexpr auto INPUT_EVENT_TRACE_DATA_SOURCE_NAME = "android.input.inputevent";
+bool isPermanentlyAllowed(gui::Uid uid) {
+ switch (uid.val()) {
+ case AID_SYSTEM:
+ case AID_SHELL:
+ case AID_ROOT:
+ return true;
+ default:
+ return false;
+ }
+}
+
} // namespace
// --- PerfettoBackend::InputEventDataSource ---
-void PerfettoBackend::InputEventDataSource::OnStart(const perfetto::DataSourceBase::StartArgs&) {
- LOG(INFO) << "Starting perfetto trace for: " << INPUT_EVENT_TRACE_DATA_SOURCE_NAME;
+PerfettoBackend::InputEventDataSource::InputEventDataSource() : mInstanceId(sNextInstanceId++) {}
+
+void PerfettoBackend::InputEventDataSource::OnSetup(const InputEventDataSource::SetupArgs& args) {
+ LOG(INFO) << "Setting up perfetto trace for: " << INPUT_EVENT_TRACE_DATA_SOURCE_NAME
+ << ", instanceId: " << mInstanceId;
+ const auto rawConfig = args.config->android_input_event_config_raw();
+ auto protoConfig = perfetto::protos::pbzero::AndroidInputEventConfig::Decoder{rawConfig};
+
+ mConfig = AndroidInputEventProtoConverter::parseConfig(protoConfig);
}
-void PerfettoBackend::InputEventDataSource::OnStop(const perfetto::DataSourceBase::StopArgs&) {
- LOG(INFO) << "Stopping perfetto trace for: " << INPUT_EVENT_TRACE_DATA_SOURCE_NAME;
+void PerfettoBackend::InputEventDataSource::OnStart(const InputEventDataSource::StartArgs&) {
+ LOG(INFO) << "Starting perfetto trace for: " << INPUT_EVENT_TRACE_DATA_SOURCE_NAME
+ << ", instanceId: " << mInstanceId;
+}
+
+void PerfettoBackend::InputEventDataSource::OnStop(const InputEventDataSource::StopArgs&) {
+ LOG(INFO) << "Stopping perfetto trace for: " << INPUT_EVENT_TRACE_DATA_SOURCE_NAME
+ << ", instanceId: " << mInstanceId;
InputEventDataSource::Trace([&](InputEventDataSource::TraceContext ctx) { ctx.Flush(); });
}
+void PerfettoBackend::InputEventDataSource::initializeUidMap(GetPackageUid getPackageUid) {
+ if (mUidMap.has_value()) {
+ return;
+ }
+
+ mUidMap = {{}};
+ for (const auto& rule : mConfig.rules) {
+ for (const auto& package : rule.matchAllPackages) {
+ mUidMap->emplace(package, getPackageUid(package));
+ }
+ for (const auto& package : rule.matchAnyPackages) {
+ mUidMap->emplace(package, getPackageUid(package));
+ }
+ }
+}
+
+bool PerfettoBackend::InputEventDataSource::shouldIgnoreTracedInputEvent(
+ const EventType& type) const {
+ if (!getFlags().test(TraceFlag::TRACE_DISPATCHER_INPUT_EVENTS)) {
+ // Ignore all input events.
+ return true;
+ }
+ if (!getFlags().test(TraceFlag::TRACE_DISPATCHER_WINDOW_DISPATCH) &&
+ type != EventType::INBOUND) {
+ // When window dispatch tracing is disabled, ignore any events that are not inbound events.
+ return true;
+ }
+ return false;
+}
+
+TraceLevel PerfettoBackend::InputEventDataSource::resolveTraceLevel(
+ const TracedEventArgs& args) const {
+ // Check for matches with the rules in the order that they are defined.
+ for (const auto& rule : mConfig.rules) {
+ if (ruleMatches(rule, args)) {
+ return rule.level;
+ }
+ }
+ // The event is not traced if it matched zero rules.
+ return TraceLevel::TRACE_LEVEL_NONE;
+}
+
+bool PerfettoBackend::InputEventDataSource::ruleMatches(const TraceRule& rule,
+ const TracedEventArgs& args) const {
+ // By default, a rule will match all events. Return early if the rule does not match.
+
+ // Match the event if it is directed to a secure window.
+ if (rule.matchSecure.has_value() && *rule.matchSecure != args.isSecure) {
+ return false;
+ }
+
+ // Match the event if all of its target packages are explicitly allowed in the "match all" list.
+ if (!rule.matchAllPackages.empty() &&
+ !std::all_of(args.targets.begin(), args.targets.end(), [&](const auto& uid) {
+ return isPermanentlyAllowed(uid) ||
+ std::any_of(rule.matchAllPackages.begin(), rule.matchAllPackages.end(),
+ [&](const auto& pkg) { return uid == mUidMap->at(pkg); });
+ })) {
+ return false;
+ }
+
+ // Match the event if any of its target packages are allowed in the "match any" list.
+ if (!rule.matchAnyPackages.empty() &&
+ !std::any_of(args.targets.begin(), args.targets.end(), [&](const auto& uid) {
+ return std::any_of(rule.matchAnyPackages.begin(), rule.matchAnyPackages.end(),
+ [&](const auto& pkg) { return uid == mUidMap->at(pkg); });
+ })) {
+ return false;
+ }
+
+ // The event matches all matchers specified in the rule.
+ return true;
+}
+
// --- PerfettoBackend ---
std::once_flag PerfettoBackend::sDataSourceRegistrationFlag{};
-PerfettoBackend::PerfettoBackend() {
+std::atomic<int32_t> PerfettoBackend::sNextInstanceId{1};
+
+PerfettoBackend::PerfettoBackend(GetPackageUid getPackagesForUid)
+ : mGetPackageUid(getPackagesForUid) {
// Use a once-flag to ensure that the data source is only registered once per boot, since
// we never unregister the InputEventDataSource.
std::call_once(sDataSourceRegistrationFlag, []() {
@@ -65,42 +167,65 @@
void PerfettoBackend::traceMotionEvent(const TracedMotionEvent& event,
const TracedEventArgs& args) {
- if (args.isSecure) {
- // For now, avoid tracing secure event entirely.
- return;
- }
InputEventDataSource::Trace([&](InputEventDataSource::TraceContext ctx) {
+ auto dataSource = ctx.GetDataSourceLocked();
+ dataSource->initializeUidMap(mGetPackageUid);
+ if (dataSource->shouldIgnoreTracedInputEvent(event.eventType)) {
+ return;
+ }
+ const TraceLevel traceLevel = dataSource->resolveTraceLevel(args);
+ if (traceLevel == TraceLevel::TRACE_LEVEL_NONE) {
+ return;
+ }
+ const bool isRedacted = traceLevel == TraceLevel::TRACE_LEVEL_REDACTED;
auto tracePacket = ctx.NewTracePacket();
auto* inputEvent = tracePacket->set_android_input_event();
- auto* dispatchMotion = inputEvent->set_dispatcher_motion_event();
- AndroidInputEventProtoConverter::toProtoMotionEvent(event, *dispatchMotion);
+ auto* dispatchMotion = isRedacted ? inputEvent->set_dispatcher_motion_event_redacted()
+ : inputEvent->set_dispatcher_motion_event();
+ AndroidInputEventProtoConverter::toProtoMotionEvent(event, *dispatchMotion, isRedacted);
});
}
void PerfettoBackend::traceKeyEvent(const TracedKeyEvent& event, const TracedEventArgs& args) {
- if (args.isSecure) {
- // For now, avoid tracing secure event entirely.
- return;
- }
InputEventDataSource::Trace([&](InputEventDataSource::TraceContext ctx) {
+ auto dataSource = ctx.GetDataSourceLocked();
+ dataSource->initializeUidMap(mGetPackageUid);
+ if (dataSource->shouldIgnoreTracedInputEvent(event.eventType)) {
+ return;
+ }
+ const TraceLevel traceLevel = dataSource->resolveTraceLevel(args);
+ if (traceLevel == TraceLevel::TRACE_LEVEL_NONE) {
+ return;
+ }
+ const bool isRedacted = traceLevel == TraceLevel::TRACE_LEVEL_REDACTED;
auto tracePacket = ctx.NewTracePacket();
auto* inputEvent = tracePacket->set_android_input_event();
- auto* dispatchKey = inputEvent->set_dispatcher_key_event();
- AndroidInputEventProtoConverter::toProtoKeyEvent(event, *dispatchKey);
+ auto* dispatchKey = isRedacted ? inputEvent->set_dispatcher_key_event_redacted()
+ : inputEvent->set_dispatcher_key_event();
+ AndroidInputEventProtoConverter::toProtoKeyEvent(event, *dispatchKey, isRedacted);
});
}
void PerfettoBackend::traceWindowDispatch(const WindowDispatchArgs& dispatchArgs,
const TracedEventArgs& args) {
- if (args.isSecure) {
- // For now, avoid tracing secure event entirely.
- return;
- }
InputEventDataSource::Trace([&](InputEventDataSource::TraceContext ctx) {
+ auto dataSource = ctx.GetDataSourceLocked();
+ dataSource->initializeUidMap(mGetPackageUid);
+ if (!dataSource->getFlags().test(TraceFlag::TRACE_DISPATCHER_WINDOW_DISPATCH)) {
+ return;
+ }
+ const TraceLevel traceLevel = dataSource->resolveTraceLevel(args);
+ if (traceLevel == TraceLevel::TRACE_LEVEL_NONE) {
+ return;
+ }
+ const bool isRedacted = traceLevel == TraceLevel::TRACE_LEVEL_REDACTED;
auto tracePacket = ctx.NewTracePacket();
auto* inputEvent = tracePacket->set_android_input_event();
- auto* dispatchEvent = inputEvent->set_dispatcher_window_dispatch_event();
- AndroidInputEventProtoConverter::toProtoWindowDispatchEvent(dispatchArgs, *dispatchEvent);
+ auto* dispatchEvent = isRedacted
+ ? inputEvent->set_dispatcher_window_dispatch_event_redacted()
+ : inputEvent->set_dispatcher_window_dispatch_event();
+ AndroidInputEventProtoConverter::toProtoWindowDispatchEvent(dispatchArgs, *dispatchEvent,
+ isRedacted);
});
}
diff --git a/services/inputflinger/dispatcher/trace/InputTracingPerfettoBackend.h b/services/inputflinger/dispatcher/trace/InputTracingPerfettoBackend.h
index d455375..af1c6b7 100644
--- a/services/inputflinger/dispatcher/trace/InputTracingPerfettoBackend.h
+++ b/services/inputflinger/dispatcher/trace/InputTracingPerfettoBackend.h
@@ -18,8 +18,12 @@
#include "InputTracingBackendInterface.h"
+#include "InputTracingPerfettoBackendConfig.h"
+
+#include <ftl/flags.h>
#include <perfetto/tracing.h>
#include <mutex>
+#include <set>
namespace android::inputdispatcher::trace::impl {
@@ -45,22 +49,46 @@
*/
class PerfettoBackend : public InputTracingBackendInterface {
public:
- PerfettoBackend();
+ using GetPackageUid = std::function<gui::Uid(std::string)>;
+
+ explicit PerfettoBackend(GetPackageUid);
~PerfettoBackend() override = default;
void traceKeyEvent(const TracedKeyEvent&, const TracedEventArgs&) override;
void traceMotionEvent(const TracedMotionEvent&, const TracedEventArgs&) override;
void traceWindowDispatch(const WindowDispatchArgs&, const TracedEventArgs&) override;
+private:
+ // Implementation of the perfetto data source.
+ // Each instance of the InputEventDataSource represents a different tracing session.
class InputEventDataSource : public perfetto::DataSource<InputEventDataSource> {
public:
- void OnSetup(const SetupArgs&) override {}
+ explicit InputEventDataSource();
+
+ void OnSetup(const SetupArgs&) override;
void OnStart(const StartArgs&) override;
void OnStop(const StopArgs&) override;
+
+ void initializeUidMap(GetPackageUid);
+ bool shouldIgnoreTracedInputEvent(const EventType&) const;
+ inline ftl::Flags<TraceFlag> getFlags() const { return mConfig.flags; }
+ TraceLevel resolveTraceLevel(const TracedEventArgs&) const;
+
+ private:
+ const int32_t mInstanceId;
+ TraceConfig mConfig;
+
+ bool ruleMatches(const TraceRule&, const TracedEventArgs&) const;
+
+ std::optional<std::map<std::string, gui::Uid>> mUidMap;
};
-private:
+ // TODO(b/330360505): Query the native package manager directly from the data source,
+ // and remove this.
+ GetPackageUid mGetPackageUid;
+
static std::once_flag sDataSourceRegistrationFlag;
+ static std::atomic<int32_t> sNextInstanceId;
};
} // namespace android::inputdispatcher::trace::impl
diff --git a/services/inputflinger/dispatcher/trace/InputTracingPerfettoBackendConfig.h b/services/inputflinger/dispatcher/trace/InputTracingPerfettoBackendConfig.h
new file mode 100644
index 0000000..536e32b
--- /dev/null
+++ b/services/inputflinger/dispatcher/trace/InputTracingPerfettoBackendConfig.h
@@ -0,0 +1,60 @@
+/*
+ * Copyright 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include <ftl/enum.h>
+#include <ftl/flags.h>
+#include <perfetto/config/android/android_input_event_config.pbzero.h>
+#include <vector>
+
+namespace android::inputdispatcher::trace::impl {
+
+/** Flags representing the configurations that are enabled in the trace. */
+enum class TraceFlag : uint32_t {
+ // Trace details about input events processed by InputDispatcher.
+ TRACE_DISPATCHER_INPUT_EVENTS = 0x1,
+ // Trace details about an event being sent to a window by InputDispatcher.
+ TRACE_DISPATCHER_WINDOW_DISPATCH = 0x2,
+
+ ftl_last = TRACE_DISPATCHER_WINDOW_DISPATCH,
+};
+
+/** Representation of AndroidInputEventConfig::TraceLevel. */
+using TraceLevel = perfetto::protos::pbzero::AndroidInputEventConfig::TraceLevel;
+
+/** Representation of AndroidInputEventConfig::TraceRule. */
+struct TraceRule {
+ TraceLevel level;
+
+ std::vector<std::string> matchAllPackages;
+ std::vector<std::string> matchAnyPackages;
+ std::optional<bool> matchSecure;
+ std::optional<bool> matchImeConnectionActive;
+};
+
+/**
+ * A complete configuration for a tracing session.
+ *
+ * The trace rules are applied as documented in the perfetto config:
+ * /external/perfetto/protos/perfetto/config/android/android_input_event_config.proto
+ */
+struct TraceConfig {
+ ftl::Flags<TraceFlag> flags;
+ std::vector<TraceRule> rules;
+};
+
+} // namespace android::inputdispatcher::trace::impl
diff --git a/services/inputflinger/dispatcher/trace/ThreadedBackend.cpp b/services/inputflinger/dispatcher/trace/ThreadedBackend.cpp
index b1791b3..1614789 100644
--- a/services/inputflinger/dispatcher/trace/ThreadedBackend.cpp
+++ b/services/inputflinger/dispatcher/trace/ThreadedBackend.cpp
@@ -38,10 +38,10 @@
template <typename Backend>
ThreadedBackend<Backend>::ThreadedBackend(Backend&& innerBackend)
- : mTracerThread(
+ : mBackend(std::move(innerBackend)),
+ mTracerThread(
"InputTracer", [this]() { threadLoop(); },
- [this]() { mThreadWakeCondition.notify_all(); }),
- mBackend(std::move(innerBackend)) {}
+ [this]() { mThreadWakeCondition.notify_all(); }) {}
template <typename Backend>
ThreadedBackend<Backend>::~ThreadedBackend() {
diff --git a/services/inputflinger/dispatcher/trace/ThreadedBackend.h b/services/inputflinger/dispatcher/trace/ThreadedBackend.h
index cab47af..3bfc72b 100644
--- a/services/inputflinger/dispatcher/trace/ThreadedBackend.h
+++ b/services/inputflinger/dispatcher/trace/ThreadedBackend.h
@@ -44,7 +44,6 @@
private:
std::mutex mLock;
- InputThread mTracerThread;
bool mThreadExit GUARDED_BY(mLock){false};
std::condition_variable mThreadWakeCondition;
Backend mBackend;
@@ -53,6 +52,11 @@
TracedEventArgs>;
std::vector<TraceEntry> mQueue GUARDED_BY(mLock);
+ // InputThread stops when its destructor is called. Initialize it last so that it is the
+ // first thing to be destructed. This will guarantee the thread will not access other
+ // members that have already been destructed.
+ InputThread mTracerThread;
+
void threadLoop();
};
diff --git a/services/inputflinger/tests/FakeInputDispatcherPolicy.h b/services/inputflinger/tests/FakeInputDispatcherPolicy.h
index fb2db06..e0a7324 100644
--- a/services/inputflinger/tests/FakeInputDispatcherPolicy.h
+++ b/services/inputflinger/tests/FakeInputDispatcherPolicy.h
@@ -86,6 +86,8 @@
void notifyDeviceInteraction(DeviceId deviceId, nsecs_t timestamp,
const std::set<gui::Uid>& uids) override {}
+
+ gui::Uid getPackageUid(std::string) override { return gui::Uid::INVALID; }
};
} // namespace android
diff --git a/services/inputflinger/tests/InputDispatcher_test.cpp b/services/inputflinger/tests/InputDispatcher_test.cpp
index 9375e92..9e3a4f1 100644
--- a/services/inputflinger/tests/InputDispatcher_test.cpp
+++ b/services/inputflinger/tests/InputDispatcher_test.cpp
@@ -660,6 +660,8 @@
verify(*mFilteredEvent);
mFilteredEvent = nullptr;
}
+
+ gui::Uid getPackageUid(std::string) override { return gui::Uid::INVALID; }
};
} // namespace
@@ -5367,6 +5369,94 @@
window->assertNoEvents();
}
+// This test verifies the occlusion detection for all rotations of the display by tapping
+// in different locations on the display, specifically points close to the four corners of a
+// window.
+TEST_P(InputDispatcherDisplayOrientationFixture, BlockUntrustClickInDifferentOrientations) {
+ constexpr static int32_t displayWidth = 400;
+ constexpr static int32_t displayHeight = 800;
+
+ std::shared_ptr<FakeApplicationHandle> untrustedWindowApplication =
+ std::make_shared<FakeApplicationHandle>();
+ std::shared_ptr<FakeApplicationHandle> application = std::make_shared<FakeApplicationHandle>();
+
+ const auto rotation = GetParam();
+
+ // Set up the display with the specified rotation.
+ const bool isRotated = rotation == ui::ROTATION_90 || rotation == ui::ROTATION_270;
+ const int32_t logicalDisplayWidth = isRotated ? displayHeight : displayWidth;
+ const int32_t logicalDisplayHeight = isRotated ? displayWidth : displayHeight;
+ const ui::Transform displayTransform(ui::Transform::toRotationFlags(rotation),
+ logicalDisplayWidth, logicalDisplayHeight);
+ addDisplayInfo(ADISPLAY_ID_DEFAULT, displayTransform);
+
+ // Create a window that not trusted.
+ const Rect untrustedWindowFrameInLogicalDisplay(100, 100, 200, 300);
+
+ const Rect untrustedWindowFrameInDisplay =
+ displayTransform.inverse().transform(untrustedWindowFrameInLogicalDisplay);
+
+ sp<FakeWindowHandle> untrustedWindow =
+ sp<FakeWindowHandle>::make(untrustedWindowApplication, mDispatcher, "UntrustedWindow",
+ ADISPLAY_ID_DEFAULT);
+ untrustedWindow->setFrame(untrustedWindowFrameInDisplay, displayTransform);
+ untrustedWindow->setTrustedOverlay(false);
+ untrustedWindow->setTouchOcclusionMode(TouchOcclusionMode::BLOCK_UNTRUSTED);
+ untrustedWindow->setTouchable(false);
+ untrustedWindow->setAlpha(1.0f);
+ untrustedWindow->setOwnerInfo(gui::Pid{1}, gui::Uid{101});
+ addWindow(untrustedWindow);
+
+ // Create a simple app window below the untrusted window.
+ const Rect simpleAppWindowFrameInLogicalDisplay(0, 0, 300, 600);
+ const Rect simpleAppWindowFrameInDisplay =
+ displayTransform.inverse().transform(simpleAppWindowFrameInLogicalDisplay);
+
+ sp<FakeWindowHandle> simpleAppWindow =
+ sp<FakeWindowHandle>::make(application, mDispatcher, "SimpleAppWindow",
+ ADISPLAY_ID_DEFAULT);
+ simpleAppWindow->setFrame(simpleAppWindowFrameInDisplay, displayTransform);
+ simpleAppWindow->setOwnerInfo(gui::Pid{2}, gui::Uid{202});
+ addWindow(simpleAppWindow);
+
+ // The following points in logical display space should be inside the untrusted window, so
+ // the simple window could not receive events that coordinate is these point.
+ static const std::array<vec2, 4> untrustedPoints{
+ {{100, 100}, {199.99, 100}, {100, 299.99}, {199.99, 299.99}}};
+
+ for (const auto untrustedPoint : untrustedPoints) {
+ const vec2 p = displayTransform.inverse().transform(untrustedPoint);
+ const PointF pointInDisplaySpace{p.x, p.y};
+ mDispatcher->notifyMotion(generateMotionArgs(AMOTION_EVENT_ACTION_DOWN,
+ AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT,
+ {pointInDisplaySpace}));
+ mDispatcher->notifyMotion(generateMotionArgs(AMOTION_EVENT_ACTION_UP,
+ AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT,
+ {pointInDisplaySpace}));
+ }
+ untrustedWindow->assertNoEvents();
+ simpleAppWindow->assertNoEvents();
+ // The following points in logical display space should be outside the untrusted window, so
+ // the simple window should receive events that coordinate is these point.
+ static const std::array<vec2, 5> trustedPoints{
+ {{200, 100}, {100, 300}, {200, 300}, {100, 99.99}, {99.99, 100}}};
+ for (const auto trustedPoint : trustedPoints) {
+ const vec2 p = displayTransform.inverse().transform(trustedPoint);
+ const PointF pointInDisplaySpace{p.x, p.y};
+ mDispatcher->notifyMotion(generateMotionArgs(AMOTION_EVENT_ACTION_DOWN,
+ AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT,
+ {pointInDisplaySpace}));
+ simpleAppWindow->consumeMotionDown(ADISPLAY_ID_DEFAULT,
+ AMOTION_EVENT_FLAG_WINDOW_IS_PARTIALLY_OBSCURED);
+ mDispatcher->notifyMotion(generateMotionArgs(AMOTION_EVENT_ACTION_UP,
+ AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT,
+ {pointInDisplaySpace}));
+ simpleAppWindow->consumeMotionUp(ADISPLAY_ID_DEFAULT,
+ AMOTION_EVENT_FLAG_WINDOW_IS_PARTIALLY_OBSCURED);
+ }
+ untrustedWindow->assertNoEvents();
+}
+
// Run the precision tests for all rotations.
INSTANTIATE_TEST_SUITE_P(InputDispatcherDisplayOrientationTests,
InputDispatcherDisplayOrientationFixture,
diff --git a/services/inputflinger/tests/PointerChoreographer_test.cpp b/services/inputflinger/tests/PointerChoreographer_test.cpp
index 8ddb672..3b2565e 100644
--- a/services/inputflinger/tests/PointerChoreographer_test.cpp
+++ b/services/inputflinger/tests/PointerChoreographer_test.cpp
@@ -46,6 +46,7 @@
constexpr int32_t ANOTHER_DISPLAY_ID = 10;
constexpr int32_t DISPLAY_WIDTH = 480;
constexpr int32_t DISPLAY_HEIGHT = 800;
+constexpr auto DRAWING_TABLET_SOURCE = AINPUT_SOURCE_MOUSE | AINPUT_SOURCE_STYLUS;
const auto MOUSE_POINTER = PointerBuilder(/*id=*/0, ToolType::MOUSE)
.axis(AMOTION_EVENT_AXIS_RELATIVE_X, 10)
@@ -759,12 +760,28 @@
assertPointerControllerRemoved(pc);
}
-TEST_F(PointerChoreographerTest,
- WhenStylusPointerIconEnabledAndDisabledDoesNotCreatePointerController) {
+using StylusFixtureParam =
+ std::tuple</*name*/ std::string_view, /*source*/ uint32_t, ControllerType>;
+
+class StylusTestFixture : public PointerChoreographerTest,
+ public ::testing::WithParamInterface<StylusFixtureParam> {};
+
+INSTANTIATE_TEST_SUITE_P(PointerChoreographerTest, StylusTestFixture,
+ ::testing::Values(std::make_tuple("DirectStylus", AINPUT_SOURCE_STYLUS,
+ ControllerType::STYLUS),
+ std::make_tuple("DrawingTablet", DRAWING_TABLET_SOURCE,
+ ControllerType::MOUSE)),
+ [](const testing::TestParamInfo<StylusFixtureParam>& p) {
+ return std::string{std::get<0>(p.param)};
+ });
+
+TEST_P(StylusTestFixture, WhenStylusPointerIconEnabledAndDisabledDoesNotCreatePointerController) {
+ const auto& [name, source, controllerType] = GetParam();
+
// Disable stylus pointer icon and add a stylus device.
mChoreographer.setStylusPointerIconEnabled(false);
mChoreographer.notifyInputDevicesChanged(
- {/*id=*/0, {generateTestDeviceInfo(DEVICE_ID, AINPUT_SOURCE_STYLUS, DISPLAY_ID)}});
+ {/*id=*/0, {generateTestDeviceInfo(DEVICE_ID, source, DISPLAY_ID)}});
assertPointerControllerNotCreated();
// Enable stylus pointer icon. PointerController still should not be created.
@@ -772,25 +789,25 @@
assertPointerControllerNotCreated();
}
-TEST_F(PointerChoreographerTest, WhenStylusHoverEventOccursCreatesPointerController) {
+TEST_P(StylusTestFixture, WhenStylusHoverEventOccursCreatesPointerController) {
+ const auto& [name, source, controllerType] = GetParam();
+
// Add a stylus device and enable stylus pointer icon.
mChoreographer.notifyInputDevicesChanged(
- {/*id=*/0, {generateTestDeviceInfo(DEVICE_ID, AINPUT_SOURCE_STYLUS, DISPLAY_ID)}});
+ {/*id=*/0, {generateTestDeviceInfo(DEVICE_ID, source, DISPLAY_ID)}});
mChoreographer.setStylusPointerIconEnabled(true);
assertPointerControllerNotCreated();
// Emit hover event. Now PointerController should be created.
- mChoreographer.notifyMotion(
- MotionArgsBuilder(AMOTION_EVENT_ACTION_HOVER_ENTER, AINPUT_SOURCE_STYLUS)
- .pointer(STYLUS_POINTER)
- .deviceId(DEVICE_ID)
- .displayId(DISPLAY_ID)
- .build());
- assertPointerControllerCreated(ControllerType::STYLUS);
+ mChoreographer.notifyMotion(MotionArgsBuilder(AMOTION_EVENT_ACTION_HOVER_ENTER, source)
+ .pointer(STYLUS_POINTER)
+ .deviceId(DEVICE_ID)
+ .displayId(DISPLAY_ID)
+ .build());
+ assertPointerControllerCreated(controllerType);
}
-TEST_F(PointerChoreographerTest,
- WhenStylusPointerIconDisabledAndHoverEventOccursDoesNotCreatePointerController) {
+TEST_F(PointerChoreographerTest, StylusHoverEventWhenStylusPointerIconDisabled) {
// Add a stylus device and disable stylus pointer icon.
mChoreographer.notifyInputDevicesChanged(
{/*id=*/0, {generateTestDeviceInfo(DEVICE_ID, AINPUT_SOURCE_STYLUS, DISPLAY_ID)}});
@@ -807,25 +824,43 @@
assertPointerControllerNotCreated();
}
-TEST_F(PointerChoreographerTest, WhenStylusDeviceIsRemovedRemovesPointerController) {
- // Make sure the PointerController is created.
+TEST_F(PointerChoreographerTest, DrawingTabletHoverEventWhenStylusPointerIconDisabled) {
+ // Add a drawing tablet and disable stylus pointer icon.
mChoreographer.notifyInputDevicesChanged(
- {/*id=*/0, {generateTestDeviceInfo(DEVICE_ID, AINPUT_SOURCE_STYLUS, DISPLAY_ID)}});
- mChoreographer.setStylusPointerIconEnabled(true);
+ {/*id=*/0, {generateTestDeviceInfo(DEVICE_ID, DRAWING_TABLET_SOURCE, DISPLAY_ID)}});
+ mChoreographer.setStylusPointerIconEnabled(false);
+ assertPointerControllerNotCreated();
+
+ // Emit hover event. Drawing tablets are not affected by "stylus pointer icon" setting.
mChoreographer.notifyMotion(
- MotionArgsBuilder(AMOTION_EVENT_ACTION_HOVER_ENTER, AINPUT_SOURCE_STYLUS)
+ MotionArgsBuilder(AMOTION_EVENT_ACTION_HOVER_ENTER, DRAWING_TABLET_SOURCE)
.pointer(STYLUS_POINTER)
.deviceId(DEVICE_ID)
.displayId(DISPLAY_ID)
.build());
- auto pc = assertPointerControllerCreated(ControllerType::STYLUS);
+ assertPointerControllerCreated(ControllerType::MOUSE);
+}
+
+TEST_P(StylusTestFixture, WhenStylusDeviceIsRemovedRemovesPointerController) {
+ const auto& [name, source, controllerType] = GetParam();
+
+ // Make sure the PointerController is created.
+ mChoreographer.notifyInputDevicesChanged(
+ {/*id=*/0, {generateTestDeviceInfo(DEVICE_ID, source, DISPLAY_ID)}});
+ mChoreographer.setStylusPointerIconEnabled(true);
+ mChoreographer.notifyMotion(MotionArgsBuilder(AMOTION_EVENT_ACTION_HOVER_ENTER, source)
+ .pointer(STYLUS_POINTER)
+ .deviceId(DEVICE_ID)
+ .displayId(DISPLAY_ID)
+ .build());
+ auto pc = assertPointerControllerCreated(controllerType);
// Remove the device.
mChoreographer.notifyInputDevicesChanged({/*id=*/1, {}});
assertPointerControllerRemoved(pc);
}
-TEST_F(PointerChoreographerTest, WhenStylusPointerIconDisabledRemovesPointerController) {
+TEST_F(PointerChoreographerTest, StylusPointerIconDisabledRemovesPointerController) {
// Make sure the PointerController is created.
mChoreographer.notifyInputDevicesChanged(
{/*id=*/0, {generateTestDeviceInfo(DEVICE_ID, AINPUT_SOURCE_STYLUS, DISPLAY_ID)}});
@@ -843,38 +878,59 @@
assertPointerControllerRemoved(pc);
}
-TEST_F(PointerChoreographerTest, SetsViewportForStylusPointerController) {
+TEST_F(PointerChoreographerTest,
+ StylusPointerIconDisabledDoesNotRemoveDrawingTabletPointerController) {
+ // Make sure the PointerController is created.
+ mChoreographer.notifyInputDevicesChanged(
+ {/*id=*/0, {generateTestDeviceInfo(DEVICE_ID, DRAWING_TABLET_SOURCE, DISPLAY_ID)}});
+ mChoreographer.setStylusPointerIconEnabled(true);
+ mChoreographer.notifyMotion(
+ MotionArgsBuilder(AMOTION_EVENT_ACTION_HOVER_ENTER, DRAWING_TABLET_SOURCE)
+ .pointer(STYLUS_POINTER)
+ .deviceId(DEVICE_ID)
+ .displayId(DISPLAY_ID)
+ .build());
+ auto pc = assertPointerControllerCreated(ControllerType::MOUSE);
+
+ // Disable stylus pointer icon. This should not affect drawing tablets.
+ mChoreographer.setStylusPointerIconEnabled(false);
+ assertPointerControllerNotRemoved(pc);
+}
+
+TEST_P(StylusTestFixture, SetsViewportForStylusPointerController) {
+ const auto& [name, source, controllerType] = GetParam();
+
// Set viewport.
mChoreographer.setDisplayViewports(createViewports({DISPLAY_ID}));
// Make sure the PointerController is created.
mChoreographer.notifyInputDevicesChanged(
- {/*id=*/0, {generateTestDeviceInfo(DEVICE_ID, AINPUT_SOURCE_STYLUS, DISPLAY_ID)}});
+ {/*id=*/0, {generateTestDeviceInfo(DEVICE_ID, source, DISPLAY_ID)}});
mChoreographer.setStylusPointerIconEnabled(true);
- mChoreographer.notifyMotion(
- MotionArgsBuilder(AMOTION_EVENT_ACTION_HOVER_ENTER, AINPUT_SOURCE_STYLUS)
- .pointer(STYLUS_POINTER)
- .deviceId(DEVICE_ID)
- .displayId(DISPLAY_ID)
- .build());
- auto pc = assertPointerControllerCreated(ControllerType::STYLUS);
+ mChoreographer.notifyMotion(MotionArgsBuilder(AMOTION_EVENT_ACTION_HOVER_ENTER, source)
+ .pointer(STYLUS_POINTER)
+ .deviceId(DEVICE_ID)
+ .displayId(DISPLAY_ID)
+ .build());
+ auto pc = assertPointerControllerCreated(controllerType);
// Check that viewport is set for the PointerController.
pc->assertViewportSet(DISPLAY_ID);
}
-TEST_F(PointerChoreographerTest, WhenViewportIsSetLaterSetsViewportForStylusPointerController) {
+TEST_P(StylusTestFixture, WhenViewportIsSetLaterSetsViewportForStylusPointerController) {
+ const auto& [name, source, controllerType] = GetParam();
+
// Make sure the PointerController is created.
mChoreographer.notifyInputDevicesChanged(
- {/*id=*/0, {generateTestDeviceInfo(DEVICE_ID, AINPUT_SOURCE_STYLUS, DISPLAY_ID)}});
+ {/*id=*/0, {generateTestDeviceInfo(DEVICE_ID, source, DISPLAY_ID)}});
mChoreographer.setStylusPointerIconEnabled(true);
- mChoreographer.notifyMotion(
- MotionArgsBuilder(AMOTION_EVENT_ACTION_HOVER_ENTER, AINPUT_SOURCE_STYLUS)
- .pointer(STYLUS_POINTER)
- .deviceId(DEVICE_ID)
- .displayId(DISPLAY_ID)
- .build());
- auto pc = assertPointerControllerCreated(ControllerType::STYLUS);
+ mChoreographer.notifyMotion(MotionArgsBuilder(AMOTION_EVENT_ACTION_HOVER_ENTER, source)
+ .pointer(STYLUS_POINTER)
+ .deviceId(DEVICE_ID)
+ .displayId(DISPLAY_ID)
+ .build());
+ auto pc = assertPointerControllerCreated(controllerType);
// Check that viewport is unset.
pc->assertViewportNotSet();
@@ -886,19 +942,19 @@
pc->assertViewportSet(DISPLAY_ID);
}
-TEST_F(PointerChoreographerTest,
- WhenViewportDoesNotMatchDoesNotSetViewportForStylusPointerController) {
+TEST_P(StylusTestFixture, WhenViewportDoesNotMatchDoesNotSetViewportForStylusPointerController) {
+ const auto& [name, source, controllerType] = GetParam();
+
// Make sure the PointerController is created.
mChoreographer.notifyInputDevicesChanged(
- {/*id=*/0, {generateTestDeviceInfo(DEVICE_ID, AINPUT_SOURCE_STYLUS, DISPLAY_ID)}});
+ {/*id=*/0, {generateTestDeviceInfo(DEVICE_ID, source, DISPLAY_ID)}});
mChoreographer.setStylusPointerIconEnabled(true);
- mChoreographer.notifyMotion(
- MotionArgsBuilder(AMOTION_EVENT_ACTION_HOVER_ENTER, AINPUT_SOURCE_STYLUS)
- .pointer(STYLUS_POINTER)
- .deviceId(DEVICE_ID)
- .displayId(DISPLAY_ID)
- .build());
- auto pc = assertPointerControllerCreated(ControllerType::STYLUS);
+ mChoreographer.notifyMotion(MotionArgsBuilder(AMOTION_EVENT_ACTION_HOVER_ENTER, source)
+ .pointer(STYLUS_POINTER)
+ .deviceId(DEVICE_ID)
+ .displayId(DISPLAY_ID)
+ .build());
+ auto pc = assertPointerControllerCreated(controllerType);
// Check that viewport is unset.
pc->assertViewportNotSet();
@@ -910,24 +966,25 @@
pc->assertViewportNotSet();
}
-TEST_F(PointerChoreographerTest, StylusHoverManipulatesPointer) {
+TEST_P(StylusTestFixture, StylusHoverManipulatesPointer) {
+ const auto& [name, source, controllerType] = GetParam();
+
mChoreographer.setStylusPointerIconEnabled(true);
mChoreographer.notifyInputDevicesChanged(
- {/*id=*/0, {generateTestDeviceInfo(DEVICE_ID, AINPUT_SOURCE_STYLUS, DISPLAY_ID)}});
+ {/*id=*/0, {generateTestDeviceInfo(DEVICE_ID, source, DISPLAY_ID)}});
mChoreographer.setDisplayViewports(createViewports({DISPLAY_ID}));
// Emit hover enter event. This is for creating PointerController.
- mChoreographer.notifyMotion(
- MotionArgsBuilder(AMOTION_EVENT_ACTION_HOVER_ENTER, AINPUT_SOURCE_STYLUS)
- .pointer(STYLUS_POINTER)
- .deviceId(DEVICE_ID)
- .displayId(DISPLAY_ID)
- .build());
- auto pc = assertPointerControllerCreated(ControllerType::STYLUS);
+ mChoreographer.notifyMotion(MotionArgsBuilder(AMOTION_EVENT_ACTION_HOVER_ENTER, source)
+ .pointer(STYLUS_POINTER)
+ .deviceId(DEVICE_ID)
+ .displayId(DISPLAY_ID)
+ .build());
+ auto pc = assertPointerControllerCreated(controllerType);
// Emit hover move event. After bounds are set, PointerController will update the position.
mChoreographer.notifyMotion(
- MotionArgsBuilder(AMOTION_EVENT_ACTION_HOVER_MOVE, AINPUT_SOURCE_STYLUS)
+ MotionArgsBuilder(AMOTION_EVENT_ACTION_HOVER_MOVE, source)
.pointer(PointerBuilder(/*id=*/0, ToolType::STYLUS).x(150).y(250))
.deviceId(DEVICE_ID)
.displayId(DISPLAY_ID)
@@ -937,7 +994,7 @@
// Emit hover exit event.
mChoreographer.notifyMotion(
- MotionArgsBuilder(AMOTION_EVENT_ACTION_HOVER_EXIT, AINPUT_SOURCE_STYLUS)
+ MotionArgsBuilder(AMOTION_EVENT_ACTION_HOVER_EXIT, source)
.pointer(PointerBuilder(/*id=*/0, ToolType::STYLUS).x(150).y(250))
.deviceId(DEVICE_ID)
.displayId(DISPLAY_ID)
@@ -946,38 +1003,38 @@
ASSERT_FALSE(pc->isPointerShown());
}
-TEST_F(PointerChoreographerTest, StylusHoverManipulatesPointerForTwoDisplays) {
+TEST_P(StylusTestFixture, StylusHoverManipulatesPointerForTwoDisplays) {
+ const auto& [name, source, controllerType] = GetParam();
+
mChoreographer.setStylusPointerIconEnabled(true);
// Add two stylus devices associated to different displays.
mChoreographer.notifyInputDevicesChanged(
{/*id=*/0,
- {generateTestDeviceInfo(DEVICE_ID, AINPUT_SOURCE_STYLUS, DISPLAY_ID),
- generateTestDeviceInfo(SECOND_DEVICE_ID, AINPUT_SOURCE_STYLUS, ANOTHER_DISPLAY_ID)}});
+ {generateTestDeviceInfo(DEVICE_ID, source, DISPLAY_ID),
+ generateTestDeviceInfo(SECOND_DEVICE_ID, source, ANOTHER_DISPLAY_ID)}});
mChoreographer.setDisplayViewports(createViewports({DISPLAY_ID, ANOTHER_DISPLAY_ID}));
// Emit hover event with first device. This is for creating PointerController.
- mChoreographer.notifyMotion(
- MotionArgsBuilder(AMOTION_EVENT_ACTION_HOVER_ENTER, AINPUT_SOURCE_STYLUS)
- .pointer(STYLUS_POINTER)
- .deviceId(DEVICE_ID)
- .displayId(DISPLAY_ID)
- .build());
- auto firstDisplayPc = assertPointerControllerCreated(ControllerType::STYLUS);
+ mChoreographer.notifyMotion(MotionArgsBuilder(AMOTION_EVENT_ACTION_HOVER_ENTER, source)
+ .pointer(STYLUS_POINTER)
+ .deviceId(DEVICE_ID)
+ .displayId(DISPLAY_ID)
+ .build());
+ auto firstDisplayPc = assertPointerControllerCreated(controllerType);
// Emit hover event with second device. This is for creating PointerController.
- mChoreographer.notifyMotion(
- MotionArgsBuilder(AMOTION_EVENT_ACTION_HOVER_ENTER, AINPUT_SOURCE_STYLUS)
- .pointer(STYLUS_POINTER)
- .deviceId(SECOND_DEVICE_ID)
- .displayId(ANOTHER_DISPLAY_ID)
- .build());
+ mChoreographer.notifyMotion(MotionArgsBuilder(AMOTION_EVENT_ACTION_HOVER_ENTER, source)
+ .pointer(STYLUS_POINTER)
+ .deviceId(SECOND_DEVICE_ID)
+ .displayId(ANOTHER_DISPLAY_ID)
+ .build());
// There should be another PointerController created.
- auto secondDisplayPc = assertPointerControllerCreated(ControllerType::STYLUS);
+ auto secondDisplayPc = assertPointerControllerCreated(controllerType);
// Emit hover event with first device.
mChoreographer.notifyMotion(
- MotionArgsBuilder(AMOTION_EVENT_ACTION_HOVER_MOVE, AINPUT_SOURCE_STYLUS)
+ MotionArgsBuilder(AMOTION_EVENT_ACTION_HOVER_MOVE, source)
.pointer(PointerBuilder(/*id=*/0, ToolType::STYLUS).x(150).y(250))
.deviceId(DEVICE_ID)
.displayId(DISPLAY_ID)
@@ -989,7 +1046,7 @@
// Emit hover event with second device.
mChoreographer.notifyMotion(
- MotionArgsBuilder(AMOTION_EVENT_ACTION_HOVER_MOVE, AINPUT_SOURCE_STYLUS)
+ MotionArgsBuilder(AMOTION_EVENT_ACTION_HOVER_MOVE, source)
.pointer(PointerBuilder(/*id=*/0, ToolType::STYLUS).x(250).y(350))
.deviceId(SECOND_DEVICE_ID)
.displayId(ANOTHER_DISPLAY_ID)
@@ -1004,19 +1061,20 @@
ASSERT_TRUE(firstDisplayPc->isPointerShown());
}
-TEST_F(PointerChoreographerTest, WhenStylusDeviceIsResetRemovesPointer) {
+TEST_P(StylusTestFixture, WhenStylusDeviceIsResetRemovesPointer) {
+ const auto& [name, source, controllerType] = GetParam();
+
// Make sure the PointerController is created and there is a pointer.
mChoreographer.notifyInputDevicesChanged(
- {/*id=*/0, {generateTestDeviceInfo(DEVICE_ID, AINPUT_SOURCE_STYLUS, DISPLAY_ID)}});
+ {/*id=*/0, {generateTestDeviceInfo(DEVICE_ID, source, DISPLAY_ID)}});
mChoreographer.setStylusPointerIconEnabled(true);
mChoreographer.setDisplayViewports(createViewports({DISPLAY_ID}));
- mChoreographer.notifyMotion(
- MotionArgsBuilder(AMOTION_EVENT_ACTION_HOVER_ENTER, AINPUT_SOURCE_STYLUS)
- .pointer(STYLUS_POINTER)
- .deviceId(DEVICE_ID)
- .displayId(DISPLAY_ID)
- .build());
- auto pc = assertPointerControllerCreated(ControllerType::STYLUS);
+ mChoreographer.notifyMotion(MotionArgsBuilder(AMOTION_EVENT_ACTION_HOVER_ENTER, source)
+ .pointer(STYLUS_POINTER)
+ .deviceId(DEVICE_ID)
+ .displayId(DISPLAY_ID)
+ .build());
+ auto pc = assertPointerControllerCreated(controllerType);
ASSERT_TRUE(pc->isPointerShown());
// Reset the device and see the pointer controller was removed.
@@ -1460,19 +1518,20 @@
firstMousePc->assertPointerIconNotSet();
}
-TEST_F(PointerChoreographerTest, SetsPointerIconForStylus) {
+TEST_P(StylusTestFixture, SetsPointerIconForStylus) {
+ const auto& [name, source, controllerType] = GetParam();
+
// Make sure there is a PointerController.
mChoreographer.notifyInputDevicesChanged(
- {/*id=*/0, {generateTestDeviceInfo(DEVICE_ID, AINPUT_SOURCE_STYLUS, DISPLAY_ID)}});
+ {/*id=*/0, {generateTestDeviceInfo(DEVICE_ID, source, DISPLAY_ID)}});
mChoreographer.setStylusPointerIconEnabled(true);
mChoreographer.setDisplayViewports(createViewports({DISPLAY_ID}));
- mChoreographer.notifyMotion(
- MotionArgsBuilder(AMOTION_EVENT_ACTION_HOVER_ENTER, AINPUT_SOURCE_STYLUS)
- .pointer(STYLUS_POINTER)
- .deviceId(DEVICE_ID)
- .displayId(DISPLAY_ID)
- .build());
- auto pc = assertPointerControllerCreated(ControllerType::STYLUS);
+ mChoreographer.notifyMotion(MotionArgsBuilder(AMOTION_EVENT_ACTION_HOVER_ENTER, source)
+ .pointer(STYLUS_POINTER)
+ .deviceId(DEVICE_ID)
+ .displayId(DISPLAY_ID)
+ .build());
+ auto pc = assertPointerControllerCreated(controllerType);
pc->assertPointerIconNotSet();
// Set pointer icon for the device.
@@ -1485,28 +1544,28 @@
pc->assertPointerIconNotSet();
// The stylus stops hovering. This should cause the icon to be reset.
- mChoreographer.notifyMotion(
- MotionArgsBuilder(AMOTION_EVENT_ACTION_HOVER_EXIT, AINPUT_SOURCE_STYLUS)
- .pointer(STYLUS_POINTER)
- .deviceId(DEVICE_ID)
- .displayId(DISPLAY_ID)
- .build());
+ mChoreographer.notifyMotion(MotionArgsBuilder(AMOTION_EVENT_ACTION_HOVER_EXIT, source)
+ .pointer(STYLUS_POINTER)
+ .deviceId(DEVICE_ID)
+ .displayId(DISPLAY_ID)
+ .build());
pc->assertPointerIconSet(PointerIconStyle::TYPE_NOT_SPECIFIED);
}
-TEST_F(PointerChoreographerTest, SetsCustomPointerIconForStylus) {
+TEST_P(StylusTestFixture, SetsCustomPointerIconForStylus) {
+ const auto& [name, source, controllerType] = GetParam();
+
// Make sure there is a PointerController.
mChoreographer.notifyInputDevicesChanged(
- {/*id=*/0, {generateTestDeviceInfo(DEVICE_ID, AINPUT_SOURCE_STYLUS, DISPLAY_ID)}});
+ {/*id=*/0, {generateTestDeviceInfo(DEVICE_ID, source, DISPLAY_ID)}});
mChoreographer.setStylusPointerIconEnabled(true);
mChoreographer.setDisplayViewports(createViewports({DISPLAY_ID}));
- mChoreographer.notifyMotion(
- MotionArgsBuilder(AMOTION_EVENT_ACTION_HOVER_ENTER, AINPUT_SOURCE_STYLUS)
- .pointer(STYLUS_POINTER)
- .deviceId(DEVICE_ID)
- .displayId(DISPLAY_ID)
- .build());
- auto pc = assertPointerControllerCreated(ControllerType::STYLUS);
+ mChoreographer.notifyMotion(MotionArgsBuilder(AMOTION_EVENT_ACTION_HOVER_ENTER, source)
+ .pointer(STYLUS_POINTER)
+ .deviceId(DEVICE_ID)
+ .displayId(DISPLAY_ID)
+ .build());
+ auto pc = assertPointerControllerCreated(controllerType);
pc->assertCustomPointerIconNotSet();
// Set custom pointer icon for the device.
@@ -1522,28 +1581,28 @@
pc->assertCustomPointerIconNotSet();
}
-TEST_F(PointerChoreographerTest, SetsPointerIconForTwoStyluses) {
+TEST_P(StylusTestFixture, SetsPointerIconForTwoStyluses) {
+ const auto& [name, source, controllerType] = GetParam();
+
// Make sure there are two StylusPointerControllers. They can be on a same display.
mChoreographer.setStylusPointerIconEnabled(true);
mChoreographer.notifyInputDevicesChanged(
{/*id=*/0,
- {generateTestDeviceInfo(DEVICE_ID, AINPUT_SOURCE_STYLUS, DISPLAY_ID),
- generateTestDeviceInfo(SECOND_DEVICE_ID, AINPUT_SOURCE_STYLUS, DISPLAY_ID)}});
+ {generateTestDeviceInfo(DEVICE_ID, source, DISPLAY_ID),
+ generateTestDeviceInfo(SECOND_DEVICE_ID, source, DISPLAY_ID)}});
mChoreographer.setDisplayViewports(createViewports({DISPLAY_ID}));
- mChoreographer.notifyMotion(
- MotionArgsBuilder(AMOTION_EVENT_ACTION_HOVER_ENTER, AINPUT_SOURCE_STYLUS)
- .pointer(STYLUS_POINTER)
- .deviceId(DEVICE_ID)
- .displayId(DISPLAY_ID)
- .build());
- auto firstStylusPc = assertPointerControllerCreated(ControllerType::STYLUS);
- mChoreographer.notifyMotion(
- MotionArgsBuilder(AMOTION_EVENT_ACTION_HOVER_ENTER, AINPUT_SOURCE_STYLUS)
- .pointer(STYLUS_POINTER)
- .deviceId(SECOND_DEVICE_ID)
- .displayId(DISPLAY_ID)
- .build());
- auto secondStylusPc = assertPointerControllerCreated(ControllerType::STYLUS);
+ mChoreographer.notifyMotion(MotionArgsBuilder(AMOTION_EVENT_ACTION_HOVER_ENTER, source)
+ .pointer(STYLUS_POINTER)
+ .deviceId(DEVICE_ID)
+ .displayId(DISPLAY_ID)
+ .build());
+ auto firstStylusPc = assertPointerControllerCreated(controllerType);
+ mChoreographer.notifyMotion(MotionArgsBuilder(AMOTION_EVENT_ACTION_HOVER_ENTER, source)
+ .pointer(STYLUS_POINTER)
+ .deviceId(SECOND_DEVICE_ID)
+ .displayId(DISPLAY_ID)
+ .build());
+ auto secondStylusPc = assertPointerControllerCreated(controllerType);
// Set pointer icon for one stylus.
ASSERT_TRUE(mChoreographer.setPointerIcon(PointerIconStyle::TYPE_TEXT, DISPLAY_ID, DEVICE_ID));
@@ -1557,14 +1616,16 @@
firstStylusPc->assertPointerIconNotSet();
}
-TEST_F(PointerChoreographerTest, SetsPointerIconForMouseAndStylus) {
+TEST_P(StylusTestFixture, SetsPointerIconForMouseAndStylus) {
+ const auto& [name, source, controllerType] = GetParam();
+
// Make sure there are PointerControllers for a mouse and a stylus.
mChoreographer.setStylusPointerIconEnabled(true);
mChoreographer.setDefaultMouseDisplayId(DISPLAY_ID);
mChoreographer.notifyInputDevicesChanged(
{/*id=*/0,
{generateTestDeviceInfo(DEVICE_ID, AINPUT_SOURCE_MOUSE, ADISPLAY_ID_NONE),
- generateTestDeviceInfo(SECOND_DEVICE_ID, AINPUT_SOURCE_STYLUS, DISPLAY_ID)}});
+ generateTestDeviceInfo(SECOND_DEVICE_ID, source, DISPLAY_ID)}});
mChoreographer.setDisplayViewports(createViewports({DISPLAY_ID}));
mChoreographer.notifyMotion(
MotionArgsBuilder(AMOTION_EVENT_ACTION_HOVER_MOVE, AINPUT_SOURCE_MOUSE)
@@ -1573,13 +1634,12 @@
.displayId(ADISPLAY_ID_NONE)
.build());
auto mousePc = assertPointerControllerCreated(ControllerType::MOUSE);
- mChoreographer.notifyMotion(
- MotionArgsBuilder(AMOTION_EVENT_ACTION_HOVER_ENTER, AINPUT_SOURCE_STYLUS)
- .pointer(STYLUS_POINTER)
- .deviceId(SECOND_DEVICE_ID)
- .displayId(DISPLAY_ID)
- .build());
- auto stylusPc = assertPointerControllerCreated(ControllerType::STYLUS);
+ mChoreographer.notifyMotion(MotionArgsBuilder(AMOTION_EVENT_ACTION_HOVER_ENTER, source)
+ .pointer(STYLUS_POINTER)
+ .deviceId(SECOND_DEVICE_ID)
+ .displayId(DISPLAY_ID)
+ .build());
+ auto stylusPc = assertPointerControllerCreated(controllerType);
// Set pointer icon for the mouse.
ASSERT_TRUE(mChoreographer.setPointerIcon(PointerIconStyle::TYPE_TEXT, DISPLAY_ID, DEVICE_ID));
@@ -1688,7 +1748,9 @@
ASSERT_FALSE(touchpadPc->isPointerShown());
}
-TEST_F(PointerChoreographerTest, SetPointerIconVisibilityHidesPointerForStylus) {
+TEST_P(StylusTestFixture, SetPointerIconVisibilityHidesPointerForStylus) {
+ const auto& [name, source, controllerType] = GetParam();
+
mChoreographer.setDisplayViewports(createViewports({DISPLAY_ID}));
mChoreographer.setStylusPointerIconEnabled(true);
@@ -1696,15 +1758,14 @@
mChoreographer.setPointerIconVisibility(DISPLAY_ID, false);
mChoreographer.notifyInputDevicesChanged(
- {/*id=*/0, {generateTestDeviceInfo(DEVICE_ID, AINPUT_SOURCE_STYLUS, DISPLAY_ID)}});
- mChoreographer.notifyMotion(
- MotionArgsBuilder(AMOTION_EVENT_ACTION_HOVER_ENTER, AINPUT_SOURCE_STYLUS)
- .pointer(STYLUS_POINTER)
- .deviceId(DEVICE_ID)
- .displayId(DISPLAY_ID)
- .build());
+ {/*id=*/0, {generateTestDeviceInfo(DEVICE_ID, source, DISPLAY_ID)}});
+ mChoreographer.notifyMotion(MotionArgsBuilder(AMOTION_EVENT_ACTION_HOVER_ENTER, source)
+ .pointer(STYLUS_POINTER)
+ .deviceId(DEVICE_ID)
+ .displayId(DISPLAY_ID)
+ .build());
ASSERT_TRUE(mChoreographer.setPointerIcon(PointerIconStyle::TYPE_TEXT, DISPLAY_ID, DEVICE_ID));
- auto pc = assertPointerControllerCreated(ControllerType::STYLUS);
+ auto pc = assertPointerControllerCreated(controllerType);
pc->assertPointerIconSet(PointerIconStyle::TYPE_TEXT);
// The pointer should not be visible.
diff --git a/services/surfaceflinger/FrameTracker.cpp b/services/surfaceflinger/FrameTracker.cpp
index 178c531..ca8cdc3 100644
--- a/services/surfaceflinger/FrameTracker.cpp
+++ b/services/surfaceflinger/FrameTracker.cpp
@@ -18,9 +18,6 @@
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wconversion"
-// This is needed for stdint.h to define INT64_MAX in C++
-#define __STDC_LIMIT_MACROS
-
#include <inttypes.h>
#include <android-base/stringprintf.h>
diff --git a/services/surfaceflinger/Scheduler/RefreshRateSelector.cpp b/services/surfaceflinger/Scheduler/RefreshRateSelector.cpp
index 56c29e2..de4fd90 100644
--- a/services/surfaceflinger/Scheduler/RefreshRateSelector.cpp
+++ b/services/surfaceflinger/Scheduler/RefreshRateSelector.cpp
@@ -474,21 +474,23 @@
}
auto RefreshRateSelector::getRankedFrameRates(const std::vector<LayerRequirement>& layers,
- GlobalSignals signals) const -> RankedFrameRates {
+ GlobalSignals signals, Fps pacesetterFps) const
+ -> RankedFrameRates {
+ GetRankedFrameRatesCache cache{layers, signals, pacesetterFps};
+
std::lock_guard lock(mLock);
- if (mGetRankedFrameRatesCache &&
- mGetRankedFrameRatesCache->arguments == std::make_pair(layers, signals)) {
+ if (mGetRankedFrameRatesCache && mGetRankedFrameRatesCache->matches(cache)) {
return mGetRankedFrameRatesCache->result;
}
- const auto result = getRankedFrameRatesLocked(layers, signals);
- mGetRankedFrameRatesCache = GetRankedFrameRatesCache{{layers, signals}, result};
- return result;
+ cache.result = getRankedFrameRatesLocked(layers, signals, pacesetterFps);
+ mGetRankedFrameRatesCache = std::move(cache);
+ return mGetRankedFrameRatesCache->result;
}
auto RefreshRateSelector::getRankedFrameRatesLocked(const std::vector<LayerRequirement>& layers,
- GlobalSignals signals) const
+ GlobalSignals signals, Fps pacesetterFps) const
-> RankedFrameRates {
using namespace fps_approx_ops;
ATRACE_CALL();
@@ -496,6 +498,24 @@
const auto& activeMode = *getActiveModeLocked().modePtr;
+ if (pacesetterFps.isValid()) {
+ ALOGV("Follower display");
+
+ const auto ranking = rankFrameRates(activeMode.getGroup(), RefreshRateOrder::Descending,
+ std::nullopt, [&](FrameRateMode mode) {
+ return mode.modePtr->getPeakFps() == pacesetterFps;
+ });
+
+ if (!ranking.empty()) {
+ ATRACE_FORMAT_INSTANT("%s (Follower display)",
+ to_string(ranking.front().frameRateMode.fps).c_str());
+
+ return {ranking, kNoSignals, pacesetterFps};
+ }
+
+ ALOGW("Follower display cannot follow the pacesetter");
+ }
+
// Keep the display at max frame rate for the duration of powering on the display.
if (signals.powerOnImminent) {
ALOGV("Power On Imminent");
diff --git a/services/surfaceflinger/Scheduler/RefreshRateSelector.h b/services/surfaceflinger/Scheduler/RefreshRateSelector.h
index 6051e89..a500063 100644
--- a/services/surfaceflinger/Scheduler/RefreshRateSelector.h
+++ b/services/surfaceflinger/Scheduler/RefreshRateSelector.h
@@ -233,14 +233,18 @@
struct RankedFrameRates {
FrameRateRanking ranking; // Ordered by descending score.
GlobalSignals consideredSignals;
+ Fps pacesetterFps;
bool operator==(const RankedFrameRates& other) const {
- return ranking == other.ranking && consideredSignals == other.consideredSignals;
+ return ranking == other.ranking && consideredSignals == other.consideredSignals &&
+ isApproxEqual(pacesetterFps, other.pacesetterFps);
}
};
- RankedFrameRates getRankedFrameRates(const std::vector<LayerRequirement>&, GlobalSignals) const
- EXCLUDES(mLock);
+ // If valid, `pacesetterFps` (used by follower displays) filters the ranking to modes matching
+ // that refresh rate.
+ RankedFrameRates getRankedFrameRates(const std::vector<LayerRequirement>&, GlobalSignals,
+ Fps pacesetterFps = {}) const EXCLUDES(mLock);
FpsRange getSupportedRefreshRateRange() const EXCLUDES(mLock) {
std::lock_guard lock(mLock);
@@ -415,7 +419,8 @@
const FrameRateMode& getActiveModeLocked() const REQUIRES(mLock);
RankedFrameRates getRankedFrameRatesLocked(const std::vector<LayerRequirement>& layers,
- GlobalSignals signals) const REQUIRES(mLock);
+ GlobalSignals signals, Fps pacesetterFps) const
+ REQUIRES(mLock);
// Returns number of display frames and remainder when dividing the layer refresh period by
// display refresh period.
@@ -534,8 +539,16 @@
Config::FrameRateOverride mFrameRateOverrideConfig;
struct GetRankedFrameRatesCache {
- std::pair<std::vector<LayerRequirement>, GlobalSignals> arguments;
+ std::vector<LayerRequirement> layers;
+ GlobalSignals signals;
+ Fps pacesetterFps;
+
RankedFrameRates result;
+
+ bool matches(const GetRankedFrameRatesCache& other) const {
+ return layers == other.layers && signals == other.signals &&
+ isApproxEqual(pacesetterFps, other.pacesetterFps);
+ }
};
mutable std::optional<GetRankedFrameRatesCache> mGetRankedFrameRatesCache GUARDED_BY(mLock);
diff --git a/services/surfaceflinger/Scheduler/Scheduler.cpp b/services/surfaceflinger/Scheduler/Scheduler.cpp
index d92edb8..7968096 100644
--- a/services/surfaceflinger/Scheduler/Scheduler.cpp
+++ b/services/surfaceflinger/Scheduler/Scheduler.cpp
@@ -306,8 +306,11 @@
const auto pacesetterOpt = pacesetterDisplayLocked();
LOG_ALWAYS_FATAL_IF(!pacesetterOpt);
const Display& pacesetter = *pacesetterOpt;
- return std::make_pair(pacesetter.selectorPtr->getActiveMode().fps,
- pacesetter.schedulePtr->period());
+ const FrameRateMode& frameRateMode = pacesetter.selectorPtr->getActiveMode();
+ const auto refreshRate = frameRateMode.fps;
+ const auto displayVsync = frameRateMode.modePtr->getVsyncRate();
+ const auto numPeriod = RefreshRateSelector::getFrameRateDivisor(displayVsync, refreshRate);
+ return std::make_pair(refreshRate, numPeriod * pacesetter.schedulePtr->period());
}();
const Period currentPeriod = period != Period::zero() ? period : refreshRate.getPeriod();
@@ -1146,38 +1149,31 @@
auto Scheduler::chooseDisplayModes() const -> DisplayModeChoiceMap {
ATRACE_CALL();
- using RankedRefreshRates = RefreshRateSelector::RankedFrameRates;
- ui::PhysicalDisplayVector<RankedRefreshRates> perDisplayRanking;
+ DisplayModeChoiceMap modeChoices;
const auto globalSignals = makeGlobalSignals();
- Fps pacesetterFps;
+
+ const Fps pacesetterFps = [&]() REQUIRES(mPolicyLock, mDisplayLock, kMainThreadContext) {
+ auto rankedFrameRates =
+ pacesetterSelectorPtrLocked()->getRankedFrameRates(mPolicy.contentRequirements,
+ globalSignals);
+
+ const Fps pacesetterFps = rankedFrameRates.ranking.front().frameRateMode.fps;
+
+ modeChoices.try_emplace(*mPacesetterDisplayId,
+ DisplayModeChoice::from(std::move(rankedFrameRates)));
+ return pacesetterFps;
+ }();
for (const auto& [id, display] : mDisplays) {
+ if (id == *mPacesetterDisplayId) continue;
+
auto rankedFrameRates =
- display.selectorPtr->getRankedFrameRates(mPolicy.contentRequirements,
- globalSignals);
- if (id == *mPacesetterDisplayId) {
- pacesetterFps = rankedFrameRates.ranking.front().frameRateMode.fps;
- }
- perDisplayRanking.push_back(std::move(rankedFrameRates));
+ display.selectorPtr->getRankedFrameRates(mPolicy.contentRequirements, globalSignals,
+ pacesetterFps);
+
+ modeChoices.try_emplace(id, DisplayModeChoice::from(std::move(rankedFrameRates)));
}
- DisplayModeChoiceMap modeChoices;
- using fps_approx_ops::operator==;
-
- for (auto& [rankings, signals] : perDisplayRanking) {
- const auto chosenFrameRateMode =
- ftl::find_if(rankings,
- [&](const auto& ranking) {
- return ranking.frameRateMode.fps == pacesetterFps;
- })
- .transform([](const auto& scoredFrameRate) {
- return scoredFrameRate.get().frameRateMode;
- })
- .value_or(rankings.front().frameRateMode);
-
- modeChoices.try_emplace(chosenFrameRateMode.modePtr->getPhysicalDisplayId(),
- DisplayModeChoice{chosenFrameRateMode, signals});
- }
return modeChoices;
}
diff --git a/services/surfaceflinger/Scheduler/Scheduler.h b/services/surfaceflinger/Scheduler/Scheduler.h
index 494a91b..ccb3aa7 100644
--- a/services/surfaceflinger/Scheduler/Scheduler.h
+++ b/services/surfaceflinger/Scheduler/Scheduler.h
@@ -402,6 +402,11 @@
DisplayModeChoice(FrameRateMode mode, GlobalSignals consideredSignals)
: mode(std::move(mode)), consideredSignals(consideredSignals) {}
+ static DisplayModeChoice from(RefreshRateSelector::RankedFrameRates rankedFrameRates) {
+ return {rankedFrameRates.ranking.front().frameRateMode,
+ rankedFrameRates.consideredSignals};
+ }
+
FrameRateMode mode;
GlobalSignals consideredSignals;
@@ -437,6 +442,7 @@
// IEventThreadCallback overrides
bool throttleVsync(TimePoint, uid_t) override;
+ // Get frame interval
Period getVsyncPeriod(uid_t) override EXCLUDES(mDisplayLock);
void resync() override EXCLUDES(mDisplayLock);
void onExpectedPresentTimePosted(TimePoint expectedPresentTime) override EXCLUDES(mDisplayLock);
diff --git a/services/surfaceflinger/SurfaceFlinger.cpp b/services/surfaceflinger/SurfaceFlinger.cpp
index bf210af..9d07671 100644
--- a/services/surfaceflinger/SurfaceFlinger.cpp
+++ b/services/surfaceflinger/SurfaceFlinger.cpp
@@ -578,11 +578,11 @@
sp<IBinder> SurfaceFlinger::createDisplay(const String8& displayName, bool secure,
float requestedRefreshRate) {
- // onTransact already checks for some permissions, but adding an additional check here.
- // This is to ensure that only system and graphics can request to create a secure
+ // SurfaceComposerAIDL checks for some permissions, but adding an additional check here.
+ // This is to ensure that only root, system, and graphics can request to create a secure
// display. Secure displays can show secure content so we add an additional restriction on it.
const int uid = IPCThreadState::self()->getCallingUid();
- if (secure && uid != AID_GRAPHICS && uid != AID_SYSTEM) {
+ if (secure && uid != AID_ROOT && uid != AID_GRAPHICS && uid != AID_SYSTEM) {
ALOGE("Only privileged processes can create a secure display");
return nullptr;
}
@@ -6244,9 +6244,9 @@
{"--frontend"s, mainThreadDumper(&SurfaceFlinger::dumpFrontEnd)},
{"--hdrinfo"s, dumper(&SurfaceFlinger::dumpHdrInfo)},
{"--hwclayers"s, mainThreadDumper(&SurfaceFlinger::dumpHwcLayersMinidump)},
- {"--latency"s, argsDumper(&SurfaceFlinger::dumpStatsLocked)},
- {"--latency-clear"s, argsDumper(&SurfaceFlinger::clearStatsLocked)},
- {"--list"s, dumper(&SurfaceFlinger::listLayersLocked)},
+ {"--latency"s, argsMainThreadDumper(&SurfaceFlinger::dumpStats)},
+ {"--latency-clear"s, argsMainThreadDumper(&SurfaceFlinger::clearStats)},
+ {"--list"s, mainThreadDumper(&SurfaceFlinger::listLayers)},
{"--planner"s, argsDumper(&SurfaceFlinger::dumpPlannerInfo)},
{"--scheduler"s, dumper(&SurfaceFlinger::dumpScheduler)},
{"--timestats"s, protoDumper(&SurfaceFlinger::dumpTimeStats)},
@@ -6279,28 +6279,29 @@
return doDump(fd, DumpArgs(), asProto);
}
-void SurfaceFlinger::listLayersLocked(std::string& result) const {
- mCurrentState.traverseInZOrder(
- [&](Layer* layer) { StringAppendF(&result, "%s\n", layer->getDebugName()); });
+void SurfaceFlinger::listLayers(std::string& result) const {
+ for (const auto& layer : mLayerLifecycleManager.getLayers()) {
+ StringAppendF(&result, "%s\n", layer->getDebugString().c_str());
+ }
}
-void SurfaceFlinger::dumpStatsLocked(const DumpArgs& args, std::string& result) const {
+void SurfaceFlinger::dumpStats(const DumpArgs& args, std::string& result) const {
StringAppendF(&result, "%" PRId64 "\n", getVsyncPeriodFromHWC());
if (args.size() < 2) return;
const auto name = String8(args[1]);
- mCurrentState.traverseInZOrder([&](Layer* layer) {
+ traverseLegacyLayers([&](Layer* layer) {
if (layer->getName() == name.c_str()) {
layer->dumpFrameStats(result);
}
});
}
-void SurfaceFlinger::clearStatsLocked(const DumpArgs& args, std::string&) {
+void SurfaceFlinger::clearStats(const DumpArgs& args, std::string&) {
const bool clearAll = args.size() < 2;
const auto name = clearAll ? String8() : String8(args[1]);
- mCurrentState.traverse([&](Layer* layer) {
+ traverseLegacyLayers([&](Layer* layer) {
if (clearAll || layer->getName() == name.c_str()) {
layer->clearFrameStats();
}
diff --git a/services/surfaceflinger/SurfaceFlinger.h b/services/surfaceflinger/SurfaceFlinger.h
index 0cc8fbb..34a7e7f 100644
--- a/services/surfaceflinger/SurfaceFlinger.h
+++ b/services/surfaceflinger/SurfaceFlinger.h
@@ -509,10 +509,7 @@
return lockedDumper(std::bind(dump, this, _1, _2, _3));
}
- template <typename F, std::enable_if_t<std::is_member_function_pointer_v<F>>* = nullptr>
- Dumper mainThreadDumper(F dump) {
- using namespace std::placeholders;
- Dumper dumper = std::bind(dump, this, _3);
+ Dumper mainThreadDumperImpl(Dumper dumper) {
return [this, dumper](const DumpArgs& args, bool asProto, std::string& result) -> void {
mScheduler
->schedule(
@@ -522,6 +519,18 @@
};
}
+ template <typename F, std::enable_if_t<std::is_member_function_pointer_v<F>>* = nullptr>
+ Dumper mainThreadDumper(F dump) {
+ using namespace std::placeholders;
+ return mainThreadDumperImpl(std::bind(dump, this, _3));
+ }
+
+ template <typename F, std::enable_if_t<std::is_member_function_pointer_v<F>>* = nullptr>
+ Dumper argsMainThreadDumper(F dump) {
+ using namespace std::placeholders;
+ return mainThreadDumperImpl(std::bind(dump, this, _1, _3));
+ }
+
// Maximum allowed number of display frames that can be set through backdoor
static const int MAX_ALLOWED_DISPLAY_FRAMES = 2048;
@@ -1113,9 +1122,9 @@
void dumpHwcLayersMinidumpLockedLegacy(std::string& result) const REQUIRES(mStateLock);
void appendSfConfigString(std::string& result) const;
- void listLayersLocked(std::string& result) const;
- void dumpStatsLocked(const DumpArgs& args, std::string& result) const REQUIRES(mStateLock);
- void clearStatsLocked(const DumpArgs& args, std::string& result);
+ void listLayers(std::string& result) const;
+ void dumpStats(const DumpArgs& args, std::string& result) const REQUIRES(mStateLock);
+ void clearStats(const DumpArgs& args, std::string& result);
void dumpTimeStats(const DumpArgs& args, bool asProto, std::string& result) const;
void dumpFrameTimeline(const DumpArgs& args, std::string& result) const;
void logFrameStats(TimePoint now) REQUIRES(kMainThreadContext);
diff --git a/services/surfaceflinger/Tracing/TransactionTracing.h b/services/surfaceflinger/Tracing/TransactionTracing.h
index 6a66fff..7a0fb5e 100644
--- a/services/surfaceflinger/Tracing/TransactionTracing.h
+++ b/services/surfaceflinger/Tracing/TransactionTracing.h
@@ -24,6 +24,7 @@
#include <mutex>
#include <optional>
+#include <set>
#include <thread>
#include "FrontEnd/DisplayInfo.h"
diff --git a/services/surfaceflinger/tests/Android.bp b/services/surfaceflinger/tests/Android.bp
index 925fe0b..1fd7ae0 100644
--- a/services/surfaceflinger/tests/Android.bp
+++ b/services/surfaceflinger/tests/Android.bp
@@ -38,6 +38,7 @@
"DereferenceSurfaceControl_test.cpp",
"DisplayConfigs_test.cpp",
"DisplayEventReceiver_test.cpp",
+ "Dumpsys_test.cpp",
"EffectLayer_test.cpp",
"HdrSdrRatioOverlay_test.cpp",
"InvalidHandles_test.cpp",
diff --git a/services/surfaceflinger/tests/Credentials_test.cpp b/services/surfaceflinger/tests/Credentials_test.cpp
index 822ac4d..9b83713 100644
--- a/services/surfaceflinger/tests/Credentials_test.cpp
+++ b/services/surfaceflinger/tests/Credentials_test.cpp
@@ -241,7 +241,7 @@
// Check with root.
{
UIDFaker f(AID_ROOT);
- ASSERT_FALSE(condition());
+ ASSERT_TRUE(condition());
}
// Check as a Graphics user.
diff --git a/services/surfaceflinger/tests/Dumpsys_test.cpp b/services/surfaceflinger/tests/Dumpsys_test.cpp
new file mode 100644
index 0000000..c3914e5
--- /dev/null
+++ b/services/surfaceflinger/tests/Dumpsys_test.cpp
@@ -0,0 +1,109 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <android/native_window.h>
+#include <gtest/gtest.h>
+#include <gui/SurfaceComposerClient.h>
+#include "android-base/stringprintf.h"
+#include "utils/Errors.h"
+
+namespace android {
+
+namespace {
+status_t runShellCommand(const std::string& cmd, std::string& result) {
+ FILE* pipe = popen(cmd.c_str(), "r");
+ if (!pipe) {
+ return UNKNOWN_ERROR;
+ }
+
+ char buffer[1024];
+ while (fgets(buffer, sizeof(buffer), pipe) != NULL) {
+ result += buffer;
+ }
+
+ pclose(pipe);
+ return OK;
+}
+} // namespace
+
+using android::hardware::graphics::common::V1_1::BufferUsage;
+
+class WaitForCompletedCallback {
+public:
+ WaitForCompletedCallback() = default;
+ ~WaitForCompletedCallback() = default;
+
+ static void transactionCompletedCallback(void* callbackContext, nsecs_t /* latchTime */,
+ const sp<Fence>& /* presentFence */,
+ const std::vector<SurfaceControlStats>& /* stats */) {
+ ASSERT_NE(callbackContext, nullptr) << "failed to get callback context";
+ WaitForCompletedCallback* context = static_cast<WaitForCompletedCallback*>(callbackContext);
+ context->notify();
+ }
+
+ void wait() {
+ std::unique_lock lock(mMutex);
+ cv.wait(lock, [this] { return mCallbackReceived; });
+ }
+
+ void notify() {
+ std::unique_lock lock(mMutex);
+ mCallbackReceived = true;
+ cv.notify_one();
+ }
+
+private:
+ std::mutex mMutex;
+ std::condition_variable cv;
+ bool mCallbackReceived = false;
+};
+
+TEST(Dumpsys, listLayers) {
+ sp<SurfaceComposerClient> client = sp<SurfaceComposerClient>::make();
+ ASSERT_EQ(NO_ERROR, client->initCheck());
+ auto newLayer =
+ client->createSurface(String8("MY_TEST_LAYER"), 100, 100, PIXEL_FORMAT_RGBA_8888, 0);
+ std::string layersAsString;
+ EXPECT_EQ(OK, runShellCommand("dumpsys SurfaceFlinger --list", layersAsString));
+ EXPECT_NE(strstr(layersAsString.c_str(), ""), nullptr);
+}
+
+TEST(Dumpsys, stats) {
+ sp<SurfaceComposerClient> client = sp<SurfaceComposerClient>::make();
+ ASSERT_EQ(NO_ERROR, client->initCheck());
+ auto newLayer =
+ client->createSurface(String8("MY_TEST_LAYER"), 100, 100, PIXEL_FORMAT_RGBA_8888, 0);
+ uint64_t usageFlags = BufferUsage::CPU_READ_OFTEN | BufferUsage::CPU_WRITE_OFTEN |
+ BufferUsage::COMPOSER_OVERLAY | BufferUsage::GPU_TEXTURE;
+
+ sp<GraphicBuffer> buffer =
+ sp<GraphicBuffer>::make(1u, 1u, PIXEL_FORMAT_RGBA_8888, 1u, usageFlags, "test");
+
+ WaitForCompletedCallback callback;
+ SurfaceComposerClient::Transaction()
+ .setBuffer(newLayer, buffer)
+ .addTransactionCompletedCallback(WaitForCompletedCallback::transactionCompletedCallback,
+ &callback)
+ .apply();
+ callback.wait();
+ std::string stats;
+ std::string layerName = base::StringPrintf("MY_TEST_LAYER#%d", newLayer->getLayerId());
+ EXPECT_EQ(OK, runShellCommand("dumpsys SurfaceFlinger --latency " + layerName, stats));
+ EXPECT_NE(std::string(""), stats);
+ EXPECT_EQ(OK, runShellCommand("dumpsys SurfaceFlinger --latency-clear " + layerName, stats));
+}
+
+} // namespace android
diff --git a/services/surfaceflinger/tests/unittests/RefreshRateSelectorTest.cpp b/services/surfaceflinger/tests/unittests/RefreshRateSelectorTest.cpp
index a155f5d..0ede612 100644
--- a/services/surfaceflinger/tests/unittests/RefreshRateSelectorTest.cpp
+++ b/services/surfaceflinger/tests/unittests/RefreshRateSelectorTest.cpp
@@ -103,8 +103,9 @@
auto& mutableGetRankedRefreshRatesCache() { return mGetRankedFrameRatesCache; }
auto getRankedFrameRates(const std::vector<LayerRequirement>& layers,
- GlobalSignals signals = {}) const {
- const auto result = RefreshRateSelector::getRankedFrameRates(layers, signals);
+ GlobalSignals signals = {}, Fps pacesetterFps = {}) const {
+ const auto result =
+ RefreshRateSelector::getRankedFrameRates(layers, signals, pacesetterFps);
EXPECT_TRUE(std::is_sorted(result.ranking.begin(), result.ranking.end(),
ScoredFrameRate::DescendingScore{}));
@@ -114,8 +115,8 @@
auto getRankedRefreshRatesAsPair(const std::vector<LayerRequirement>& layers,
GlobalSignals signals) const {
- const auto [ranking, consideredSignals] = getRankedFrameRates(layers, signals);
- return std::make_pair(ranking, consideredSignals);
+ const auto result = getRankedFrameRates(layers, signals);
+ return std::make_pair(result.ranking, result.consideredSignals);
}
FrameRateMode getBestFrameRateMode(const std::vector<LayerRequirement>& layers = {},
@@ -1387,7 +1388,7 @@
TEST_P(RefreshRateSelectorTest, powerOnImminentConsidered) {
auto selector = createSelector(kModes_60_90, kModeId60);
- auto [refreshRates, signals] = selector.getRankedFrameRates({}, {});
+ auto [refreshRates, signals, _] = selector.getRankedFrameRates({}, {});
EXPECT_FALSE(signals.powerOnImminent);
auto expectedRefreshRates = []() -> std::vector<FrameRateMode> {
@@ -1471,10 +1472,32 @@
}
}
+TEST_P(RefreshRateSelectorTest, pacesetterConsidered) {
+ auto selector = createSelector(kModes_60_90, kModeId60);
+ constexpr RefreshRateSelector::GlobalSignals kNoSignals;
+
+ std::vector<LayerRequirement> layers = {{.weight = 1.f}};
+ layers[0].vote = LayerVoteType::Min;
+
+ // The pacesetterFps takes precedence over the LayerRequirement.
+ {
+ const auto result = selector.getRankedFrameRates(layers, {}, 90_Hz);
+ EXPECT_EQ(kMode90, result.ranking.front().frameRateMode.modePtr);
+ EXPECT_EQ(kNoSignals, result.consideredSignals);
+ }
+
+ // The pacesetterFps takes precedence over GlobalSignals.
+ {
+ const auto result = selector.getRankedFrameRates(layers, {.touch = true}, 60_Hz);
+ EXPECT_EQ(kMode60, result.ranking.front().frameRateMode.modePtr);
+ EXPECT_EQ(kNoSignals, result.consideredSignals);
+ }
+}
+
TEST_P(RefreshRateSelectorTest, touchConsidered) {
auto selector = createSelector(kModes_60_90, kModeId60);
- auto [_, signals] = selector.getRankedFrameRates({}, {});
+ auto signals = selector.getRankedFrameRates({}, {}).consideredSignals;
EXPECT_FALSE(signals.touch);
std::tie(std::ignore, signals) = selector.getRankedRefreshRatesAsPair({}, {.touch = true});
@@ -2363,7 +2386,7 @@
lr.name = "60Hz ExplicitDefault";
lr.focused = true;
- const auto [rankedFrameRate, signals] =
+ const auto [rankedFrameRate, signals, _] =
selector.getRankedFrameRates(layers, {.touch = true, .idle = true});
EXPECT_EQ(rankedFrameRate.begin()->frameRateMode.modePtr, kMode60);
@@ -2587,7 +2610,7 @@
EXPECT_EQ(SetPolicyResult::Changed,
selector.setDisplayManagerPolicy({kModeId90, {k90, k90}, {k60_90, k60_90}}));
- const auto [ranking, signals] = selector.getRankedFrameRates({}, {});
+ const auto [ranking, signals, _] = selector.getRankedFrameRates({}, {});
EXPECT_EQ(ranking.front().frameRateMode.modePtr, kMode90);
EXPECT_FALSE(signals.touch);
@@ -2971,7 +2994,7 @@
layers[0].vote = voteType;
layers[0].desiredRefreshRate = 90_Hz;
- const auto [ranking, signals] =
+ const auto [ranking, signals, _] =
selector.getRankedFrameRates(layers, {.touch = touchActive, .idle = true});
// Refresh rate will be chosen by either touch state or idle state.
@@ -3121,16 +3144,17 @@
auto selector = createSelector(kModes_30_60_72_90_120, kModeId60);
using GlobalSignals = RefreshRateSelector::GlobalSignals;
- const auto args = std::make_pair(std::vector<LayerRequirement>{},
- GlobalSignals{.touch = true, .idle = true});
-
const RefreshRateSelector::RankedFrameRates result = {{RefreshRateSelector::ScoredFrameRate{
{90_Hz, kMode90}}},
GlobalSignals{.touch = true}};
- selector.mutableGetRankedRefreshRatesCache() = {args, result};
+ selector.mutableGetRankedRefreshRatesCache() = {.layers = std::vector<LayerRequirement>{},
+ .signals = GlobalSignals{.touch = true,
+ .idle = true},
+ .result = result};
- EXPECT_EQ(result, selector.getRankedFrameRates(args.first, args.second));
+ const auto& cache = *selector.mutableGetRankedRefreshRatesCache();
+ EXPECT_EQ(result, selector.getRankedFrameRates(cache.layers, cache.signals));
}
TEST_P(RefreshRateSelectorTest, getBestFrameRateMode_WritesCache) {
@@ -3138,15 +3162,18 @@
EXPECT_FALSE(selector.mutableGetRankedRefreshRatesCache());
- std::vector<LayerRequirement> layers = {{.weight = 1.f}, {.weight = 0.5f}};
- RefreshRateSelector::GlobalSignals globalSignals{.touch = true, .idle = true};
+ const std::vector<LayerRequirement> layers = {{.weight = 1.f}, {.weight = 0.5f}};
+ const RefreshRateSelector::GlobalSignals globalSignals{.touch = true, .idle = true};
+ const Fps pacesetterFps = 60_Hz;
- const auto result = selector.getRankedFrameRates(layers, globalSignals);
+ const auto result = selector.getRankedFrameRates(layers, globalSignals, pacesetterFps);
const auto& cache = selector.mutableGetRankedRefreshRatesCache();
ASSERT_TRUE(cache);
- EXPECT_EQ(cache->arguments, std::make_pair(layers, globalSignals));
+ EXPECT_EQ(cache->layers, layers);
+ EXPECT_EQ(cache->signals, globalSignals);
+ EXPECT_EQ(cache->pacesetterFps, pacesetterFps);
EXPECT_EQ(cache->result, result);
}
@@ -4073,7 +4100,7 @@
layers[0].vote = voteType;
layers[0].desiredRefreshRate = 90_Hz;
- const auto [ranking, signals] =
+ const auto [ranking, signals, _] =
selector.getRankedFrameRates(layers, {.touch = touchActive, .idle = true});
// Refresh rate will be chosen by either touch state or idle state.
diff --git a/services/surfaceflinger/tests/unittests/SchedulerTest.cpp b/services/surfaceflinger/tests/unittests/SchedulerTest.cpp
index d4735c7..8ac8eda 100644
--- a/services/surfaceflinger/tests/unittests/SchedulerTest.cpp
+++ b/services/surfaceflinger/tests/unittests/SchedulerTest.cpp
@@ -362,7 +362,7 @@
globalSignals)(kDisplayId2,
FrameRateMode{60_Hz,
kDisplay2Mode60},
- globalSignals);
+ GlobalSignals{});
std::vector<RefreshRateSelector::LayerRequirement> layers = {{.weight = 1.f},
{.weight = 1.f}};
@@ -381,7 +381,7 @@
globalSignals)(kDisplayId2,
FrameRateMode{120_Hz,
kDisplay2Mode120},
- globalSignals);
+ GlobalSignals{});
mScheduler->setTouchStateAndIdleTimerPolicy(globalSignals);
@@ -400,7 +400,7 @@
globalSignals)(kDisplayId2,
FrameRateMode{120_Hz,
kDisplay2Mode120},
- globalSignals);
+ GlobalSignals{});
const auto actualChoices = mScheduler->chooseDisplayModes();
EXPECT_EQ(expectedChoices, actualChoices);
@@ -422,10 +422,10 @@
DisplayModeChoice>(kDisplayId1, FrameRateMode{120_Hz, kDisplay1Mode120},
globalSignals)(kDisplayId2,
FrameRateMode{120_Hz, kDisplay2Mode120},
- globalSignals)(kDisplayId3,
- FrameRateMode{60_Hz,
- kDisplay3Mode60},
- globalSignals);
+ GlobalSignals{})(kDisplayId3,
+ FrameRateMode{60_Hz,
+ kDisplay3Mode60},
+ GlobalSignals{});
const auto actualChoices = mScheduler->chooseDisplayModes();
EXPECT_EQ(expectedChoices, actualChoices);
@@ -440,12 +440,12 @@
expectedChoices = ftl::init::map<
const PhysicalDisplayId&,
DisplayModeChoice>(kDisplayId1, FrameRateMode{60_Hz, kDisplay1Mode60},
- globalSignals)(kDisplayId2,
- FrameRateMode{60_Hz, kDisplay2Mode60},
- globalSignals)(kDisplayId3,
- FrameRateMode{60_Hz,
- kDisplay3Mode60},
- globalSignals);
+ GlobalSignals{})(kDisplayId2,
+ FrameRateMode{60_Hz, kDisplay2Mode60},
+ GlobalSignals{})(kDisplayId3,
+ FrameRateMode{60_Hz,
+ kDisplay3Mode60},
+ globalSignals);
const auto actualChoices = mScheduler->chooseDisplayModes();
EXPECT_EQ(expectedChoices, actualChoices);