diff options
45 files changed, 845 insertions, 174 deletions
diff --git a/Android.bp b/Android.bp index 2520a71968..119c7ea5a4 100644 --- a/Android.bp +++ b/Android.bp @@ -119,3 +119,9 @@ filegroup { srcs: ["aidl/android/hardware/display/IDeviceProductInfoConstants.aidl"], path: "aidl", } + +dirgroup { + name: "trusty_dirgroup_frameworks_native", + dirs: ["libs/binder"], + visibility: ["//trusty/vendor/google/aosp/scripts"], +} diff --git a/cmds/dumpstate/DumpstateInternal.cpp b/cmds/dumpstate/DumpstateInternal.cpp index 6f7fea3432..ce7c55c7b3 100644 --- a/cmds/dumpstate/DumpstateInternal.cpp +++ b/cmds/dumpstate/DumpstateInternal.cpp @@ -108,7 +108,7 @@ bool DropRootUser() { const uint32_t cap_syslog_mask = CAP_TO_MASK(CAP_SYSLOG); const uint32_t cap_syslog_index = CAP_TO_INDEX(CAP_SYSLOG); - bool has_cap_syslog = (capdata[cap_syslog_index].effective & cap_syslog_mask) != 0; + bool has_cap_syslog = (capdata[cap_syslog_index].permitted & cap_syslog_mask) != 0; memset(&capdata, 0, sizeof(capdata)); if (has_cap_syslog) { diff --git a/libs/binder/Android.bp b/libs/binder/Android.bp index 379b609e9f..6903cb5d68 100644 --- a/libs/binder/Android.bp +++ b/libs/binder/Android.bp @@ -489,6 +489,7 @@ cc_defaults { "ProcessState.cpp", "Static.cpp", ":libbinder_aidl", + ":libbinder_accessor_aidl", ":libbinder_device_interface_sources", ], target: { @@ -801,7 +802,6 @@ filegroup { "aidl/android/os/IServiceManager.aidl", "aidl/android/os/Service.aidl", "aidl/android/os/ServiceDebugInfo.aidl", - ":libbinder_accessor_aidl", ], path: "aidl", } @@ -812,26 +812,7 @@ filegroup { "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/BackendUnifiedServiceManager.cpp b/libs/binder/BackendUnifiedServiceManager.cpp index 52b485a6f6..f7b9f059a5 100644 --- a/libs/binder/BackendUnifiedServiceManager.cpp +++ b/libs/binder/BackendUnifiedServiceManager.cpp @@ -31,7 +31,8 @@ constexpr bool kUseCache = false; #endif using AidlServiceManager = android::os::IServiceManager; -using IAccessor = android::os::IAccessor; +using android::os::IAccessor; +using binder::Status; static const char* kStaticCachableList[] = { // go/keep-sorted start @@ -39,10 +40,14 @@ static const char* kStaticCachableList[] = { "account", "activity", "alarm", + "android.frameworks.stats.IStats/default", "android.system.keystore2.IKeystoreService/default", "appops", "audio", + "autofill", + "batteryproperties", "batterystats", + "biometic", "carrier_config", "connectivity", "content", @@ -58,6 +63,7 @@ static const char* kStaticCachableList[] = { "jobscheduler", "legacy_permission", "location", + "lock_settings", "media.extractor", "media.metrics", "media.player", @@ -78,15 +84,19 @@ static const char* kStaticCachableList[] = { "phone", "platform_compat", "power", + "processinfo", "role", + "sensitive_content_protection_service", "sensorservice", "statscompanion", "telephony.registry", "thermalservice", "time_detector", + "tracing.proxy", "trust", "uimode", "user", + "vibrator", "virtualdevice", "virtualdevice_native", "webviewupdate", @@ -109,19 +119,38 @@ bool BinderCacheWithInvalidation::isClientSideCachingEnabled(const std::string& return false; } -binder::Status BackendUnifiedServiceManager::updateCache(const std::string& serviceName, - const os::Service& service) { +Status BackendUnifiedServiceManager::updateCache(const std::string& serviceName, + const os::Service& service) { if (!kUseCache) { - return binder::Status::ok(); + return Status::ok(); } + std::string traceStr; + if (atrace_is_tag_enabled(ATRACE_TAG_AIDL)) { + traceStr = "BinderCacheWithInvalidation::updateCache : " + serviceName; + } + binder::ScopedTrace aidlTrace(ATRACE_TAG_AIDL, traceStr.c_str()); + if (service.getTag() == os::Service::Tag::binder) { sp<IBinder> binder = service.get<os::Service::Tag::binder>(); - if (binder && mCacheForGetService->isClientSideCachingEnabled(serviceName) && - binder->isBinderAlive()) { + if (!binder) { + binder::ScopedTrace + aidlTrace(ATRACE_TAG_AIDL, + "BinderCacheWithInvalidation::updateCache failed: binder_null"); + } else if (!binder->isBinderAlive()) { + binder::ScopedTrace aidlTrace(ATRACE_TAG_AIDL, + "BinderCacheWithInvalidation::updateCache failed: " + "isBinderAlive_false"); + } else if (mCacheForGetService->isClientSideCachingEnabled(serviceName)) { + binder::ScopedTrace aidlTrace(ATRACE_TAG_AIDL, + "BinderCacheWithInvalidation::updateCache successful"); return mCacheForGetService->setItem(serviceName, binder); + } else { + binder::ScopedTrace aidlTrace(ATRACE_TAG_AIDL, + "BinderCacheWithInvalidation::updateCache failed: " + "caching_not_enabled"); } } - return binder::Status::ok(); + return Status::ok(); } bool BackendUnifiedServiceManager::returnIfCached(const std::string& serviceName, @@ -147,21 +176,20 @@ sp<AidlServiceManager> BackendUnifiedServiceManager::getImpl() { return mTheRealServiceManager; } -binder::Status BackendUnifiedServiceManager::getService(const ::std::string& name, - sp<IBinder>* _aidl_return) { +Status BackendUnifiedServiceManager::getService(const ::std::string& name, + sp<IBinder>* _aidl_return) { os::Service service; - binder::Status status = getService2(name, &service); + Status status = getService2(name, &service); *_aidl_return = service.get<os::Service::Tag::binder>(); return status; } -binder::Status BackendUnifiedServiceManager::getService2(const ::std::string& name, - os::Service* _out) { +Status BackendUnifiedServiceManager::getService2(const ::std::string& name, os::Service* _out) { if (returnIfCached(name, _out)) { - return binder::Status::ok(); + return Status::ok(); } os::Service service; - binder::Status status = mTheRealServiceManager->getService2(name, &service); + Status status = mTheRealServiceManager->getService2(name, &service); if (status.isOk()) { status = toBinderService(name, service, _out); @@ -172,14 +200,13 @@ binder::Status BackendUnifiedServiceManager::getService2(const ::std::string& na return status; } -binder::Status BackendUnifiedServiceManager::checkService(const ::std::string& name, - os::Service* _out) { +Status BackendUnifiedServiceManager::checkService(const ::std::string& name, os::Service* _out) { os::Service service; if (returnIfCached(name, _out)) { - return binder::Status::ok(); + return Status::ok(); } - binder::Status status = mTheRealServiceManager->checkService(name, &service); + Status status = mTheRealServiceManager->checkService(name, &service); if (status.isOk()) { status = toBinderService(name, service, _out); if (status.isOk()) { @@ -189,16 +216,15 @@ binder::Status BackendUnifiedServiceManager::checkService(const ::std::string& n return status; } -binder::Status BackendUnifiedServiceManager::toBinderService(const ::std::string& name, - const os::Service& in, - os::Service* _out) { +Status BackendUnifiedServiceManager::toBinderService(const ::std::string& name, + const os::Service& in, os::Service* _out) { switch (in.getTag()) { case os::Service::Tag::binder: { if (in.get<os::Service::Tag::binder>() == nullptr) { // failed to find a service. Check to see if we have any local // injected Accessors for this service. os::Service accessor; - binder::Status status = getInjectedAccessor(name, &accessor); + Status status = getInjectedAccessor(name, &accessor); if (!status.isOk()) { *_out = os::Service::make<os::Service::Tag::binder>(nullptr); return status; @@ -214,7 +240,7 @@ binder::Status BackendUnifiedServiceManager::toBinderService(const ::std::string } *_out = in; - return binder::Status::ok(); + return Status::ok(); } case os::Service::Tag::accessor: { sp<IBinder> accessorBinder = in.get<os::Service::Tag::accessor>(); @@ -222,11 +248,11 @@ binder::Status BackendUnifiedServiceManager::toBinderService(const ::std::string if (accessor == nullptr) { ALOGE("Service#accessor doesn't have accessor. VM is maybe starting..."); *_out = os::Service::make<os::Service::Tag::binder>(nullptr); - return binder::Status::ok(); + return Status::ok(); } auto request = [=] { os::ParcelFileDescriptor fd; - binder::Status ret = accessor->addConnection(&fd); + Status ret = accessor->addConnection(&fd); if (ret.isOk()) { return base::unique_fd(fd.release()); } else { @@ -239,11 +265,11 @@ binder::Status BackendUnifiedServiceManager::toBinderService(const ::std::string if (status != OK) { ALOGE("Failed to set up preconnected binder RPC client: %s", statusToString(status).c_str()); - return binder::Status::fromStatusT(status); + return Status::fromStatusT(status); } session->setSessionSpecificRoot(accessorBinder); *_out = os::Service::make<os::Service::Tag::binder>(session->getRootObject()); - return binder::Status::ok(); + return Status::ok(); } default: { LOG_ALWAYS_FATAL("Unknown service type: %d", in.getTag()); @@ -251,53 +277,52 @@ binder::Status BackendUnifiedServiceManager::toBinderService(const ::std::string } } -binder::Status BackendUnifiedServiceManager::addService(const ::std::string& name, - const sp<IBinder>& service, - bool allowIsolated, int32_t dumpPriority) { +Status BackendUnifiedServiceManager::addService(const ::std::string& name, + const sp<IBinder>& service, bool allowIsolated, + int32_t dumpPriority) { return mTheRealServiceManager->addService(name, service, allowIsolated, dumpPriority); } -binder::Status BackendUnifiedServiceManager::listServices( - int32_t dumpPriority, ::std::vector<::std::string>* _aidl_return) { +Status BackendUnifiedServiceManager::listServices(int32_t dumpPriority, + ::std::vector<::std::string>* _aidl_return) { return mTheRealServiceManager->listServices(dumpPriority, _aidl_return); } -binder::Status BackendUnifiedServiceManager::registerForNotifications( +Status BackendUnifiedServiceManager::registerForNotifications( const ::std::string& name, const sp<os::IServiceCallback>& callback) { return mTheRealServiceManager->registerForNotifications(name, callback); } -binder::Status BackendUnifiedServiceManager::unregisterForNotifications( +Status BackendUnifiedServiceManager::unregisterForNotifications( const ::std::string& name, const sp<os::IServiceCallback>& callback) { return mTheRealServiceManager->unregisterForNotifications(name, callback); } -binder::Status BackendUnifiedServiceManager::isDeclared(const ::std::string& name, - bool* _aidl_return) { +Status BackendUnifiedServiceManager::isDeclared(const ::std::string& name, bool* _aidl_return) { return mTheRealServiceManager->isDeclared(name, _aidl_return); } -binder::Status BackendUnifiedServiceManager::getDeclaredInstances( +Status BackendUnifiedServiceManager::getDeclaredInstances( const ::std::string& iface, ::std::vector<::std::string>* _aidl_return) { return mTheRealServiceManager->getDeclaredInstances(iface, _aidl_return); } -binder::Status BackendUnifiedServiceManager::updatableViaApex( +Status BackendUnifiedServiceManager::updatableViaApex( const ::std::string& name, ::std::optional<::std::string>* _aidl_return) { return mTheRealServiceManager->updatableViaApex(name, _aidl_return); } -binder::Status BackendUnifiedServiceManager::getUpdatableNames( - const ::std::string& apexName, ::std::vector<::std::string>* _aidl_return) { +Status BackendUnifiedServiceManager::getUpdatableNames(const ::std::string& apexName, + ::std::vector<::std::string>* _aidl_return) { return mTheRealServiceManager->getUpdatableNames(apexName, _aidl_return); } -binder::Status BackendUnifiedServiceManager::getConnectionInfo( +Status BackendUnifiedServiceManager::getConnectionInfo( const ::std::string& name, ::std::optional<os::ConnectionInfo>* _aidl_return) { return mTheRealServiceManager->getConnectionInfo(name, _aidl_return); } -binder::Status BackendUnifiedServiceManager::registerClientCallback( +Status BackendUnifiedServiceManager::registerClientCallback( const ::std::string& name, const sp<IBinder>& service, const sp<os::IClientCallback>& callback) { return mTheRealServiceManager->registerClientCallback(name, service, callback); } -binder::Status BackendUnifiedServiceManager::tryUnregisterService(const ::std::string& name, - const sp<IBinder>& service) { +Status BackendUnifiedServiceManager::tryUnregisterService(const ::std::string& name, + const sp<IBinder>& service) { return mTheRealServiceManager->tryUnregisterService(name, service); } -binder::Status BackendUnifiedServiceManager::getServiceDebugInfo( +Status BackendUnifiedServiceManager::getServiceDebugInfo( ::std::vector<os::ServiceDebugInfo>* _aidl_return) { return mTheRealServiceManager->getServiceDebugInfo(_aidl_return); } diff --git a/libs/binder/BackendUnifiedServiceManager.h b/libs/binder/BackendUnifiedServiceManager.h index 47b2ec9f2c..feb8470032 100644 --- a/libs/binder/BackendUnifiedServiceManager.h +++ b/libs/binder/BackendUnifiedServiceManager.h @@ -18,6 +18,7 @@ #include <android/os/BnServiceManager.h> #include <android/os/IServiceManager.h> #include <binder/IPCThreadState.h> +#include <binder/Trace.h> #include <map> #include <memory> @@ -59,6 +60,12 @@ public: } bool removeItem(const std::string& key, const sp<IBinder>& who) { + std::string traceStr; + uint64_t tag = ATRACE_TAG_AIDL; + if (atrace_is_tag_enabled(tag)) { + traceStr = "BinderCacheWithInvalidation::removeItem " + key; + } + binder::ScopedTrace aidlTrace(tag, traceStr.c_str()); std::lock_guard<std::mutex> lock(mCacheMutex); if (auto it = mCache.find(key); it != mCache.end()) { if (it->second.service == who) { @@ -81,11 +88,22 @@ public: if (item->localBinder() == nullptr) { status_t status = item->linkToDeath(deathRecipient); if (status != android::OK) { + std::string traceStr; + uint64_t tag = ATRACE_TAG_AIDL; + if (atrace_is_tag_enabled(tag)) { + traceStr = + "BinderCacheWithInvalidation::setItem Failed LinkToDeath for service " + + key + " : " + std::to_string(status); + } + binder::ScopedTrace aidlTrace(tag, traceStr.c_str()); + ALOGE("Failed to linkToDeath binder for service %s. Error: %d", key.c_str(), status); return binder::Status::fromStatusT(status); } } + binder::ScopedTrace aidlTrace(ATRACE_TAG_AIDL, + "BinderCacheWithInvalidation::setItem Successfully Cached"); std::lock_guard<std::mutex> lock(mCacheMutex); Entry entry = {.service = item, .deathRecipient = deathRecipient}; mCache[key] = entry; diff --git a/libs/binder/BpBinder.cpp b/libs/binder/BpBinder.cpp index eae844ca03..3758b6521c 100644 --- a/libs/binder/BpBinder.cpp +++ b/libs/binder/BpBinder.cpp @@ -197,7 +197,9 @@ sp<BpBinder> BpBinder::create(int32_t handle, std::function<void()>* postTask) { && 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 77b80ef3de..32388db076 100644 --- a/libs/binder/IServiceManager.cpp +++ b/libs/binder/IServiceManager.cpp @@ -386,7 +386,7 @@ status_t validateAccessor(const String16& instance, const sp<IBinder>& binder) { 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 @@ sp<IBinder> createAccessor(const String16& instance, 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/TEST_MAPPING b/libs/binder/TEST_MAPPING index 95a5da20ae..ab449572d6 100644 --- a/libs/binder/TEST_MAPPING +++ b/libs/binder/TEST_MAPPING @@ -94,6 +94,9 @@ }, { "name": "libbinder_rpc_unstable_bindgen_test" + }, + { + "name": "binderCacheUnitTest" } ], "presubmit-large": [ @@ -133,9 +136,6 @@ { "name": "binder_sdk_test", "host": true - }, - { - "name": "binderCacheUnitTest" } ], "imports": [ diff --git a/libs/binder/aidl/android/content/pm/ApexStagedEvent.aidl b/libs/binder/aidl/android/content/pm/ApexStagedEvent.aidl index 75f87530f9..9bac38641e 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 @@ package android.content.pm; * @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 3ddfefa311..0f0be2f22b 100644 --- a/libs/binder/aidl/android/content/pm/IPackageManagerNative.aidl +++ b/libs/binder/aidl/android/content/pm/IPackageManagerNative.aidl @@ -135,13 +135,7 @@ interface IPackageManagerNative { 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 949835b452..8f7ad30779 100644 --- a/libs/binder/aidl/android/content/pm/StagedApexInfo.aidl +++ b/libs/binder/aidl/android/content/pm/StagedApexInfo.aidl @@ -22,6 +22,7 @@ package android.content.pm; * * @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 2b23276d1c..81f7cdbcb1 100644 --- a/libs/binder/include/binder/IServiceManager.h +++ b/libs/binder/include/binder/IServiceManager.h @@ -291,6 +291,28 @@ LIBBINDER_EXPORTED sp<IBinder> createAccessor(const String16& instance, * \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 c394ac70c8..15a0da765e 100644 --- a/libs/binder/include/binder/Parcel.h +++ b/libs/binder/include/binder/Parcel.h @@ -1240,7 +1240,7 @@ private: 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/SafeInterface.h b/libs/binder/include/binder/SafeInterface.h index c671eed039..0b4f196b8f 100644 --- a/libs/binder/include/binder/SafeInterface.h +++ b/libs/binder/include/binder/SafeInterface.h @@ -152,6 +152,14 @@ public: return callParcel("writeParcelableVector", [&]() { return parcel->writeParcelableVector(v); }); } + + status_t read(const Parcel& parcel, std::vector<bool>* v) const { + return callParcel("readBoolVector", [&]() { return parcel.readBoolVector(v); }); + } + status_t write(Parcel* parcel, const std::vector<bool>& v) const { + return callParcel("writeBoolVector", [&]() { return parcel->writeBoolVector(v); }); + } + status_t read(const Parcel& parcel, float* f) const { return callParcel("readFloat", [&]() { return parcel.readFloat(f); }); } diff --git a/libs/binder/ndk/binder_rpc.cpp b/libs/binder/ndk/binder_rpc.cpp index 3c32a39e98..886eb4bdf8 100644 --- a/libs/binder/ndk/binder_rpc.cpp +++ b/libs/binder/ndk/binder_rpc.cpp @@ -255,7 +255,7 @@ ABinderRpc_Accessor* ABinderRpc_Accessor_new( "new variant was added to the ABinderRpc_ConnectionInfo and this needs to be " "updated."); } - return OK; + return STATUS_OK; }; sp<IBinder> accessorBinder = android::createAccessor(String16(instance), std::move(generate)); if (accessorBinder == nullptr) { @@ -302,6 +302,28 @@ ABinderRpc_Accessor* ABinderRpc_Accessor_fromBinder(const char* instance, AIBind } } +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 STATUS_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 ABinderRpc_Connection_new"); 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 8296356d6b..0ad110ee83 100644 --- a/libs/binder/ndk/include_cpp/android/binder_interface_utils.h +++ b/libs/binder/ndk/include_cpp/android/binder_interface_utils.h @@ -30,7 +30,7 @@ #include <android/binder_auto_utils.h> #include <android/binder_ibinder.h> -#if defined(__ANDROID_VENDOR__) +#if defined(__ANDROID_VENDOR_API__) #include <android/llndk-versioning.h> #elif !defined(API_LEVEL_AT_LEAST) #if defined(__BIONIC__) @@ -39,7 +39,7 @@ #else #define API_LEVEL_AT_LEAST(sdk_api_level, vendor_api_level) (true) #endif // __BIONIC__ -#endif // __ANDROID_VENDOR__ +#endif // __ANDROID_VENDOR_API__ #if __has_include(<android/binder_shell.h>) #include <android/binder_shell.h> @@ -297,11 +297,10 @@ AIBinder_Class* ICInterface::defineClass(const char* interfaceDescriptor, } #endif -// TODO(b/368559337): fix versioning on product partition -#if !defined(__ANDROID_PRODUCT__) && \ - (defined(__ANDROID_UNAVAILABLE_SYMBOLS_ARE_WEAK__) || __ANDROID_API__ >= 36) +#if defined(__ANDROID_UNAVAILABLE_SYMBOLS_ARE_WEAK__) || __ANDROID_API__ >= 36 if API_LEVEL_AT_LEAST (36, 202504) { - if (codeToFunction != nullptr) { + if (codeToFunction != nullptr && + (&AIBinder_Class_setTransactionCodeToFunctionNameMap != nullptr)) { AIBinder_Class_setTransactionCodeToFunctionNameMap(clazz, codeToFunction, functionCount); } 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 c1d0e9f9fe..83976b3771 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_platform/android/binder_rpc.h b/libs/binder/ndk/include_platform/android/binder_rpc.h index 71325541cd..66667d33bd 100644 --- a/libs/binder/ndk/include_platform/android/binder_rpc.h +++ b/libs/binder/ndk/include_platform/android/binder_rpc.h @@ -265,6 +265,40 @@ ABinderRpc_Accessor* _Nullable ABinderRpc_Accessor_fromBinder(const char* _Nonnu __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. + * \return STATUS_OK on success. + * STATUS_UNEXPECTED_NULL if instance or binder arguments are null. + * STATUS_BAD_TYPE if the binder is not an IAccessor. + * STATUS_NAME_NOT_FOUND if the binder is an IAccessor, but not + * associated with the provided instance name. + */ +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). * diff --git a/libs/binder/ndk/libbinder_ndk.map.txt b/libs/binder/ndk/libbinder_ndk.map.txt index c885816e56..4d691f8e8a 100644 --- a/libs/binder/ndk/libbinder_ndk.map.txt +++ b/libs/binder/ndk/libbinder_ndk.map.txt @@ -257,6 +257,7 @@ LIBBINDER_NDK36 { # introduced=36 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/rust/Android.bp b/libs/binder/rust/Android.bp index 4545d7bb2e..020ebcce95 100644 --- a/libs/binder/rust/Android.bp +++ b/libs/binder/rust/Android.bp @@ -16,7 +16,6 @@ rust_library { "libdowncast_rs", "liblibc", "liblog_rust", - "libnix", ], host_supported: true, vendor_available: true, @@ -200,7 +199,6 @@ rust_test { "libdowncast_rs", "liblibc", "liblog_rust", - "libnix", ], } diff --git a/libs/binder/rust/src/lib.rs b/libs/binder/rust/src/lib.rs index 0e8e388c2b..f7f3f35c9f 100644 --- a/libs/binder/rust/src/lib.rs +++ b/libs/binder/rust/src/lib.rs @@ -123,7 +123,7 @@ pub use service::{ #[cfg(not(trusty))] pub use state::{ProcessState, ThreadState}; #[cfg(not(any(android_vendor, android_vndk)))] -pub use system_only::{Accessor, ConnectionInfo}; +pub use system_only::{delegate_accessor, Accessor, ConnectionInfo}; /// Binder result containing a [`Status`] on error. pub type Result<T> = std::result::Result<T, Status>; diff --git a/libs/binder/rust/src/system_only.rs b/libs/binder/rust/src/system_only.rs index a91d84d865..9833cbe4e2 100644 --- a/libs/binder/rust/src/system_only.rs +++ b/libs/binder/rust/src/system_only.rs @@ -14,16 +14,17 @@ * 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 libc::{sockaddr, sockaddr_un, sockaddr_vm, socklen_t}; use std::sync::Arc; -use std::{fmt, ptr}; +use std::{fmt, mem, ptr}; /// Rust wrapper around ABinderRpc_Accessor objects for RPC binder service management. /// @@ -42,9 +43,9 @@ impl fmt::Debug for Accessor { #[derive(Debug, Clone, Copy, PartialEq, Eq)] pub enum ConnectionInfo { /// For vsock connection - Vsock(VsockAddr), + Vsock(sockaddr_vm), /// For unix domain socket connection - Unix(UnixAddr), + Unix(sockaddr_un), } /// Safety: A `Accessor` is a wrapper around `ABinderRpc_Accessor` which is @@ -146,13 +147,21 @@ impl Accessor { 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()) } + unsafe { + sys::ABinderRpc_ConnectionInfo_new( + &addr as *const sockaddr_vm as *const sockaddr, + mem::size_of::<sockaddr_vm>() as socklen_t, + ) + } } 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()) + sys::ABinderRpc_ConnectionInfo_new( + &addr as *const sockaddr_un as *const sockaddr, + mem::size_of::<sockaddr_un>() as socklen_t, + ) } } } @@ -185,3 +194,27 @@ impl Drop for 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/tests/integration.rs b/libs/binder/rust/tests/integration.rs index bdb7e4a8d8..489fa0aa69 100644 --- a/libs/binder/rust/tests/integration.rs +++ b/libs/binder/rust/tests/integration.rs @@ -945,6 +945,43 @@ mod tests { 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/tests/Android.bp b/libs/binder/tests/Android.bp index 8b0dda33dd..28a3f654b8 100644 --- a/libs/binder/tests/Android.bp +++ b/libs/binder/tests/Android.bp @@ -731,6 +731,9 @@ cc_test { "liblog", "libutils", ], + static_libs: [ + "libgmock", + ], test_suites: [ "general-tests", "vts", diff --git a/libs/binder/tests/binderLibTest.cpp b/libs/binder/tests/binderLibTest.cpp index bcab6decca..ec2f50ca16 100644 --- a/libs/binder/tests/binderLibTest.cpp +++ b/libs/binder/tests/binderLibTest.cpp @@ -2261,7 +2261,7 @@ public: 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 077a33a201..506fc716cd 100644 --- a/libs/binder/tests/binderRpcTest.cpp +++ b/libs/binder/tests/binderRpcTest.cpp @@ -75,6 +75,8 @@ constexpr bool kEnableSharedLibs = true; 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)); @@ -1549,22 +1551,47 @@ TEST_F(BinderARpcNdk, ARpcNullArgs_ConnectionInfo_new) { 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); @@ -1580,6 +1607,45 @@ TEST_P(BinderRpcAccessor, ARpcGetService) { 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 @@ -1845,7 +1911,7 @@ TEST(BinderRpc, Java) { 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/binderSafeInterfaceTest.cpp b/libs/binder/tests/binderSafeInterfaceTest.cpp index 0aa678dd65..849dc7c4d5 100644 --- a/libs/binder/tests/binderSafeInterfaceTest.cpp +++ b/libs/binder/tests/binderSafeInterfaceTest.cpp @@ -40,6 +40,8 @@ #include <sys/eventfd.h> #include <sys/prctl.h> +#include <gmock/gmock.h> + using namespace std::chrono_literals; // NOLINT - google-build-using-namespace using android::binder::unique_fd; @@ -222,6 +224,7 @@ public: SetDeathToken = IBinder::FIRST_CALL_TRANSACTION, ReturnsNoMemory, LogicalNot, + LogicalNotVector, ModifyEnum, IncrementFlattenable, IncrementLightFlattenable, @@ -249,6 +252,7 @@ public: // These are ordered according to their corresponding methods in SafeInterface::ParcelHandler virtual status_t logicalNot(bool a, bool* notA) const = 0; + virtual status_t logicalNot(const std::vector<bool>& a, std::vector<bool>* notA) const = 0; virtual status_t modifyEnum(TestEnum a, TestEnum* b) const = 0; virtual status_t increment(const TestFlattenable& a, TestFlattenable* aPlusOne) const = 0; virtual status_t increment(const TestLightFlattenable& a, @@ -288,7 +292,14 @@ public: } status_t logicalNot(bool a, bool* notA) const override { ALOG(LOG_INFO, getLogTag(), "%s", __PRETTY_FUNCTION__); - return callRemote<decltype(&ISafeInterfaceTest::logicalNot)>(Tag::LogicalNot, a, notA); + using Signature = status_t (ISafeInterfaceTest::*)(bool, bool*) const; + return callRemote<Signature>(Tag::LogicalNot, a, notA); + } + status_t logicalNot(const std::vector<bool>& a, std::vector<bool>* notA) const override { + ALOG(LOG_INFO, getLogTag(), "%s", __PRETTY_FUNCTION__); + using Signature = status_t (ISafeInterfaceTest::*)(const std::vector<bool>&, + std::vector<bool>*) const; + return callRemote<Signature>(Tag::LogicalNotVector, a, notA); } status_t modifyEnum(TestEnum a, TestEnum* b) const override { ALOG(LOG_INFO, getLogTag(), "%s", __PRETTY_FUNCTION__); @@ -406,6 +417,14 @@ public: *notA = !a; return NO_ERROR; } + status_t logicalNot(const std::vector<bool>& a, std::vector<bool>* notA) const override { + ALOG(LOG_INFO, getLogTag(), "%s", __PRETTY_FUNCTION__); + notA->clear(); + for (bool value : a) { + notA->push_back(!value); + } + return NO_ERROR; + } status_t modifyEnum(TestEnum a, TestEnum* b) const override { ALOG(LOG_INFO, getLogTag(), "%s", __PRETTY_FUNCTION__); *b = (a == TestEnum::INITIAL) ? TestEnum::FINAL : TestEnum::INVALID; @@ -513,7 +532,13 @@ public: return callLocal(data, reply, &ISafeInterfaceTest::returnsNoMemory); } case ISafeInterfaceTest::Tag::LogicalNot: { - return callLocal(data, reply, &ISafeInterfaceTest::logicalNot); + using Signature = status_t (ISafeInterfaceTest::*)(bool a, bool* notA) const; + return callLocal<Signature>(data, reply, &ISafeInterfaceTest::logicalNot); + } + case ISafeInterfaceTest::Tag::LogicalNotVector: { + using Signature = status_t (ISafeInterfaceTest::*)(const std::vector<bool>& a, + std::vector<bool>* notA) const; + return callLocal<Signature>(data, reply, &ISafeInterfaceTest::logicalNot); } case ISafeInterfaceTest::Tag::ModifyEnum: { return callLocal(data, reply, &ISafeInterfaceTest::modifyEnum); @@ -639,6 +664,15 @@ TEST_F(SafeInterfaceTest, TestLogicalNot) { ASSERT_EQ(!b, notB); } +TEST_F(SafeInterfaceTest, TestLogicalNotVector) { + const std::vector<bool> a = {true, false, true}; + std::vector<bool> notA; + status_t result = mSafeInterfaceTest->logicalNot(a, ¬A); + ASSERT_EQ(NO_ERROR, result); + std::vector<bool> expected = {false, true, false}; + ASSERT_THAT(notA, testing::ContainerEq(expected)); +} + TEST_F(SafeInterfaceTest, TestModifyEnum) { const TestEnum a = TestEnum::INITIAL; TestEnum b = TestEnum::INVALID; diff --git a/libs/gui/BufferQueueProducer.cpp b/libs/gui/BufferQueueProducer.cpp index a4d105d320..ead9f0f014 100644 --- a/libs/gui/BufferQueueProducer.cpp +++ b/libs/gui/BufferQueueProducer.cpp @@ -1501,7 +1501,6 @@ void BufferQueueProducer::allocateBuffers(uint32_t width, uint32_t height, const bool useDefaultSize = !width && !height; while (true) { - size_t newBufferCount = 0; uint32_t allocWidth = 0; uint32_t allocHeight = 0; PixelFormat allocFormat = PIXEL_FORMAT_UNKNOWN; @@ -1523,8 +1522,9 @@ void BufferQueueProducer::allocateBuffers(uint32_t width, uint32_t height, // Only allocate one buffer at a time to reduce risks of overlapping an allocation from // both allocateBuffers and dequeueBuffer. - newBufferCount = mCore->mFreeSlots.empty() ? 0 : 1; - if (newBufferCount == 0) { + if (mCore->mFreeSlots.empty()) { + BQ_LOGV("allocateBuffers: a slot was occupied while " + "allocating. Dropping allocated buffer."); return; } @@ -1566,27 +1566,23 @@ void BufferQueueProducer::allocateBuffers(uint32_t width, uint32_t height, }; #endif - Vector<sp<GraphicBuffer>> buffers; - for (size_t i = 0; i < newBufferCount; ++i) { #if COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(BQ_EXTENDEDALLOCATE) - sp<GraphicBuffer> graphicBuffer = new GraphicBuffer(allocRequest); + sp<GraphicBuffer> graphicBuffer = new GraphicBuffer(allocRequest); #else - sp<GraphicBuffer> graphicBuffer = new GraphicBuffer( - allocWidth, allocHeight, allocFormat, BQ_LAYER_COUNT, - allocUsage, allocName); + sp<GraphicBuffer> graphicBuffer = new GraphicBuffer( + allocWidth, allocHeight, allocFormat, BQ_LAYER_COUNT, + allocUsage, allocName); #endif - status_t result = graphicBuffer->initCheck(); + status_t result = graphicBuffer->initCheck(); - if (result != NO_ERROR) { - BQ_LOGE("allocateBuffers: failed to allocate buffer (%u x %u, format" - " %u, usage %#" PRIx64 ")", width, height, format, usage); - std::lock_guard<std::mutex> lock(mCore->mMutex); - mCore->mIsAllocating = false; - mCore->mIsAllocatingCondition.notify_all(); - return; - } - buffers.push_back(graphicBuffer); + if (result != NO_ERROR) { + BQ_LOGE("allocateBuffers: failed to allocate buffer (%u x %u, format" + " %u, usage %#" PRIx64 ")", width, height, format, usage); + std::lock_guard<std::mutex> lock(mCore->mMutex); + mCore->mIsAllocating = false; + mCore->mIsAllocatingCondition.notify_all(); + return; } { // Autolock scope @@ -1614,15 +1610,13 @@ void BufferQueueProducer::allocateBuffers(uint32_t width, uint32_t height, continue; } - for (size_t i = 0; i < newBufferCount; ++i) { - if (mCore->mFreeSlots.empty()) { - BQ_LOGV("allocateBuffers: a slot was occupied while " - "allocating. Dropping allocated buffer."); - continue; - } + if (mCore->mFreeSlots.empty()) { + BQ_LOGV("allocateBuffers: a slot was occupied while " + "allocating. Dropping allocated buffer."); + } else { auto slot = mCore->mFreeSlots.begin(); mCore->clearBufferSlotLocked(*slot); // Clean up the slot first - mSlots[*slot].mGraphicBuffer = buffers[i]; + mSlots[*slot].mGraphicBuffer = graphicBuffer; mSlots[*slot].mFence = Fence::NO_FENCE; #if COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(BQ_EXTENDEDALLOCATE) mSlots[*slot].mAdditionalOptionsGenerationId = allocOptionsGenId; diff --git a/libs/gui/tests/BufferItemConsumer_test.cpp b/libs/gui/tests/BufferItemConsumer_test.cpp index 6880678050..656453411d 100644 --- a/libs/gui/tests/BufferItemConsumer_test.cpp +++ b/libs/gui/tests/BufferItemConsumer_test.cpp @@ -28,6 +28,7 @@ static constexpr int kWidth = 100; static constexpr int kHeight = 100; static constexpr int kMaxLockedBuffers = 3; static constexpr int kFormat = HAL_PIXEL_FORMAT_RGBA_8888; +static constexpr int kUsage = GRALLOC_USAGE_SW_READ_RARELY; static constexpr int kFrameSleepUs = 30 * 1000; class BufferItemConsumerTest : public ::testing::Test { @@ -44,8 +45,7 @@ class BufferItemConsumerTest : public ::testing::Test { void SetUp() override { BufferQueue::createBufferQueue(&mProducer, &mConsumer); - mBIC = - new BufferItemConsumer(mConsumer, kFormat, kMaxLockedBuffers, true); + mBIC = new BufferItemConsumer(mConsumer, kUsage, kMaxLockedBuffers, true); String8 name("BufferItemConsumer_Under_Test"); mBIC->setName(name); mBFL = new BufferFreedListener(this); diff --git a/libs/nativewindow/rust/src/lib.rs b/libs/nativewindow/rust/src/lib.rs index 931c311e65..f19b908074 100644 --- a/libs/nativewindow/rust/src/lib.rs +++ b/libs/nativewindow/rust/src/lib.rs @@ -19,10 +19,9 @@ extern crate nativewindow_bindgen as ffi; mod handle; mod surface; -pub use handle::NativeHandle; -pub use surface::Surface; - pub use ffi::{AHardwareBuffer_Format, AHardwareBuffer_UsageFlags}; +pub use handle::NativeHandle; +pub use surface::{buffer::Buffer, Surface}; use binder::{ binder_impl::{BorrowedParcel, UnstructuredParcelable}, diff --git a/libs/nativewindow/rust/src/surface.rs b/libs/nativewindow/rust/src/surface.rs index 9eddfcd497..ed52471aa1 100644 --- a/libs/nativewindow/rust/src/surface.rs +++ b/libs/nativewindow/rust/src/surface.rs @@ -14,6 +14,8 @@ //! Rust wrapper for `ANativeWindow` and related types. +pub(crate) mod buffer; + use binder::{ binder_impl::{BorrowedParcel, UnstructuredParcelable}, impl_deserialize_for_unstructured_parcelable, impl_serialize_for_unstructured_parcelable, @@ -21,17 +23,18 @@ use binder::{ StatusCode, }; use bitflags::bitflags; +use buffer::Buffer; use nativewindow_bindgen::{ ADataSpace, AHardwareBuffer_Format, ANativeWindow, ANativeWindow_acquire, ANativeWindow_getBuffersDataSpace, ANativeWindow_getBuffersDefaultDataSpace, - ANativeWindow_getFormat, ANativeWindow_getHeight, ANativeWindow_getWidth, + ANativeWindow_getFormat, ANativeWindow_getHeight, ANativeWindow_getWidth, ANativeWindow_lock, ANativeWindow_readFromParcel, ANativeWindow_release, ANativeWindow_setBuffersDataSpace, ANativeWindow_setBuffersGeometry, ANativeWindow_setBuffersTransform, - ANativeWindow_writeToParcel, + ANativeWindow_unlockAndPost, ANativeWindow_writeToParcel, ARect, }; use std::error::Error; use std::fmt::{self, Debug, Display, Formatter}; -use std::ptr::{null_mut, NonNull}; +use std::ptr::{self, null_mut, NonNull}; /// Wrapper around an opaque C `ANativeWindow`. #[derive(PartialEq, Eq)] @@ -153,6 +156,43 @@ impl Surface { Ok(ADataSpace(data_space)) } } + + /// Locks the window's next drawing surface for writing, and returns it. + pub fn lock(&mut self, bounds: Option<&mut ARect>) -> Result<Buffer, ErrorCode> { + let mut buffer = buffer::EMPTY; + // SAFETY: The ANativeWindow pointer we pass is guaranteed to be non-null and valid because + // it must have been allocated by `ANativeWindow_allocate` or `ANativeWindow_readFromParcel` + // and we have not yet released it. The other pointers must be valid because the come from + // references, and aren't retained after the function returns. + let status = unsafe { + ANativeWindow_lock( + self.0.as_ptr(), + &mut buffer, + bounds.map(ptr::from_mut).unwrap_or(null_mut()), + ) + }; + if status != 0 { + return Err(ErrorCode(status)); + } + + Ok(Buffer::new(buffer, self)) + } + + /// Unlocks the window's drawing surface which was previously locked, posting the new buffer to + /// the display. + /// + /// This shouldn't be called directly but via the [`Buffer`], hence is not public here. + fn unlock_and_post(&mut self) -> Result<(), ErrorCode> { + // SAFETY: The ANativeWindow pointer we pass is guaranteed to be non-null and valid because + // it must have been allocated by `ANativeWindow_allocate` or `ANativeWindow_readFromParcel` + // and we have not yet released it. + let status = unsafe { ANativeWindow_unlockAndPost(self.0.as_ptr()) }; + if status == 0 { + Ok(()) + } else { + Err(ErrorCode(status)) + } + } } impl Drop for Surface { diff --git a/libs/nativewindow/rust/src/surface/buffer.rs b/libs/nativewindow/rust/src/surface/buffer.rs new file mode 100644 index 0000000000..a2d74d4ed6 --- /dev/null +++ b/libs/nativewindow/rust/src/surface/buffer.rs @@ -0,0 +1,68 @@ +// 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 super::{ErrorCode, Surface}; +use nativewindow_bindgen::{AHardwareBuffer_Format, ANativeWindow_Buffer}; +use std::ptr::null_mut; + +/// An empty `ANativeWindow_Buffer`. +pub const EMPTY: ANativeWindow_Buffer = ANativeWindow_Buffer { + width: 0, + height: 0, + stride: 0, + format: 0, + bits: null_mut(), + reserved: [0; 6], +}; + +/// Rust wrapper for `ANativeWindow_Buffer`, representing a locked buffer from a [`Surface`]. +pub struct Buffer<'a> { + /// The wrapped `ANativeWindow_Buffer`. + pub buffer: ANativeWindow_Buffer, + surface: &'a mut Surface, +} + +impl<'a> Buffer<'a> { + pub(crate) fn new(buffer: ANativeWindow_Buffer, surface: &'a mut Surface) -> Self { + Self { buffer, surface } + } + + /// Unlocks the window's drawing surface which was previously locked to create this buffer, + /// posting the buffer to the display. + pub fn unlock_and_post(self) -> Result<(), ErrorCode> { + self.surface.unlock_and_post() + } + + /// The number of pixels that are shown horizontally. + pub fn width(&self) -> i32 { + self.buffer.width + } + + /// The number of pixels that are shown vertically. + pub fn height(&self) -> i32 { + self.buffer.height + } + + /// The number of pixels that a line in the buffer takes in memory. + /// + /// This may be greater than the width. + pub fn stride(&self) -> i32 { + self.buffer.stride + } + + /// The pixel format of the buffer. + pub fn format(&self) -> Result<AHardwareBuffer_Format::Type, ErrorCode> { + self.buffer.format.try_into().map_err(|_| ErrorCode(self.buffer.format)) + } +} diff --git a/services/gpuservice/vts/Android.bp b/services/gpuservice/vts/Android.bp index a24822a7d1..6e0a9f7ace 100644 --- a/services/gpuservice/vts/Android.bp +++ b/services/gpuservice/vts/Android.bp @@ -13,6 +13,7 @@ // limitations under the License. package { + default_team: "trendy_team_android_gpu", default_applicable_licenses: ["frameworks_native_license"], } diff --git a/services/inputflinger/dispatcher/CancelationOptions.h b/services/inputflinger/dispatcher/CancelationOptions.h index 4a0889f596..568d348ba9 100644 --- a/services/inputflinger/dispatcher/CancelationOptions.h +++ b/services/inputflinger/dispatcher/CancelationOptions.h @@ -32,7 +32,8 @@ struct CancelationOptions { 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 9809148853..1ed6c29851 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 android { 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 @@ struct DragState { 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 f9fbfef3ec..7eb7e36386 100644 --- a/services/inputflinger/dispatcher/InputDispatcher.cpp +++ b/services/inputflinger/dispatcher/InputDispatcher.cpp @@ -746,7 +746,8 @@ std::vector<TouchedWindow> getHoveringWindowsLocked(const TouchState* oldState, } 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 @@ std::pair<bool /*cancelPointers*/, bool /*cancelNonPointers*/> expandCancellatio 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 @@ std::vector<InputTarget> InputDispatcher::findTouchedWindowTargetsLocked( 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::finishDragAndDrop(ui::LogicalDisplayId displayId, float x, } 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 @@ void InputDispatcher::setInputWindowsLocked( } } + // 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 @@ bool InputDispatcher::transferTouchGesture(const sp<IBinder>& fromToken, const s } // 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 dfbe02f332..e283fc3b4d 100644 --- a/services/inputflinger/dispatcher/InputState.cpp +++ b/services/inputflinger/dispatcher/InputState.cpp @@ -638,6 +638,8 @@ bool InputState::shouldCancelMotion(const MotionMemento& memento, 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 0c9ad3c7b7..2bf63beb05 100644 --- a/services/inputflinger/dispatcher/TouchState.cpp +++ b/services/inputflinger/dispatcher/TouchState.cpp @@ -112,17 +112,18 @@ android::base::Result<void> TouchState::addOrUpdateWindow( } 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 9d4bb3d943..3fbe584a64 100644 --- a/services/inputflinger/dispatcher/TouchState.h +++ b/services/inputflinger/dispatcher/TouchState.h @@ -49,7 +49,8 @@ struct TouchState { 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 1f86f6635a..fa5be1a719 100644 --- a/services/inputflinger/dispatcher/TouchedWindow.cpp +++ b/services/inputflinger/dispatcher/TouchedWindow.cpp @@ -36,6 +36,13 @@ bool hasPointerId(const std::vector<PointerProperties>& pointers, int32_t pointe }) != 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 @@ bool TouchedWindow::hasHoveringPointer(DeviceId deviceId, int32_t pointerId) con 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 @@ bool TouchedWindow::hasActiveStylus() const { 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 @@ void TouchedWindow::removeHoveringPointer(DeviceId deviceId, int32_t pointerId) } 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 @@ void TouchedWindow::removeHoveringPointer(DeviceId deviceId, int32_t pointerId) } } +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 @@ std::string TouchedWindow::dump() const { 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 4f0ad1628a..c38681eef0 100644 --- a/services/inputflinger/dispatcher/TouchedWindow.h +++ b/services/inputflinger/dispatcher/TouchedWindow.h @@ -38,7 +38,7 @@ struct TouchedWindow { 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 @@ struct TouchedWindow { 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 @@ private: // 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 2056372f2d..48930ef444 100644 --- a/services/inputflinger/tests/InputDispatcher_test.cpp +++ b/services/inputflinger/tests/InputDispatcher_test.cpp @@ -1923,6 +1923,99 @@ TEST_F(InputDispatcherTest, HoverMoveAndScroll) { 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 @@ TEST_F(InputDispatcherDragTests, NoDragAndDropWithHoveringPointer) { << "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/stats/Android.bp b/services/stats/Android.bp index 6b99627f5a..f698515545 100644 --- a/services/stats/Android.bp +++ b/services/stats/Android.bp @@ -7,6 +7,11 @@ package { default_applicable_licenses: ["frameworks_native_license"], } +vintf_fragment { + name: "android.frameworks.stats-service.xml", + src: "android.frameworks.stats-service.xml", +} + cc_library_shared { name: "libstatshidl", srcs: [ @@ -38,7 +43,7 @@ cc_library_shared { local_include_dirs: [ "include/stats", ], - vintf_fragments: [ + vintf_fragment_modules: [ "android.frameworks.stats-service.xml", ], } diff --git a/services/surfaceflinger/tests/unittests/RefreshRateSelectorTest.cpp b/services/surfaceflinger/tests/unittests/RefreshRateSelectorTest.cpp index 4ffd52b607..d64cf2f52b 100644 --- a/services/surfaceflinger/tests/unittests/RefreshRateSelectorTest.cpp +++ b/services/surfaceflinger/tests/unittests/RefreshRateSelectorTest.cpp @@ -100,7 +100,9 @@ struct TestableRefreshRateSelector : RefreshRateSelector { 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 @@ struct TestableRefreshRateSelector : RefreshRateSelector { 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/vulkan/vkjson/vkjson.cc b/vulkan/vkjson/vkjson.cc index 0284192cc4..bfb7bd6d6f 100644 --- a/vulkan/vkjson/vkjson.cc +++ b/vulkan/vkjson/vkjson.cc @@ -1169,7 +1169,7 @@ inline Json::Value ArrayToJsonValue(uint32_t count, const T* values) { return array; } -template <typename T, unsigned int N> +template <typename T, size_t N> inline Json::Value ToJsonValue(const T (&value)[N]) { return ArrayToJsonValue(N, value); } @@ -1293,7 +1293,7 @@ inline bool AsArray(Json::Value* json_value, uint32_t count, T* values) { return true; } -template <typename T, unsigned int N> +template <typename T, size_t N> inline bool AsValue(Json::Value* json_value, T (*value)[N]) { return AsArray(json_value, N, *value); } |