Merge "Add methods to get properties of Buffer." into main
diff --git a/libs/binder/Android.bp b/libs/binder/Android.bp
index 379b609..6903cb5 100644
--- a/libs/binder/Android.bp
+++ b/libs/binder/Android.bp
@@ -489,6 +489,7 @@
"ProcessState.cpp",
"Static.cpp",
":libbinder_aidl",
+ ":libbinder_accessor_aidl",
":libbinder_device_interface_sources",
],
target: {
@@ -801,7 +802,6 @@
"aidl/android/os/IServiceManager.aidl",
"aidl/android/os/Service.aidl",
"aidl/android/os/ServiceDebugInfo.aidl",
- ":libbinder_accessor_aidl",
],
path: "aidl",
}
@@ -812,26 +812,7 @@
"aidl/android/os/IAccessor.aidl",
],
path: "aidl",
-}
-
-// TODO(b/353492849): Make this interface private to libbinder.
-aidl_interface {
- name: "android.os.accessor",
- srcs: [":libbinder_accessor_aidl"],
- unstable: true,
- backend: {
- rust: {
- enabled: true,
- apex_available: [
- "com.android.virt",
- ],
- },
- },
- visibility: [
- ":__subpackages__",
- "//system/tools/aidl:__subpackages__",
- "//packages/modules/Virtualization:__subpackages__",
- ],
+ visibility: [":__subpackages__"],
}
aidl_interface {
diff --git a/libs/binder/BpBinder.cpp b/libs/binder/BpBinder.cpp
index eae844c..3758b65 100644
--- a/libs/binder/BpBinder.cpp
+++ b/libs/binder/BpBinder.cpp
@@ -197,7 +197,9 @@
&& currentValue < sBinderProxyCountHighWatermark
&& ((trackedValue & WARNING_REACHED_MASK) == 0)) [[unlikely]] {
sTrackingMap[trackedUid] |= WARNING_REACHED_MASK;
- if (sWarningCallback) sWarningCallback(trackedUid);
+ if (sWarningCallback) {
+ *postTask = [=]() { sWarningCallback(trackedUid); };
+ }
} else if (currentValue >= sBinderProxyCountHighWatermark) {
ALOGE("Too many binder proxy objects sent to uid %d from uid %d (%d proxies held)",
getuid(), trackedUid, trackedValue);
diff --git a/libs/binder/IServiceManager.cpp b/libs/binder/IServiceManager.cpp
index 77b80ef..32388db 100644
--- a/libs/binder/IServiceManager.cpp
+++ b/libs/binder/IServiceManager.cpp
@@ -386,7 +386,7 @@
ALOGE("Binder is null");
return BAD_VALUE;
}
- sp<IAccessor> accessor = interface_cast<IAccessor>(binder);
+ sp<IAccessor> accessor = checked_interface_cast<IAccessor>(binder);
if (accessor == nullptr) {
ALOGE("This binder for %s is not an IAccessor binder", String8(instance).c_str());
return BAD_TYPE;
@@ -420,6 +420,28 @@
return binder;
}
+status_t delegateAccessor(const String16& name, const sp<IBinder>& accessor,
+ sp<IBinder>* delegator) {
+ LOG_ALWAYS_FATAL_IF(delegator == nullptr, "delegateAccessor called with a null out param");
+ if (accessor == nullptr) {
+ ALOGW("Accessor argument to delegateAccessor is null.");
+ *delegator = nullptr;
+ return OK;
+ }
+ status_t status = validateAccessor(name, accessor);
+ if (status != OK) {
+ ALOGE("The provided accessor binder is not an IAccessor for instance %s. Status: "
+ "%s",
+ String8(name).c_str(), statusToString(status).c_str());
+ return status;
+ }
+ // validateAccessor already called checked_interface_cast and made sure this
+ // is a valid accessor object.
+ *delegator = sp<android::os::IAccessorDelegator>::make(interface_cast<IAccessor>(accessor));
+
+ return OK;
+}
+
#if !defined(__ANDROID_VNDK__)
// IPermissionController is not accessible to vendors
diff --git a/libs/binder/OS.h b/libs/binder/OS.h
index 04869a1..64b1fd4 100644
--- a/libs/binder/OS.h
+++ b/libs/binder/OS.h
@@ -27,6 +27,7 @@
LIBBINDER_EXPORTED void trace_begin(uint64_t tag, const char* name);
LIBBINDER_EXPORTED void trace_end(uint64_t tag);
LIBBINDER_EXPORTED void trace_int(uint64_t tag, const char* name, int32_t value);
+LIBBINDER_EXPORTED uint64_t get_trace_enabled_tags();
status_t setNonBlocking(borrowed_fd fd);
diff --git a/libs/binder/OS_android.cpp b/libs/binder/OS_android.cpp
index 893ee15..4e9230c 100644
--- a/libs/binder/OS_android.cpp
+++ b/libs/binder/OS_android.cpp
@@ -48,6 +48,10 @@
atrace_int(tag, name, value);
}
+uint64_t get_trace_enabled_tags() {
+ return atrace_enabled_tags;
+}
+
} // namespace os
// Legacy trace symbol. To be removed once all of downstream rebuilds.
diff --git a/libs/binder/OS_non_android_linux.cpp b/libs/binder/OS_non_android_linux.cpp
index 0c64eb6..6bba823 100644
--- a/libs/binder/OS_non_android_linux.cpp
+++ b/libs/binder/OS_non_android_linux.cpp
@@ -41,6 +41,10 @@
void trace_int(uint64_t, const char*, int32_t) {}
+uint64_t get_trace_enabled_tags() {
+ return 0;
+}
+
uint64_t GetThreadId() {
return syscall(__NR_gettid);
}
diff --git a/libs/binder/Parcel.cpp b/libs/binder/Parcel.cpp
index 4b7af45..3711362 100644
--- a/libs/binder/Parcel.cpp
+++ b/libs/binder/Parcel.cpp
@@ -683,7 +683,7 @@
return err;
}
-int Parcel::compareData(const Parcel& other) {
+int Parcel::compareData(const Parcel& other) const {
size_t size = dataSize();
if (size != other.dataSize()) {
return size < other.dataSize() ? -1 : 1;
diff --git a/libs/binder/Status.cpp b/libs/binder/Status.cpp
index dba6587..9a98097 100644
--- a/libs/binder/Status.cpp
+++ b/libs/binder/Status.cpp
@@ -99,27 +99,28 @@
return status;
}
+ if (mException == EX_HAS_NOTED_APPOPS_REPLY_HEADER) {
+ status = skipUnusedHeader(parcel);
+ if (status != OK) {
+ setFromStatusT(status);
+ return status;
+ }
+ // Read next exception code.
+ status = parcel.readInt32(&mException);
+ if (status != OK) {
+ setFromStatusT(status);
+ return status;
+ }
+ }
+
// Skip over fat response headers. Not used (or propagated) in native code.
if (mException == EX_HAS_REPLY_HEADER) {
- // Note that the header size includes the 4 byte size field.
- const size_t header_start = parcel.dataPosition();
- // Get available size before reading more
- const size_t header_avail = parcel.dataAvail();
-
- int32_t header_size;
- status = parcel.readInt32(&header_size);
+ status = skipUnusedHeader(parcel);
if (status != OK) {
setFromStatusT(status);
return status;
}
- if (header_size < 0 || static_cast<size_t>(header_size) > header_avail) {
- android_errorWriteLog(0x534e4554, "132650049");
- setFromStatusT(UNKNOWN_ERROR);
- return UNKNOWN_ERROR;
- }
-
- parcel.setDataPosition(header_start + header_size);
// And fat response headers are currently only used when there are no
// exceptions, so act like there was no error.
mException = EX_NONE;
@@ -257,5 +258,28 @@
return ret;
}
+status_t Status::skipUnusedHeader(const Parcel& parcel) {
+ // Note that the header size includes the 4 byte size field.
+ const size_t header_start = parcel.dataPosition();
+ // Get available size before reading more
+ const size_t header_avail = parcel.dataAvail();
+
+ int32_t header_size;
+ status_t status = parcel.readInt32(&header_size);
+ ALOGD("Skip unused header. exception code: %d, start: %zu, size: %d.",
+ mException, header_start, header_size);
+ if (status != OK) {
+ return status;
+ }
+
+ if (header_size < 0 || static_cast<size_t>(header_size) > header_avail) {
+ android_errorWriteLog(0x534e4554, "132650049");
+ return UNKNOWN_ERROR;
+ }
+
+ parcel.setDataPosition(header_start + header_size);
+ return OK;
+}
+
} // namespace binder
} // namespace android
diff --git a/libs/binder/aidl/android/content/pm/ApexStagedEvent.aidl b/libs/binder/aidl/android/content/pm/ApexStagedEvent.aidl
index 75f8753..9bac386 100644
--- a/libs/binder/aidl/android/content/pm/ApexStagedEvent.aidl
+++ b/libs/binder/aidl/android/content/pm/ApexStagedEvent.aidl
@@ -16,6 +16,8 @@
package android.content.pm;
+import android.content.pm.StagedApexInfo;
+
/**
* This event is designed for notification to native code listener about
* any changes to set of apex packages staged for installation on next boot.
@@ -23,5 +25,5 @@
* @hide
*/
parcelable ApexStagedEvent {
- @utf8InCpp String[] stagedApexModuleNames;
+ StagedApexInfo[] stagedApexInfos;
}
diff --git a/libs/binder/aidl/android/content/pm/IPackageManagerNative.aidl b/libs/binder/aidl/android/content/pm/IPackageManagerNative.aidl
index 3ddfefa..0f0be2f 100644
--- a/libs/binder/aidl/android/content/pm/IPackageManagerNative.aidl
+++ b/libs/binder/aidl/android/content/pm/IPackageManagerNative.aidl
@@ -135,13 +135,7 @@
void unregisterStagedApexObserver(in IStagedApexObserver observer);
/**
- * Get APEX module names of all APEX that are staged ready for installation
+ * Get information of staged APEXes.
*/
- @utf8InCpp String[] getStagedApexModuleNames();
-
- /**
- * Get information of APEX which is staged ready for installation.
- * Returns null if no such APEX is found.
- */
- @nullable StagedApexInfo getStagedApexInfo(in @utf8InCpp String moduleName);
+ StagedApexInfo[] getStagedApexInfos();
}
diff --git a/libs/binder/aidl/android/content/pm/StagedApexInfo.aidl b/libs/binder/aidl/android/content/pm/StagedApexInfo.aidl
index 949835b..8f7ad30 100644
--- a/libs/binder/aidl/android/content/pm/StagedApexInfo.aidl
+++ b/libs/binder/aidl/android/content/pm/StagedApexInfo.aidl
@@ -22,6 +22,7 @@
*
* @hide
*/
+@JavaDerive(equals=true)
parcelable StagedApexInfo {
@utf8InCpp String moduleName;
@utf8InCpp String diskImagePath;
diff --git a/libs/binder/include/binder/IServiceManager.h b/libs/binder/include/binder/IServiceManager.h
index 2b23276..81f7cdb 100644
--- a/libs/binder/include/binder/IServiceManager.h
+++ b/libs/binder/include/binder/IServiceManager.h
@@ -291,6 +291,28 @@
* \return OK if the binder is an IAccessor for `instance`
*/
LIBBINDER_EXPORTED status_t validateAccessor(const String16& instance, const sp<IBinder>& binder);
+
+/**
+ * Have libbinder wrap this IAccessor binder in an IAccessorDelegator and return
+ * it.
+ *
+ * This is required only in very specific situations when the process that has
+ * permissions to connect the to RPC service's socket and create the FD for it
+ * is in a separate process from this process that wants to service the Accessor
+ * binder and the communication between these two processes is binder RPC. This
+ * is needed because the binder passed over the binder RPC connection can not be
+ * used as a kernel binder, and needs to be wrapped by a kernel binder that can
+ * then be registered with service manager.
+ *
+ * \param instance name of the Accessor.
+ * \param binder to wrap in a Delegator and register with service manager.
+ * \param outDelegator the wrapped kernel binder for IAccessorDelegator
+ *
+ * \return OK if the binder is an IAccessor for `instance` and the delegator was
+ * successfully created.
+ */
+LIBBINDER_EXPORTED status_t delegateAccessor(const String16& name, const sp<IBinder>& accessor,
+ sp<IBinder>* delegator);
#endif // __TRUSTY__
#ifndef __ANDROID__
diff --git a/libs/binder/include/binder/Parcel.h b/libs/binder/include/binder/Parcel.h
index 5cc0830..15a0da7 100644
--- a/libs/binder/include/binder/Parcel.h
+++ b/libs/binder/include/binder/Parcel.h
@@ -92,7 +92,7 @@
LIBBINDER_EXPORTED status_t appendFrom(const Parcel* parcel, size_t start, size_t len);
- LIBBINDER_EXPORTED int compareData(const Parcel& other);
+ LIBBINDER_EXPORTED int compareData(const Parcel& other) const;
LIBBINDER_EXPORTED status_t compareDataInRange(size_t thisOffset, const Parcel& other,
size_t otherOffset, size_t length,
int* result) const;
@@ -637,9 +637,6 @@
LIBBINDER_EXPORTED const flat_binder_object* readObject(bool nullMetaData) const;
- // Explicitly close all file descriptors in the parcel.
- LIBBINDER_EXPORTED void closeFileDescriptors();
-
// Debugging: get metrics on current allocations.
LIBBINDER_EXPORTED static size_t getGlobalAllocSize();
LIBBINDER_EXPORTED static size_t getGlobalAllocCount();
@@ -652,6 +649,9 @@
LIBBINDER_EXPORTED void print(std::ostream& to, uint32_t flags = 0) const;
private:
+ // Explicitly close all file descriptors in the parcel.
+ void closeFileDescriptors();
+
// `objects` and `objectsSize` always 0 for RPC Parcels.
typedef void (*release_func)(const uint8_t* data, size_t dataSize, const binder_size_t* objects,
size_t objectsSize);
@@ -1240,7 +1240,7 @@
if (__builtin_mul_overflow(size, sizeof(T), &dataLen)) {
return -EOVERFLOW;
}
- auto data = reinterpret_cast<const T*>(readInplace(dataLen));
+ auto data = readInplace(dataLen);
if (data == nullptr) return BAD_VALUE;
// std::vector::insert and similar methods will require type-dependent
// byte alignment when inserting from a const iterator such as `data`,
diff --git a/libs/binder/include/binder/Status.h b/libs/binder/include/binder/Status.h
index 49ccf7c..d69f662 100644
--- a/libs/binder/include/binder/Status.h
+++ b/libs/binder/include/binder/Status.h
@@ -67,6 +67,9 @@
EX_SERVICE_SPECIFIC = -8,
EX_PARCELABLE = -9,
+ // See android/os/Parcel.java. We need to handle this in native code.
+ EX_HAS_NOTED_APPOPS_REPLY_HEADER = -127,
+
// This is special and Java specific; see Parcel.java.
EX_HAS_REPLY_HEADER = -128,
// This is special, and indicates to C++ binder proxies that the
@@ -150,6 +153,8 @@
Status(int32_t exceptionCode, int32_t errorCode);
Status(int32_t exceptionCode, int32_t errorCode, const String8& message);
+ status_t skipUnusedHeader(const Parcel& parcel);
+
// If |mException| == EX_TRANSACTION_FAILED, generated code will return
// |mErrorCode| as the result of the transaction rather than write an
// exception to the reply parcel.
diff --git a/libs/binder/include/binder/Trace.h b/libs/binder/include/binder/Trace.h
index 2f450cb..a3e6c8a 100644
--- a/libs/binder/include/binder/Trace.h
+++ b/libs/binder/include/binder/Trace.h
@@ -42,6 +42,7 @@
void trace_begin(uint64_t tag, const char* name);
void trace_end(uint64_t tag);
void trace_int(uint64_t tag, const char* name, int32_t value);
+uint64_t get_trace_enabled_tags();
} // namespace os
class LIBBINDER_EXPORTED ScopedTrace {
diff --git a/libs/binder/ndk/binder_rpc.cpp b/libs/binder/ndk/binder_rpc.cpp
index 2cc5f81..07b8c40 100644
--- a/libs/binder/ndk/binder_rpc.cpp
+++ b/libs/binder/ndk/binder_rpc.cpp
@@ -94,7 +94,7 @@
}
void* mData;
ABinderRpc_AccessorProviderUserData_deleteCallback mOnDelete;
- // needs to be copyable for std::function, but we will never copy it
+ // needs to be copy-able for std::function, but we will never copy it
OnDeleteProviderHolder(const OnDeleteProviderHolder&) {
LOG_ALWAYS_FATAL("This object can't be copied!");
}
@@ -113,7 +113,7 @@
}
if (data && onDelete == nullptr) {
ALOGE("If a non-null data ptr is passed to ABinderRpc_registerAccessorProvider, then a "
- "ABinderRpc_AccessorProviderUserData_deleteCallback must alse be passed to delete "
+ "ABinderRpc_AccessorProviderUserData_deleteCallback must also be passed to delete "
"the data object once the ABinderRpc_AccessorProvider is removed.");
return nullptr;
}
@@ -179,7 +179,7 @@
}
void* mData;
ABinderRpc_ConnectionInfoProviderUserData_delete mOnDelete;
- // needs to be copyable for std::function, but we will never copy it
+ // needs to be copy-able for std::function, but we will never copy it
OnDeleteConnectionInfoHolder(const OnDeleteConnectionInfoHolder&) {
LOG_ALWAYS_FATAL("This object can't be copied!");
}
@@ -197,7 +197,7 @@
}
if (data && onDelete == nullptr) {
ALOGE("If a non-null data ptr is passed to ABinderRpc_Accessor_new, then a "
- "ABinderRpc_ConnectionInfoProviderUserData_delete callback must alse be passed to "
+ "ABinderRpc_ConnectionInfoProviderUserData_delete callback must also be passed to "
"delete "
"the data object once the ABinderRpc_Accessor is deleted.");
return nullptr;
@@ -302,9 +302,31 @@
}
}
+binder_status_t ABinderRpc_Accessor_delegateAccessor(const char* instance, AIBinder* accessor,
+ AIBinder** outDelegator) {
+ LOG_ALWAYS_FATAL_IF(outDelegator == nullptr, "The outDelegator argument is null");
+ if (instance == nullptr || accessor == nullptr) {
+ ALOGW("instance or accessor arguments to ABinderRpc_Accessor_delegateBinder are null");
+ *outDelegator = nullptr;
+ return STATUS_UNEXPECTED_NULL;
+ }
+ sp<IBinder> accessorBinder = accessor->getBinder();
+
+ sp<IBinder> delegator;
+ status_t status = android::delegateAccessor(String16(instance), accessorBinder, &delegator);
+ if (status != OK) {
+ return PruneStatusT(status);
+ }
+ sp<AIBinder> binder = ABpBinder::lookupOrCreateFromBinder(delegator);
+ // This AIBinder needs a strong ref to pass ownership to the caller
+ binder->incStrong(nullptr);
+ *outDelegator = binder.get();
+ return OK;
+}
+
ABinderRpc_ConnectionInfo* ABinderRpc_ConnectionInfo_new(const sockaddr* addr, socklen_t len) {
if (addr == nullptr || len < 0 || static_cast<size_t>(len) < sizeof(sa_family_t)) {
- ALOGE("Invalid arguments in Arpc_Connection_new");
+ ALOGE("Invalid arguments in ABinderRpc_Connection_new");
return nullptr;
}
// socklen_t was int32_t on 32-bit and uint32_t on 64 bit.
@@ -317,8 +339,9 @@
return nullptr;
}
sockaddr_vm vm = *reinterpret_cast<const sockaddr_vm*>(addr);
- LOG_ACCESSOR_DEBUG("Arpc_ConnectionInfo_new found AF_VSOCK. family %d, port %d, cid %d",
- vm.svm_family, vm.svm_port, vm.svm_cid);
+ LOG_ACCESSOR_DEBUG(
+ "ABinderRpc_ConnectionInfo_new found AF_VSOCK. family %d, port %d, cid %d",
+ vm.svm_family, vm.svm_port, vm.svm_cid);
return new ABinderRpc_ConnectionInfo(vm);
} else if (addr->sa_family == AF_UNIX) {
if (len != sizeof(sockaddr_un)) {
@@ -327,7 +350,7 @@
return nullptr;
}
sockaddr_un un = *reinterpret_cast<const sockaddr_un*>(addr);
- LOG_ACCESSOR_DEBUG("Arpc_ConnectionInfo_new found AF_UNIX. family %d, path %s",
+ LOG_ACCESSOR_DEBUG("ABinderRpc_ConnectionInfo_new found AF_UNIX. family %d, path %s",
un.sun_family, un.sun_path);
return new ABinderRpc_ConnectionInfo(un);
} else if (addr->sa_family == AF_INET) {
@@ -337,12 +360,14 @@
return nullptr;
}
sockaddr_in in = *reinterpret_cast<const sockaddr_in*>(addr);
- LOG_ACCESSOR_DEBUG("Arpc_ConnectionInfo_new found AF_INET. family %d, address %s, port %d",
- in.sin_family, inet_ntoa(in.sin_addr), ntohs(in.sin_port));
+ LOG_ACCESSOR_DEBUG(
+ "ABinderRpc_ConnectionInfo_new found AF_INET. family %d, address %s, port %d",
+ in.sin_family, inet_ntoa(in.sin_addr), ntohs(in.sin_port));
return new ABinderRpc_ConnectionInfo(in);
}
- ALOGE("ARpc APIs only support AF_VSOCK right now but the supplied sockadder::sa_family is: %hu",
+ ALOGE("ABinderRpc APIs only support AF_VSOCK right now but the supplied sockaddr::sa_family "
+ "is: %hu",
addr->sa_family);
return nullptr;
}
diff --git a/libs/binder/ndk/ibinder.cpp b/libs/binder/ndk/ibinder.cpp
index af280d3..ff31dd0 100644
--- a/libs/binder/ndk/ibinder.cpp
+++ b/libs/binder/ndk/ibinder.cpp
@@ -18,8 +18,10 @@
#include <android/binder_ibinder_platform.h>
#include <android/binder_stability.h>
#include <android/binder_status.h>
+#include <binder/Functional.h>
#include <binder/IPCThreadState.h>
#include <binder/IResultReceiver.h>
+#include <binder/Trace.h>
#if __has_include(<private/android_filesystem_config.h>)
#include <private/android_filesystem_config.h>
#endif
@@ -40,6 +42,23 @@
using ::android::String16;
using ::android::String8;
using ::android::wp;
+using ::android::binder::impl::make_scope_guard;
+using ::android::binder::impl::scope_guard;
+using ::android::binder::os::get_trace_enabled_tags;
+using ::android::binder::os::trace_begin;
+using ::android::binder::os::trace_end;
+
+// transaction codes for getInterfaceHash and getInterfaceVersion are defined
+// in file : system/tools/aidl/aidl.cpp
+static constexpr int kGetInterfaceVersionId = 0x00fffffe;
+static const char* kInterfaceVersion = "getInterfaceVersion";
+static constexpr int kGetInterfaceHashId = 0x00fffffd;
+static const char* kInterfaceHash = "getInterfaceHash";
+static const char* kNdkTrace = "AIDL::ndk::";
+static const char* kServerTrace = "::server";
+static const char* kClientTrace = "::client";
+static const char* kSeparator = "::";
+static const char* kUnknownCode = "Unknown_Transaction_Code:";
namespace ABBinderTag {
@@ -90,6 +109,51 @@
return sanitized;
}
+const std::string getMethodName(const AIBinder_Class* clazz, transaction_code_t code) {
+ // TODO(b/150155678) - Move getInterfaceHash and getInterfaceVersion to libbinder and remove
+ // hardcoded cases.
+ if (code <= clazz->getTransactionCodeToFunctionLength() && code >= FIRST_CALL_TRANSACTION) {
+ // Codes have FIRST_CALL_TRANSACTION as added offset. Subtract to access function name
+ return clazz->getFunctionName(code);
+ } else if (code == kGetInterfaceVersionId) {
+ return kInterfaceVersion;
+ } else if (code == kGetInterfaceHashId) {
+ return kInterfaceHash;
+ }
+ return kUnknownCode + std::to_string(code);
+}
+
+const std::string getTraceSectionName(const AIBinder_Class* clazz, transaction_code_t code,
+ bool isServer) {
+ if (clazz == nullptr) {
+ ALOGE("class associated with binder is null. Class is needed to add trace with interface "
+ "name and function name");
+ return kNdkTrace;
+ }
+
+ const std::string descriptor = clazz->getInterfaceDescriptorUtf8();
+ const std::string methodName = getMethodName(clazz, code);
+
+ size_t traceSize =
+ strlen(kNdkTrace) + descriptor.size() + strlen(kSeparator) + methodName.size();
+ traceSize += isServer ? strlen(kServerTrace) : strlen(kClientTrace);
+
+ std::string trace;
+ // reserve to avoid repeated allocations
+ trace.reserve(traceSize);
+
+ trace += kNdkTrace;
+ trace += clazz->getInterfaceDescriptorUtf8();
+ trace += kSeparator;
+ trace += methodName;
+ trace += isServer ? kServerTrace : kClientTrace;
+
+ LOG_ALWAYS_FATAL_IF(trace.size() != traceSize, "Trace size mismatch. Expected %zu, got %zu",
+ traceSize, trace.size());
+
+ return trace;
+}
+
bool AIBinder::associateClass(const AIBinder_Class* clazz) {
if (clazz == nullptr) return false;
@@ -203,6 +267,17 @@
status_t ABBinder::onTransact(transaction_code_t code, const Parcel& data, Parcel* reply,
binder_flags_t flags) {
+ std::string sectionName;
+ bool tracingEnabled = get_trace_enabled_tags() & ATRACE_TAG_AIDL;
+ if (tracingEnabled) {
+ sectionName = getTraceSectionName(getClass(), code, true /*isServer*/);
+ trace_begin(ATRACE_TAG_AIDL, sectionName.c_str());
+ }
+
+ scope_guard guard = make_scope_guard([&]() {
+ if (tracingEnabled) trace_end(ATRACE_TAG_AIDL);
+ });
+
if (isUserCommand(code)) {
if (getClass()->writeHeader && !data.checkInterface(this)) {
return STATUS_BAD_TYPE;
@@ -385,6 +460,31 @@
mInterfaceDescriptor(interfaceDescriptor),
mWideInterfaceDescriptor(interfaceDescriptor) {}
+bool AIBinder_Class::setTransactionCodeMap(const char** transactionCodeMap, size_t length) {
+ if (mTransactionCodeToFunction != nullptr) {
+ ALOGE("mTransactionCodeToFunction is already set!");
+ return false;
+ }
+ mTransactionCodeToFunction = transactionCodeMap;
+ mTransactionCodeToFunctionLength = length;
+ return true;
+}
+
+const char* AIBinder_Class::getFunctionName(transaction_code_t code) const {
+ if (mTransactionCodeToFunction == nullptr) {
+ ALOGE("mTransactionCodeToFunction is not set!");
+ return nullptr;
+ }
+
+ if (code < FIRST_CALL_TRANSACTION ||
+ code - FIRST_CALL_TRANSACTION >= mTransactionCodeToFunctionLength) {
+ ALOGE("Function name for requested code not found!");
+ return nullptr;
+ }
+
+ return mTransactionCodeToFunction[code - FIRST_CALL_TRANSACTION];
+}
+
AIBinder_Class* AIBinder_Class_define(const char* interfaceDescriptor,
AIBinder_Class_onCreate onCreate,
AIBinder_Class_onDestroy onDestroy,
@@ -404,6 +504,24 @@
clazz->onDump = onDump;
}
+void AIBinder_Class_setTransactionCodeToFunctionNameMap(AIBinder_Class* clazz,
+ const char** transactionCodeToFunction,
+ size_t length) {
+ LOG_ALWAYS_FATAL_IF(clazz == nullptr || transactionCodeToFunction == nullptr,
+ "Valid clazz and transactionCodeToFunction are needed to set code to "
+ "function mapping.");
+ LOG_ALWAYS_FATAL_IF(!clazz->setTransactionCodeMap(transactionCodeToFunction, length),
+ "Failed to set transactionCodeToFunction to clazz! Is "
+ "transactionCodeToFunction already set?");
+}
+
+const char* AIBinder_Class_getFunctionName(AIBinder_Class* clazz, transaction_code_t code) {
+ LOG_ALWAYS_FATAL_IF(
+ clazz == nullptr,
+ "Valid clazz is needed to get function name for requested transaction code");
+ return clazz->getFunctionName(code);
+}
+
void AIBinder_Class_disableInterfaceTokenHeader(AIBinder_Class* clazz) {
LOG_ALWAYS_FATAL_IF(clazz == nullptr, "disableInterfaceTokenHeader requires non-null clazz");
@@ -734,6 +852,19 @@
binder_status_t AIBinder_transact(AIBinder* binder, transaction_code_t code, AParcel** in,
AParcel** out, binder_flags_t flags) {
+ const AIBinder_Class* clazz = binder ? binder->getClass() : nullptr;
+
+ std::string sectionName;
+ bool tracingEnabled = get_trace_enabled_tags() & ATRACE_TAG_AIDL;
+ if (tracingEnabled) {
+ sectionName = getTraceSectionName(clazz, code, false /*isServer*/);
+ trace_begin(ATRACE_TAG_AIDL, sectionName.c_str());
+ }
+
+ scope_guard guard = make_scope_guard([&]() {
+ if (tracingEnabled) trace_end(ATRACE_TAG_AIDL);
+ });
+
if (in == nullptr) {
ALOGE("%s: requires non-null in parameter", __func__);
return STATUS_UNEXPECTED_NULL;
@@ -872,4 +1003,4 @@
"AIBinder_setInheritRt must be called on a local binder");
localBinder->setInheritRt(inheritRt);
-}
+}
\ No newline at end of file
diff --git a/libs/binder/ndk/ibinder_internal.h b/libs/binder/ndk/ibinder_internal.h
index f5b738c..a93dc1f 100644
--- a/libs/binder/ndk/ibinder_internal.h
+++ b/libs/binder/ndk/ibinder_internal.h
@@ -132,6 +132,9 @@
const ::android::String16& getInterfaceDescriptor() const { return mWideInterfaceDescriptor; }
const char* getInterfaceDescriptorUtf8() const { return mInterfaceDescriptor.c_str(); }
+ bool setTransactionCodeMap(const char** transactionCodeMap, size_t transactionCodeMapSize);
+ const char* getFunctionName(transaction_code_t code) const;
+ size_t getTransactionCodeToFunctionLength() const { return mTransactionCodeToFunctionLength; }
// whether a transaction header should be written
bool writeHeader = true;
@@ -151,6 +154,10 @@
// This must be a String16 since BBinder virtual getInterfaceDescriptor returns a reference to
// one.
const ::android::String16 mWideInterfaceDescriptor;
+ // Array which holds names of the functions
+ const char** mTransactionCodeToFunction = nullptr;
+ // length of mmTransactionCodeToFunctionLength array
+ size_t mTransactionCodeToFunctionLength = 0;
};
// Ownership is like this (when linked to death):
diff --git a/libs/binder/ndk/include_cpp/android/binder_interface_utils.h b/libs/binder/ndk/include_cpp/android/binder_interface_utils.h
index af56bf0..0ad110e 100644
--- a/libs/binder/ndk/include_cpp/android/binder_interface_utils.h
+++ b/libs/binder/ndk/include_cpp/android/binder_interface_utils.h
@@ -30,6 +30,17 @@
#include <android/binder_auto_utils.h>
#include <android/binder_ibinder.h>
+#if defined(__ANDROID_VENDOR_API__)
+#include <android/llndk-versioning.h>
+#elif !defined(API_LEVEL_AT_LEAST)
+#if defined(__BIONIC__)
+#define API_LEVEL_AT_LEAST(sdk_api_level, vendor_api_level) \
+ (__builtin_available(android sdk_api_level, *))
+#else
+#define API_LEVEL_AT_LEAST(sdk_api_level, vendor_api_level) (true)
+#endif // __BIONIC__
+#endif // __ANDROID_VENDOR_API__
+
#if __has_include(<android/binder_shell.h>)
#include <android/binder_shell.h>
#define HAS_BINDER_SHELL_COMMAND
@@ -164,6 +175,10 @@
* Helper method to create a class
*/
static inline AIBinder_Class* defineClass(const char* interfaceDescriptor,
+ AIBinder_Class_onTransact onTransact,
+ const char** codeToFunction, size_t functionCount);
+
+ static inline AIBinder_Class* defineClass(const char* interfaceDescriptor,
AIBinder_Class_onTransact onTransact);
private:
@@ -256,6 +271,13 @@
AIBinder_Class* ICInterface::defineClass(const char* interfaceDescriptor,
AIBinder_Class_onTransact onTransact) {
+
+ return defineClass(interfaceDescriptor, onTransact, nullptr, 0);
+}
+
+AIBinder_Class* ICInterface::defineClass(const char* interfaceDescriptor,
+ AIBinder_Class_onTransact onTransact,
+ const char** codeToFunction, size_t functionCount) {
AIBinder_Class* clazz = AIBinder_Class_define(interfaceDescriptor, ICInterfaceData::onCreate,
ICInterfaceData::onDestroy, onTransact);
if (clazz == nullptr) {
@@ -274,6 +296,19 @@
AIBinder_Class_setHandleShellCommand(clazz, ICInterfaceData::handleShellCommand);
}
#endif
+
+#if defined(__ANDROID_UNAVAILABLE_SYMBOLS_ARE_WEAK__) || __ANDROID_API__ >= 36
+ if API_LEVEL_AT_LEAST (36, 202504) {
+ if (codeToFunction != nullptr &&
+ (&AIBinder_Class_setTransactionCodeToFunctionNameMap != nullptr)) {
+ AIBinder_Class_setTransactionCodeToFunctionNameMap(clazz, codeToFunction,
+ functionCount);
+ }
+ }
+#else
+ (void)codeToFunction;
+ (void)functionCount;
+#endif // defined(__ANDROID_UNAVAILABLE_SYMBOLS_ARE_WEAK__) || __ANDROID_API__ >= 36
return clazz;
}
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 c1d0e9f..83976b3 100644
--- a/libs/binder/ndk/include_cpp/android/persistable_bundle_aidl.h
+++ b/libs/binder/ndk/include_cpp/android/persistable_bundle_aidl.h
@@ -22,8 +22,8 @@
#include <set>
#include <sstream>
-// Include llndk-versioning.h only for vendor build as it is not available for NDK headers.
-#if defined(__ANDROID_VENDOR__)
+// Include llndk-versioning.h only for non-system build as it is not available for NDK headers.
+#if defined(__ANDROID_VENDOR_API__)
#include <android/llndk-versioning.h>
#elif !defined(API_LEVEL_AT_LEAST)
#if defined(__BIONIC__)
@@ -32,7 +32,7 @@
#else
#define API_LEVEL_AT_LEAST(sdk_api_level, vendor_api_level) (true)
#endif // __BIONIC__
-#endif // __ANDROID_VENDOR__
+#endif // __ANDROID_VENDOR_API__
namespace aidl::android::os {
diff --git a/libs/binder/ndk/include_ndk/android/binder_ibinder.h b/libs/binder/ndk/include_ndk/android/binder_ibinder.h
index 72d255e..bd46c47 100644
--- a/libs/binder/ndk/include_ndk/android/binder_ibinder.h
+++ b/libs/binder/ndk/include_ndk/android/binder_ibinder.h
@@ -219,6 +219,50 @@
void AIBinder_Class_setOnDump(AIBinder_Class* clazz, AIBinder_onDump onDump) __INTRODUCED_IN(29);
/**
+ * Associates a mapping of transaction codes(transaction_code_t) to function names for the given
+ * class.
+ *
+ * Trace messages will use the provided names instead of bare integer codes when set. If not set by
+ * this function, trace messages will only be identified by the bare code. This should be called one
+ * time during clazz initialization. clazz is defined using AIBinder_Class_define and
+ * transactionCodeToFunctionMap should have same scope as clazz. Resetting/clearing the
+ * transactionCodeToFunctionMap is not allowed. Passing null for either clazz or
+ * transactionCodeToFunctionMap will abort.
+ *
+ * Available since API level 36.
+ *
+ * \param clazz class which should use this transaction to code function map.
+ * \param transactionCodeToFunctionMap array of function names indexed by transaction code.
+ * Transaction codes start from 1, functions with transaction code 1 will correspond to index 0 in
+ * transactionCodeToFunctionMap. When defining methods, transaction codes are expected to be
+ * contiguous, and this is required for maximum memory efficiency.
+ * You can use nullptr if certain transaction codes are not used. Lifetime should be same as clazz.
+ * \param length number of elements in the transactionCodeToFunctionMap
+ */
+void AIBinder_Class_setTransactionCodeToFunctionNameMap(AIBinder_Class* clazz,
+ const char** transactionCodeToFunctionMap,
+ size_t length) __INTRODUCED_IN(36);
+
+/**
+ * Get function name associated with transaction code for given class
+ *
+ * This function returns function name associated with provided transaction code for given class.
+ * AIBinder_Class_setTransactionCodeToFunctionNameMap should be called first to associate function
+ * to transaction code mapping.
+ *
+ * Available since API level 36.
+ *
+ * \param clazz class for which function name is requested
+ * \param transactionCode transaction_code_t for which function name is requested.
+ *
+ * \return function name in form of const char* if transaction code is valid for given class.
+ * The value returned is valid for the lifetime of clazz. if transaction code is invalid or
+ * transactionCodeToFunctionMap is not set, nullptr is returned.
+ */
+const char* AIBinder_Class_getFunctionName(AIBinder_Class* clazz, transaction_code_t code)
+ __INTRODUCED_IN(36);
+
+/**
* This tells users of this class not to use a transaction header. By default, libbinder_ndk users
* read/write transaction headers implicitly (in the SDK, this must be manually written by
* android.os.Parcel#writeInterfaceToken, and it is read/checked with
diff --git a/libs/binder/ndk/include_platform/android/binder_rpc.h b/libs/binder/ndk/include_platform/android/binder_rpc.h
index 4c5471f..9fe5d78 100644
--- a/libs/binder/ndk/include_platform/android/binder_rpc.h
+++ b/libs/binder/ndk/include_platform/android/binder_rpc.h
@@ -22,6 +22,22 @@
__BEGIN_DECLS
/**
+ * @defgroup ABinderRpc Binder RPC
+ *
+ * This set of APIs makes it possible for a process to use the AServiceManager
+ * APIs to get binder objects for services that are available over sockets
+ * instead of the traditional kernel binder with the extra ServiceManager
+ * process.
+ *
+ * These APIs are used to supply libbinder with enough information to create
+ * and manage the socket connections underneath the ServiceManager APIs so the
+ * clients do not need to know the service implementation details or what
+ * transport they use for communication.
+ *
+ * @{
+ */
+
+/**
* This represents an IAccessor implementation from libbinder that is
* responsible for providing a pre-connected socket file descriptor for a
* specific service. The service is an RpcServer and the pre-connected socket is
@@ -66,7 +82,8 @@
* libbinder.
*
* \param instance name of the service like
- * `android.hardware.vibrator.IVibrator/default`
+ * `android.hardware.vibrator.IVibrator/default`. This string must remain
+ * valid and unchanged for the duration of this function call.
* \param data the data that was associated with this instance when the callback
* was registered.
* \return The ABinderRpc_Accessor associated with the service `instance`. This
@@ -103,13 +120,15 @@
* instance is being registered that was previously registered, this call
* will fail and the ABinderRpc_AccessorProviderUserData_deleteCallback
* will be called to clean up the data.
+ * This array of strings must remain valid and unchanged for the duration
+ * of this function call.
* \param number of instances in the instances array.
* \param data pointer that is passed to the ABinderRpc_AccessorProvider callback.
* IMPORTANT: The ABinderRpc_AccessorProvider now OWNS that object that data
* points to. It can be used as necessary in the callback. The data MUST
* remain valid for the lifetime of the provider callback.
* Do not attempt to give ownership of the same object to different
- * providers throguh multiple calls to this function because the first
+ * providers through multiple calls to this function because the first
* one to be deleted will call the onDelete callback.
* \param onDelete callback used to delete the objects that `data` points to.
* This is called after ABinderRpc_AccessorProvider is guaranteed to never be
@@ -151,8 +170,9 @@
* connect to a socket that a given service is listening on. This is needed to
* create an ABinderRpc_Accessor so it can connect to these services.
*
- * \param instance name of the service to connect to
- * \param data userdata for this callback. The pointer is provided in
+ * \param instance name of the service to connect to. This string must remain
+ * valid and unchanged for the duration of this function call.
+ * \param data user data for this callback. The pointer is provided in
* ABinderRpc_Accessor_new.
* \return ABinderRpc_ConnectionInfo with socket connection information for `instance`
*/
@@ -177,7 +197,9 @@
* that can use the info from the ABinderRpc_ConnectionInfoProvider to connect to a
* socket that the service with `instance` name is listening to.
*
- * \param instance name of the service that is listening on the socket
+ * \param instance name of the service that is listening on the socket. This
+ * string must remain valid and unchanged for the duration of this
+ * function call.
* \param provider callback that can get the socket connection information for the
* instance. This connection information may be dynamic, so the
* provider will be called any time a new connection is required.
@@ -219,24 +241,59 @@
/**
* Return the ABinderRpc_Accessor associated with an AIBinder. The instance must match
- * the ABinderRpc_Accessor implementation, and the AIBinder must a proxy binder for a
- * remote service (ABpBinder).
- * This can be used when receivng an AIBinder from another process that the
+ * the ABinderRpc_Accessor implementation.
+ * This can be used when receiving an AIBinder from another process that the
* other process obtained from ABinderRpc_Accessor_asBinder.
*
* \param instance name of the service that the Accessor is responsible for.
- * \param accessorBinder proxy binder from another processes ABinderRpc_Accessor.
+ * This string must remain valid and unchanged for the duration of this
+ * function call.
+ * \param accessorBinder proxy binder from another process's ABinderRpc_Accessor.
+ * This function preserves the refcount of this binder object and the
+ * caller still owns it.
* \return ABinderRpc_Accessor representing the other processes ABinderRpc_Accessor
- * implementation. This function does not take ownership of the
- * ABinderRpc_Accessor (so the caller needs to delete with
- * ABinderRpc_Accessor_delete), and it preserves the recount of the bidner
- * object.
+ * implementation. The caller owns this ABinderRpc_Accessor instance and
+ * is responsible for deleting it with ABinderRpc_Accessor_delete or
+ * passing ownership of it elsewhere, like returning it through
+ * ABinderRpc_AccessorProvider_getAccessorCallback.
+ * nullptr on error when the accessorBinder is not a valid binder from
+ * an IAccessor implementation or the IAccessor implementation is not
+ * associated with the provided instance.
*/
ABinderRpc_Accessor* _Nullable ABinderRpc_Accessor_fromBinder(const char* _Nonnull instance,
AIBinder* _Nonnull accessorBinder)
__INTRODUCED_IN(36);
/**
+ * Wrap an ABinderRpc_Accessor proxy binder with a delegator binder.
+ *
+ * The IAccessorDelegator binder delegates all calls to the proxy binder.
+ *
+ * This is required only in very specific situations when the process that has
+ * permissions to connect the to RPC service's socket and create the FD for it
+ * is in a separate process from this process that wants to serve the Accessor
+ * binder and the communication between these two processes is binder RPC. This
+ * is needed because the binder passed over the binder RPC connection can not be
+ * used as a kernel binder, and needs to be wrapped by a kernel binder that can
+ * then be registered with service manager.
+ *
+ * \param instance name of the service associated with the Accessor
+ * \param binder the AIBinder* from the ABinderRpc_Accessor from the
+ * ABinderRpc_Accessor_asBinder. The other process across the binder RPC
+ * connection will have called this and passed the AIBinder* across a
+ * binder interface to the process calling this function.
+ * \param outDelegator the AIBinder* for the kernel binder that wraps the
+ * 'binder' argument and delegates all calls to it. The caller now owns
+ * this object with one strong ref count and is responsible for removing
+ * that ref count with with AIBinder_decStrong when the caller wishes to
+ * drop the reference.
+ */
+binder_status_t ABinderRpc_Accessor_delegateAccessor(const char* _Nonnull instance,
+ AIBinder* _Nonnull binder,
+ AIBinder* _Nullable* _Nonnull outDelegator)
+ __INTRODUCED_IN(36);
+
+/**
* Create a new ABinderRpc_ConnectionInfo with sockaddr. This can be supported socket
* types like sockaddr_vm (vsock) and sockaddr_un (Unix Domain Sockets).
*
@@ -258,4 +315,6 @@
*/
void ABinderRpc_ConnectionInfo_delete(ABinderRpc_ConnectionInfo* _Nonnull info) __INTRODUCED_IN(36);
+/** @} */
+
__END_DECLS
diff --git a/libs/binder/ndk/libbinder_ndk.map.txt b/libs/binder/ndk/libbinder_ndk.map.txt
index c9e669e..4d691f8 100644
--- a/libs/binder/ndk/libbinder_ndk.map.txt
+++ b/libs/binder/ndk/libbinder_ndk.map.txt
@@ -250,9 +250,14 @@
LIBBINDER_NDK36 { # introduced=36
global:
+ AIBinder_Class_setTransactionCodeToFunctionNameMap;
+ AIBinder_Class_setTransactionCodeToFunctionNameMap; # llndk=202504
+ AIBinder_Class_getFunctionName;
+ AIBinder_Class_getFunctionName; # llndk=202504
ABinderRpc_registerAccessorProvider; # systemapi
ABinderRpc_unregisterAccessorProvider; # systemapi
ABinderRpc_Accessor_new; # systemapi
+ ABinderRpc_Accessor_delegateAccessor; #systemapi
ABinderRpc_Accessor_delete; # systemapi
ABinderRpc_Accessor_asBinder; # systemapi
ABinderRpc_Accessor_fromBinder; # systemapi
diff --git a/libs/binder/ndk/tests/libbinder_ndk_unit_test.cpp b/libs/binder/ndk/tests/libbinder_ndk_unit_test.cpp
index 3cd2b9a..e5a3da4 100644
--- a/libs/binder/ndk/tests/libbinder_ndk_unit_test.cpp
+++ b/libs/binder/ndk/tests/libbinder_ndk_unit_test.cpp
@@ -1108,6 +1108,37 @@
EXPECT_EQ(deleteCount, 0);
}
+void* EmptyOnCreate(void* args) {
+ return args;
+}
+void EmptyOnDestroy(void* /*userData*/) {}
+binder_status_t EmptyOnTransact(AIBinder* /*binder*/, transaction_code_t /*code*/,
+ const AParcel* /*in*/, AParcel* /*out*/) {
+ return STATUS_OK;
+}
+
+TEST(NdkBinder_DeathTest, SetCodeMapTwice) {
+ const char* codeToFunction1[] = {"function-1", "function-2", "function-3"};
+ const char* codeToFunction2[] = {"function-4", "function-5"};
+ const char* interfaceName = "interface_descriptor";
+ AIBinder_Class* clazz =
+ AIBinder_Class_define(interfaceName, EmptyOnCreate, EmptyOnDestroy, EmptyOnTransact);
+ AIBinder_Class_setTransactionCodeToFunctionNameMap(clazz, codeToFunction1, 3);
+ // Reset/clear is not allowed
+ EXPECT_DEATH(AIBinder_Class_setTransactionCodeToFunctionNameMap(clazz, codeToFunction2, 2), "");
+}
+
+TEST(NdkBinder_DeathTest, SetNullCodeMap) {
+ const char* codeToFunction[] = {"function-1", "function-2", "function-3"};
+ const char* interfaceName = "interface_descriptor";
+ AIBinder_Class* clazz =
+ AIBinder_Class_define(interfaceName, EmptyOnCreate, EmptyOnDestroy, EmptyOnTransact);
+ EXPECT_DEATH(AIBinder_Class_setTransactionCodeToFunctionNameMap(nullptr, codeToFunction, 3),
+ "");
+ EXPECT_DEATH(AIBinder_Class_setTransactionCodeToFunctionNameMap(clazz, nullptr, 0), "");
+ EXPECT_DEATH(AIBinder_Class_setTransactionCodeToFunctionNameMap(nullptr, nullptr, 0), "");
+}
+
int main(int argc, char* argv[]) {
::testing::InitGoogleTest(&argc, argv);
diff --git a/libs/binder/rust/Android.bp b/libs/binder/rust/Android.bp
index 2deb254..4545d7b 100644
--- a/libs/binder/rust/Android.bp
+++ b/libs/binder/rust/Android.bp
@@ -15,6 +15,8 @@
"libbinder_ndk_sys",
"libdowncast_rs",
"liblibc",
+ "liblog_rust",
+ "libnix",
],
host_supported: true,
vendor_available: true,
@@ -79,6 +81,9 @@
shared_libs: [
"libbinder_ndk",
],
+ rustlibs: [
+ "liblibc",
+ ],
host_supported: true,
vendor_available: true,
product_available: true,
@@ -129,9 +134,18 @@
// rustified
"libbinder_ndk_bindgen_flags.txt",
],
+ bindgen_flags: [
+ "--blocklist-type",
+ "sockaddr",
+ "--raw-line",
+ "use libc::sockaddr;",
+ ],
shared_libs: [
"libbinder_ndk",
],
+ rustlibs: [
+ "liblibc",
+ ],
host_supported: true,
vendor_available: true,
product_available: true,
@@ -185,6 +199,8 @@
"libbinder_ndk_sys",
"libdowncast_rs",
"liblibc",
+ "liblog_rust",
+ "libnix",
],
}
@@ -196,4 +212,7 @@
auto_gen_config: true,
clippy_lints: "none",
lints: "none",
+ rustlibs: [
+ "liblibc",
+ ],
}
diff --git a/libs/binder/rust/src/binder.rs b/libs/binder/rust/src/binder.rs
index 9a252b8..23026e5 100644
--- a/libs/binder/rust/src/binder.rs
+++ b/libs/binder/rust/src/binder.rs
@@ -136,6 +136,31 @@
}
}
+/// Same as `Stability`, but in the form of a trait. Used when the stability should be encoded in
+/// the type.
+///
+/// When/if the `adt_const_params` Rust feature is stabilized, this could be replace by using
+/// `Stability` directly with const generics.
+pub trait StabilityType {
+ /// The `Stability` represented by this type.
+ const VALUE: Stability;
+}
+
+/// `Stability::Local`.
+#[derive(Debug)]
+pub enum LocalStabilityType {}
+/// `Stability::Vintf`.
+#[derive(Debug)]
+pub enum VintfStabilityType {}
+
+impl StabilityType for LocalStabilityType {
+ const VALUE: Stability = Stability::Local;
+}
+
+impl StabilityType for VintfStabilityType {
+ const VALUE: Stability = Stability::Vintf;
+}
+
/// A local service that can be remotable via Binder.
///
/// An object that implement this interface made be made into a Binder service
diff --git a/libs/binder/rust/src/lib.rs b/libs/binder/rust/src/lib.rs
index e70f4f0..f7f3f35 100644
--- a/libs/binder/rust/src/lib.rs
+++ b/libs/binder/rust/src/lib.rs
@@ -104,6 +104,8 @@
mod service;
#[cfg(not(trusty))]
mod state;
+#[cfg(not(any(android_vendor, android_vndk)))]
+mod system_only;
use binder_ndk_sys as sys;
@@ -120,6 +122,8 @@
};
#[cfg(not(trusty))]
pub use state::{ProcessState, ThreadState};
+#[cfg(not(any(android_vendor, android_vndk)))]
+pub use system_only::{delegate_accessor, Accessor, ConnectionInfo};
/// Binder result containing a [`Status`] on error.
pub type Result<T> = std::result::Result<T, Status>;
@@ -128,9 +132,10 @@
/// without AIDL.
pub mod binder_impl {
pub use crate::binder::{
- IBinderInternal, InterfaceClass, Remotable, Stability, ToAsyncInterface, ToSyncInterface,
- TransactionCode, TransactionFlags, FIRST_CALL_TRANSACTION, FLAG_CLEAR_BUF, FLAG_ONEWAY,
- FLAG_PRIVATE_LOCAL, LAST_CALL_TRANSACTION,
+ IBinderInternal, InterfaceClass, LocalStabilityType, Remotable, Stability, StabilityType,
+ ToAsyncInterface, ToSyncInterface, TransactionCode, TransactionFlags, VintfStabilityType,
+ FIRST_CALL_TRANSACTION, FLAG_CLEAR_BUF, FLAG_ONEWAY, FLAG_PRIVATE_LOCAL,
+ LAST_CALL_TRANSACTION,
};
pub use crate::binder_async::BinderAsyncRuntime;
pub use crate::error::status_t;
diff --git a/libs/binder/rust/src/parcel/parcelable_holder.rs b/libs/binder/rust/src/parcel/parcelable_holder.rs
index f906113..87b42ab 100644
--- a/libs/binder/rust/src/parcel/parcelable_holder.rs
+++ b/libs/binder/rust/src/parcel/parcelable_holder.rs
@@ -15,6 +15,7 @@
*/
use crate::binder::Stability;
+use crate::binder::StabilityType;
use crate::error::StatusCode;
use crate::parcel::{
BorrowedParcel, Deserialize, Parcel, Parcelable, Serialize, NON_NULL_PARCELABLE_FLAG,
@@ -60,7 +61,7 @@
/// `Send` nor `Sync`), mainly because it internally contains
/// a `Parcel` which in turn is not thread-safe.
#[derive(Debug)]
-pub struct ParcelableHolder {
+pub struct ParcelableHolder<STABILITY: StabilityType> {
// This is a `Mutex` because of `get_parcelable`
// which takes `&self` for consistency with C++.
// We could make `get_parcelable` take a `&mut self`
@@ -68,13 +69,17 @@
// improvement, but then callers would require a mutable
// `ParcelableHolder` even for that getter method.
data: Mutex<ParcelableHolderData>,
- stability: Stability,
+
+ _stability_phantom: std::marker::PhantomData<STABILITY>,
}
-impl ParcelableHolder {
+impl<STABILITY: StabilityType> ParcelableHolder<STABILITY> {
/// Construct a new `ParcelableHolder` with the given stability.
- pub fn new(stability: Stability) -> Self {
- Self { data: Mutex::new(ParcelableHolderData::Empty), stability }
+ pub fn new() -> Self {
+ Self {
+ data: Mutex::new(ParcelableHolderData::Empty),
+ _stability_phantom: Default::default(),
+ }
}
/// Reset the contents of this `ParcelableHolder`.
@@ -91,7 +96,7 @@
where
T: Any + Parcelable + ParcelableMetadata + std::fmt::Debug + Send + Sync,
{
- if self.stability > p.get_stability() {
+ if STABILITY::VALUE > p.get_stability() {
return Err(StatusCode::BAD_VALUE);
}
@@ -157,30 +162,36 @@
/// Return the stability value of this object.
pub fn get_stability(&self) -> Stability {
- self.stability
+ STABILITY::VALUE
}
}
-impl Clone for ParcelableHolder {
- fn clone(&self) -> ParcelableHolder {
+impl<STABILITY: StabilityType> Default for ParcelableHolder<STABILITY> {
+ fn default() -> Self {
+ Self::new()
+ }
+}
+
+impl<STABILITY: StabilityType> Clone for ParcelableHolder<STABILITY> {
+ fn clone(&self) -> Self {
ParcelableHolder {
data: Mutex::new(self.data.lock().unwrap().clone()),
- stability: self.stability,
+ _stability_phantom: Default::default(),
}
}
}
-impl Serialize for ParcelableHolder {
+impl<STABILITY: StabilityType> Serialize for ParcelableHolder<STABILITY> {
fn serialize(&self, parcel: &mut BorrowedParcel<'_>) -> Result<(), StatusCode> {
parcel.write(&NON_NULL_PARCELABLE_FLAG)?;
self.write_to_parcel(parcel)
}
}
-impl Deserialize for ParcelableHolder {
+impl<STABILITY: StabilityType> Deserialize for ParcelableHolder<STABILITY> {
type UninitType = Self;
fn uninit() -> Self::UninitType {
- Self::new(Default::default())
+ Self::new()
}
fn from_init(value: Self) -> Self::UninitType {
value
@@ -191,16 +202,16 @@
if status == NULL_PARCELABLE_FLAG {
Err(StatusCode::UNEXPECTED_NULL)
} else {
- let mut parcelable = ParcelableHolder::new(Default::default());
+ let mut parcelable = Self::new();
parcelable.read_from_parcel(parcel)?;
Ok(parcelable)
}
}
}
-impl Parcelable for ParcelableHolder {
+impl<STABILITY: StabilityType> Parcelable for ParcelableHolder<STABILITY> {
fn write_to_parcel(&self, parcel: &mut BorrowedParcel<'_>) -> Result<(), StatusCode> {
- parcel.write(&self.stability)?;
+ parcel.write(&STABILITY::VALUE)?;
let mut data = self.data.lock().unwrap();
match *data {
@@ -236,7 +247,7 @@
}
fn read_from_parcel(&mut self, parcel: &BorrowedParcel<'_>) -> Result<(), StatusCode> {
- if self.stability != parcel.read()? {
+ if self.get_stability() != parcel.read()? {
return Err(StatusCode::BAD_VALUE);
}
diff --git a/libs/binder/rust/src/system_only.rs b/libs/binder/rust/src/system_only.rs
new file mode 100644
index 0000000..08582ab
--- /dev/null
+++ b/libs/binder/rust/src/system_only.rs
@@ -0,0 +1,213 @@
+/*
+ * 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.
+ */
+
+use crate::binder::AsNative;
+use crate::error::{status_result, Result};
+use crate::proxy::SpIBinder;
+use crate::sys;
+
+use std::ffi::{c_void, CStr, CString};
+use std::os::raw::c_char;
+
+use libc::sockaddr;
+use nix::sys::socket::{SockaddrLike, UnixAddr, VsockAddr};
+use std::sync::Arc;
+use std::{fmt, ptr};
+
+/// Rust wrapper around ABinderRpc_Accessor objects for RPC binder service management.
+///
+/// Dropping the `Accessor` will drop the underlying object and the binder it owns.
+pub struct Accessor {
+ accessor: *mut sys::ABinderRpc_Accessor,
+}
+
+impl fmt::Debug for Accessor {
+ fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+ write!(f, "ABinderRpc_Accessor({:p})", self.accessor)
+ }
+}
+
+/// Socket connection info required for libbinder to connect to a service.
+#[derive(Debug, Clone, Copy, PartialEq, Eq)]
+pub enum ConnectionInfo {
+ /// For vsock connection
+ Vsock(VsockAddr),
+ /// For unix domain socket connection
+ Unix(UnixAddr),
+}
+
+/// Safety: A `Accessor` is a wrapper around `ABinderRpc_Accessor` which is
+/// `Sync` and `Send`. As
+/// `ABinderRpc_Accessor` is threadsafe, this structure is too.
+/// The Fn owned the Accessor has `Sync` and `Send` properties
+unsafe impl Send for Accessor {}
+
+/// Safety: A `Accessor` is a wrapper around `ABinderRpc_Accessor` which is
+/// `Sync` and `Send`. As `ABinderRpc_Accessor` is threadsafe, this structure is too.
+/// The Fn owned the Accessor has `Sync` and `Send` properties
+unsafe impl Sync for Accessor {}
+
+impl Accessor {
+ /// Create a new accessor that will call the given callback when its
+ /// connection info is required.
+ /// The callback object and all objects it captures are owned by the Accessor
+ /// and will be deleted some time after the Accessor is Dropped. If the callback
+ /// is being called when the Accessor is Dropped, the callback will not be deleted
+ /// immediately.
+ pub fn new<F>(instance: &str, callback: F) -> Accessor
+ where
+ F: Fn(&str) -> Option<ConnectionInfo> + Send + Sync + 'static,
+ {
+ let callback: *mut c_void = Arc::into_raw(Arc::new(callback)) as *mut c_void;
+ let inst = CString::new(instance).unwrap();
+
+ // Safety: The function pointer is a valid connection_info callback.
+ // This call returns an owned `ABinderRpc_Accessor` pointer which
+ // must be destroyed via `ABinderRpc_Accessor_delete` when no longer
+ // needed.
+ // When the underlying ABinderRpc_Accessor is deleted, it will call
+ // the cookie_decr_refcount callback to release its strong ref.
+ let accessor = unsafe {
+ sys::ABinderRpc_Accessor_new(
+ inst.as_ptr(),
+ Some(Self::connection_info::<F>),
+ callback,
+ Some(Self::cookie_decr_refcount::<F>),
+ )
+ };
+
+ Accessor { accessor }
+ }
+
+ /// Get the underlying binder for this Accessor for when it needs to be either
+ /// registered with service manager or sent to another process.
+ pub fn as_binder(&self) -> Option<SpIBinder> {
+ // Safety: `ABinderRpc_Accessor_asBinder` returns either a null pointer or a
+ // valid pointer to an owned `AIBinder`. Either of these values is safe to
+ // pass to `SpIBinder::from_raw`.
+ unsafe { SpIBinder::from_raw(sys::ABinderRpc_Accessor_asBinder(self.accessor)) }
+ }
+
+ /// Callback invoked from C++ when the connection info is needed.
+ ///
+ /// # Safety
+ ///
+ /// The `instance` parameter must be a non-null pointer to a valid C string for
+ /// CStr::from_ptr. The memory must contain a valid null terminator at the end of
+ /// the string within isize::MAX from the pointer. The memory must not be mutated for
+ /// the duration of this function call and must be valid for reads from the pointer
+ /// to the null terminator.
+ /// The `cookie` parameter must be the cookie for an `Arc<F>` and
+ /// the caller must hold a ref-count to it.
+ unsafe extern "C" fn connection_info<F>(
+ instance: *const c_char,
+ cookie: *mut c_void,
+ ) -> *mut binder_ndk_sys::ABinderRpc_ConnectionInfo
+ where
+ F: Fn(&str) -> Option<ConnectionInfo> + Send + Sync + 'static,
+ {
+ if cookie.is_null() || instance.is_null() {
+ log::error!("Cookie({cookie:p}) or instance({instance:p}) is null!");
+ return ptr::null_mut();
+ }
+ // Safety: The caller promises that `cookie` is for an Arc<F>.
+ let callback = unsafe { (cookie as *const F).as_ref().unwrap() };
+
+ // Safety: The caller in libbinder_ndk will have already verified this is a valid
+ // C string
+ let inst = unsafe {
+ match CStr::from_ptr(instance).to_str() {
+ Ok(s) => s,
+ Err(err) => {
+ log::error!("Failed to get a valid C string! {err:?}");
+ return ptr::null_mut();
+ }
+ }
+ };
+
+ let connection = match callback(inst) {
+ Some(con) => con,
+ None => {
+ return ptr::null_mut();
+ }
+ };
+
+ match connection {
+ ConnectionInfo::Vsock(addr) => {
+ // Safety: The sockaddr is being copied in the NDK API
+ unsafe { sys::ABinderRpc_ConnectionInfo_new(addr.as_ptr(), addr.len()) }
+ }
+ ConnectionInfo::Unix(addr) => {
+ // Safety: The sockaddr is being copied in the NDK API
+ // The cast is from sockaddr_un* to sockaddr*.
+ unsafe {
+ sys::ABinderRpc_ConnectionInfo_new(addr.as_ptr() as *const sockaddr, addr.len())
+ }
+ }
+ }
+ }
+
+ /// Callback that decrements the ref-count.
+ /// This is invoked from C++ when a binder is unlinked.
+ ///
+ /// # Safety
+ ///
+ /// The `cookie` parameter must be the cookie for an `Arc<F>` and
+ /// the owner must give up a ref-count to it.
+ unsafe extern "C" fn cookie_decr_refcount<F>(cookie: *mut c_void)
+ where
+ F: Fn(&str) -> Option<ConnectionInfo> + Send + Sync + 'static,
+ {
+ // Safety: The caller promises that `cookie` is for an Arc<F>.
+ unsafe { Arc::decrement_strong_count(cookie as *const F) };
+ }
+}
+
+impl Drop for Accessor {
+ fn drop(&mut self) {
+ // Safety: `self.accessor` is always a valid, owned
+ // `ABinderRpc_Accessor` pointer returned by
+ // `ABinderRpc_Accessor_new` when `self` was created. This delete
+ // method can only be called once when `self` is dropped.
+ unsafe {
+ sys::ABinderRpc_Accessor_delete(self.accessor);
+ }
+ }
+}
+
+/// Register a new service with the default service manager.
+///
+/// Registers the given binder object with the given identifier. If successful,
+/// this service can then be retrieved using that identifier.
+///
+/// This function will panic if the identifier contains a 0 byte (NUL).
+pub fn delegate_accessor(name: &str, mut binder: SpIBinder) -> Result<SpIBinder> {
+ let instance = CString::new(name).unwrap();
+ let mut delegator = ptr::null_mut();
+ let status =
+ // Safety: `AServiceManager_addService` expects valid `AIBinder` and C
+ // string pointers. Caller retains ownership of both pointers.
+ // `AServiceManager_addService` creates a new strong reference and copies
+ // the string, so both pointers need only be valid until the call returns.
+ unsafe { sys::ABinderRpc_Accessor_delegateAccessor(instance.as_ptr(),
+ binder.as_native_mut(), &mut delegator) };
+
+ status_result(status)?;
+
+ // Safety: `delegator` is either null or a valid, owned pointer at this
+ // point, so can be safely passed to `SpIBinder::from_raw`.
+ Ok(unsafe { SpIBinder::from_raw(delegator).expect("Expected valid binder at this point") })
+}
diff --git a/libs/binder/rust/sys/BinderBindings.hpp b/libs/binder/rust/sys/BinderBindings.hpp
index 65fa2ca..bd666fe 100644
--- a/libs/binder/rust/sys/BinderBindings.hpp
+++ b/libs/binder/rust/sys/BinderBindings.hpp
@@ -20,6 +20,7 @@
#include <android/binder_parcel.h>
#include <android/binder_parcel_platform.h>
#include <android/binder_process.h>
+#include <android/binder_rpc.h>
#include <android/binder_shell.h>
#include <android/binder_stability.h>
#include <android/binder_status.h>
diff --git a/libs/binder/rust/tests/integration.rs b/libs/binder/rust/tests/integration.rs
index 5359832..489fa0a 100644
--- a/libs/binder/rust/tests/integration.rs
+++ b/libs/binder/rust/tests/integration.rs
@@ -384,8 +384,8 @@
use std::time::Duration;
use binder::{
- BinderFeatures, DeathRecipient, FromIBinder, IBinder, Interface, SpIBinder, StatusCode,
- Strong,
+ Accessor, BinderFeatures, DeathRecipient, FromIBinder, IBinder, Interface, SpIBinder,
+ StatusCode, Strong,
};
// Import from impl API for testing only, should not be necessary as long as
// you are using AIDL.
@@ -908,6 +908,80 @@
assert_eq!(service.test().unwrap(), service_name);
}
+ struct ToBeDeleted {
+ deleted: Arc<AtomicBool>,
+ }
+
+ impl Drop for ToBeDeleted {
+ fn drop(&mut self) {
+ assert!(!self.deleted.load(Ordering::Relaxed));
+ self.deleted.store(true, Ordering::Relaxed);
+ }
+ }
+
+ #[test]
+ fn test_accessor_callback_destruction() {
+ let deleted: Arc<AtomicBool> = Arc::new(AtomicBool::new(false));
+ {
+ let accessor: Accessor;
+ {
+ let helper = ToBeDeleted { deleted: deleted.clone() };
+ let get_connection_info = move |_instance: &str| {
+ // Capture this object so we can see it get destructed
+ // after the parent scope
+ let _ = &helper;
+ None
+ };
+ accessor = Accessor::new("foo.service", get_connection_info);
+ }
+
+ match accessor.as_binder() {
+ Some(_) => {
+ assert!(!deleted.load(Ordering::Relaxed));
+ }
+ None => panic!("failed to get that accessor binder"),
+ }
+ }
+ assert!(deleted.load(Ordering::Relaxed));
+ }
+
+ #[test]
+ fn test_accessor_delegator_new_each_time() {
+ let get_connection_info = move |_instance: &str| None;
+ let accessor = Accessor::new("foo.service", get_connection_info);
+ let delegator_binder =
+ binder::delegate_accessor("foo.service", accessor.as_binder().unwrap());
+ let delegator_binder2 =
+ binder::delegate_accessor("foo.service", accessor.as_binder().unwrap());
+
+ // The delegate_accessor creates new delegators each time
+ assert!(delegator_binder != delegator_binder2);
+ }
+
+ #[test]
+ fn test_accessor_delegate_the_delegator() {
+ let get_connection_info = move |_instance: &str| None;
+ let accessor = Accessor::new("foo.service", get_connection_info);
+ let delegator_binder =
+ binder::delegate_accessor("foo.service", accessor.as_binder().unwrap());
+ let delegator_binder2 =
+ binder::delegate_accessor("foo.service", delegator_binder.clone().unwrap());
+
+ assert!(delegator_binder.clone() == delegator_binder);
+ // The delegate_accessor creates new delegators each time. Even when they are delegators
+ // of delegators.
+ assert!(delegator_binder != delegator_binder2);
+ }
+
+ #[test]
+ fn test_accessor_delegator_wrong_name() {
+ let get_connection_info = move |_instance: &str| None;
+ let accessor = Accessor::new("foo.service", get_connection_info);
+ let delegator_binder =
+ binder::delegate_accessor("NOT.foo.service", accessor.as_binder().unwrap());
+ assert_eq!(delegator_binder, Err(StatusCode::NAME_NOT_FOUND));
+ }
+
#[tokio::test]
async fn reassociate_rust_binder_async() {
let service_name = "testing_service";
diff --git a/libs/binder/rust/tests/parcel_fuzzer/parcel_fuzzer.rs b/libs/binder/rust/tests/parcel_fuzzer/parcel_fuzzer.rs
index ce0f742..ee20a22 100644
--- a/libs/binder/rust/tests/parcel_fuzzer/parcel_fuzzer.rs
+++ b/libs/binder/rust/tests/parcel_fuzzer/parcel_fuzzer.rs
@@ -21,7 +21,8 @@
use crate::read_utils::READ_FUNCS;
use binder::binder_impl::{
- Binder, BorrowedParcel, IBinderInternal, Parcel, Stability, TransactionCode,
+ Binder, BorrowedParcel, IBinderInternal, LocalStabilityType, Parcel, TransactionCode,
+ VintfStabilityType,
};
use binder::{
declare_binder_interface, BinderFeatures, Interface, Parcelable, ParcelableHolder, SpIBinder,
@@ -121,13 +122,15 @@
}
ReadOperation::ReadParcelableHolder { is_vintf } => {
- let stability = if is_vintf { Stability::Vintf } else { Stability::Local };
- let mut holder: ParcelableHolder = ParcelableHolder::new(stability);
- match holder.read_from_parcel(parcel.borrowed_ref()) {
- Ok(result) => result,
- Err(err) => {
- println!("error occurred while reading from parcel: {:?}", err)
- }
+ let result = if is_vintf {
+ ParcelableHolder::<VintfStabilityType>::new()
+ .read_from_parcel(parcel.borrowed_ref())
+ } else {
+ ParcelableHolder::<LocalStabilityType>::new()
+ .read_from_parcel(parcel.borrowed_ref())
+ };
+ if let Err(e) = result {
+ println!("error occurred while reading from parcel: {e:?}")
}
}
diff --git a/libs/binder/tests/binderCacheUnitTest.cpp b/libs/binder/tests/binderCacheUnitTest.cpp
index 482d197..c5ad793 100644
--- a/libs/binder/tests/binderCacheUnitTest.cpp
+++ b/libs/binder/tests/binderCacheUnitTest.cpp
@@ -149,7 +149,16 @@
EXPECT_EQ(OK, mServiceManager->addService(kCachedServiceName, binder2));
// Confirm that new service is returned instead of old.
- sp<IBinder> result2 = mServiceManager->checkService(kCachedServiceName);
+ int retry_count = 20;
+ sp<IBinder> result2;
+ do {
+ std::this_thread::sleep_for(std::chrono::milliseconds(50));
+ if (retry_count-- == 0) {
+ break;
+ }
+ result2 = mServiceManager->checkService(kCachedServiceName);
+ } while (result2 != binder2);
+
ASSERT_EQ(binder2, result2);
}
diff --git a/libs/binder/tests/binderLibTest.cpp b/libs/binder/tests/binderLibTest.cpp
index bcab6de..ec2f50c 100644
--- a/libs/binder/tests/binderLibTest.cpp
+++ b/libs/binder/tests/binderLibTest.cpp
@@ -2261,7 +2261,7 @@
if (ret != NO_ERROR) {
return ret;
}
- auto event = frozenStateChangeCallback->events.popWithTimeout(10ms);
+ auto event = frozenStateChangeCallback->events.popWithTimeout(1000ms);
if (!event.has_value()) {
return NOT_ENOUGH_DATA;
}
diff --git a/libs/binder/tests/binderRpcTest.cpp b/libs/binder/tests/binderRpcTest.cpp
index 11150bc..506fc71 100644
--- a/libs/binder/tests/binderRpcTest.cpp
+++ b/libs/binder/tests/binderRpcTest.cpp
@@ -75,6 +75,8 @@
constexpr char kTrustyIpcDevice[] = "/dev/trusty-ipc-dev0";
#endif
+constexpr char kKnownAidlService[] = "activity";
+
static std::string WaitStatusToString(int wstatus) {
if (WIFEXITED(wstatus)) {
return "exit status " + std::to_string(WEXITSTATUS(wstatus));
@@ -1544,36 +1546,52 @@
ABinderRpc_Accessor_delete(accessor);
}
-TEST_F(BinderARpcNdk, ARpcDoubleRemoveProvider) {
- ABinderRpc_AccessorProvider* provider =
- ABinderRpc_registerAccessorProvider(getAccessor, kARpcSupportedServices,
- kARpcNumSupportedServices, nullptr, nullptr);
- ASSERT_NE(nullptr, provider);
- ABinderRpc_unregisterAccessorProvider(provider);
- EXPECT_DEATH(ABinderRpc_unregisterAccessorProvider(provider), " was already unregistered");
-}
-
TEST_F(BinderARpcNdk, ARpcNullArgs_ConnectionInfo_new) {
sockaddr_storage addr;
EXPECT_EQ(nullptr, ABinderRpc_ConnectionInfo_new(reinterpret_cast<const sockaddr*>(&addr), 0));
}
-TEST_P(BinderRpcAccessor, ARpcGetService) {
+TEST_F(BinderARpcNdk, ARpcDelegateAccessorWrongInstance) {
+ AccessorProviderData* data = new AccessorProviderData();
+ ABinderRpc_Accessor* accessor = getAccessor(kARpcInstance, data);
+ ASSERT_NE(accessor, nullptr);
+ AIBinder* localAccessorBinder = ABinderRpc_Accessor_asBinder(accessor);
+ EXPECT_NE(localAccessorBinder, nullptr);
+
+ AIBinder* delegatorBinder = nullptr;
+ binder_status_t status =
+ ABinderRpc_Accessor_delegateAccessor("bar", localAccessorBinder, &delegatorBinder);
+ EXPECT_EQ(status, NAME_NOT_FOUND);
+
+ AIBinder_decStrong(localAccessorBinder);
+ ABinderRpc_Accessor_delete(accessor);
+ delete data;
+}
+
+TEST_F(BinderARpcNdk, ARpcDelegateNonAccessor) {
+ auto service = defaultServiceManager()->checkService(String16(kKnownAidlService));
+ ASSERT_NE(nullptr, service);
+ ndk::SpAIBinder binder = ndk::SpAIBinder(AIBinder_fromPlatformBinder(service));
+
+ AIBinder* delegatorBinder = nullptr;
+ binder_status_t status =
+ ABinderRpc_Accessor_delegateAccessor("bar", binder.get(), &delegatorBinder);
+
+ EXPECT_EQ(status, BAD_TYPE);
+}
+
+inline void getServiceTest(BinderRpcTestProcessSession& proc,
+ ABinderRpc_AccessorProvider_getAccessorCallback getAccessor) {
constexpr size_t kNumThreads = 10;
bool isDeleted = false;
- auto proc = createRpcTestSocketServerProcess({.numThreads = kNumThreads});
- EXPECT_EQ(OK, proc.rootBinder->pingBinder());
-
AccessorProviderData* data =
new AccessorProviderData{proc.proc->sessions[0].addr, proc.proc->sessions[0].addrLen,
&isDeleted};
-
ABinderRpc_AccessorProvider* provider =
ABinderRpc_registerAccessorProvider(getAccessor, kARpcSupportedServices,
kARpcNumSupportedServices, data,
accessorProviderDataOnDelete);
-
EXPECT_NE(provider, nullptr);
EXPECT_FALSE(isDeleted);
@@ -1589,6 +1607,45 @@
waitForExtraSessionCleanup(proc);
}
+TEST_P(BinderRpcAccessor, ARpcGetService) {
+ constexpr size_t kNumThreads = 10;
+ auto proc = createRpcTestSocketServerProcess({.numThreads = kNumThreads});
+ EXPECT_EQ(OK, proc.rootBinder->pingBinder());
+
+ getServiceTest(proc, getAccessor);
+}
+
+// Create accessors and wrap each of the accessors in a delegator
+ABinderRpc_Accessor* getDelegatedAccessor(const char* instance, void* cookie) {
+ ABinderRpc_Accessor* accessor = getAccessor(instance, cookie);
+ AIBinder* accessorBinder = ABinderRpc_Accessor_asBinder(accessor);
+ // Once we have a handle to the AIBinder which holds a reference to the
+ // underlying accessor IBinder, we can get rid of the ABinderRpc_Accessor
+ ABinderRpc_Accessor_delete(accessor);
+
+ AIBinder* delegatorBinder = nullptr;
+ binder_status_t status =
+ ABinderRpc_Accessor_delegateAccessor(instance, accessorBinder, &delegatorBinder);
+ // No longer need this AIBinder. The delegator has a reference to the
+ // underlying IBinder on success, and on failure we are done here.
+ AIBinder_decStrong(accessorBinder);
+ if (status != OK || delegatorBinder == nullptr) {
+ ALOGE("Unexpected behavior. Status: %s, delegator ptr: %p", statusToString(status).c_str(),
+ delegatorBinder);
+ return nullptr;
+ }
+
+ return ABinderRpc_Accessor_fromBinder(instance, delegatorBinder);
+}
+
+TEST_P(BinderRpcAccessor, ARpcGetServiceWithDelegator) {
+ constexpr size_t kNumThreads = 10;
+ auto proc = createRpcTestSocketServerProcess({.numThreads = kNumThreads});
+ EXPECT_EQ(OK, proc.rootBinder->pingBinder());
+
+ getServiceTest(proc, getDelegatedAccessor);
+}
+
#endif // BINDER_WITH_KERNEL_IPC
#ifdef BINDER_RPC_TO_TRUSTY_TEST
@@ -1854,7 +1911,7 @@
ASSERT_NE(nullptr, sm);
// Any Java service with non-empty getInterfaceDescriptor() would do.
// Let's pick activity.
- auto binder = sm->checkService(String16("activity"));
+ auto binder = sm->checkService(String16(kKnownAidlService));
ASSERT_NE(nullptr, binder);
auto descriptor = binder->getInterfaceDescriptor();
ASSERT_GE(descriptor.size(), 0u);
diff --git a/libs/binder/tests/parcel_fuzzer/Android.bp b/libs/binder/tests/parcel_fuzzer/Android.bp
index fbab8f0..cac054e 100644
--- a/libs/binder/tests/parcel_fuzzer/Android.bp
+++ b/libs/binder/tests/parcel_fuzzer/Android.bp
@@ -39,6 +39,7 @@
"smoreland@google.com",
"waghpawan@google.com",
],
+ triage_assignee: "smoreland@google.com",
use_for_presubmit: true,
},
diff --git a/libs/binder/tests/parcel_fuzzer/binder_ndk.cpp b/libs/binder/tests/parcel_fuzzer/binder_ndk.cpp
index 3a1471e..e3a3371 100644
--- a/libs/binder/tests/parcel_fuzzer/binder_ndk.cpp
+++ b/libs/binder/tests/parcel_fuzzer/binder_ndk.cpp
@@ -49,7 +49,8 @@
return STATUS_UNKNOWN_TRANSACTION;
}
-static AIBinder_Class* g_class = ::ndk::ICInterface::defineClass("ISomeInterface", onTransact);
+static AIBinder_Class* g_class =
+ ::ndk::ICInterface::defineClass("ISomeInterface", onTransact, nullptr, 0);
class BpSomeInterface : public ::ndk::BpCInterface<ISomeInterface> {
public:
diff --git a/libs/binder/trusty/OS.cpp b/libs/binder/trusty/OS.cpp
index 157ab3c..ba9e457 100644
--- a/libs/binder/trusty/OS.cpp
+++ b/libs/binder/trusty/OS.cpp
@@ -42,6 +42,10 @@
void trace_int(uint64_t, const char*, int32_t) {}
+uint64_t get_trace_enabled_tags() {
+ return 0;
+}
+
uint64_t GetThreadId() {
return 0;
}
diff --git a/libs/binder/trusty/rust/binder_rpc_test/main.rs b/libs/binder/trusty/rust/binder_rpc_test/main.rs
index baea5a8..da1a86f 100644
--- a/libs/binder/trusty/rust/binder_rpc_test/main.rs
+++ b/libs/binder/trusty/rust/binder_rpc_test/main.rs
@@ -19,7 +19,7 @@
use binder_rpc_test_aidl::aidl::IBinderRpcSession::{BnBinderRpcSession, IBinderRpcSession};
use binder_rpc_test_aidl::aidl::IBinderRpcTest::{BnBinderRpcTest, IBinderRpcTest};
use binder_rpc_test_session::MyBinderRpcSession;
-use libc::{clock_gettime, CLOCK_REALTIME};
+use libc::{clock_gettime, CLOCK_BOOTTIME};
use rpcbinder::RpcSession;
use trusty_std::ffi::{CString, FallibleCString};
@@ -56,7 +56,7 @@
let mut ts = libc::timespec { tv_sec: 0, tv_nsec: 0 };
// Safety: Passing valid pointer to variable ts which lives past end of call
- assert_eq!(unsafe { clock_gettime(CLOCK_REALTIME, &mut ts) }, 0);
+ assert_eq!(unsafe { clock_gettime(CLOCK_BOOTTIME, &mut ts) }, 0);
ts.tv_sec as u64 * 1_000_000_000u64 + ts.tv_nsec as u64
}
diff --git a/libs/binder/trusty/rust/rpcbinder/rules.mk b/libs/binder/trusty/rust/rpcbinder/rules.mk
index 97f5c03..04c63f7 100644
--- a/libs/binder/trusty/rust/rpcbinder/rules.mk
+++ b/libs/binder/trusty/rust/rpcbinder/rules.mk
@@ -29,8 +29,8 @@
$(LIBBINDER_DIR)/trusty/rust/binder_ndk_sys \
$(LIBBINDER_DIR)/trusty/rust/binder_rpc_unstable_bindgen \
$(LIBBINDER_DIR)/trusty/rust/binder_rpc_server_bindgen \
- external/rust/crates/cfg-if \
- external/rust/crates/foreign-types \
+ $(call FIND_CRATE,cfg-if) \
+ $(call FIND_CRATE,foreign-types) \
trusty/user/base/lib/tipc/rust \
trusty/user/base/lib/trusty-sys \
diff --git a/libs/binder/trusty/rust/rules.mk b/libs/binder/trusty/rust/rules.mk
index 36bd3a2..e622b22 100644
--- a/libs/binder/trusty/rust/rules.mk
+++ b/libs/binder/trusty/rust/rules.mk
@@ -27,8 +27,8 @@
$(LIBBINDER_DIR)/trusty/ndk \
$(LIBBINDER_DIR)/trusty/rust/binder_ndk_sys \
$(LIBBINDER_DIR)/trusty/rust/binder_rpc_unstable_bindgen \
- external/rust/crates/downcast-rs \
- external/rust/crates/libc \
+ $(call FIND_CRATE,downcast-rs) \
+ $(call FIND_CRATE,libc) \
trusty/user/base/lib/trusty-sys \
MODULE_RUSTFLAGS += \
diff --git a/libs/renderengine/OWNERS b/libs/renderengine/OWNERS
index 66e1aa1..17ab29f 100644
--- a/libs/renderengine/OWNERS
+++ b/libs/renderengine/OWNERS
@@ -5,4 +5,6 @@
djsollen@google.com
jreck@google.com
lpy@google.com
+nscobie@google.com
+sallyqi@google.com
scroggo@google.com
diff --git a/libs/ultrahdr/Android.bp b/libs/ultrahdr/Android.bp
deleted file mode 100644
index eda5ea4..0000000
--- a/libs/ultrahdr/Android.bp
+++ /dev/null
@@ -1,85 +0,0 @@
-// Copyright 2022 The Android Open Source Project
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-// http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-package {
- // See: http://go/android-license-faq
- default_applicable_licenses: [
- "frameworks_native_license",
- "adobe_hdr_gain_map_license",
- ],
-}
-
-cc_library {
- name: "libultrahdr-deprecated",
- enabled: false,
- host_supported: true,
- vendor_available: true,
- export_include_dirs: ["include"],
- local_include_dirs: ["include"],
-
- srcs: [
- "icc.cpp",
- "jpegr.cpp",
- "gainmapmath.cpp",
- "jpegrutils.cpp",
- "multipictureformat.cpp",
- ],
-
- shared_libs: [
- "libimage_io",
- "libjpeg",
- "libjpegencoder",
- "libjpegdecoder",
- "liblog",
- "libutils",
- ],
-}
-
-cc_library {
- name: "libjpegencoder-deprecated",
- enabled: false,
- host_supported: true,
- vendor_available: true,
-
- shared_libs: [
- "libjpeg",
- "liblog",
- "libutils",
- ],
-
- export_include_dirs: ["include"],
-
- srcs: [
- "jpegencoderhelper.cpp",
- ],
-}
-
-cc_library {
- name: "libjpegdecoder-deprecated",
- enabled: false,
- host_supported: true,
- vendor_available: true,
-
- shared_libs: [
- "libjpeg",
- "liblog",
- "libutils",
- ],
-
- export_include_dirs: ["include"],
-
- srcs: [
- "jpegdecoderhelper.cpp",
- ],
-}
diff --git a/libs/ultrahdr/fuzzer/Android.bp b/libs/ultrahdr/fuzzer/Android.bp
deleted file mode 100644
index 8d9132f..0000000
--- a/libs/ultrahdr/fuzzer/Android.bp
+++ /dev/null
@@ -1,72 +0,0 @@
-// Copyright 2023 The Android Open Source Project
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-// http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-package {
- // See: http://go/android-license-faq
- // A large-scale-change added 'default_applicable_licenses' to import
- // all of the 'license_kinds' from "frameworks_native_license"
- // to get the below license kinds:
- // SPDX-license-identifier-Apache-2.0
- default_applicable_licenses: ["frameworks_native_license"],
-}
-
-cc_defaults {
- name: "ultrahdr_fuzzer_defaults-deprecated",
- enabled: false,
- host_supported: true,
- shared_libs: [
- "libimage_io",
- "libjpeg",
- ],
- static_libs: [
- "libjpegdecoder",
- "libjpegencoder",
- "libultrahdr",
- "libutils",
- "liblog",
- ],
- target: {
- darwin: {
- enabled: false,
- },
- },
- fuzz_config: {
- cc: [
- "android-media-fuzzing-reports@google.com",
- ],
- description: "The fuzzers target the APIs of jpeg hdr",
- service_privilege: "constrained",
- users: "multi_user",
- fuzzed_code_usage: "future_version",
- vector: "local_no_privileges_required",
- },
-}
-
-cc_fuzz {
- name: "ultrahdr_enc_fuzzer-deprecated",
- enabled: false,
- defaults: ["ultrahdr_fuzzer_defaults"],
- srcs: [
- "ultrahdr_enc_fuzzer.cpp",
- ],
-}
-
-cc_fuzz {
- name: "ultrahdr_dec_fuzzer-deprecated",
- enabled: false,
- defaults: ["ultrahdr_fuzzer_defaults"],
- srcs: [
- "ultrahdr_dec_fuzzer.cpp",
- ],
-}
diff --git a/libs/ultrahdr/tests/Android.bp b/libs/ultrahdr/tests/Android.bp
deleted file mode 100644
index 00cc797..0000000
--- a/libs/ultrahdr/tests/Android.bp
+++ /dev/null
@@ -1,51 +0,0 @@
-// Copyright 2022 The Android Open Source Project
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-// http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-package {
- // See: http://go/android-license-faq
- // A large-scale-change added 'default_applicable_licenses' to import
- // all of the 'license_kinds' from "frameworks_native_license"
- // to get the below license kinds:
- // SPDX-license-identifier-Apache-2.0
- default_applicable_licenses: ["frameworks_native_license"],
-}
-
-cc_test {
- name: "ultrahdr_unit_test-deprecated",
- enabled: false,
- test_suites: ["device-tests"],
- srcs: [
- "gainmapmath_test.cpp",
- "icchelper_test.cpp",
- "jpegr_test.cpp",
- "jpegencoderhelper_test.cpp",
- "jpegdecoderhelper_test.cpp",
- ],
- shared_libs: [
- "libimage_io",
- "libjpeg",
- "liblog",
- ],
- static_libs: [
- "libgmock",
- "libgtest",
- "libjpegdecoder",
- "libjpegencoder",
- "libultrahdr",
- "libutils",
- ],
- data: [
- "./data/*.*",
- ],
-}
diff --git a/services/inputflinger/PointerChoreographer.cpp b/services/inputflinger/PointerChoreographer.cpp
index 00dd6ba..ed77146 100644
--- a/services/inputflinger/PointerChoreographer.cpp
+++ b/services/inputflinger/PointerChoreographer.cpp
@@ -367,7 +367,8 @@
const uint8_t actionIndex = MotionEvent::getActionIndex(args.action);
std::array<uint32_t, MAX_POINTER_ID + 1> idToIndex;
BitSet32 idBits;
- if (maskedAction != AMOTION_EVENT_ACTION_UP && maskedAction != AMOTION_EVENT_ACTION_CANCEL) {
+ if (maskedAction != AMOTION_EVENT_ACTION_UP && maskedAction != AMOTION_EVENT_ACTION_CANCEL &&
+ maskedAction != AMOTION_EVENT_ACTION_HOVER_EXIT) {
for (size_t i = 0; i < args.getPointerCount(); i++) {
if (maskedAction == AMOTION_EVENT_ACTION_POINTER_UP && actionIndex == i) {
continue;
diff --git a/services/inputflinger/dispatcher/CancelationOptions.h b/services/inputflinger/dispatcher/CancelationOptions.h
index 4a0889f..568d348 100644
--- a/services/inputflinger/dispatcher/CancelationOptions.h
+++ b/services/inputflinger/dispatcher/CancelationOptions.h
@@ -32,7 +32,8 @@
CANCEL_POINTER_EVENTS = 1,
CANCEL_NON_POINTER_EVENTS = 2,
CANCEL_FALLBACK_EVENTS = 3,
- ftl_last = CANCEL_FALLBACK_EVENTS,
+ CANCEL_HOVER_EVENTS = 4,
+ ftl_last = CANCEL_HOVER_EVENTS
};
// The criterion to use to determine which events should be canceled.
diff --git a/services/inputflinger/dispatcher/DragState.h b/services/inputflinger/dispatcher/DragState.h
index 9809148..1ed6c29 100644
--- a/services/inputflinger/dispatcher/DragState.h
+++ b/services/inputflinger/dispatcher/DragState.h
@@ -17,6 +17,7 @@
#pragma once
#include <gui/WindowInfo.h>
+#include <input/Input.h>
#include <utils/StrongPointer.h>
#include <string>
@@ -25,8 +26,9 @@
namespace inputdispatcher {
struct DragState {
- DragState(const sp<android::gui::WindowInfoHandle>& windowHandle, int32_t pointerId)
- : dragWindow(windowHandle), pointerId(pointerId) {}
+ DragState(const sp<android::gui::WindowInfoHandle>& windowHandle, DeviceId deviceId,
+ int32_t pointerId)
+ : dragWindow(windowHandle), deviceId(deviceId), pointerId(pointerId) {}
void dump(std::string& dump, const char* prefix = "");
// The window being dragged.
@@ -37,6 +39,8 @@
bool isStartDrag = false;
// Indicate if the stylus button is down at the start of the drag.
bool isStylusButtonDownAtStart = false;
+ // Indicate which device started this drag and drop.
+ const DeviceId deviceId;
// Indicate which pointer id is tracked by the drag and drop.
const int32_t pointerId;
};
diff --git a/services/inputflinger/dispatcher/InputDispatcher.cpp b/services/inputflinger/dispatcher/InputDispatcher.cpp
index f9fbfef..7eb7e36 100644
--- a/services/inputflinger/dispatcher/InputDispatcher.cpp
+++ b/services/inputflinger/dispatcher/InputDispatcher.cpp
@@ -746,7 +746,8 @@
}
touchedWindow.dispatchMode = InputTarget::DispatchMode::AS_IS;
}
- touchedWindow.addHoveringPointer(entry.deviceId, pointer);
+ const auto [x, y] = resolveTouchedPosition(entry);
+ touchedWindow.addHoveringPointer(entry.deviceId, pointer, x, y);
if (canReceiveForegroundTouches(*newWindow->getInfo())) {
touchedWindow.targetFlags |= InputTarget::Flags::FOREGROUND;
}
@@ -873,6 +874,8 @@
return {false, true};
case CancelationOptions::Mode::CANCEL_FALLBACK_EVENTS:
return {false, true};
+ case CancelationOptions::Mode::CANCEL_HOVER_EVENTS:
+ return {true, false};
}
}
@@ -2511,7 +2514,8 @@
if (isHoverAction) {
// The "windowHandle" is the target of this hovering pointer.
- tempTouchState.addHoveringPointerToWindow(windowHandle, entry.deviceId, pointer);
+ tempTouchState.addHoveringPointerToWindow(windowHandle, entry.deviceId, pointer, x,
+ y);
}
// Set target flags.
@@ -2870,7 +2874,8 @@
}
void InputDispatcher::addDragEventLocked(const MotionEntry& entry) {
- if (!mDragState || mDragState->dragWindow->getInfo()->displayId != entry.displayId) {
+ if (!mDragState || mDragState->dragWindow->getInfo()->displayId != entry.displayId ||
+ mDragState->deviceId != entry.deviceId) {
return;
}
@@ -5436,6 +5441,31 @@
}
}
+ // Check if the hovering should stop because the window is no longer eligible to receive it
+ // (for example, if the touchable region changed)
+ if (const auto& it = mTouchStatesByDisplay.find(displayId); it != mTouchStatesByDisplay.end()) {
+ TouchState& state = it->second;
+ for (TouchedWindow& touchedWindow : state.windows) {
+ std::vector<DeviceId> erasedDevices = touchedWindow.eraseHoveringPointersIf(
+ [this, displayId, &touchedWindow](const PointerProperties& properties, float x,
+ float y) REQUIRES(mLock) {
+ const bool isStylus = properties.toolType == ToolType::STYLUS;
+ const ui::Transform displayTransform = getTransformLocked(displayId);
+ const bool stillAcceptsTouch =
+ windowAcceptsTouchAt(*touchedWindow.windowHandle->getInfo(),
+ displayId, x, y, isStylus, displayTransform);
+ return !stillAcceptsTouch;
+ });
+
+ for (DeviceId deviceId : erasedDevices) {
+ CancelationOptions options(CancelationOptions::Mode::CANCEL_HOVER_EVENTS,
+ "WindowInfo changed", traceContext.getTracker());
+ options.deviceId = deviceId;
+ synthesizeCancelationEventsForWindowLocked(touchedWindow.windowHandle, options);
+ }
+ }
+ }
+
// Release information for windows that are no longer present.
// This ensures that unused input channels are released promptly.
// Otherwise, they might stick around until the window handle is destroyed
@@ -5758,7 +5788,7 @@
}
// Track the pointer id for drag window and generate the drag state.
const size_t id = pointers.begin()->id;
- mDragState = std::make_unique<DragState>(toWindowHandle, id);
+ mDragState = std::make_unique<DragState>(toWindowHandle, deviceId, id);
}
// Synthesize cancel for old window and down for new window.
diff --git a/services/inputflinger/dispatcher/InputState.cpp b/services/inputflinger/dispatcher/InputState.cpp
index dfbe02f..e283fc3 100644
--- a/services/inputflinger/dispatcher/InputState.cpp
+++ b/services/inputflinger/dispatcher/InputState.cpp
@@ -638,6 +638,8 @@
return memento.source & AINPUT_SOURCE_CLASS_POINTER;
case CancelationOptions::Mode::CANCEL_NON_POINTER_EVENTS:
return !(memento.source & AINPUT_SOURCE_CLASS_POINTER);
+ case CancelationOptions::Mode::CANCEL_HOVER_EVENTS:
+ return memento.hovering;
default:
return false;
}
diff --git a/services/inputflinger/dispatcher/TouchState.cpp b/services/inputflinger/dispatcher/TouchState.cpp
index 0c9ad3c..2bf63be 100644
--- a/services/inputflinger/dispatcher/TouchState.cpp
+++ b/services/inputflinger/dispatcher/TouchState.cpp
@@ -112,17 +112,18 @@
}
void TouchState::addHoveringPointerToWindow(const sp<WindowInfoHandle>& windowHandle,
- DeviceId deviceId, const PointerProperties& pointer) {
+ DeviceId deviceId, const PointerProperties& pointer,
+ float x, float y) {
for (TouchedWindow& touchedWindow : windows) {
if (touchedWindow.windowHandle == windowHandle) {
- touchedWindow.addHoveringPointer(deviceId, pointer);
+ touchedWindow.addHoveringPointer(deviceId, pointer, x, y);
return;
}
}
TouchedWindow touchedWindow;
touchedWindow.windowHandle = windowHandle;
- touchedWindow.addHoveringPointer(deviceId, pointer);
+ touchedWindow.addHoveringPointer(deviceId, pointer, x, y);
windows.push_back(touchedWindow);
}
diff --git a/services/inputflinger/dispatcher/TouchState.h b/services/inputflinger/dispatcher/TouchState.h
index 9d4bb3d..3fbe584 100644
--- a/services/inputflinger/dispatcher/TouchState.h
+++ b/services/inputflinger/dispatcher/TouchState.h
@@ -49,7 +49,8 @@
DeviceId deviceId, const std::vector<PointerProperties>& touchingPointers,
std::optional<nsecs_t> firstDownTimeInTarget = std::nullopt);
void addHoveringPointerToWindow(const sp<android::gui::WindowInfoHandle>& windowHandle,
- DeviceId deviceId, const PointerProperties& pointer);
+ DeviceId deviceId, const PointerProperties& pointer, float x,
+ float y);
void removeHoveringPointer(DeviceId deviceId, int32_t pointerId);
void clearHoveringPointers(DeviceId deviceId);
diff --git a/services/inputflinger/dispatcher/TouchedWindow.cpp b/services/inputflinger/dispatcher/TouchedWindow.cpp
index 1f86f66..fa5be1a 100644
--- a/services/inputflinger/dispatcher/TouchedWindow.cpp
+++ b/services/inputflinger/dispatcher/TouchedWindow.cpp
@@ -36,6 +36,13 @@
}) != pointers.end();
}
+bool hasPointerId(const std::vector<TouchedWindow::HoveringPointer>& pointers, int32_t pointerId) {
+ return std::find_if(pointers.begin(), pointers.end(),
+ [&pointerId](const TouchedWindow::HoveringPointer& pointer) {
+ return pointer.properties.id == pointerId;
+ }) != pointers.end();
+}
+
} // namespace
bool TouchedWindow::hasHoveringPointers() const {
@@ -78,16 +85,18 @@
return hasPointerId(state.hoveringPointers, pointerId);
}
-void TouchedWindow::addHoveringPointer(DeviceId deviceId, const PointerProperties& pointer) {
- std::vector<PointerProperties>& hoveringPointers = mDeviceStates[deviceId].hoveringPointers;
+void TouchedWindow::addHoveringPointer(DeviceId deviceId, const PointerProperties& properties,
+ float x, float y) {
+ std::vector<HoveringPointer>& hoveringPointers = mDeviceStates[deviceId].hoveringPointers;
const size_t initialSize = hoveringPointers.size();
- std::erase_if(hoveringPointers, [&pointer](const PointerProperties& properties) {
- return properties.id == pointer.id;
+ std::erase_if(hoveringPointers, [&properties](const HoveringPointer& pointer) {
+ return pointer.properties.id == properties.id;
});
if (hoveringPointers.size() != initialSize) {
- LOG(ERROR) << __func__ << ": " << pointer << ", device " << deviceId << " was in " << *this;
+ LOG(ERROR) << __func__ << ": " << properties << ", device " << deviceId << " was in "
+ << *this;
}
- hoveringPointers.push_back(pointer);
+ hoveringPointers.push_back({properties, x, y});
}
Result<void> TouchedWindow::addTouchingPointers(DeviceId deviceId,
@@ -173,8 +182,8 @@
return true;
}
}
- for (const PointerProperties& properties : state.hoveringPointers) {
- if (properties.toolType == ToolType::STYLUS) {
+ for (const HoveringPointer& pointer : state.hoveringPointers) {
+ if (pointer.properties.toolType == ToolType::STYLUS) {
return true;
}
}
@@ -270,8 +279,8 @@
}
DeviceState& state = stateIt->second;
- std::erase_if(state.hoveringPointers, [&pointerId](const PointerProperties& properties) {
- return properties.id == pointerId;
+ std::erase_if(state.hoveringPointers, [&pointerId](const HoveringPointer& pointer) {
+ return pointer.properties.id == pointerId;
});
if (!state.hasPointers()) {
@@ -279,6 +288,22 @@
}
}
+std::vector<DeviceId> TouchedWindow::eraseHoveringPointersIf(
+ std::function<bool(const PointerProperties&, float /*x*/, float /*y*/)> condition) {
+ std::vector<DeviceId> erasedDevices;
+ for (auto& [deviceId, state] : mDeviceStates) {
+ std::erase_if(state.hoveringPointers, [&](const HoveringPointer& pointer) {
+ if (condition(pointer.properties, pointer.x, pointer.y)) {
+ erasedDevices.push_back(deviceId);
+ return true;
+ }
+ return false;
+ });
+ }
+
+ return erasedDevices;
+}
+
void TouchedWindow::removeAllHoveringPointersForDevice(DeviceId deviceId) {
const auto stateIt = mDeviceStates.find(deviceId);
if (stateIt == mDeviceStates.end()) {
@@ -312,6 +337,11 @@
return out;
}
+std::ostream& operator<<(std::ostream& out, const TouchedWindow::HoveringPointer& pointer) {
+ out << pointer.properties << " at (" << pointer.x << ", " << pointer.y << ")";
+ return out;
+}
+
std::ostream& operator<<(std::ostream& out, const TouchedWindow& window) {
out << window.dump();
return out;
diff --git a/services/inputflinger/dispatcher/TouchedWindow.h b/services/inputflinger/dispatcher/TouchedWindow.h
index 4f0ad16..c38681e 100644
--- a/services/inputflinger/dispatcher/TouchedWindow.h
+++ b/services/inputflinger/dispatcher/TouchedWindow.h
@@ -38,7 +38,7 @@
bool hasHoveringPointers() const;
bool hasHoveringPointers(DeviceId deviceId) const;
bool hasHoveringPointer(DeviceId deviceId, int32_t pointerId) const;
- void addHoveringPointer(DeviceId deviceId, const PointerProperties& pointer);
+ void addHoveringPointer(DeviceId deviceId, const PointerProperties& pointer, float x, float y);
void removeHoveringPointer(DeviceId deviceId, int32_t pointerId);
// Touching
@@ -69,6 +69,15 @@
void clearHoveringPointers(DeviceId deviceId);
std::string dump() const;
+ struct HoveringPointer {
+ PointerProperties properties;
+ float x;
+ float y;
+ };
+
+ std::vector<DeviceId> eraseHoveringPointersIf(
+ std::function<bool(const PointerProperties&, float /*x*/, float /*y*/)> condition);
+
private:
struct DeviceState {
std::vector<PointerProperties> touchingPointers;
@@ -78,7 +87,7 @@
// NOTE: This is not initialized in case of HOVER entry/exit and DISPATCH_AS_OUTSIDE
// scenario.
std::optional<nsecs_t> downTimeInTarget;
- std::vector<PointerProperties> hoveringPointers;
+ std::vector<HoveringPointer> hoveringPointers;
bool hasPointers() const { return !touchingPointers.empty() || !hoveringPointers.empty(); };
};
diff --git a/services/inputflinger/tests/InputDispatcher_test.cpp b/services/inputflinger/tests/InputDispatcher_test.cpp
index 2056372..48930ef 100644
--- a/services/inputflinger/tests/InputDispatcher_test.cpp
+++ b/services/inputflinger/tests/InputDispatcher_test.cpp
@@ -1923,6 +1923,99 @@
window->consumeMotionEvent(WithMotionAction(ACTION_SCROLL));
}
+/**
+ * Two windows: a trusted overlay and a regular window underneath. Both windows are visible.
+ * Mouse is hovered, and the hover event should only go to the overlay.
+ * However, next, the touchable region of the trusted overlay shrinks. The mouse position hasn't
+ * changed, but the cursor would now end up hovering above the regular window underneatch.
+ * If the mouse is now clicked, this would generate an ACTION_DOWN event, which would go to the
+ * regular window. However, the trusted overlay is also watching for outside touch.
+ * The trusted overlay should get two events:
+ * 1) The ACTION_OUTSIDE event, since the click is now not inside its touchable region
+ * 2) The HOVER_EXIT event, since the mouse pointer is no longer hovering inside this window
+ *
+ * This test reproduces a crash where there is an overlap between dispatch modes for the trusted
+ * overlay touch target, since the event is causing both an ACTION_OUTSIDE, and as a HOVER_EXIT.
+ */
+TEST_F(InputDispatcherTest, MouseClickUnderShrinkingTrustedOverlay) {
+ std::shared_ptr<FakeApplicationHandle> app = std::make_shared<FakeApplicationHandle>();
+ sp<FakeWindowHandle> overlay = sp<FakeWindowHandle>::make(app, mDispatcher, "Trusted overlay",
+ ui::LogicalDisplayId::DEFAULT);
+ overlay->setTrustedOverlay(true);
+ overlay->setWatchOutsideTouch(true);
+ overlay->setFrame(Rect(0, 0, 200, 200));
+
+ sp<FakeWindowHandle> window = sp<FakeWindowHandle>::make(app, mDispatcher, "Regular window",
+ ui::LogicalDisplayId::DEFAULT);
+ window->setFrame(Rect(0, 0, 200, 200));
+
+ mDispatcher->onWindowInfosChanged({{*overlay->getInfo(), *window->getInfo()}, {}, 0, 0});
+ // Hover the mouse into the overlay
+ mDispatcher->notifyMotion(MotionArgsBuilder(ACTION_HOVER_MOVE, AINPUT_SOURCE_MOUSE)
+ .pointer(PointerBuilder(0, ToolType::MOUSE).x(100).y(110))
+ .build());
+ overlay->consumeMotionEvent(WithMotionAction(ACTION_HOVER_ENTER));
+
+ // Now, shrink the touchable region of the overlay! This will cause the cursor to suddenly have
+ // the regular window as the touch target
+ overlay->setTouchableRegion(Region({0, 0, 0, 0}));
+ mDispatcher->onWindowInfosChanged({{*overlay->getInfo(), *window->getInfo()}, {}, 0, 0});
+
+ // Now we can click with the mouse. The click should go into the regular window
+ mDispatcher->notifyMotion(MotionArgsBuilder(ACTION_DOWN, AINPUT_SOURCE_MOUSE)
+ .pointer(PointerBuilder(0, ToolType::MOUSE).x(100).y(110))
+ .build());
+ overlay->consumeMotionEvent(WithMotionAction(ACTION_HOVER_EXIT));
+ overlay->consumeMotionEvent(WithMotionAction(ACTION_OUTSIDE));
+ window->consumeMotionEvent(WithMotionAction(ACTION_DOWN));
+}
+
+/**
+ * Similar to above, but also has a spy on top that also catches the HOVER
+ * events. Also, instead of ACTION_DOWN, we are continuing to send the hovering
+ * stream to ensure that the spy receives hover events correctly.
+ */
+TEST_F(InputDispatcherTest, MouseClickUnderShrinkingTrustedOverlayWithSpy) {
+ std::shared_ptr<FakeApplicationHandle> app = std::make_shared<FakeApplicationHandle>();
+ sp<FakeWindowHandle> spyWindow =
+ sp<FakeWindowHandle>::make(app, mDispatcher, "Spy", ui::LogicalDisplayId::DEFAULT);
+ spyWindow->setFrame(Rect(0, 0, 200, 200));
+ spyWindow->setTrustedOverlay(true);
+ spyWindow->setSpy(true);
+ sp<FakeWindowHandle> overlay = sp<FakeWindowHandle>::make(app, mDispatcher, "Trusted overlay",
+ ui::LogicalDisplayId::DEFAULT);
+ overlay->setTrustedOverlay(true);
+ overlay->setWatchOutsideTouch(true);
+ overlay->setFrame(Rect(0, 0, 200, 200));
+
+ sp<FakeWindowHandle> window = sp<FakeWindowHandle>::make(app, mDispatcher, "Regular window",
+ ui::LogicalDisplayId::DEFAULT);
+ window->setFrame(Rect(0, 0, 200, 200));
+
+ mDispatcher->onWindowInfosChanged(
+ {{*spyWindow->getInfo(), *overlay->getInfo(), *window->getInfo()}, {}, 0, 0});
+ // Hover the mouse into the overlay
+ mDispatcher->notifyMotion(MotionArgsBuilder(ACTION_HOVER_MOVE, AINPUT_SOURCE_MOUSE)
+ .pointer(PointerBuilder(0, ToolType::MOUSE).x(100).y(110))
+ .build());
+ spyWindow->consumeMotionEvent(WithMotionAction(ACTION_HOVER_ENTER));
+ overlay->consumeMotionEvent(WithMotionAction(ACTION_HOVER_ENTER));
+
+ // Now, shrink the touchable region of the overlay! This will cause the cursor to suddenly have
+ // the regular window as the touch target
+ overlay->setTouchableRegion(Region({0, 0, 0, 0}));
+ mDispatcher->onWindowInfosChanged(
+ {{*spyWindow->getInfo(), *overlay->getInfo(), *window->getInfo()}, {}, 0, 0});
+
+ // Now we can click with the mouse. The click should go into the regular window
+ mDispatcher->notifyMotion(MotionArgsBuilder(ACTION_HOVER_MOVE, AINPUT_SOURCE_MOUSE)
+ .pointer(PointerBuilder(0, ToolType::MOUSE).x(110).y(110))
+ .build());
+ spyWindow->consumeMotionEvent(WithMotionAction(ACTION_HOVER_MOVE));
+ overlay->consumeMotionEvent(WithMotionAction(ACTION_HOVER_EXIT));
+ window->consumeMotionEvent(WithMotionAction(ACTION_HOVER_ENTER));
+}
+
using InputDispatcherMultiDeviceTest = InputDispatcherTest;
/**
@@ -12192,6 +12285,87 @@
<< "Drag and drop should not work with a hovering pointer";
}
+/**
+ * Two devices, we use the second pointer of Device A to start the drag, during the drag process, if
+ * we perform a click using Device B, the dispatcher should work well.
+ */
+TEST_F(InputDispatcherDragTests, DragAndDropWhenSplitTouchAndMultiDevice) {
+ const DeviceId deviceA = 1;
+ const DeviceId deviceB = 2;
+ // First down on second window with deviceA.
+ mDispatcher->notifyMotion(MotionArgsBuilder(ACTION_DOWN, AINPUT_SOURCE_TOUCHSCREEN)
+ .deviceId(deviceA)
+ .pointer(PointerBuilder(0, ToolType::FINGER).x(150).y(50))
+ .build());
+ mSecondWindow->consumeMotionEvent(AllOf(WithMotionAction(ACTION_DOWN), WithDeviceId(deviceA),
+ WithDisplayId(ui::LogicalDisplayId::DEFAULT)));
+
+ // Second down on first window with deviceA
+ mDispatcher->notifyMotion(MotionArgsBuilder(POINTER_1_DOWN, AINPUT_SOURCE_TOUCHSCREEN)
+ .deviceId(deviceA)
+ .pointer(PointerBuilder(0, ToolType::FINGER).x(150).y(50))
+ .pointer(PointerBuilder(1, ToolType::FINGER).x(50).y(50))
+ .build());
+ mWindow->consumeMotionEvent(AllOf(WithMotionAction(ACTION_DOWN), WithDeviceId(deviceA),
+ WithDisplayId(ui::LogicalDisplayId::DEFAULT)));
+ mSecondWindow->consumeMotionEvent(AllOf(WithMotionAction(ACTION_MOVE), WithDeviceId(deviceA),
+ WithDisplayId(ui::LogicalDisplayId::DEFAULT)));
+
+ // Perform drag and drop from first window.
+ ASSERT_TRUE(startDrag(/*sendDown=*/false));
+
+ // Click first window with device B, we should ensure dispatcher work well.
+ mDispatcher->notifyMotion(MotionArgsBuilder(ACTION_DOWN, AINPUT_SOURCE_MOUSE)
+ .deviceId(deviceB)
+ .pointer(PointerBuilder(0, ToolType::FINGER).x(50).y(50))
+ .build());
+ mWindow->consumeMotionEvent(AllOf(WithMotionAction(ACTION_DOWN), WithDeviceId(deviceB),
+ WithDisplayId(ui::LogicalDisplayId::DEFAULT)));
+
+ mDispatcher->notifyMotion(MotionArgsBuilder(ACTION_UP, AINPUT_SOURCE_MOUSE)
+ .deviceId(deviceB)
+ .pointer(PointerBuilder(0, ToolType::FINGER).x(50).y(50))
+ .build());
+ mWindow->consumeMotionEvent(AllOf(WithMotionAction(ACTION_UP), WithDeviceId(deviceB),
+ WithDisplayId(ui::LogicalDisplayId::DEFAULT)));
+
+ // Move with device A.
+ mDispatcher->notifyMotion(MotionArgsBuilder(ACTION_MOVE, AINPUT_SOURCE_TOUCHSCREEN)
+ .deviceId(deviceA)
+ .pointer(PointerBuilder(0, ToolType::FINGER).x(151).y(51))
+ .pointer(PointerBuilder(1, ToolType::FINGER).x(51).y(51))
+ .build());
+
+ mDragWindow->consumeMotionEvent(AllOf(WithMotionAction(ACTION_MOVE), WithDeviceId(deviceA),
+ WithDisplayId(ui::LogicalDisplayId::DEFAULT),
+ WithFlags(AMOTION_EVENT_FLAG_NO_FOCUS_CHANGE)));
+ mWindow->consumeDragEvent(false, 51, 51);
+ mSecondWindow->consumeMotionEvent(AllOf(WithMotionAction(ACTION_MOVE), WithDeviceId(deviceA),
+ WithDisplayId(ui::LogicalDisplayId::DEFAULT)));
+
+ // Releasing the drag pointer should cause drop.
+ mDispatcher->notifyMotion(MotionArgsBuilder(POINTER_1_UP, AINPUT_SOURCE_TOUCHSCREEN)
+ .deviceId(deviceA)
+ .pointer(PointerBuilder(0, ToolType::FINGER).x(151).y(51))
+ .pointer(PointerBuilder(1, ToolType::FINGER).x(51).y(51))
+ .build());
+ mDragWindow->consumeMotionEvent(AllOf(WithMotionAction(ACTION_UP), WithDeviceId(deviceA),
+ WithDisplayId(ui::LogicalDisplayId::DEFAULT),
+ WithFlags(AMOTION_EVENT_FLAG_NO_FOCUS_CHANGE)));
+ mFakePolicy->assertDropTargetEquals(*mDispatcher, mWindow->getToken());
+ mSecondWindow->consumeMotionEvent(AllOf(WithMotionAction(ACTION_MOVE), WithDeviceId(deviceA),
+ WithDisplayId(ui::LogicalDisplayId::DEFAULT)));
+
+ // Release all pointers.
+ mDispatcher->notifyMotion(MotionArgsBuilder(ACTION_UP, AINPUT_SOURCE_TOUCHSCREEN)
+ .deviceId(deviceA)
+ .pointer(PointerBuilder(0, ToolType::FINGER).x(151).y(51))
+ .build());
+ mSecondWindow->consumeMotionEvent(AllOf(WithMotionAction(ACTION_UP), WithDeviceId(deviceA),
+ WithDisplayId(ui::LogicalDisplayId::DEFAULT)));
+ mWindow->assertNoEvents();
+}
+
class InputDispatcherDropInputFeatureTest : public InputDispatcherTest {};
TEST_F(InputDispatcherDropInputFeatureTest, WindowDropsInput) {
diff --git a/services/inputflinger/tests/PointerChoreographer_test.cpp b/services/inputflinger/tests/PointerChoreographer_test.cpp
index 9a5b6a7..a1279ff 100644
--- a/services/inputflinger/tests/PointerChoreographer_test.cpp
+++ b/services/inputflinger/tests/PointerChoreographer_test.cpp
@@ -830,15 +830,20 @@
pc->assertSpotCount(DISPLAY_ID, 0);
}
+/**
+ * In this test, we simulate the complete event of the stylus approaching and clicking on the
+ * screen, and then leaving the screen. We should ensure that spots are displayed correctly.
+ */
TEST_F(PointerChoreographerTest, TouchSetsSpotsForStylusEvent) {
mChoreographer.setShowTouchesEnabled(true);
+ mChoreographer.setStylusPointerIconEnabled(false);
mChoreographer.notifyInputDevicesChanged(
{/*id=*/0,
{generateTestDeviceInfo(DEVICE_ID, AINPUT_SOURCE_TOUCHSCREEN | AINPUT_SOURCE_STYLUS,
DISPLAY_ID)}});
- // Emit down event with stylus properties.
- mChoreographer.notifyMotion(MotionArgsBuilder(AMOTION_EVENT_ACTION_DOWN,
+ // First, the stylus begin to approach the screen.
+ mChoreographer.notifyMotion(MotionArgsBuilder(AMOTION_EVENT_ACTION_HOVER_ENTER,
AINPUT_SOURCE_TOUCHSCREEN | AINPUT_SOURCE_STYLUS)
.pointer(STYLUS_POINTER)
.deviceId(DEVICE_ID)
@@ -846,6 +851,72 @@
.build());
auto pc = assertPointerControllerCreated(ControllerType::TOUCH);
pc->assertSpotCount(DISPLAY_ID, 1);
+
+ mChoreographer.notifyMotion(MotionArgsBuilder(AMOTION_EVENT_ACTION_HOVER_MOVE,
+ AINPUT_SOURCE_TOUCHSCREEN | AINPUT_SOURCE_STYLUS)
+ .pointer(STYLUS_POINTER)
+ .deviceId(DEVICE_ID)
+ .displayId(DISPLAY_ID)
+ .build());
+ pc->assertSpotCount(DISPLAY_ID, 1);
+
+ mChoreographer.notifyMotion(MotionArgsBuilder(AMOTION_EVENT_ACTION_HOVER_EXIT,
+ AINPUT_SOURCE_TOUCHSCREEN | AINPUT_SOURCE_STYLUS)
+ .pointer(STYLUS_POINTER)
+ .deviceId(DEVICE_ID)
+ .displayId(DISPLAY_ID)
+ .build());
+ pc->assertSpotCount(DISPLAY_ID, 0);
+
+ // Now, use stylus touch the screen.
+ mChoreographer.notifyMotion(MotionArgsBuilder(AMOTION_EVENT_ACTION_DOWN,
+ AINPUT_SOURCE_TOUCHSCREEN | AINPUT_SOURCE_STYLUS)
+ .pointer(STYLUS_POINTER)
+ .deviceId(DEVICE_ID)
+ .displayId(DISPLAY_ID)
+ .build());
+ pc->assertSpotCount(DISPLAY_ID, 1);
+
+ mChoreographer.notifyMotion(MotionArgsBuilder(AMOTION_EVENT_ACTION_MOVE,
+ AINPUT_SOURCE_TOUCHSCREEN | AINPUT_SOURCE_STYLUS)
+ .pointer(STYLUS_POINTER)
+ .deviceId(DEVICE_ID)
+ .displayId(DISPLAY_ID)
+ .build());
+ pc->assertSpotCount(DISPLAY_ID, 1);
+
+ mChoreographer.notifyMotion(MotionArgsBuilder(AMOTION_EVENT_ACTION_UP,
+ AINPUT_SOURCE_TOUCHSCREEN | AINPUT_SOURCE_STYLUS)
+ .pointer(STYLUS_POINTER)
+ .deviceId(DEVICE_ID)
+ .displayId(DISPLAY_ID)
+ .build());
+ pc->assertSpotCount(DISPLAY_ID, 0);
+
+ // Then, the stylus start leave from the screen.
+ mChoreographer.notifyMotion(MotionArgsBuilder(AMOTION_EVENT_ACTION_HOVER_ENTER,
+ AINPUT_SOURCE_TOUCHSCREEN | AINPUT_SOURCE_STYLUS)
+ .pointer(STYLUS_POINTER)
+ .deviceId(DEVICE_ID)
+ .displayId(DISPLAY_ID)
+ .build());
+ pc->assertSpotCount(DISPLAY_ID, 1);
+
+ mChoreographer.notifyMotion(MotionArgsBuilder(AMOTION_EVENT_ACTION_HOVER_MOVE,
+ AINPUT_SOURCE_TOUCHSCREEN | AINPUT_SOURCE_STYLUS)
+ .pointer(STYLUS_POINTER)
+ .deviceId(DEVICE_ID)
+ .displayId(DISPLAY_ID)
+ .build());
+ pc->assertSpotCount(DISPLAY_ID, 1);
+
+ mChoreographer.notifyMotion(MotionArgsBuilder(AMOTION_EVENT_ACTION_HOVER_EXIT,
+ AINPUT_SOURCE_TOUCHSCREEN | AINPUT_SOURCE_STYLUS)
+ .pointer(STYLUS_POINTER)
+ .deviceId(DEVICE_ID)
+ .displayId(DISPLAY_ID)
+ .build());
+ pc->assertSpotCount(DISPLAY_ID, 0);
}
TEST_F(PointerChoreographerTest, TouchSetsSpotsForTwoDisplays) {
diff --git a/services/surfaceflinger/OWNERS b/services/surfaceflinger/OWNERS
index ffc1dd7..fa0ecee 100644
--- a/services/surfaceflinger/OWNERS
+++ b/services/surfaceflinger/OWNERS
@@ -5,6 +5,8 @@
domlaskowski@google.com
jreck@google.com
lpy@google.com
+mattbuckley@google.com
+melodymhsu@google.com
pdwilliams@google.com
racarr@google.com
ramindani@google.com
@@ -12,3 +14,4 @@
sallyqi@google.com
scroggo@google.com
vishnun@google.com
+xwxw@google.com
diff --git a/services/surfaceflinger/PowerAdvisor/OWNERS b/services/surfaceflinger/PowerAdvisor/OWNERS
new file mode 100644
index 0000000..9f40e27
--- /dev/null
+++ b/services/surfaceflinger/PowerAdvisor/OWNERS
@@ -0,0 +1 @@
+file:platform/frameworks/base:/ADPF_OWNERS
\ No newline at end of file
diff --git a/services/surfaceflinger/tests/unittests/RefreshRateSelectorTest.cpp b/services/surfaceflinger/tests/unittests/RefreshRateSelectorTest.cpp
index 4ffd52b..d64cf2f 100644
--- a/services/surfaceflinger/tests/unittests/RefreshRateSelectorTest.cpp
+++ b/services/surfaceflinger/tests/unittests/RefreshRateSelectorTest.cpp
@@ -100,7 +100,9 @@
const std::vector<Fps>& knownFrameRates() const { return mKnownFrameRates; }
using RefreshRateSelector::GetRankedFrameRatesCache;
- auto& mutableGetRankedRefreshRatesCache() { return mGetRankedFrameRatesCache; }
+ auto& mutableGetRankedRefreshRatesCache() NO_THREAD_SAFETY_ANALYSIS {
+ return mGetRankedFrameRatesCache;
+ }
auto getRankedFrameRates(const std::vector<LayerRequirement>& layers,
GlobalSignals signals = {}, Fps pacesetterFps = {}) const {
@@ -138,7 +140,9 @@
return setPolicy(policy);
}
- const auto& getPrimaryFrameRates() const { return mPrimaryFrameRates; }
+ const auto& getPrimaryFrameRates() const NO_THREAD_SAFETY_ANALYSIS {
+ return mPrimaryFrameRates;
+ }
};
class RefreshRateSelectorTest : public testing::TestWithParam<Config::FrameRateOverride> {
diff --git a/services/surfaceflinger/tests/unittests/TestableSurfaceFlinger.h b/services/surfaceflinger/tests/unittests/TestableSurfaceFlinger.h
index c11b555..0d13dc5 100644
--- a/services/surfaceflinger/tests/unittests/TestableSurfaceFlinger.h
+++ b/services/surfaceflinger/tests/unittests/TestableSurfaceFlinger.h
@@ -516,8 +516,10 @@
return mFlinger->SurfaceFlinger::getDisplayNativePrimaries(displayToken, primaries);
}
- auto& getTransactionQueue() { return mFlinger->mTransactionHandler.mLocklessTransactionQueue; }
- auto& getPendingTransactionQueue() {
+ auto& getTransactionQueue() NO_THREAD_SAFETY_ANALYSIS {
+ return mFlinger->mTransactionHandler.mLocklessTransactionQueue;
+ }
+ auto& getPendingTransactionQueue() NO_THREAD_SAFETY_ANALYSIS {
ftl::FakeGuard guard(kMainThreadContext);
return mFlinger->mTransactionHandler.mPendingTransactionQueues;
}
@@ -706,7 +708,9 @@
auto& mutableMinAcquiredBuffers() { return SurfaceFlinger::minAcquiredBuffers; }
auto& mutableLayersPendingRemoval() { return mFlinger->mLayersPendingRemoval; }
- auto& mutableLayerSnapshotBuilder() { return mFlinger->mLayerSnapshotBuilder; };
+ auto& mutableLayerSnapshotBuilder() NO_THREAD_SAFETY_ANALYSIS {
+ return mFlinger->mLayerSnapshotBuilder;
+ }
auto fromHandle(const sp<IBinder>& handle) { return LayerHandle::getLayer(handle); }