diff options
116 files changed, 3080 insertions, 805 deletions
diff --git a/cmds/dumpstate/dumpstate.cpp b/cmds/dumpstate/dumpstate.cpp index 220fef6f8d..d427ecf280 100644 --- a/cmds/dumpstate/dumpstate.cpp +++ b/cmds/dumpstate/dumpstate.cpp @@ -170,6 +170,7 @@ void add_mountinfo(); #define ALT_PSTORE_LAST_KMSG "/sys/fs/pstore/console-ramoops-0" #define BLK_DEV_SYS_DIR "/sys/block" +#define AFLAGS "/system/bin/aflags" #define RECOVERY_DIR "/cache/recovery" #define RECOVERY_DATA_DIR "/data/misc/recovery" #define UPDATE_ENGINE_LOG_DIR "/data/misc/update_engine_log" @@ -1792,6 +1793,10 @@ Dumpstate::RunStatus Dumpstate::dumpstate() { RunCommand("ACONFIG FLAGS", {PRINT_FLAGS}, CommandOptions::WithTimeout(10).Always().DropRoot().Build()); + RunCommand("ACONFIG FLAGS DUMP", {AFLAGS, "list"}, + CommandOptions::WithTimeout(10).Always().AsRootIfAvailable().Build()); + RunCommand("WHICH ACONFIG FLAG STORAGE", {AFLAGS, "which-backing"}, + CommandOptions::WithTimeout(10).Always().AsRootIfAvailable().Build()); RunCommand("STORAGED IO INFO", {"storaged", "-u", "-p"}); @@ -3533,7 +3538,7 @@ std::future<std::string> Dumpstate::MaybeSnapshotSystemTraceAsync() { // the dumpstate's own activity which is irrelevant. RunCommand( SERIALIZE_PERFETTO_TRACE_TASK, {"perfetto", "--save-for-bugreport"}, - CommandOptions::WithTimeout(10).DropRoot().CloseAllFileDescriptorsOnExec().Build(), + CommandOptions::WithTimeout(30).DropRoot().CloseAllFileDescriptorsOnExec().Build(), false, outFd); // MaybeAddSystemTraceToZip() will take care of copying the trace in the zip // file in the later stages. diff --git a/data/etc/android.software.opengles.deqp.level-2023-03-01.xml b/data/etc/android.software.opengles.deqp.level-2023-03-01.xml index d0b594c73d..7aef9ec6c4 100644 --- a/data/etc/android.software.opengles.deqp.level-2023-03-01.xml +++ b/data/etc/android.software.opengles.deqp.level-2023-03-01.xml @@ -1,5 +1,5 @@ <?xml version="1.0" encoding="utf-8"?> -<!-- Copyright 2021 The Android Open Source Project +<!-- 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. diff --git a/data/etc/android.software.opengles.deqp.level-2024-03-01.xml b/data/etc/android.software.opengles.deqp.level-2024-03-01.xml index 4eeed2af4f..3d8a17819c 100644 --- a/data/etc/android.software.opengles.deqp.level-2024-03-01.xml +++ b/data/etc/android.software.opengles.deqp.level-2024-03-01.xml @@ -1,5 +1,5 @@ <?xml version="1.0" encoding="utf-8"?> -<!-- Copyright 2021 The Android Open Source Project +<!-- Copyright 2024 The Android Open Source Project Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. @@ -15,7 +15,7 @@ --> <!-- This is the standard feature indicating that the device passes OpenGL ES - dEQP tests associated with date 2023-03-01 (0x07E70301). --> + dEQP tests associated with date 2024-03-01 (0x7e80301). --> <permissions> <feature name="android.software.opengles.deqp.level" version="132645633" /> </permissions> diff --git a/data/etc/android.software.opengles.deqp.level-2025-03-01.xml b/data/etc/android.software.opengles.deqp.level-2025-03-01.xml new file mode 100644 index 0000000000..e005356555 --- /dev/null +++ b/data/etc/android.software.opengles.deqp.level-2025-03-01.xml @@ -0,0 +1,21 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- Copyright 2025 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. +--> + +<!-- This is the standard feature indicating that the device passes OpenGL ES + dEQP tests associated with date 2025-03-01 (0x7e90301). --> +<permissions> + <feature name="android.software.opengles.deqp.level" version="132711169" /> +</permissions> diff --git a/data/etc/android.software.opengles.deqp.level-latest.xml b/data/etc/android.software.opengles.deqp.level-latest.xml index 62bb10161a..1ad35bce07 100644 --- a/data/etc/android.software.opengles.deqp.level-latest.xml +++ b/data/etc/android.software.opengles.deqp.level-latest.xml @@ -1,5 +1,5 @@ <?xml version="1.0" encoding="utf-8"?> -<!-- Copyright 2023 The Android Open Source Project +<!-- Copyright 2025 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. @@ -17,5 +17,5 @@ <!-- This is the standard feature indicating that the device passes OpenGL ES dEQP tests associated with the most recent level for this Android version. --> <permissions> - <feature name="android.software.opengles.deqp.level" version="132645633" /> + <feature name="android.software.opengles.deqp.level" version="132711169" /> </permissions> diff --git a/data/etc/android.software.vulkan.deqp.level-2024-03-01.xml b/data/etc/android.software.vulkan.deqp.level-2024-03-01.xml index 8b2b4c8199..185edbf4bd 100644 --- a/data/etc/android.software.vulkan.deqp.level-2024-03-01.xml +++ b/data/etc/android.software.vulkan.deqp.level-2024-03-01.xml @@ -1,5 +1,5 @@ <?xml version="1.0" encoding="utf-8"?> -<!-- Copyright 2021 The Android Open Source Project +<!-- Copyright 2024 The Android Open Source Project Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. @@ -15,7 +15,7 @@ --> <!-- This is the standard feature indicating that the device passes Vulkan dEQP - tests associated with date 2023-03-01 (0x07E70301). --> + tests associated with date 2024-03-01 (0x7e80301). --> <permissions> <feature name="android.software.vulkan.deqp.level" version="132645633" /> </permissions> diff --git a/data/etc/android.software.vulkan.deqp.level-2025-03-01.xml b/data/etc/android.software.vulkan.deqp.level-2025-03-01.xml new file mode 100644 index 0000000000..b424667b3a --- /dev/null +++ b/data/etc/android.software.vulkan.deqp.level-2025-03-01.xml @@ -0,0 +1,21 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- Copyright 2025 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. +--> + +<!-- This is the standard feature indicating that the device passes Vulkan dEQP + tests associated with date 2025-03-01 (0x7e90301). --> +<permissions> + <feature name="android.software.vulkan.deqp.level" version="132711169" /> +</permissions> diff --git a/data/etc/android.software.vulkan.deqp.level-latest.xml b/data/etc/android.software.vulkan.deqp.level-latest.xml index 0fc12b3b5f..4128a95ffd 100644 --- a/data/etc/android.software.vulkan.deqp.level-latest.xml +++ b/data/etc/android.software.vulkan.deqp.level-latest.xml @@ -1,5 +1,5 @@ <?xml version="1.0" encoding="utf-8"?> -<!-- Copyright 2023 The Android Open Source Project +<!-- Copyright 2025 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. @@ -17,5 +17,5 @@ <!-- This is the standard feature indicating that the device passes Vulkan dEQP tests associated with the most recent level for this Android version. --> <permissions> - <feature name="android.software.vulkan.deqp.level" version="132645633" /> + <feature name="android.software.vulkan.deqp.level" version="132711169" /> </permissions> diff --git a/include/input/InputConsumerNoResampling.h b/include/input/InputConsumerNoResampling.h index 358a19158e..228347d818 100644 --- a/include/input/InputConsumerNoResampling.h +++ b/include/input/InputConsumerNoResampling.h @@ -16,8 +16,13 @@ #pragma once +#include <functional> +#include <map> +#include <memory> +#include <optional> + +#include <input/Input.h> #include <input/InputTransport.h> -#include <input/LooperInterface.h> #include <input/Resampler.h> #include <utils/Looper.h> @@ -36,7 +41,7 @@ public: /** * When you receive this callback, you must (eventually) call "consumeBatchedInputEvents". * If you don't want batching, then call "consumeBatchedInputEvents" immediately with - * std::nullopt frameTime to receive the pending motion event(s). + * std::nullopt requestedFrameTime to receive the pending motion event(s). * @param pendingBatchSource the source of the pending batch. */ virtual void onBatchedInputEventPending(int32_t pendingBatchSource) = 0; @@ -67,26 +72,17 @@ public: class InputConsumerNoResampling final { public: /** - * This constructor is exclusively for test code. Any real use of InputConsumerNoResampling must - * use the constructor that takes an sp<Looper> parameter instead of - * std::shared_ptr<LooperInterface>. - */ - explicit InputConsumerNoResampling(const std::shared_ptr<InputChannel>& channel, - std::shared_ptr<LooperInterface> looper, - InputConsumerCallbacks& callbacks, - std::unique_ptr<Resampler> resampler); - - /** * @param callbacks are used to interact with InputConsumerNoResampling. They're called whenever * the event is ready to consume. * @param looper needs to be sp and not shared_ptr because it inherits from * RefBase - * @param resampler the resampling strategy to use. If null, no resampling will be - * performed. + * @param resamplerCreator callable that returns the resampling strategy to be used. If null, no + * resampling will be performed. resamplerCreator must never return nullptr. */ - explicit InputConsumerNoResampling(const std::shared_ptr<InputChannel>& channel, - sp<Looper> looper, InputConsumerCallbacks& callbacks, - std::unique_ptr<Resampler> resampler); + explicit InputConsumerNoResampling( + const std::shared_ptr<InputChannel>& channel, sp<Looper> looper, + InputConsumerCallbacks& callbacks, + std::function<std::unique_ptr<Resampler>()> resamplerCreator); ~InputConsumerNoResampling(); @@ -96,15 +92,17 @@ public: void finishInputEvent(uint32_t seq, bool handled); void reportTimeline(int32_t inputEventId, nsecs_t gpuCompletedTime, nsecs_t presentTime); /** - * If you want to consume all events immediately (disable batching), the you still must call - * this. For frameTime, use a std::nullopt. - * @param frameTime the time up to which consume the events. When there's double (or triple) - * buffering, you may want to not consume all events currently available, because you could be - * still working on an older frame, but there could already have been events that arrived that - * are more recent. + * If you want to consume all events immediately (disable batching), then you still must call + * this. For requestedFrameTime, use a std::nullopt. It is not guaranteed that the consumption + * will occur at requestedFrameTime. The resampling strategy may modify it. + * @param requestedFrameTime the time up to which consume the events. When there's double (or + * triple) buffering, you may want to not consume all events currently available, because you + * could be still working on an older frame, but there could already have been events that + * arrived that are more recent. * @return whether any events were actually consumed */ - bool consumeBatchedInputEvents(std::optional<nsecs_t> frameTime); + bool consumeBatchedInputEvents(std::optional<nsecs_t> requestedFrameTime); + /** * Returns true when there is *likely* a pending batch or a pending event in the channel. * @@ -119,9 +117,15 @@ public: private: std::shared_ptr<InputChannel> mChannel; - std::shared_ptr<LooperInterface> mLooper; + sp<Looper> mLooper; InputConsumerCallbacks& mCallbacks; - std::unique_ptr<Resampler> mResampler; + const std::function<std::unique_ptr<Resampler>()> mResamplerCreator; + + /** + * A map to manage multidevice resampling. Each contained resampler is never null. This map is + * only modified by handleMessages. + */ + std::map<DeviceId, std::unique_ptr<Resampler>> mResamplers; // Looper-related infrastructure /** @@ -194,26 +198,42 @@ private: /** * Batch messages that can be batched. When an unbatchable message is encountered, send it * to the InputConsumerCallbacks immediately. If there are batches remaining, - * notify InputConsumerCallbacks. + * notify InputConsumerCallbacks. If a resampleable ACTION_DOWN message is received, then a + * resampler is inserted for that deviceId in mResamplers. If a resampleable ACTION_UP or + * ACTION_CANCEL message is received then the resampler associated to that deviceId is erased + * from mResamplers. */ void handleMessages(std::vector<InputMessage>&& messages); /** * Batched InputMessages, per deviceId. * For each device, we are storing a queue of batched messages. These will all be collapsed into - * a single MotionEvent (up to a specific frameTime) when the consumer calls + * a single MotionEvent (up to a specific requestedFrameTime) when the consumer calls * `consumeBatchedInputEvents`. */ std::map<DeviceId, std::queue<InputMessage>> mBatches; /** * Creates a MotionEvent by consuming samples from the provided queue. If one message has - * eventTime > frameTime, all subsequent messages in the queue will be skipped. It is assumed - * that messages are queued in chronological order. In other words, only events that occurred - * prior to the requested frameTime will be consumed. - * @param frameTime the time up to which to consume events + * eventTime > adjustedFrameTime, all subsequent messages in the queue will be skipped. It is + * assumed that messages are queued in chronological order. In other words, only events that + * occurred prior to the adjustedFrameTime will be consumed. + * @param requestedFrameTime the time up to which to consume events. * @param messages the queue of messages to consume from */ std::pair<std::unique_ptr<MotionEvent>, std::optional<uint32_t>> createBatchedMotionEvent( - const nsecs_t frameTime, std::queue<InputMessage>& messages); + const nsecs_t requestedFrameTime, std::queue<InputMessage>& messages); + + /** + * Consumes the batched input events, optionally allowing the caller to specify a device id + * and/or requestedFrameTime threshold. It is not guaranteed that consumption will occur at + * requestedFrameTime. + * @param deviceId The device id from which to consume events. If std::nullopt, consumes events + * from any device id. + * @param requestedFrameTime The time up to which consume the events. If std::nullopt, consumes + * input events with any timestamp. + * @return Whether or not any events were consumed. + */ + bool consumeBatchedInputEvents(std::optional<DeviceId> deviceId, + std::optional<nsecs_t> requestedFrameTime); /** * A map from a single sequence number to several sequence numbers. This is needed because of * batching. When batching is enabled, a single MotionEvent will contain several samples. Each diff --git a/include/input/KeyCharacterMap.h b/include/input/KeyCharacterMap.h index 92d5ec4d4e..67b37b1213 100644 --- a/include/input/KeyCharacterMap.h +++ b/include/input/KeyCharacterMap.h @@ -126,9 +126,9 @@ public: bool getEvents(int32_t deviceId, const char16_t* chars, size_t numChars, Vector<KeyEvent>& outEvents) const; - /* Maps an Android key code to another Android key code. This mapping is applied after scanCode - * and usageCodes are mapped to corresponding Android Keycode */ - void addKeyRemapping(int32_t fromKeyCode, int32_t toKeyCode); + /* Maps some Android key code to another Android key code. This mapping is applied after + * scanCode and usageCodes are mapped to corresponding Android Keycode */ + void setKeyRemapping(const std::map<int32_t, int32_t>& keyRemapping); /* Maps a scan code and usage code to a key code, in case this key map overrides * the mapping in some way. */ diff --git a/include/input/Resampler.h b/include/input/Resampler.h index dcb25b729f..4aaeddd159 100644 --- a/include/input/Resampler.h +++ b/include/input/Resampler.h @@ -92,12 +92,6 @@ private: }; /** - * Keeps track of the previous MotionEvent deviceId to enable comparison between the previous - * and the current deviceId. - */ - std::optional<DeviceId> mPreviousDeviceId; - - /** * Up to two latest samples from MotionEvent. Updated every time resampleMotionEvent is called. * Note: We store up to two samples in order to simplify the implementation. Although, * calculations are possible with only one previous sample. diff --git a/libs/binder/BackendUnifiedServiceManager.cpp b/libs/binder/BackendUnifiedServiceManager.cpp index 5680798d0d..52b485a6f6 100644 --- a/libs/binder/BackendUnifiedServiceManager.cpp +++ b/libs/binder/BackendUnifiedServiceManager.cpp @@ -34,37 +34,47 @@ using AidlServiceManager = android::os::IServiceManager; using IAccessor = android::os::IAccessor; static const char* kStaticCachableList[] = { + // go/keep-sorted start + "accessibility", + "account", "activity", - "android.hardware.thermal.IThermal/default", - "android.hardware.power.IPower/default", - "android.frameworks.stats.IStats/default", - "android.system.suspend.ISystemSuspend/default", + "alarm", + "android.system.keystore2.IKeystoreService/default", "appops", "audio", "batterystats", "carrier_config", "connectivity", + "content", "content_capture", "device_policy", "display", "dropbox", "econtroller", + "graphicsstats", + "input", + "input_method", "isub", + "jobscheduler", "legacy_permission", "location", "media.extractor", "media.metrics", "media.player", "media.resource_manager", + "media_resource_monitor", + "mount", "netd_listener", "netstats", "network_management", "nfc", + "notification", + "package", "package_native", "performance_hint", "permission", - "permissionmgr", "permission_checker", + "permissionmgr", "phone", "platform_compat", "power", @@ -76,9 +86,12 @@ static const char* kStaticCachableList[] = { "time_detector", "trust", "uimode", + "user", "virtualdevice", "virtualdevice_native", "webviewupdate", + "window", + // go/keep-sorted end }; bool BinderCacheWithInvalidation::isClientSideCachingEnabled(const std::string& serviceName) { diff --git a/libs/binder/IPCThreadState.cpp b/libs/binder/IPCThreadState.cpp index 1d26d8543d..6698d0c0cd 100644 --- a/libs/binder/IPCThreadState.cpp +++ b/libs/binder/IPCThreadState.cpp @@ -232,6 +232,15 @@ static const void* printReturnCommand(std::ostream& out, const void* _cmd) { return cmd; } +static void printReturnCommandParcel(std::ostream& out, const Parcel& parcel) { + const void* cmds = parcel.data(); + out << "\t" << HexDump(cmds, parcel.dataSize()) << "\n"; + IF_LOG_COMMANDS() { + const void* end = parcel.data() + parcel.dataSize(); + while (cmds < end) cmds = printReturnCommand(out, cmds); + } +} + static const void* printCommand(std::ostream& out, const void* _cmd) { static const size_t N = sizeof(kCommandStrings)/sizeof(kCommandStrings[0]); const int32_t* cmd = (const int32_t*)_cmd; @@ -1235,13 +1244,15 @@ status_t IPCThreadState::talkWithDriver(bool doReceive) if (err >= NO_ERROR) { if (bwr.write_consumed > 0) { - if (bwr.write_consumed < mOut.dataSize()) + if (bwr.write_consumed < mOut.dataSize()) { + std::ostringstream logStream; + printReturnCommandParcel(logStream, mIn); LOG_ALWAYS_FATAL("Driver did not consume write buffer. " - "err: %s consumed: %zu of %zu", - statusToString(err).c_str(), - (size_t)bwr.write_consumed, - mOut.dataSize()); - else { + "err: %s consumed: %zu of %zu.\n" + "Return command: %s", + statusToString(err).c_str(), (size_t)bwr.write_consumed, + mOut.dataSize(), logStream.str().c_str()); + } else { mOut.setDataSize(0); processPostWriteDerefs(); } @@ -1252,14 +1263,8 @@ status_t IPCThreadState::talkWithDriver(bool doReceive) } IF_LOG_COMMANDS() { std::ostringstream logStream; - logStream << "Remaining data size: " << mOut.dataSize() << "\n"; - logStream << "Received commands from driver: "; - const void* cmds = mIn.data(); - const void* end = mIn.data() + mIn.dataSize(); - logStream << "\t" << HexDump(cmds, mIn.dataSize()) << "\n"; - while (cmds < end) cmds = printReturnCommand(logStream, cmds); - std::string message = logStream.str(); - ALOGI("%s", message.c_str()); + printReturnCommandParcel(logStream, mIn); + ALOGI("%s", logStream.str().c_str()); } return NO_ERROR; } diff --git a/libs/binder/IServiceManager.cpp b/libs/binder/IServiceManager.cpp index 88761d772f..77b80ef3de 100644 --- a/libs/binder/IServiceManager.cpp +++ b/libs/binder/IServiceManager.cpp @@ -157,12 +157,21 @@ protected: class AccessorProvider { public: - AccessorProvider(RpcAccessorProvider&& provider) : mProvider(std::move(provider)) {} - sp<IBinder> provide(const String16& name) { return mProvider(name); } + AccessorProvider(std::set<std::string>&& instances, RpcAccessorProvider&& provider) + : mInstances(std::move(instances)), mProvider(std::move(provider)) {} + sp<IBinder> provide(const String16& name) { + if (mInstances.count(String8(name).c_str()) > 0) { + return mProvider(name); + } else { + return nullptr; + } + } + const std::set<std::string>& instances() { return mInstances; } private: AccessorProvider() = delete; + std::set<std::string> mInstances; RpcAccessorProvider mProvider; }; @@ -318,10 +327,32 @@ sp<IServiceManager> getServiceManagerShimFromAidlServiceManagerForTests( return sp<CppBackendShim>::make(sp<BackendUnifiedServiceManager>::make(sm)); } -std::weak_ptr<AccessorProvider> addAccessorProvider(RpcAccessorProvider&& providerCallback) { +// gAccessorProvidersMutex must be locked already +static bool isInstanceProvidedLocked(const std::string& instance) { + return gAccessorProviders.end() != + std::find_if(gAccessorProviders.begin(), gAccessorProviders.end(), + [&instance](const AccessorProviderEntry& entry) { + return entry.mProvider->instances().count(instance) > 0; + }); +} + +std::weak_ptr<AccessorProvider> addAccessorProvider(std::set<std::string>&& instances, + RpcAccessorProvider&& providerCallback) { + if (instances.empty()) { + ALOGE("Set of instances is empty! Need a non empty set of instances to provide for."); + return std::weak_ptr<AccessorProvider>(); + } std::lock_guard<std::mutex> lock(gAccessorProvidersMutex); + for (const auto& instance : instances) { + if (isInstanceProvidedLocked(instance)) { + ALOGE("The instance %s is already provided for by a previously added " + "RpcAccessorProvider.", + instance.c_str()); + return std::weak_ptr<AccessorProvider>(); + } + } std::shared_ptr<AccessorProvider> provider = - std::make_shared<AccessorProvider>(std::move(providerCallback)); + std::make_shared<AccessorProvider>(std::move(instances), std::move(providerCallback)); std::weak_ptr<AccessorProvider> receipt = provider; gAccessorProviders.push_back(AccessorProviderEntry(std::move(provider))); @@ -331,8 +362,9 @@ std::weak_ptr<AccessorProvider> addAccessorProvider(RpcAccessorProvider&& provid status_t removeAccessorProvider(std::weak_ptr<AccessorProvider> wProvider) { std::shared_ptr<AccessorProvider> provider = wProvider.lock(); if (provider == nullptr) { - ALOGE("The provider supplied to removeAccessorProvider has already been removed."); - return NAME_NOT_FOUND; + ALOGE("The provider supplied to removeAccessorProvider has already been removed or the " + "argument to this function was nullptr."); + return BAD_VALUE; } std::lock_guard<std::mutex> lock(gAccessorProvidersMutex); size_t sizeBefore = gAccessorProviders.size(); diff --git a/libs/binder/OS.h b/libs/binder/OS.h index 04869a170f..64b1fd48fd 100644 --- a/libs/binder/OS.h +++ b/libs/binder/OS.h @@ -27,6 +27,7 @@ namespace android::binder::os { 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 893ee15578..4e9230ce58 100644 --- a/libs/binder/OS_android.cpp +++ b/libs/binder/OS_android.cpp @@ -48,6 +48,10 @@ void trace_int(uint64_t tag, const char* name, int32_t value) { 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 0c64eb61c6..6bba823897 100644 --- a/libs/binder/OS_non_android_linux.cpp +++ b/libs/binder/OS_non_android_linux.cpp @@ -41,6 +41,10 @@ void trace_end(uint64_t) {} 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/include/binder/IServiceManager.h b/libs/binder/include/binder/IServiceManager.h index 879f319c5e..2b23276d1c 100644 --- a/libs/binder/include/binder/IServiceManager.h +++ b/libs/binder/include/binder/IServiceManager.h @@ -24,6 +24,7 @@ #include <utils/String16.h> #include <utils/Vector.h> #include <optional> +#include <set> namespace android { @@ -224,20 +225,36 @@ LIBBINDER_EXPORTED bool checkPermission(const String16& permission, pid_t pid, u typedef std::function<status_t(const String16& name, sockaddr* outAddr, socklen_t addrSize)> RpcSocketAddressProvider; -typedef std::function<sp<IBinder>(const String16& name)> RpcAccessorProvider; +/** + * This callback provides a way for clients to get access to remote services by + * providing an Accessor object from libbinder that can connect to the remote + * service over sockets. + * + * \param instance name of the service that the callback will provide an + * Accessor for. The provided accessor will be used to set up a client + * RPC connection in libbinder in order to return a binder for the + * associated remote service. + * + * \return IBinder of the Accessor object that libbinder implements. + * nullptr if the provider callback doesn't know how to reach the + * service or doesn't want to provide access for any other reason. + */ +typedef std::function<sp<IBinder>(const String16& instance)> RpcAccessorProvider; class AccessorProvider; /** - * Register an accessor provider for the service manager APIs. + * Register a RpcAccessorProvider for the service manager APIs. * + * \param instances that the RpcAccessorProvider knows about and can provide an + * Accessor for. * \param provider callback that generates Accessors. * * \return A pointer used as a recept for the successful addition of the * AccessorProvider. This is needed to unregister it later. */ [[nodiscard]] LIBBINDER_EXPORTED std::weak_ptr<AccessorProvider> addAccessorProvider( - RpcAccessorProvider&& providerCallback); + std::set<std::string>&& instances, RpcAccessorProvider&& providerCallback); /** * Remove an accessor provider using the pointer provided by addAccessorProvider diff --git a/libs/binder/include/binder/Trace.h b/libs/binder/include/binder/Trace.h index 2f450cb36b..a3e6c8a12b 100644 --- a/libs/binder/include/binder/Trace.h +++ b/libs/binder/include/binder/Trace.h @@ -42,6 +42,7 @@ namespace os { 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/Android.bp b/libs/binder/ndk/Android.bp index 5f45cb2f07..a7423b3d2a 100644 --- a/libs/binder/ndk/Android.bp +++ b/libs/binder/ndk/Android.bp @@ -95,6 +95,7 @@ cc_library { "persistable_bundle.cpp", "process.cpp", "service_manager.cpp", + "binder_rpc.cpp", ], static_libs: [ diff --git a/libs/binder/ndk/binder_rpc.cpp b/libs/binder/ndk/binder_rpc.cpp new file mode 100644 index 0000000000..2cc5f8117e --- /dev/null +++ b/libs/binder/ndk/binder_rpc.cpp @@ -0,0 +1,352 @@ +/* + * Copyright (C) 2024 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include <android/binder_rpc.h> +#include <arpa/inet.h> +#include <binder/IServiceManager.h> +#include <linux/vm_sockets.h> +#include <netinet/in.h> +#include <sys/socket.h> +#include <sys/un.h> + +#include <variant> + +#include "ibinder_internal.h" +#include "status_internal.h" + +using ::android::defaultServiceManager; +using ::android::IBinder; +using ::android::IServiceManager; +using ::android::OK; +using ::android::sp; +using ::android::status_t; +using ::android::String16; +using ::android::String8; +using ::android::binder::Status; + +#define LOG_ACCESSOR_DEBUG(...) +// #define LOG_ACCESSOR_DEBUG(...) ALOGW(__VA_ARGS__) + +struct ABinderRpc_ConnectionInfo { + std::variant<sockaddr_vm, sockaddr_un, sockaddr_in> addr; +}; + +struct ABinderRpc_Accessor final : public ::android::RefBase { + static ABinderRpc_Accessor* make(const char* instance, const sp<IBinder>& binder) { + LOG_ALWAYS_FATAL_IF(binder == nullptr, "ABinderRpc_Accessor requires a non-null binder"); + status_t status = android::validateAccessor(String16(instance), binder); + if (status != OK) { + ALOGE("The given binder is not a valid IAccessor for %s. Status: %s", instance, + android::statusToString(status).c_str()); + return nullptr; + } + return new ABinderRpc_Accessor(binder); + } + + sp<IBinder> asBinder() { return mAccessorBinder; } + + ~ABinderRpc_Accessor() { LOG_ACCESSOR_DEBUG("ABinderRpc_Accessor dtor"); } + + private: + ABinderRpc_Accessor(sp<IBinder> accessor) : mAccessorBinder(accessor) {} + ABinderRpc_Accessor() = delete; + sp<IBinder> mAccessorBinder; +}; + +struct ABinderRpc_AccessorProvider { + public: + static ABinderRpc_AccessorProvider* make(std::weak_ptr<android::AccessorProvider> cookie) { + if (cookie.expired()) { + ALOGE("Null AccessorProvider cookie from libbinder"); + return nullptr; + } + return new ABinderRpc_AccessorProvider(cookie); + } + std::weak_ptr<android::AccessorProvider> mProviderCookie; + + private: + ABinderRpc_AccessorProvider() = delete; + + ABinderRpc_AccessorProvider(std::weak_ptr<android::AccessorProvider> provider) + : mProviderCookie(provider) {} +}; + +struct OnDeleteProviderHolder { + OnDeleteProviderHolder(void* data, ABinderRpc_AccessorProviderUserData_deleteCallback onDelete) + : mData(data), mOnDelete(onDelete) {} + ~OnDeleteProviderHolder() { + if (mOnDelete) { + mOnDelete(mData); + } + } + void* mData; + ABinderRpc_AccessorProviderUserData_deleteCallback mOnDelete; + // needs to be copyable for std::function, but we will never copy it + OnDeleteProviderHolder(const OnDeleteProviderHolder&) { + LOG_ALWAYS_FATAL("This object can't be copied!"); + } + + private: + OnDeleteProviderHolder() = delete; +}; + +ABinderRpc_AccessorProvider* ABinderRpc_registerAccessorProvider( + ABinderRpc_AccessorProvider_getAccessorCallback provider, const char** instances, + size_t numInstances, void* data, + ABinderRpc_AccessorProviderUserData_deleteCallback onDelete) { + if (provider == nullptr) { + ALOGE("Null provider passed to ABinderRpc_registerAccessorProvider"); + return nullptr; + } + 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 " + "the data object once the ABinderRpc_AccessorProvider is removed."); + return nullptr; + } + if (numInstances == 0 || instances == nullptr) { + ALOGE("No instances passed to ABinderRpc_registerAccessorProvider. numInstances: %zu", + numInstances); + return nullptr; + } + std::set<std::string> instanceStrings; + for (size_t i = 0; i < numInstances; i++) { + instanceStrings.emplace(instances[i]); + } + // call the onDelete when the last reference of this goes away (when the + // last reference to the generate std::function goes away). + std::shared_ptr<OnDeleteProviderHolder> onDeleteHolder = + std::make_shared<OnDeleteProviderHolder>(data, onDelete); + android::RpcAccessorProvider generate = [provider, + onDeleteHolder](const String16& name) -> sp<IBinder> { + ABinderRpc_Accessor* accessor = provider(String8(name).c_str(), onDeleteHolder->mData); + if (accessor == nullptr) { + ALOGE("The supplied ABinderRpc_AccessorProvider_getAccessorCallback returned nullptr"); + return nullptr; + } + sp<IBinder> binder = accessor->asBinder(); + ABinderRpc_Accessor_delete(accessor); + return binder; + }; + + std::weak_ptr<android::AccessorProvider> cookie = + android::addAccessorProvider(std::move(instanceStrings), std::move(generate)); + return ABinderRpc_AccessorProvider::make(cookie); +} + +void ABinderRpc_unregisterAccessorProvider(ABinderRpc_AccessorProvider* provider) { + if (provider == nullptr) { + LOG_ALWAYS_FATAL("Attempting to remove a null ABinderRpc_AccessorProvider"); + } + + status_t status = android::removeAccessorProvider(provider->mProviderCookie); + // There shouldn't be a way to get here because the caller won't have a + // ABinderRpc_AccessorProvider* without calling ABinderRpc_registerAccessorProvider + LOG_ALWAYS_FATAL_IF(status == android::BAD_VALUE, "Provider (%p) is not valid. Status: %s", + provider, android::statusToString(status).c_str()); + LOG_ALWAYS_FATAL_IF(status == android::NAME_NOT_FOUND, + "Provider (%p) was already unregistered. Status: %s", provider, + android::statusToString(status).c_str()); + LOG_ALWAYS_FATAL_IF(status != OK, + "Unknown error when attempting to unregister ABinderRpc_AccessorProvider " + "(%p). Status: %s", + provider, android::statusToString(status).c_str()); + + delete provider; +} + +struct OnDeleteConnectionInfoHolder { + OnDeleteConnectionInfoHolder(void* data, + ABinderRpc_ConnectionInfoProviderUserData_delete onDelete) + : mData(data), mOnDelete(onDelete) {} + ~OnDeleteConnectionInfoHolder() { + if (mOnDelete) { + mOnDelete(mData); + } + } + void* mData; + ABinderRpc_ConnectionInfoProviderUserData_delete mOnDelete; + // needs to be copyable for std::function, but we will never copy it + OnDeleteConnectionInfoHolder(const OnDeleteConnectionInfoHolder&) { + LOG_ALWAYS_FATAL("This object can't be copied!"); + } + + private: + OnDeleteConnectionInfoHolder() = delete; +}; + +ABinderRpc_Accessor* ABinderRpc_Accessor_new( + const char* instance, ABinderRpc_ConnectionInfoProvider provider, void* data, + ABinderRpc_ConnectionInfoProviderUserData_delete onDelete) { + if (instance == nullptr) { + ALOGE("Instance argument must be valid when calling ABinderRpc_Accessor_new"); + return nullptr; + } + 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 " + "delete " + "the data object once the ABinderRpc_Accessor is deleted."); + return nullptr; + } + std::shared_ptr<OnDeleteConnectionInfoHolder> onDeleteHolder = + std::make_shared<OnDeleteConnectionInfoHolder>(data, onDelete); + if (provider == nullptr) { + ALOGE("Can't create a new ABinderRpc_Accessor without a ABinderRpc_ConnectionInfoProvider " + "and it is " + "null"); + return nullptr; + } + android::RpcSocketAddressProvider generate = [provider, onDeleteHolder]( + const String16& name, sockaddr* outAddr, + size_t addrLen) -> status_t { + std::unique_ptr<ABinderRpc_ConnectionInfo> info( + provider(String8(name).c_str(), onDeleteHolder->mData)); + if (info == nullptr) { + ALOGE("The supplied ABinderRpc_ConnectionInfoProvider returned nullptr"); + return android::NAME_NOT_FOUND; + } + if (auto addr = std::get_if<sockaddr_vm>(&info->addr)) { + LOG_ALWAYS_FATAL_IF(addr->svm_family != AF_VSOCK, + "ABinderRpc_ConnectionInfo invalid family"); + if (addrLen < sizeof(sockaddr_vm)) { + ALOGE("Provided outAddr is too small! Expecting %zu, got %zu", sizeof(sockaddr_vm), + addrLen); + return android::BAD_VALUE; + } + LOG_ACCESSOR_DEBUG( + "Connection info provider found AF_VSOCK. family %d, port %d, cid %d", + addr->svm_family, addr->svm_port, addr->svm_cid); + *reinterpret_cast<sockaddr_vm*>(outAddr) = *addr; + } else if (auto addr = std::get_if<sockaddr_un>(&info->addr)) { + LOG_ALWAYS_FATAL_IF(addr->sun_family != AF_UNIX, + "ABinderRpc_ConnectionInfo invalid family"); + if (addrLen < sizeof(sockaddr_un)) { + ALOGE("Provided outAddr is too small! Expecting %zu, got %zu", sizeof(sockaddr_un), + addrLen); + return android::BAD_VALUE; + } + *reinterpret_cast<sockaddr_un*>(outAddr) = *addr; + } else if (auto addr = std::get_if<sockaddr_in>(&info->addr)) { + LOG_ALWAYS_FATAL_IF(addr->sin_family != AF_INET, + "ABinderRpc_ConnectionInfo invalid family"); + if (addrLen < sizeof(sockaddr_in)) { + ALOGE("Provided outAddr is too small! Expecting %zu, got %zu", sizeof(sockaddr_in), + addrLen); + return android::BAD_VALUE; + } + *reinterpret_cast<sockaddr_in*>(outAddr) = *addr; + } else { + LOG_ALWAYS_FATAL( + "Unsupported address family type when trying to get ARpcConnection info. A " + "new variant was added to the ABinderRpc_ConnectionInfo and this needs to be " + "updated."); + } + return OK; + }; + sp<IBinder> accessorBinder = android::createAccessor(String16(instance), std::move(generate)); + if (accessorBinder == nullptr) { + ALOGE("service manager did not get us an accessor"); + return nullptr; + } + LOG_ACCESSOR_DEBUG("service manager found an accessor, so returning one now from _new"); + return ABinderRpc_Accessor::make(instance, accessorBinder); +} + +void ABinderRpc_Accessor_delete(ABinderRpc_Accessor* accessor) { + delete accessor; +} + +AIBinder* ABinderRpc_Accessor_asBinder(ABinderRpc_Accessor* accessor) { + if (!accessor) { + ALOGE("ABinderRpc_Accessor argument is null."); + return nullptr; + } + + sp<IBinder> binder = accessor->asBinder(); + sp<AIBinder> aBinder = ABpBinder::lookupOrCreateFromBinder(binder); + AIBinder* ptr = aBinder.get(); + if (ptr == nullptr) { + LOG_ALWAYS_FATAL("Failed to lookupOrCreateFromBinder"); + } + ptr->incStrong(nullptr); + return ptr; +} + +ABinderRpc_Accessor* ABinderRpc_Accessor_fromBinder(const char* instance, AIBinder* binder) { + if (!binder) { + ALOGE("binder argument is null"); + return nullptr; + } + sp<IBinder> accessorBinder = binder->getBinder(); + if (accessorBinder) { + return ABinderRpc_Accessor::make(instance, accessorBinder); + } else { + ALOGE("Attempting to get an ABinderRpc_Accessor for %s but AIBinder::getBinder returned " + "null", + instance); + return nullptr; + } +} + +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"); + return nullptr; + } + // socklen_t was int32_t on 32-bit and uint32_t on 64 bit. + size_t socklen = len < 0 || static_cast<uintmax_t>(len) > SIZE_MAX ? 0 : len; + + if (addr->sa_family == AF_VSOCK) { + if (len != sizeof(sockaddr_vm)) { + ALOGE("Incorrect size of %zu for AF_VSOCK sockaddr_vm. Expecting %zu", socklen, + sizeof(sockaddr_vm)); + 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); + return new ABinderRpc_ConnectionInfo(vm); + } else if (addr->sa_family == AF_UNIX) { + if (len != sizeof(sockaddr_un)) { + ALOGE("Incorrect size of %zu for AF_UNIX sockaddr_un. Expecting %zu", socklen, + sizeof(sockaddr_un)); + return nullptr; + } + sockaddr_un un = *reinterpret_cast<const sockaddr_un*>(addr); + LOG_ACCESSOR_DEBUG("Arpc_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) { + if (len != sizeof(sockaddr_in)) { + ALOGE("Incorrect size of %zu for AF_INET sockaddr_in. Expecting %zu", socklen, + sizeof(sockaddr_in)); + 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)); + return new ABinderRpc_ConnectionInfo(in); + } + + ALOGE("ARpc APIs only support AF_VSOCK right now but the supplied sockadder::sa_family is: %hu", + addr->sa_family); + return nullptr; +} + +void ABinderRpc_ConnectionInfo_delete(ABinderRpc_ConnectionInfo* info) { + delete info; +} diff --git a/libs/binder/ndk/ibinder.cpp b/libs/binder/ndk/ibinder.cpp index af280d38a3..ff31dd0193 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::statusToString; 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 @@ static std::string SanitizeString(const String16& str) { 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::dump(int fd, const ::android::Vector<String16>& args) { 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 @@ AIBinder_Class::AIBinder_Class(const char* interfaceDescriptor, AIBinder_Class_o 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 @@ void AIBinder_Class_setOnDump(AIBinder_Class* clazz, AIBinder_onDump onDump) { 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 @@ static void DestroyParcel(AParcel** parcel) { 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 @@ void AIBinder_setInheritRt(AIBinder* binder, bool inheritRt) { "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 f5b738c1ef..a93dc1f674 100644 --- a/libs/binder/ndk/ibinder_internal.h +++ b/libs/binder/ndk/ibinder_internal.h @@ -132,6 +132,9 @@ struct AIBinder_Class { 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 @@ struct AIBinder_Class { // 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 af56bf0da1..8296356d6b 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__) +#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__ + #if __has_include(<android/binder_shell.h>) #include <android/binder_shell.h> #define HAS_BINDER_SHELL_COMMAND @@ -164,6 +175,10 @@ class ICInterface : public SharedRefBase { * 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 @@ std::shared_ptr<ICInterface> ICInterface::asInterface(AIBinder* binder) { 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,20 @@ AIBinder_Class* ICInterface::defineClass(const char* interfaceDescriptor, AIBinder_Class_setHandleShellCommand(clazz, ICInterfaceData::handleShellCommand); } #endif + +// TODO(b/368559337): fix versioning on product partition +#if !defined(__ANDROID_PRODUCT__) && \ + (defined(__ANDROID_UNAVAILABLE_SYMBOLS_ARE_WEAK__) || __ANDROID_API__ >= 36) + if API_LEVEL_AT_LEAST (36, 202504) { + if (codeToFunction != 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_ndk/android/binder_ibinder.h b/libs/binder/ndk/include_ndk/android/binder_ibinder.h index 72d255e816..2f6c4e3642 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 @@ typedef binder_status_t (*AIBinder_onDump)(AIBinder* binder, int fd, const char* 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 and transactionCodeToFunctionMap should have same + * lifetime. Resetting/clearing the transactionCodeToFunctionMap is not allowed. + * + * 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 + * + * \return true if setting codeToFunction to clazz is successful. return false if clazz or + * codeToFunction is nullptr. + */ +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. + * 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_ndk/android/persistable_bundle.h b/libs/binder/ndk/include_ndk/android/persistable_bundle.h index 5e0d4da97b..1d516aea9d 100644 --- a/libs/binder/ndk/include_ndk/android/persistable_bundle.h +++ b/libs/binder/ndk/include_ndk/android/persistable_bundle.h @@ -17,13 +17,6 @@ #pragma once #include <android/binder_parcel.h> -#if defined(__ANDROID_VENDOR__) -#include <android/llndk-versioning.h> -#else -#if !defined(__INTRODUCED_IN_LLNDK) -#define __INTRODUCED_IN_LLNDK(level) __attribute__((annotate("introduced_in_llndk=" #level))) -#endif -#endif // __ANDROID_VENDOR__ #include <stdbool.h> #include <stdint.h> #include <sys/cdefs.h> @@ -83,8 +76,7 @@ typedef char* _Nullable (*_Nonnull APersistableBundle_stringAllocator)(int32_t s * * \return Pointer to a new APersistableBundle */ -APersistableBundle* _Nullable APersistableBundle_new() __INTRODUCED_IN(__ANDROID_API_V__) - __INTRODUCED_IN_LLNDK(202404); +APersistableBundle* _Nullable APersistableBundle_new() __INTRODUCED_IN(__ANDROID_API_V__); /** * Create a new APersistableBundle based off an existing APersistableBundle. @@ -98,7 +90,7 @@ APersistableBundle* _Nullable APersistableBundle_new() __INTRODUCED_IN(__ANDROID * \return Pointer to a new APersistableBundle */ APersistableBundle* _Nullable APersistableBundle_dup(const APersistableBundle* _Nonnull pBundle) - __INTRODUCED_IN(__ANDROID_API_V__) __INTRODUCED_IN_LLNDK(202404); + __INTRODUCED_IN(__ANDROID_API_V__); /** * Delete an APersistableBundle. This must always be called when finished using @@ -109,7 +101,7 @@ APersistableBundle* _Nullable APersistableBundle_dup(const APersistableBundle* _ * Available since API level 202404. */ void APersistableBundle_delete(APersistableBundle* _Nullable pBundle) - __INTRODUCED_IN(__ANDROID_API_V__) __INTRODUCED_IN_LLNDK(202404); + __INTRODUCED_IN(__ANDROID_API_V__); /** * Check for equality of APersistableBundles. @@ -123,7 +115,7 @@ void APersistableBundle_delete(APersistableBundle* _Nullable pBundle) */ bool APersistableBundle_isEqual(const APersistableBundle* _Nonnull lhs, const APersistableBundle* _Nonnull rhs) - __INTRODUCED_IN(__ANDROID_API_V__) __INTRODUCED_IN_LLNDK(202404); + __INTRODUCED_IN(__ANDROID_API_V__); /** * Read an APersistableBundle from an AParcel. @@ -142,7 +134,7 @@ bool APersistableBundle_isEqual(const APersistableBundle* _Nonnull lhs, */ binder_status_t APersistableBundle_readFromParcel( const AParcel* _Nonnull parcel, APersistableBundle* _Nullable* _Nonnull outPBundle) - __INTRODUCED_IN(__ANDROID_API_V__) __INTRODUCED_IN_LLNDK(202404); + __INTRODUCED_IN(__ANDROID_API_V__); /** * Write an APersistableBundle to an AParcel. @@ -162,7 +154,7 @@ binder_status_t APersistableBundle_readFromParcel( */ binder_status_t APersistableBundle_writeToParcel(const APersistableBundle* _Nonnull pBundle, AParcel* _Nonnull parcel) - __INTRODUCED_IN(__ANDROID_API_V__) __INTRODUCED_IN_LLNDK(202404); + __INTRODUCED_IN(__ANDROID_API_V__); /** * Get the size of an APersistableBundle. This is the number of mappings in the @@ -175,7 +167,7 @@ binder_status_t APersistableBundle_writeToParcel(const APersistableBundle* _Nonn * \return number of mappings in the object */ int32_t APersistableBundle_size(const APersistableBundle* _Nonnull pBundle) - __INTRODUCED_IN(__ANDROID_API_V__) __INTRODUCED_IN_LLNDK(202404); + __INTRODUCED_IN(__ANDROID_API_V__); /** * Erase any entries added with the provided key. @@ -188,7 +180,7 @@ int32_t APersistableBundle_size(const APersistableBundle* _Nonnull pBundle) * \return number of entries erased. Either 0 or 1. */ int32_t APersistableBundle_erase(APersistableBundle* _Nonnull pBundle, const char* _Nonnull key) - __INTRODUCED_IN(__ANDROID_API_V__) __INTRODUCED_IN_LLNDK(202404); + __INTRODUCED_IN(__ANDROID_API_V__); /** * Put a boolean associated with the provided key. @@ -201,8 +193,7 @@ int32_t APersistableBundle_erase(APersistableBundle* _Nonnull pBundle, const cha * Available since API level 202404. */ void APersistableBundle_putBoolean(APersistableBundle* _Nonnull pBundle, const char* _Nonnull key, - bool val) __INTRODUCED_IN(__ANDROID_API_V__) - __INTRODUCED_IN_LLNDK(202404); + bool val) __INTRODUCED_IN(__ANDROID_API_V__); /** * Put an int32_t associated with the provided key. @@ -215,8 +206,7 @@ void APersistableBundle_putBoolean(APersistableBundle* _Nonnull pBundle, const c * Available since API level 202404. */ void APersistableBundle_putInt(APersistableBundle* _Nonnull pBundle, const char* _Nonnull key, - int32_t val) __INTRODUCED_IN(__ANDROID_API_V__) - __INTRODUCED_IN_LLNDK(202404); + int32_t val) __INTRODUCED_IN(__ANDROID_API_V__); /** * Put an int64_t associated with the provided key. @@ -229,8 +219,7 @@ void APersistableBundle_putInt(APersistableBundle* _Nonnull pBundle, const char* * Available since API level 202404. */ void APersistableBundle_putLong(APersistableBundle* _Nonnull pBundle, const char* _Nonnull key, - int64_t val) __INTRODUCED_IN(__ANDROID_API_V__) - __INTRODUCED_IN_LLNDK(202404); + int64_t val) __INTRODUCED_IN(__ANDROID_API_V__); /** * Put a double associated with the provided key. @@ -243,8 +232,7 @@ void APersistableBundle_putLong(APersistableBundle* _Nonnull pBundle, const char * Available since API level 202404. */ void APersistableBundle_putDouble(APersistableBundle* _Nonnull pBundle, const char* _Nonnull key, - double val) __INTRODUCED_IN(__ANDROID_API_V__) - __INTRODUCED_IN_LLNDK(202404); + double val) __INTRODUCED_IN(__ANDROID_API_V__); /** * Put a string associated with the provided key. @@ -258,8 +246,7 @@ void APersistableBundle_putDouble(APersistableBundle* _Nonnull pBundle, const ch * Available since API level 202404. */ void APersistableBundle_putString(APersistableBundle* _Nonnull pBundle, const char* _Nonnull key, - const char* _Nonnull val) __INTRODUCED_IN(__ANDROID_API_V__) - __INTRODUCED_IN_LLNDK(202404); + const char* _Nonnull val) __INTRODUCED_IN(__ANDROID_API_V__); /** * Put a boolean vector associated with the provided key. @@ -275,8 +262,7 @@ void APersistableBundle_putString(APersistableBundle* _Nonnull pBundle, const ch */ void APersistableBundle_putBooleanVector(APersistableBundle* _Nonnull pBundle, const char* _Nonnull key, const bool* _Nonnull vec, - int32_t num) __INTRODUCED_IN(__ANDROID_API_V__) - __INTRODUCED_IN_LLNDK(202404); + int32_t num) __INTRODUCED_IN(__ANDROID_API_V__); /** * Put an int32_t vector associated with the provided key. @@ -292,7 +278,7 @@ void APersistableBundle_putBooleanVector(APersistableBundle* _Nonnull pBundle, */ void APersistableBundle_putIntVector(APersistableBundle* _Nonnull pBundle, const char* _Nonnull key, const int32_t* _Nonnull vec, int32_t num) - __INTRODUCED_IN(__ANDROID_API_V__) __INTRODUCED_IN_LLNDK(202404); + __INTRODUCED_IN(__ANDROID_API_V__); /** * Put an int64_t vector associated with the provided key. @@ -308,8 +294,7 @@ void APersistableBundle_putIntVector(APersistableBundle* _Nonnull pBundle, const */ void APersistableBundle_putLongVector(APersistableBundle* _Nonnull pBundle, const char* _Nonnull key, const int64_t* _Nonnull vec, - int32_t num) __INTRODUCED_IN(__ANDROID_API_V__) - __INTRODUCED_IN_LLNDK(202404); + int32_t num) __INTRODUCED_IN(__ANDROID_API_V__); /** * Put a double vector associated with the provided key. @@ -325,8 +310,7 @@ void APersistableBundle_putLongVector(APersistableBundle* _Nonnull pBundle, */ void APersistableBundle_putDoubleVector(APersistableBundle* _Nonnull pBundle, const char* _Nonnull key, const double* _Nonnull vec, - int32_t num) __INTRODUCED_IN(__ANDROID_API_V__) - __INTRODUCED_IN_LLNDK(202404); + int32_t num) __INTRODUCED_IN(__ANDROID_API_V__); /** * Put a string vector associated with the provided key. @@ -343,7 +327,7 @@ void APersistableBundle_putDoubleVector(APersistableBundle* _Nonnull pBundle, void APersistableBundle_putStringVector(APersistableBundle* _Nonnull pBundle, const char* _Nonnull key, const char* _Nullable const* _Nullable vec, int32_t num) - __INTRODUCED_IN(__ANDROID_API_V__) __INTRODUCED_IN_LLNDK(202404); + __INTRODUCED_IN(__ANDROID_API_V__); /** * Put an APersistableBundle associated with the provided key. @@ -359,7 +343,7 @@ void APersistableBundle_putStringVector(APersistableBundle* _Nonnull pBundle, void APersistableBundle_putPersistableBundle(APersistableBundle* _Nonnull pBundle, const char* _Nonnull key, const APersistableBundle* _Nonnull val) - __INTRODUCED_IN(__ANDROID_API_V__) __INTRODUCED_IN_LLNDK(202404); + __INTRODUCED_IN(__ANDROID_API_V__); /** * Get a boolean associated with the provided key. @@ -374,7 +358,7 @@ void APersistableBundle_putPersistableBundle(APersistableBundle* _Nonnull pBundl */ bool APersistableBundle_getBoolean(const APersistableBundle* _Nonnull pBundle, const char* _Nonnull key, bool* _Nonnull val) - __INTRODUCED_IN(__ANDROID_API_V__) __INTRODUCED_IN_LLNDK(202404); + __INTRODUCED_IN(__ANDROID_API_V__); /** * Get an int32_t associated with the provided key. @@ -388,8 +372,7 @@ bool APersistableBundle_getBoolean(const APersistableBundle* _Nonnull pBundle, * \return true if a value exists for the provided key */ bool APersistableBundle_getInt(const APersistableBundle* _Nonnull pBundle, const char* _Nonnull key, - int32_t* _Nonnull val) __INTRODUCED_IN(__ANDROID_API_V__) - __INTRODUCED_IN_LLNDK(202404); + int32_t* _Nonnull val) __INTRODUCED_IN(__ANDROID_API_V__); /** * Get an int64_t associated with the provided key. @@ -404,7 +387,7 @@ bool APersistableBundle_getInt(const APersistableBundle* _Nonnull pBundle, const */ bool APersistableBundle_getLong(const APersistableBundle* _Nonnull pBundle, const char* _Nonnull key, int64_t* _Nonnull val) - __INTRODUCED_IN(__ANDROID_API_V__) __INTRODUCED_IN_LLNDK(202404); + __INTRODUCED_IN(__ANDROID_API_V__); /** * Get a double associated with the provided key. @@ -419,7 +402,7 @@ bool APersistableBundle_getLong(const APersistableBundle* _Nonnull pBundle, */ bool APersistableBundle_getDouble(const APersistableBundle* _Nonnull pBundle, const char* _Nonnull key, double* _Nonnull val) - __INTRODUCED_IN(__ANDROID_API_V__) __INTRODUCED_IN_LLNDK(202404); + __INTRODUCED_IN(__ANDROID_API_V__); /** * Get a string associated with the provided key. @@ -440,8 +423,7 @@ bool APersistableBundle_getDouble(const APersistableBundle* _Nonnull pBundle, int32_t APersistableBundle_getString(const APersistableBundle* _Nonnull pBundle, const char* _Nonnull key, char* _Nullable* _Nonnull val, APersistableBundle_stringAllocator stringAllocator, - void* _Nullable context) __INTRODUCED_IN(__ANDROID_API_V__) - __INTRODUCED_IN_LLNDK(202404); + void* _Nullable context) __INTRODUCED_IN(__ANDROID_API_V__); /** * Get a boolean vector associated with the provided key and place it in the @@ -468,7 +450,7 @@ int32_t APersistableBundle_getString(const APersistableBundle* _Nonnull pBundle, int32_t APersistableBundle_getBooleanVector(const APersistableBundle* _Nonnull pBundle, const char* _Nonnull key, bool* _Nullable buffer, int32_t bufferSizeBytes) - __INTRODUCED_IN(__ANDROID_API_V__) __INTRODUCED_IN_LLNDK(202404); + __INTRODUCED_IN(__ANDROID_API_V__); /** * Get an int32_t vector associated with the provided key and place it in the @@ -494,8 +476,7 @@ int32_t APersistableBundle_getBooleanVector(const APersistableBundle* _Nonnull p */ int32_t APersistableBundle_getIntVector(const APersistableBundle* _Nonnull pBundle, const char* _Nonnull key, int32_t* _Nullable buffer, - int32_t bufferSizeBytes) __INTRODUCED_IN(__ANDROID_API_V__) - __INTRODUCED_IN_LLNDK(202404); + int32_t bufferSizeBytes) __INTRODUCED_IN(__ANDROID_API_V__); /** * Get an int64_t vector associated with the provided key and place it in the @@ -521,8 +502,8 @@ int32_t APersistableBundle_getIntVector(const APersistableBundle* _Nonnull pBund */ int32_t APersistableBundle_getLongVector(const APersistableBundle* _Nonnull pBundle, const char* _Nonnull key, int64_t* _Nullable buffer, - int32_t bufferSizeBytes) __INTRODUCED_IN(__ANDROID_API_V__) - __INTRODUCED_IN_LLNDK(202404); + int32_t bufferSizeBytes) + __INTRODUCED_IN(__ANDROID_API_V__); /** * Get a double vector associated with the provided key and place it in the @@ -549,7 +530,7 @@ int32_t APersistableBundle_getLongVector(const APersistableBundle* _Nonnull pBun int32_t APersistableBundle_getDoubleVector(const APersistableBundle* _Nonnull pBundle, const char* _Nonnull key, double* _Nullable buffer, int32_t bufferSizeBytes) - __INTRODUCED_IN(__ANDROID_API_V__) __INTRODUCED_IN_LLNDK(202404); + __INTRODUCED_IN(__ANDROID_API_V__); /** * Get a string vector associated with the provided key and place it in the @@ -586,7 +567,7 @@ int32_t APersistableBundle_getStringVector(const APersistableBundle* _Nonnull pB int32_t bufferSizeBytes, APersistableBundle_stringAllocator stringAllocator, void* _Nullable context) - __INTRODUCED_IN(__ANDROID_API_V__) __INTRODUCED_IN_LLNDK(202404); + __INTRODUCED_IN(__ANDROID_API_V__); /** * Get an APersistableBundle* associated with the provided key. @@ -605,7 +586,7 @@ int32_t APersistableBundle_getStringVector(const APersistableBundle* _Nonnull pB bool APersistableBundle_getPersistableBundle(const APersistableBundle* _Nonnull pBundle, const char* _Nonnull key, APersistableBundle* _Nullable* _Nonnull outBundle) - __INTRODUCED_IN(__ANDROID_API_V__) __INTRODUCED_IN_LLNDK(202404); + __INTRODUCED_IN(__ANDROID_API_V__); /** * Get all of the keys associated with this specific type and place it in the @@ -638,7 +619,7 @@ int32_t APersistableBundle_getBooleanKeys(const APersistableBundle* _Nonnull pBu int32_t bufferSizeBytes, APersistableBundle_stringAllocator stringAllocator, void* _Nullable context) - __INTRODUCED_IN(__ANDROID_API_V__) __INTRODUCED_IN_LLNDK(202404); + __INTRODUCED_IN(__ANDROID_API_V__); /** * Get all of the keys associated with this specific type and place it in the @@ -669,8 +650,7 @@ int32_t APersistableBundle_getBooleanKeys(const APersistableBundle* _Nonnull pBu int32_t APersistableBundle_getIntKeys(const APersistableBundle* _Nonnull pBundle, char* _Nullable* _Nullable outKeys, int32_t bufferSizeBytes, APersistableBundle_stringAllocator stringAllocator, - void* _Nullable context) __INTRODUCED_IN(__ANDROID_API_V__) - __INTRODUCED_IN_LLNDK(202404); + void* _Nullable context) __INTRODUCED_IN(__ANDROID_API_V__); /** * Get all of the keys associated with this specific type and place it in the @@ -701,8 +681,7 @@ int32_t APersistableBundle_getIntKeys(const APersistableBundle* _Nonnull pBundle int32_t APersistableBundle_getLongKeys(const APersistableBundle* _Nonnull pBundle, char* _Nullable* _Nullable outKeys, int32_t bufferSizeBytes, APersistableBundle_stringAllocator stringAllocator, - void* _Nullable context) __INTRODUCED_IN(__ANDROID_API_V__) - __INTRODUCED_IN_LLNDK(202404); + void* _Nullable context) __INTRODUCED_IN(__ANDROID_API_V__); /** * Get all of the keys associated with this specific type and place it in the @@ -734,8 +713,8 @@ int32_t APersistableBundle_getDoubleKeys(const APersistableBundle* _Nonnull pBun char* _Nullable* _Nullable outKeys, int32_t bufferSizeBytes, APersistableBundle_stringAllocator stringAllocator, - void* _Nullable context) __INTRODUCED_IN(__ANDROID_API_V__) - __INTRODUCED_IN_LLNDK(202404); + void* _Nullable context) + __INTRODUCED_IN(__ANDROID_API_V__); /** * Get all of the keys associated with this specific type and place it in the @@ -767,8 +746,8 @@ int32_t APersistableBundle_getStringKeys(const APersistableBundle* _Nonnull pBun char* _Nullable* _Nullable outKeys, int32_t bufferSizeBytes, APersistableBundle_stringAllocator stringAllocator, - void* _Nullable context) __INTRODUCED_IN(__ANDROID_API_V__) - __INTRODUCED_IN_LLNDK(202404); + void* _Nullable context) + __INTRODUCED_IN(__ANDROID_API_V__); /** * Get all of the keys associated with this specific type and place it in the @@ -801,7 +780,7 @@ int32_t APersistableBundle_getBooleanVectorKeys(const APersistableBundle* _Nonnu int32_t bufferSizeBytes, APersistableBundle_stringAllocator stringAllocator, void* _Nullable context) - __INTRODUCED_IN(__ANDROID_API_V__) __INTRODUCED_IN_LLNDK(202404); + __INTRODUCED_IN(__ANDROID_API_V__); /** * Get all of the keys associated with this specific type and place it in the @@ -834,7 +813,7 @@ int32_t APersistableBundle_getIntVectorKeys(const APersistableBundle* _Nonnull p int32_t bufferSizeBytes, APersistableBundle_stringAllocator stringAllocator, void* _Nullable context) - __INTRODUCED_IN(__ANDROID_API_V__) __INTRODUCED_IN_LLNDK(202404); + __INTRODUCED_IN(__ANDROID_API_V__); /** * Get all of the keys associated with this specific type and place it in the @@ -867,7 +846,7 @@ int32_t APersistableBundle_getLongVectorKeys(const APersistableBundle* _Nonnull int32_t bufferSizeBytes, APersistableBundle_stringAllocator stringAllocator, void* _Nullable context) - __INTRODUCED_IN(__ANDROID_API_V__) __INTRODUCED_IN_LLNDK(202404); + __INTRODUCED_IN(__ANDROID_API_V__); /** * Get all of the keys associated with this specific type and place it in the @@ -899,7 +878,7 @@ int32_t APersistableBundle_getDoubleVectorKeys(const APersistableBundle* _Nonnul int32_t bufferSizeBytes, APersistableBundle_stringAllocator stringAllocator, void* _Nullable context) - __INTRODUCED_IN(__ANDROID_API_V__) __INTRODUCED_IN_LLNDK(202404); + __INTRODUCED_IN(__ANDROID_API_V__); /** * Get all of the keys associated with this specific type and place it in the @@ -932,7 +911,7 @@ int32_t APersistableBundle_getStringVectorKeys(const APersistableBundle* _Nonnul int32_t bufferSizeBytes, APersistableBundle_stringAllocator stringAllocator, void* _Nullable context) - __INTRODUCED_IN(__ANDROID_API_V__) __INTRODUCED_IN_LLNDK(202404); + __INTRODUCED_IN(__ANDROID_API_V__); /** * Get all of the keys associated with this specific type and place it in the @@ -963,6 +942,6 @@ int32_t APersistableBundle_getStringVectorKeys(const APersistableBundle* _Nonnul int32_t APersistableBundle_getPersistableBundleKeys( const APersistableBundle* _Nonnull pBundle, char* _Nullable* _Nullable outKeys, int32_t bufferSizeBytes, APersistableBundle_stringAllocator stringAllocator, - void* _Nullable context) __INTRODUCED_IN(__ANDROID_API_V__) __INTRODUCED_IN_LLNDK(202404); + void* _Nullable context) __INTRODUCED_IN(__ANDROID_API_V__); __END_DECLS diff --git a/libs/binder/ndk/include_platform/android/binder_manager.h b/libs/binder/ndk/include_platform/android/binder_manager.h index 41b30a0a0f..cc4943b9c3 100644 --- a/libs/binder/ndk/include_platform/android/binder_manager.h +++ b/libs/binder/ndk/include_platform/android/binder_manager.h @@ -18,7 +18,6 @@ #include <android/binder_ibinder.h> #include <android/binder_status.h> -#include <android/llndk-versioning.h> #include <sys/cdefs.h> __BEGIN_DECLS @@ -257,8 +256,7 @@ void AServiceManager_getUpdatableApexName(const char* instance, void* context, * \return the result of dlopen of the specified HAL */ void* AServiceManager_openDeclaredPassthroughHal(const char* interface, const char* instance, - int flag) __INTRODUCED_IN(__ANDROID_API_V__) - __INTRODUCED_IN_LLNDK(202404); + int flag) __INTRODUCED_IN(__ANDROID_API_V__); /** * Prevent lazy services without client from shutting down their process diff --git a/libs/binder/ndk/include_platform/android/binder_rpc.h b/libs/binder/ndk/include_platform/android/binder_rpc.h new file mode 100644 index 0000000000..4c5471ff70 --- /dev/null +++ b/libs/binder/ndk/include_platform/android/binder_rpc.h @@ -0,0 +1,261 @@ +/* + * 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. + */ + +#pragma once + +#include <android/binder_ibinder.h> +#include <sys/socket.h> + +__BEGIN_DECLS + +/** + * 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 + * used to set up a client RpcSession underneath libbinder's IServiceManager APIs + * to provide the client with the service's binder for remote communication. + */ +typedef struct ABinderRpc_Accessor ABinderRpc_Accessor; + +/** + * This represents an object that supplies ABinderRpc_Accessors to libbinder + * when they are requested. They are requested any time a client is attempting + * to get a service through IServiceManager APIs when the services aren't known by + * servicemanager. + */ +typedef struct ABinderRpc_AccessorProvider ABinderRpc_AccessorProvider; + +/** + * This represents information necessary for libbinder to be able to connect to a + * remote service. + * It supports connecting to linux sockets and is created using sockaddr + * types for sockets supported by libbinder like sockaddr_in, sockaddr_un, + * sockaddr_vm. + */ +typedef struct ABinderRpc_ConnectionInfo ABinderRpc_ConnectionInfo; + +/** + * These APIs provide a way for clients of binder services to be able to get a + * binder object of that service through the existing libbinder/libbinder_ndk + * Service Manager APIs when that service is using RPC Binder over sockets + * instead kernel binder. + * + * Some of these APIs are used on Android hosts when kernel binder is supported + * and the usual servicemanager process is available. Some of these APIs are + * only required when there is no kernel binder or extra servicemanager process + * such as the case of microdroid or similar VMs. + */ + +/** + * This callback is responsible for returning ABinderRpc_Accessor objects for a given + * service instance. These ABinderRpc_Accessor objects are implemented by + * libbinder_ndk and backed by implementations of android::os::IAccessor in + * libbinder. + * + * \param instance name of the service like + * `android.hardware.vibrator.IVibrator/default` + * \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 + * callback gives up ownership of the object once it returns it. The + * caller of this callback (libbinder_ndk) is responsible for deleting it + * with ABinderRpc_Accessor_delete. + */ +typedef ABinderRpc_Accessor* _Nullable (*ABinderRpc_AccessorProvider_getAccessorCallback)( + const char* _Nonnull instance, void* _Nullable data); + +/** + * This callback is responsible deleting the `void* data` object that is passed + * in to ABinderRpc_registerAccessorProvider for the ABinderRpc_AccessorProvider_getAccessorCallback + * to use. That object is owned by the ABinderRpc_AccessorProvider and must remain valid for the + * lifetime of the callback because it may be called and use the object. + * This _delete callback is called after the ABinderRpc_AccessorProvider is remove and + * is guaranteed never to be called again. + * + * \param data a pointer to data that the ABinderRpc_AccessorProvider_getAccessorCallback uses which + * is to be deleted by this call. + */ +typedef void (*ABinderRpc_AccessorProviderUserData_deleteCallback)(void* _Nullable data); + +/** + * Inject an ABinderRpc_AccessorProvider_getAccessorCallback into the process for + * the Service Manager APIs to use to retrieve ABinderRpc_Accessor objects associated + * with different RPC Binder services. + * + * \param provider callback that returns ABinderRpc_Accessors for libbinder to set up + * RPC clients with. + * \param instances array of instances that are supported by this provider. It + * will only be called if the client is looking for an instance that is + * in this list. These instances must be unique per-process. If an + * 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. + * \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 + * 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 + * called again. Before this callback is called, `data` must remain + * valid. + * \return nullptr on error if the data pointer is non-null and the onDelete + * callback is null or if an instance in the instances list was previously + * registered. In the error case of duplicate instances, if data was + * provided with a ABinderRpc_AccessorProviderUserData_deleteCallback, + * the callback will be called to delete the data. + * Otherwise returns a pointer to the ABinderRpc_AccessorProvider that + * can be used to remove with ABinderRpc_unregisterAccessorProvider. + */ +ABinderRpc_AccessorProvider* _Nullable ABinderRpc_registerAccessorProvider( + ABinderRpc_AccessorProvider_getAccessorCallback _Nonnull provider, + const char* _Nullable* _Nonnull instances, size_t numInstances, void* _Nullable data, + ABinderRpc_AccessorProviderUserData_deleteCallback _Nullable onDelete) __INTRODUCED_IN(36); + +/** + * Remove an ABinderRpc_AccessorProvider from libbinder. This will remove references + * from the ABinderRpc_AccessorProvider and will no longer call the + * ABinderRpc_AccessorProvider_getAccessorCallback. + * + * Note: The `data` object that was used when adding the accessor will be + * deleted by the ABinderRpc_AccessorProviderUserData_deleteCallback at some + * point after this call. Do not use the object and do not try to delete + * it through any other means. + * Note: This will abort when used incorrectly if this provider was never + * registered or if it were already unregistered. + * + * \param provider to be removed and deleted + * + */ +void ABinderRpc_unregisterAccessorProvider(ABinderRpc_AccessorProvider* _Nonnull provider) + __INTRODUCED_IN(36); + +/** + * Callback which returns the RPC connection information for libbinder to use to + * 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 + * ABinderRpc_Accessor_new. + * \return ABinderRpc_ConnectionInfo with socket connection information for `instance` + */ +typedef ABinderRpc_ConnectionInfo* _Nullable (*ABinderRpc_ConnectionInfoProvider)( + const char* _Nonnull instance, void* _Nullable data) __INTRODUCED_IN(36); +/** + * This callback is responsible deleting the `void* data` object that is passed + * in to ABinderRpc_Accessor_new for the ABinderRpc_ConnectionInfoProvider to use. That + * object is owned by the ABinderRpc_Accessor and must remain valid for the + * lifetime the Accessor because it may be used by the connection info provider + * callback. + * This _delete callback is called after the ABinderRpc_Accessor is removed and + * is guaranteed never to be called again. + * + * \param data a pointer to data that the ABinderRpc_AccessorProvider uses which is to + * be deleted by this call. + */ +typedef void (*ABinderRpc_ConnectionInfoProviderUserData_delete)(void* _Nullable data); + +/** + * Create a new ABinderRpc_Accessor. This creates an IAccessor object in libbinder + * 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 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. + * \param data pointer that is passed to the ABinderRpc_ConnectionInfoProvider callback. + * IMPORTANT: The ABinderRpc_ConnectionInfoProvider 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 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_ConnectionInfoProvider is guaranteed to never be + * called again. Before this callback is called, `data` must remain + * valid. + * \return an ABinderRpc_Accessor instance. This is deleted by the caller once it is + * no longer needed. + */ +ABinderRpc_Accessor* _Nullable ABinderRpc_Accessor_new( + const char* _Nonnull instance, ABinderRpc_ConnectionInfoProvider _Nonnull provider, + void* _Nullable data, ABinderRpc_ConnectionInfoProviderUserData_delete _Nullable onDelete) + __INTRODUCED_IN(36); + +/** + * Delete an ABinderRpc_Accessor + * + * \param accessor to delete + */ +void ABinderRpc_Accessor_delete(ABinderRpc_Accessor* _Nonnull accessor) __INTRODUCED_IN(36); + +/** + * Return the AIBinder associated with an ABinderRpc_Accessor. This can be used to + * send the Accessor to another process or even register it with servicemanager. + * + * \param accessor to get the AIBinder for + * \return binder of the supplied accessor with one strong ref count + */ +AIBinder* _Nullable ABinderRpc_Accessor_asBinder(ABinderRpc_Accessor* _Nonnull accessor) + __INTRODUCED_IN(36); + +/** + * 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 + * 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. + * \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. + */ +ABinderRpc_Accessor* _Nullable ABinderRpc_Accessor_fromBinder(const char* _Nonnull instance, + AIBinder* _Nonnull accessorBinder) + __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). + * + * \param addr sockaddr pointer that can come from supported socket + * types like sockaddr_vm (vsock) and sockaddr_un (Unix Domain Sockets). + * \param len length of the concrete sockaddr type being used. Like + * sizeof(sockaddr_vm) when sockaddr_vm is used. + * \return the connection info based on the given sockaddr + */ +ABinderRpc_ConnectionInfo* _Nullable ABinderRpc_ConnectionInfo_new(const sockaddr* _Nonnull addr, + socklen_t len) + __INTRODUCED_IN(36); + +/** + * Delete an ABinderRpc_ConnectionInfo object that was created with + * ABinderRpc_ConnectionInfo_new. + * + * \param info object to be deleted + */ +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 826e199093..c885816e56 100644 --- a/libs/binder/ndk/libbinder_ndk.map.txt +++ b/libs/binder/ndk/libbinder_ndk.map.txt @@ -248,6 +248,22 @@ LIBBINDER_NDK35 { # introduced=VanillaIceCream AServiceManager_openDeclaredPassthroughHal; # systemapi llndk=202404 }; +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_delete; # systemapi + ABinderRpc_Accessor_asBinder; # systemapi + ABinderRpc_Accessor_fromBinder; # systemapi + ABinderRpc_ConnectionInfo_new; # systemapi + ABinderRpc_ConnectionInfo_delete; # systemapi +}; + LIBBINDER_NDK_PLATFORM { global: AParcel_getAllowFds; diff --git a/libs/binder/ndk/tests/libbinder_ndk_unit_test.cpp b/libs/binder/ndk/tests/libbinder_ndk_unit_test.cpp index 3cd2b9a891..e5a3da460e 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 @@ TEST(NdkBinder_ScopedAResource, Release) { 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/src/binder.rs b/libs/binder/rust/src/binder.rs index 9a252b853b..23026e593c 100644 --- a/libs/binder/rust/src/binder.rs +++ b/libs/binder/rust/src/binder.rs @@ -136,6 +136,31 @@ impl TryFrom<i32> for Stability { } } +/// 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 e70f4f0232..e048696e88 100644 --- a/libs/binder/rust/src/lib.rs +++ b/libs/binder/rust/src/lib.rs @@ -128,9 +128,10 @@ pub type Result<T> = std::result::Result<T, Status>; /// 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 f90611361f..87b42ab68d 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 @@ enum ParcelableHolderData { /// `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 @@ pub struct ParcelableHolder { // 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 @@ impl ParcelableHolder { 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 @@ impl ParcelableHolder { /// Return the stability value of this object. pub fn get_stability(&self) -> Stability { - self.stability + STABILITY::VALUE + } +} + +impl<STABILITY: StabilityType> Default for ParcelableHolder<STABILITY> { + fn default() -> Self { + Self::new() } } -impl Clone for ParcelableHolder { - fn clone(&self) -> ParcelableHolder { +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 @@ impl Deserialize for ParcelableHolder { 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 @@ impl Parcelable for ParcelableHolder { } 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/tests/parcel_fuzzer/parcel_fuzzer.rs b/libs/binder/rust/tests/parcel_fuzzer/parcel_fuzzer.rs index ce0f742934..ee20a22345 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 @@ mod read_utils; 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 @@ fn do_read_fuzz(read_operations: Vec<ReadOperation>, data: &[u8]) { } 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/Android.bp b/libs/binder/tests/Android.bp index 0e653af707..8b0dda33dd 100644 --- a/libs/binder/tests/Android.bp +++ b/libs/binder/tests/Android.bp @@ -532,6 +532,9 @@ cc_test { static_libs: [ "libbinder_rpc_single_threaded", ], + shared_libs: [ + "libbinder_ndk", + ], } cc_test { diff --git a/libs/binder/tests/binderRpcTest.cpp b/libs/binder/tests/binderRpcTest.cpp index 0ef200bcbe..077a33a201 100644 --- a/libs/binder/tests/binderRpcTest.cpp +++ b/libs/binder/tests/binderRpcTest.cpp @@ -46,6 +46,13 @@ #include "binderRpcTestCommon.h" #include "binderRpcTestFixture.h" +// TODO need to add IServiceManager.cpp/.h to libbinder_no_kernel +#ifdef BINDER_WITH_KERNEL_IPC +#include "android-base/logging.h" +#include "android/binder_manager.h" +#include "android/binder_rpc.h" +#endif // BINDER_WITH_KERNEL_IPC + using namespace std::chrono_literals; using namespace std::placeholders; using android::binder::borrowed_fd; @@ -1204,27 +1211,29 @@ TEST_P(BinderRpcAccessor, InjectAndGetServiceHappyPath) { auto proc = createRpcTestSocketServerProcess({.numThreads = kNumThreads}); EXPECT_EQ(OK, proc.rootBinder->pingBinder()); - auto receipt = addAccessorProvider([&](const String16& name) -> sp<IBinder> { - return createAccessor(name, - [&](const String16& name, sockaddr* outAddr, - socklen_t addrSize) -> status_t { - if (outAddr == nullptr || - addrSize < proc.proc->sessions[0].addrLen) { - return BAD_VALUE; - } - if (name == kInstanceName) { - if (proc.proc->sessions[0].addr.ss_family == AF_UNIX) { - sockaddr_un* un = reinterpret_cast<sockaddr_un*>( - &proc.proc->sessions[0].addr); - ALOGE("inside callback: %s", un->sun_path); - } - std::memcpy(outAddr, &proc.proc->sessions[0].addr, - proc.proc->sessions[0].addrLen); - return OK; - } - return NAME_NOT_FOUND; - }); - }); + auto receipt = addAccessorProvider( + {String8(kInstanceName).c_str()}, [&](const String16& name) -> sp<IBinder> { + return createAccessor(name, + [&](const String16& name, sockaddr* outAddr, + socklen_t addrSize) -> status_t { + if (outAddr == nullptr || + addrSize < proc.proc->sessions[0].addrLen) { + return BAD_VALUE; + } + if (name == kInstanceName) { + if (proc.proc->sessions[0].addr.ss_family == + AF_UNIX) { + sockaddr_un* un = reinterpret_cast<sockaddr_un*>( + &proc.proc->sessions[0].addr); + ALOGE("inside callback: %s", un->sun_path); + } + std::memcpy(outAddr, &proc.proc->sessions[0].addr, + proc.proc->sessions[0].addrLen); + return OK; + } + return NAME_NOT_FOUND; + }); + }); EXPECT_FALSE(receipt.expired()); @@ -1251,7 +1260,8 @@ TEST_P(BinderRpcAccessor, InjectNoAccessorProvided) { bool isProviderDeleted = false; - auto receipt = addAccessorProvider([&](const String16&) -> sp<IBinder> { return nullptr; }); + auto receipt = addAccessorProvider({String8(kInstanceName).c_str()}, + [&](const String16&) -> sp<IBinder> { return nullptr; }); EXPECT_FALSE(receipt.expired()); sp<IBinder> binder = defaultServiceManager()->checkService(kInstanceName); @@ -1261,6 +1271,32 @@ TEST_P(BinderRpcAccessor, InjectNoAccessorProvided) { EXPECT_EQ(status, OK); } +TEST_P(BinderRpcAccessor, InjectDuplicateAccessorProvider) { + const String16 kInstanceName("super.cool.service/better_than_default"); + const String16 kInstanceName2("super.cool.service/better_than_default2"); + + auto receipt = + addAccessorProvider({String8(kInstanceName).c_str(), String8(kInstanceName2).c_str()}, + [&](const String16&) -> sp<IBinder> { return nullptr; }); + EXPECT_FALSE(receipt.expired()); + // reject this because it's associated with an already used instance name + auto receipt2 = addAccessorProvider({String8(kInstanceName).c_str()}, + [&](const String16&) -> sp<IBinder> { return nullptr; }); + EXPECT_TRUE(receipt2.expired()); + + // the first provider should still be usable + sp<IBinder> binder = defaultServiceManager()->checkService(kInstanceName); + EXPECT_EQ(binder, nullptr); + + status_t status = removeAccessorProvider(receipt); + EXPECT_EQ(status, OK); +} + +TEST_P(BinderRpcAccessor, InjectAccessorProviderNoInstance) { + auto receipt = addAccessorProvider({}, [&](const String16&) -> sp<IBinder> { return nullptr; }); + EXPECT_TRUE(receipt.expired()); +} + TEST_P(BinderRpcAccessor, InjectNoSockaddrProvided) { constexpr size_t kNumThreads = 10; const String16 kInstanceName("super.cool.service/better_than_default"); @@ -1271,12 +1307,15 @@ TEST_P(BinderRpcAccessor, InjectNoSockaddrProvided) { bool isProviderDeleted = false; bool isAccessorDeleted = false; - auto receipt = addAccessorProvider([&](const String16& name) -> sp<IBinder> { - return createAccessor(name, [&](const String16&, sockaddr*, socklen_t) -> status_t { - // don't fill in outAddr - return NAME_NOT_FOUND; - }); - }); + auto receipt = addAccessorProvider({String8(kInstanceName).c_str()}, + [&](const String16& name) -> sp<IBinder> { + return createAccessor(name, + [&](const String16&, sockaddr*, + socklen_t) -> status_t { + // don't fill in outAddr + return NAME_NOT_FOUND; + }); + }); EXPECT_FALSE(receipt.expired()); @@ -1287,6 +1326,260 @@ TEST_P(BinderRpcAccessor, InjectNoSockaddrProvided) { EXPECT_EQ(status, OK); } +constexpr const char* kARpcInstance = "some.instance.name.IFoo/default"; +const char* kARpcSupportedServices[] = { + kARpcInstance, +}; +const uint32_t kARpcNumSupportedServices = 1; + +struct ConnectionInfoData { + sockaddr_storage addr; + socklen_t len; + bool* isDeleted; + ~ConnectionInfoData() { + if (isDeleted) *isDeleted = true; + } +}; + +struct AccessorProviderData { + sockaddr_storage addr; + socklen_t len; + bool* isDeleted; + ~AccessorProviderData() { + if (isDeleted) *isDeleted = true; + } +}; + +void accessorProviderDataOnDelete(void* data) { + delete reinterpret_cast<AccessorProviderData*>(data); +} +void infoProviderDataOnDelete(void* data) { + delete reinterpret_cast<ConnectionInfoData*>(data); +} + +ABinderRpc_ConnectionInfo* infoProvider(const char* instance, void* cookie) { + if (instance == nullptr || cookie == nullptr) return nullptr; + ConnectionInfoData* data = reinterpret_cast<ConnectionInfoData*>(cookie); + return ABinderRpc_ConnectionInfo_new(reinterpret_cast<const sockaddr*>(&data->addr), data->len); +} + +ABinderRpc_Accessor* getAccessor(const char* instance, void* cookie) { + if (instance == nullptr || cookie == nullptr) return nullptr; + if (0 != strcmp(instance, kARpcInstance)) return nullptr; + + AccessorProviderData* data = reinterpret_cast<AccessorProviderData*>(cookie); + + ConnectionInfoData* info = new ConnectionInfoData{ + .addr = data->addr, + .len = data->len, + .isDeleted = nullptr, + }; + + return ABinderRpc_Accessor_new(instance, infoProvider, info, infoProviderDataOnDelete); +} + +class BinderARpcNdk : public ::testing::Test {}; + +TEST_F(BinderARpcNdk, ARpcProviderNewDelete) { + bool isDeleted = false; + + AccessorProviderData* data = new AccessorProviderData{{}, 0, &isDeleted}; + + ABinderRpc_AccessorProvider* provider = + ABinderRpc_registerAccessorProvider(getAccessor, kARpcSupportedServices, + kARpcNumSupportedServices, data, + accessorProviderDataOnDelete); + + ASSERT_NE(provider, nullptr); + EXPECT_FALSE(isDeleted); + + ABinderRpc_unregisterAccessorProvider(provider); + + EXPECT_TRUE(isDeleted); +} + +TEST_F(BinderARpcNdk, ARpcProviderDuplicateInstance) { + const char* instance = "some.instance.name.IFoo/default"; + const uint32_t numInstances = 2; + const char* instances[numInstances] = { + instance, + "some.other.instance/default", + }; + + bool isDeleted = false; + + AccessorProviderData* data = new AccessorProviderData{{}, 0, &isDeleted}; + + ABinderRpc_AccessorProvider* provider = + ABinderRpc_registerAccessorProvider(getAccessor, instances, numInstances, data, + accessorProviderDataOnDelete); + + ASSERT_NE(provider, nullptr); + EXPECT_FALSE(isDeleted); + + const uint32_t numInstances2 = 1; + const char* instances2[numInstances2] = { + instance, + }; + bool isDeleted2 = false; + AccessorProviderData* data2 = new AccessorProviderData{{}, 0, &isDeleted2}; + ABinderRpc_AccessorProvider* provider2 = + ABinderRpc_registerAccessorProvider(getAccessor, instances2, numInstances2, data2, + accessorProviderDataOnDelete); + + EXPECT_EQ(provider2, nullptr); + // If it fails to be registered, the data is still cleaned up with + // accessorProviderDataOnDelete + EXPECT_TRUE(isDeleted2); + + ABinderRpc_unregisterAccessorProvider(provider); + + EXPECT_TRUE(isDeleted); +} + +TEST_F(BinderARpcNdk, ARpcProviderRegisterNoInstance) { + const uint32_t numInstances = 0; + const char* instances[numInstances] = {}; + + bool isDeleted = false; + AccessorProviderData* data = new AccessorProviderData{{}, 0, &isDeleted}; + + ABinderRpc_AccessorProvider* provider = + ABinderRpc_registerAccessorProvider(getAccessor, instances, numInstances, data, + accessorProviderDataOnDelete); + ASSERT_EQ(provider, nullptr); +} + +TEST_F(BinderARpcNdk, ARpcAccessorNewDelete) { + bool isDeleted = false; + + ConnectionInfoData* data = new ConnectionInfoData{{}, 0, &isDeleted}; + + ABinderRpc_Accessor* accessor = + ABinderRpc_Accessor_new("gshoe_service", infoProvider, data, infoProviderDataOnDelete); + ASSERT_NE(accessor, nullptr); + EXPECT_FALSE(isDeleted); + + ABinderRpc_Accessor_delete(accessor); + EXPECT_TRUE(isDeleted); +} + +TEST_F(BinderARpcNdk, ARpcConnectionInfoNewDelete) { + sockaddr_vm addr{ + .svm_family = AF_VSOCK, + .svm_port = VMADDR_PORT_ANY, + .svm_cid = VMADDR_CID_ANY, + }; + + ABinderRpc_ConnectionInfo* info = + ABinderRpc_ConnectionInfo_new(reinterpret_cast<sockaddr*>(&addr), sizeof(sockaddr_vm)); + EXPECT_NE(info, nullptr); + + ABinderRpc_ConnectionInfo_delete(info); +} + +TEST_F(BinderARpcNdk, ARpcAsFromBinderAsBinder) { + bool isDeleted = false; + + ConnectionInfoData* data = new ConnectionInfoData{{}, 0, &isDeleted}; + + ABinderRpc_Accessor* accessor = + ABinderRpc_Accessor_new("gshoe_service", infoProvider, data, infoProviderDataOnDelete); + ASSERT_NE(accessor, nullptr); + EXPECT_FALSE(isDeleted); + + { + ndk::SpAIBinder binder = ndk::SpAIBinder(ABinderRpc_Accessor_asBinder(accessor)); + EXPECT_NE(binder.get(), nullptr); + + ABinderRpc_Accessor* accessor2 = + ABinderRpc_Accessor_fromBinder("wrong_service_name", binder.get()); + // The API checks for the expected service name that is associated with + // the accessor! + EXPECT_EQ(accessor2, nullptr); + + accessor2 = ABinderRpc_Accessor_fromBinder("gshoe_service", binder.get()); + EXPECT_NE(accessor2, nullptr); + + // this is a new ABinderRpc_Accessor object that wraps the underlying + // libbinder object. + EXPECT_NE(accessor, accessor2); + + ndk::SpAIBinder binder2 = ndk::SpAIBinder(ABinderRpc_Accessor_asBinder(accessor2)); + EXPECT_EQ(binder.get(), binder2.get()); + + ABinderRpc_Accessor_delete(accessor2); + } + + EXPECT_FALSE(isDeleted); + ABinderRpc_Accessor_delete(accessor); + EXPECT_TRUE(isDeleted); +} + +TEST_F(BinderARpcNdk, ARpcRequireProviderOnDeleteCallback) { + EXPECT_EQ(nullptr, + ABinderRpc_registerAccessorProvider(getAccessor, kARpcSupportedServices, + kARpcNumSupportedServices, + reinterpret_cast<void*>(1), nullptr)); +} + +TEST_F(BinderARpcNdk, ARpcRequireInfoOnDeleteCallback) { + EXPECT_EQ(nullptr, + ABinderRpc_Accessor_new("the_best_service_name", infoProvider, + reinterpret_cast<void*>(1), nullptr)); +} + +TEST_F(BinderARpcNdk, ARpcNoDataNoProviderOnDeleteCallback) { + ABinderRpc_AccessorProvider* provider = + ABinderRpc_registerAccessorProvider(getAccessor, kARpcSupportedServices, + kARpcNumSupportedServices, nullptr, nullptr); + ASSERT_NE(nullptr, provider); + ABinderRpc_unregisterAccessorProvider(provider); +} + +TEST_F(BinderARpcNdk, ARpcNoDataNoInfoOnDeleteCallback) { + ABinderRpc_Accessor* accessor = + ABinderRpc_Accessor_new("the_best_service_name", infoProvider, nullptr, nullptr); + ASSERT_NE(nullptr, accessor); + ABinderRpc_Accessor_delete(accessor); +} + +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) { + 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); + + { + ndk::SpAIBinder binder = ndk::SpAIBinder(AServiceManager_checkService(kARpcInstance)); + ASSERT_NE(binder.get(), nullptr); + EXPECT_EQ(STATUS_OK, AIBinder_ping(binder.get())); + } + + ABinderRpc_unregisterAccessorProvider(provider); + EXPECT_TRUE(isDeleted); + + waitForExtraSessionCleanup(proc); +} + #endif // BINDER_WITH_KERNEL_IPC #ifdef BINDER_RPC_TO_TRUSTY_TEST diff --git a/libs/binder/tests/parcel_fuzzer/binder_ndk.cpp b/libs/binder/tests/parcel_fuzzer/binder_ndk.cpp index 3a1471eabe..e3a337171f 100644 --- a/libs/binder/tests/parcel_fuzzer/binder_ndk.cpp +++ b/libs/binder/tests/parcel_fuzzer/binder_ndk.cpp @@ -49,7 +49,8 @@ static binder_status_t onTransact(AIBinder*, transaction_code_t, const AParcel*, 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 157ab3c85d..ba9e457a75 100644 --- a/libs/binder/trusty/OS.cpp +++ b/libs/binder/trusty/OS.cpp @@ -42,6 +42,10 @@ void trace_end(uint64_t) {} 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/ndk/include/android/llndk-versioning.h b/libs/binder/trusty/ndk/include/android/llndk-versioning.h index 3ae3d8f577..e955a34bdf 100644 --- a/libs/binder/trusty/ndk/include/android/llndk-versioning.h +++ b/libs/binder/trusty/ndk/include/android/llndk-versioning.h @@ -15,4 +15,5 @@ */ #pragma once -#define __INTRODUCED_IN_LLNDK(x) /* nothing on Trusty */ +// TODO(b/349936395): set to true for Trusty +#define API_LEVEL_AT_LEAST(sdk_api_level, vendor_api_level) (false) diff --git a/libs/debugstore/OWNERS b/libs/debugstore/OWNERS index 428a1a2215..c8e22b70ff 100644 --- a/libs/debugstore/OWNERS +++ b/libs/debugstore/OWNERS @@ -1,3 +1,2 @@ benmiles@google.com -gaillard@google.com mohamadmahmoud@google.com diff --git a/libs/debugstore/rust/Android.bp b/libs/debugstore/rust/Android.bp index 55ba3c32d1..9475333a72 100644 --- a/libs/debugstore/rust/Android.bp +++ b/libs/debugstore/rust/Android.bp @@ -23,7 +23,6 @@ rust_defaults { rustlibs: [ "libcrossbeam_queue", "libparking_lot", - "libonce_cell", "libcxx", ], shared_libs: ["libutils"], diff --git a/libs/debugstore/rust/src/core.rs b/libs/debugstore/rust/src/core.rs index 1dfa512151..6bf79d4e57 100644 --- a/libs/debugstore/rust/src/core.rs +++ b/libs/debugstore/rust/src/core.rs @@ -17,12 +17,14 @@ use super::event::Event; use super::event_type::EventType; use super::storage::Storage; use crate::cxxffi::uptimeMillis; -use once_cell::sync::Lazy; use std::fmt; -use std::sync::atomic::{AtomicU64, Ordering}; +use std::sync::{ + atomic::{AtomicU64, Ordering}, + LazyLock, +}; // Lazily initialized static instance of DebugStore. -static INSTANCE: Lazy<DebugStore> = Lazy::new(DebugStore::new); +static INSTANCE: LazyLock<DebugStore> = LazyLock::new(DebugStore::new); /// The `DebugStore` struct is responsible for managing debug events and data. pub struct DebugStore { diff --git a/libs/gui/BLASTBufferQueue.cpp b/libs/gui/BLASTBufferQueue.cpp index c65eafa541..25e6a52ed1 100644 --- a/libs/gui/BLASTBufferQueue.cpp +++ b/libs/gui/BLASTBufferQueue.cpp @@ -31,7 +31,6 @@ #include <sys/epoll.h> #include <sys/eventfd.h> -#include <gui/FenceMonitor.h> #include <gui/FrameRateUtils.h> #include <gui/GLConsumer.h> #include <gui/IProducerListener.h> @@ -476,16 +475,6 @@ void BLASTBufferQueue::releaseBufferCallbackLocked( ATRACE_CALL(); BQA_LOGV("releaseBufferCallback %s", id.to_string().c_str()); - if (CC_UNLIKELY(atrace_is_tag_enabled(ATRACE_TAG_GRAPHICS))) { - if (!mFenceMonitor) { - std::string monitorName = "release :"; - monitorName.append(mName.c_str()); - mFenceMonitor.emplace(monitorName.c_str()); - } - - mFenceMonitor->queueFence(releaseFence); - } - // Calculate how many buffers we need to hold before we release them back // to the buffer queue. This will prevent higher latency when we are running // on a lower refresh rate than the max supported. We only do that for EGL @@ -1135,6 +1124,17 @@ public: AsyncWorker::getInstance().post( [listener = mListener, slots = slots]() { listener->onBuffersDiscarded(slots); }); } + + void onBufferDetached(int slot) override { + AsyncWorker::getInstance().post( + [listener = mListener, slot = slot]() { listener->onBufferDetached(slot); }); + } + +#if COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(BQ_CONSUMER_ATTACH_CALLBACK) + void onBufferAttached() override { + AsyncWorker::getInstance().post([listener = mListener]() { listener->onBufferAttached(); }); + } +#endif }; // Extends the BufferQueueProducer to create a wrapper around the listener so the listener calls diff --git a/libs/gui/FenceMonitor.cpp b/libs/gui/FenceMonitor.cpp index e38f1a8ce6..230c81a0b3 100644 --- a/libs/gui/FenceMonitor.cpp +++ b/libs/gui/FenceMonitor.cpp @@ -25,18 +25,9 @@ namespace android::gui { FenceMonitor::FenceMonitor(const char* name) : mName(name), mFencesQueued(0), mFencesSignaled(0) { - mThread = std::thread(&FenceMonitor::loop, this); -} - -FenceMonitor::~FenceMonitor() { - { - std::lock_guard<std::mutex> lock(mMutex); - mStopped = true; - mCondition.notify_one(); - } - if (mThread.joinable()) { - mThread.join(); - } + std::thread thread(&FenceMonitor::loop, this); + pthread_setname_np(thread.native_handle(), mName); + thread.detach(); } void FenceMonitor::queueFence(const sp<Fence>& fence) { @@ -44,26 +35,24 @@ void FenceMonitor::queueFence(const sp<Fence>& fence) { std::lock_guard<std::mutex> lock(mMutex); if (fence->getSignalTime() != Fence::SIGNAL_TIME_PENDING) { - snprintf(message, sizeof(message), "%s fence %u has signaled", mName.c_str(), - mFencesQueued); + snprintf(message, sizeof(message), "%s fence %u has signaled", mName, mFencesQueued); ATRACE_NAME(message); // Need an increment on both to make the trace number correct. mFencesQueued++; mFencesSignaled++; return; } - snprintf(message, sizeof(message), "Trace %s fence %u", mName.c_str(), mFencesQueued); + snprintf(message, sizeof(message), "Trace %s fence %u", mName, mFencesQueued); ATRACE_NAME(message); mQueue.push_back(fence); mCondition.notify_one(); mFencesQueued++; - ATRACE_INT(mName.c_str(), int32_t(mQueue.size())); + ATRACE_INT(mName, int32_t(mQueue.size())); } void FenceMonitor::loop() { - pthread_setname_np(pthread_self(), mName.c_str()); - while (!mStopped) { + while (true) { threadLoop(); } } @@ -73,18 +62,15 @@ void FenceMonitor::threadLoop() { uint32_t fenceNum; { std::unique_lock<std::mutex> lock(mMutex); - while (mQueue.empty() && !mStopped) { + while (mQueue.empty()) { mCondition.wait(lock); } - if (mStopped) { - return; - } fence = mQueue[0]; fenceNum = mFencesSignaled; } { char message[64]; - snprintf(message, sizeof(message), "waiting for %s %u", mName.c_str(), fenceNum); + snprintf(message, sizeof(message), "waiting for %s %u", mName, fenceNum); ATRACE_NAME(message); status_t result = fence->waitForever(message); @@ -96,8 +82,8 @@ void FenceMonitor::threadLoop() { std::lock_guard<std::mutex> lock(mMutex); mQueue.pop_front(); mFencesSignaled++; - ATRACE_INT(mName.c_str(), int32_t(mQueue.size())); + ATRACE_INT(mName, int32_t(mQueue.size())); } } -} // namespace android::gui +} // namespace android::gui
\ No newline at end of file diff --git a/libs/gui/IProducerListener.cpp b/libs/gui/IProducerListener.cpp index 7700795e1d..8b9b090496 100644 --- a/libs/gui/IProducerListener.cpp +++ b/libs/gui/IProducerListener.cpp @@ -184,4 +184,10 @@ bool BnProducerListener::needsReleaseNotify() { void BnProducerListener::onBuffersDiscarded(const std::vector<int32_t>& /*discardedSlots*/) { } +#if COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(BQ_CONSUMER_ATTACH_CALLBACK) +bool BnProducerListener::needsAttachNotify() { + return true; +} +#endif + } // namespace android diff --git a/libs/gui/SurfaceControl.cpp b/libs/gui/SurfaceControl.cpp index c5f9c38ca3..f126c0be2f 100644 --- a/libs/gui/SurfaceControl.cpp +++ b/libs/gui/SurfaceControl.cpp @@ -139,9 +139,9 @@ sp<Surface> SurfaceControl::generateSurfaceLocked() uint32_t ignore; auto flags = mCreateFlags & (ISurfaceComposerClient::eCursorWindow | ISurfaceComposerClient::eOpaque); - mBbqChild = mClient->createSurface(String8("bbq-wrapper"), 0, 0, mFormat, + mBbqChild = mClient->createSurface(String8::format("[BBQ] %s", mName.c_str()), 0, 0, mFormat, flags, mHandle, {}, &ignore); - mBbq = sp<BLASTBufferQueue>::make("bbq-adapter", mBbqChild, mWidth, mHeight, mFormat); + mBbq = sp<BLASTBufferQueue>::make("[BBQ]" + mName, mBbqChild, mWidth, mHeight, mFormat); // This surface is always consumed by SurfaceFlinger, so the // producerControlledByApp value doesn't matter; using false. diff --git a/libs/gui/include/gui/BLASTBufferQueue.h b/libs/gui/include/gui/BLASTBufferQueue.h index ba58a15e17..8592cffd15 100644 --- a/libs/gui/include/gui/BLASTBufferQueue.h +++ b/libs/gui/include/gui/BLASTBufferQueue.h @@ -20,7 +20,6 @@ #include <com_android_graphics_libgui_flags.h> #include <gui/BufferItem.h> #include <gui/BufferItemConsumer.h> -#include <gui/FenceMonitor.h> #include <gui/IGraphicBufferConsumer.h> #include <gui/IGraphicBufferProducer.h> #include <gui/SurfaceComposerClient.h> @@ -317,8 +316,6 @@ private: std::unordered_set<uint64_t> mSyncedFrameNumbers GUARDED_BY(mMutex); - std::optional<gui::FenceMonitor> mFenceMonitor GUARDED_BY(mMutex); - #if COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(BUFFER_RELEASE_CHANNEL) class BufferReleaseReader { public: diff --git a/libs/gui/include/gui/FenceMonitor.h b/libs/gui/include/gui/FenceMonitor.h index ac5cc0a574..62ceddee5f 100644 --- a/libs/gui/include/gui/FenceMonitor.h +++ b/libs/gui/include/gui/FenceMonitor.h @@ -19,7 +19,6 @@ #include <cstdint> #include <deque> #include <mutex> -#include <thread> #include <ui/Fence.h> @@ -29,20 +28,17 @@ class FenceMonitor { public: explicit FenceMonitor(const char* name); void queueFence(const sp<Fence>& fence); - ~FenceMonitor(); private: void loop(); void threadLoop(); - std::string mName; + const char* mName; uint32_t mFencesQueued; uint32_t mFencesSignaled; std::deque<sp<Fence>> mQueue; std::condition_variable mCondition; std::mutex mMutex; - std::thread mThread; - std::atomic_bool mStopped = false; }; -} // namespace android::gui +} // namespace android::gui
\ No newline at end of file diff --git a/include/input/LooperInterface.h b/libs/gui/include/gui/Flags.h index 2d6719c965..735375a1e7 100644 --- a/include/input/LooperInterface.h +++ b/libs/gui/include/gui/Flags.h @@ -1,4 +1,4 @@ -/** +/* * Copyright 2024 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); @@ -16,24 +16,9 @@ #pragma once -#include <utils/Looper.h> -#include <utils/StrongPointer.h> +#include <com_android_graphics_libgui_flags.h> -namespace android { - -/** - * LooperInterface allows the use of TestLooper in InputConsumerNoResampling without reassigning to - * Looper. LooperInterface is needed to control how InputConsumerNoResampling consumes and batches - * InputMessages. - */ -class LooperInterface { -public: - virtual ~LooperInterface() = default; - - virtual int addFd(int fd, int ident, int events, const sp<LooperCallback>& callback, - void* data) = 0; - virtual int removeFd(int fd) = 0; - - virtual sp<Looper> getLooper() const = 0; -}; -} // namespace android +#define WB_CAMERA3_AND_PROCESSORS_WITH_DEPENDENCIES \ + (COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(WB_CAMERA3_AND_PROCESSORS) && \ + COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(WB_CONSUMER_BASE_OWNS_BQ) && \ + COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(WB_PLATFORM_API_IMPROVEMENTS))
\ No newline at end of file diff --git a/libs/gui/include/gui/IProducerListener.h b/libs/gui/include/gui/IProducerListener.h index 3dcc6b6670..43bf6a7d4b 100644 --- a/libs/gui/include/gui/IProducerListener.h +++ b/libs/gui/include/gui/IProducerListener.h @@ -90,6 +90,9 @@ public: Parcel* reply, uint32_t flags = 0); virtual bool needsReleaseNotify(); virtual void onBuffersDiscarded(const std::vector<int32_t>& slots); +#if COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(BQ_CONSUMER_ATTACH_CALLBACK) + virtual bool needsAttachNotify(); +#endif }; #else @@ -103,6 +106,9 @@ public: virtual ~StubProducerListener(); virtual void onBufferReleased() {} virtual bool needsReleaseNotify() { return false; } +#if COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(BQ_CONSUMER_ATTACH_CALLBACK) + virtual bool needsAttachNotify() { return false; } +#endif }; } // namespace android diff --git a/libs/gui/include/gui/Surface.h b/libs/gui/include/gui/Surface.h index e74f9ad1dc..14a351316d 100644 --- a/libs/gui/include/gui/Surface.h +++ b/libs/gui/include/gui/Surface.h @@ -34,8 +34,6 @@ #include <shared_mutex> #include <unordered_set> -#include <com_android_graphics_libgui_flags.h> - namespace android { class GraphicBuffer; diff --git a/libs/input/InputConsumerNoResampling.cpp b/libs/input/InputConsumerNoResampling.cpp index de55828a39..ce8bb43a76 100644 --- a/libs/input/InputConsumerNoResampling.cpp +++ b/libs/input/InputConsumerNoResampling.cpp @@ -17,8 +17,6 @@ #define LOG_TAG "InputConsumerNoResampling" #define ATRACE_TAG ATRACE_TAG_INPUT -#include <chrono> - #include <inttypes.h> #include <android-base/logging.h> @@ -39,6 +37,8 @@ namespace { using std::chrono::nanoseconds; +using android::base::Result; + /** * Log debug messages relating to the consumer end of the transport channel. * Enable this via "adb shell setprop log.tag.InputTransportConsumer DEBUG" (requires restart) @@ -46,27 +46,6 @@ using std::chrono::nanoseconds; const bool DEBUG_TRANSPORT_CONSUMER = __android_log_is_loggable(ANDROID_LOG_DEBUG, LOG_TAG "Consumer", ANDROID_LOG_INFO); -/** - * RealLooper is a wrapper of Looper. All the member functions exclusively call the internal looper. - * This class' behavior is the same as Looper. - */ -class RealLooper final : public LooperInterface { -public: - RealLooper(sp<Looper> looper) : mLooper{looper} {} - - int addFd(int fd, int ident, int events, const sp<LooperCallback>& callback, - void* data) override { - return mLooper->addFd(fd, ident, events, callback, data); - } - - int removeFd(int fd) override { return mLooper->removeFd(fd); } - - sp<Looper> getLooper() const override { return mLooper; } - -private: - sp<Looper> mLooper; -}; - std::unique_ptr<KeyEvent> createKeyEvent(const InputMessage& msg) { std::unique_ptr<KeyEvent> event = std::make_unique<KeyEvent>(); event->initialize(msg.body.key.eventId, msg.body.key.deviceId, msg.body.key.source, @@ -190,24 +169,18 @@ InputMessage createTimelineMessage(int32_t inputEventId, nsecs_t gpuCompletedTim msg.body.timeline.graphicsTimeline[GraphicsTimeline::PRESENT_TIME] = presentTime; return msg; } - -bool isPointerEvent(const MotionEvent& motionEvent) { - return (motionEvent.getSource() & AINPUT_SOURCE_CLASS_POINTER) == AINPUT_SOURCE_CLASS_POINTER; -} } // namespace -using android::base::Result; - // --- InputConsumerNoResampling --- -InputConsumerNoResampling::InputConsumerNoResampling(const std::shared_ptr<InputChannel>& channel, - std::shared_ptr<LooperInterface> looper, - InputConsumerCallbacks& callbacks, - std::unique_ptr<Resampler> resampler) +InputConsumerNoResampling::InputConsumerNoResampling( + const std::shared_ptr<InputChannel>& channel, sp<Looper> looper, + InputConsumerCallbacks& callbacks, + std::function<std::unique_ptr<Resampler>()> resamplerCreator) : mChannel{channel}, mLooper{looper}, - mCallbacks(callbacks), - mResampler{std::move(resampler)}, + mCallbacks{callbacks}, + mResamplerCreator{std::move(resamplerCreator)}, mFdEvents(0) { LOG_ALWAYS_FATAL_IF(mLooper == nullptr); mCallback = sp<LooperEventCallback>::make( @@ -218,16 +191,9 @@ InputConsumerNoResampling::InputConsumerNoResampling(const std::shared_ptr<Input setFdEvents(ALOOPER_EVENT_INPUT); } -InputConsumerNoResampling::InputConsumerNoResampling(const std::shared_ptr<InputChannel>& channel, - sp<Looper> looper, - InputConsumerCallbacks& callbacks, - std::unique_ptr<Resampler> resampler) - : InputConsumerNoResampling(channel, std::make_shared<RealLooper>(looper), callbacks, - std::move(resampler)) {} - InputConsumerNoResampling::~InputConsumerNoResampling() { ensureCalledOnLooperThread(__func__); - consumeBatchedInputEvents(std::nullopt); + consumeBatchedInputEvents(/*requestedFrameTime=*/std::nullopt); while (!mOutboundQueue.empty()) { processOutboundEvents(); // This is our last chance to ack the events. If we don't ack them here, we will get an ANR, @@ -253,8 +219,7 @@ int InputConsumerNoResampling::handleReceiveCallback(int events) { int handledEvents = 0; if (events & ALOOPER_EVENT_INPUT) { - std::vector<InputMessage> messages = readAllMessages(); - handleMessages(std::move(messages)); + handleMessages(readAllMessages()); handledEvents |= ALOOPER_EVENT_INPUT; } @@ -348,7 +313,6 @@ void InputConsumerNoResampling::setFdEvents(int events) { } void InputConsumerNoResampling::handleMessages(std::vector<InputMessage>&& messages) { - // TODO(b/297226446) : add resampling for (const InputMessage& msg : messages) { if (msg.header.type == InputMessage::Type::MOTION) { const int32_t action = msg.body.motion.action; @@ -358,14 +322,31 @@ void InputConsumerNoResampling::handleMessages(std::vector<InputMessage>&& messa action == AMOTION_EVENT_ACTION_HOVER_MOVE) && (isFromSource(source, AINPUT_SOURCE_CLASS_POINTER) || isFromSource(source, AINPUT_SOURCE_CLASS_JOYSTICK)); + + const bool canResample = (mResamplerCreator != nullptr) && + (isFromSource(source, AINPUT_SOURCE_CLASS_POINTER)); + if (canResample) { + if (action == AMOTION_EVENT_ACTION_DOWN) { + if (std::unique_ptr<Resampler> resampler = mResamplerCreator(); + resampler != nullptr) { + const auto [_, inserted] = + mResamplers.insert(std::pair(deviceId, std::move(resampler))); + LOG_IF(WARNING, !inserted) << deviceId << "already exists in mResamplers"; + } + } + } + if (batchableEvent) { // add it to batch mBatches[deviceId].emplace(msg); } else { - // consume all pending batches for this event immediately - // TODO(b/329776327): figure out if this could be smarter by limiting the - // consumption only to the current device. - consumeBatchedInputEvents(std::nullopt); + // consume all pending batches for this device immediately + consumeBatchedInputEvents(deviceId, /*requestedFrameTime=*/std::nullopt); + if (canResample && + (action == AMOTION_EVENT_ACTION_UP || action == AMOTION_EVENT_ACTION_CANCEL)) { + LOG_IF(INFO, mResamplers.erase(deviceId) == 0) + << deviceId << "does not exist in mResamplers"; + } handleMessage(msg); } } else { @@ -483,13 +464,18 @@ void InputConsumerNoResampling::handleMessage(const InputMessage& msg) const { } std::pair<std::unique_ptr<MotionEvent>, std::optional<uint32_t>> -InputConsumerNoResampling::createBatchedMotionEvent(const nsecs_t frameTime, +InputConsumerNoResampling::createBatchedMotionEvent(const nsecs_t requestedFrameTime, std::queue<InputMessage>& messages) { std::unique_ptr<MotionEvent> motionEvent; std::optional<uint32_t> firstSeqForBatch; - const nanoseconds resampleLatency = - (mResampler != nullptr) ? mResampler->getResampleLatency() : nanoseconds{0}; - const nanoseconds adjustedFrameTime = nanoseconds{frameTime} - resampleLatency; + + LOG_IF(FATAL, messages.empty()) << "messages queue is empty!"; + const DeviceId deviceId = messages.front().body.motion.deviceId; + const auto resampler = mResamplers.find(deviceId); + const nanoseconds resampleLatency = (resampler != mResamplers.cend()) + ? resampler->second->getResampleLatency() + : nanoseconds{0}; + const nanoseconds adjustedFrameTime = nanoseconds{requestedFrameTime} - resampleLatency; while (!messages.empty() && (messages.front().body.motion.eventTime <= adjustedFrameTime.count())) { @@ -505,45 +491,63 @@ InputConsumerNoResampling::createBatchedMotionEvent(const nsecs_t frameTime, } messages.pop(); } + // Check if resampling should be performed. - if (motionEvent != nullptr && isPointerEvent(*motionEvent) && mResampler != nullptr) { - InputMessage* futureSample = nullptr; - if (!messages.empty()) { - futureSample = &messages.front(); - } - mResampler->resampleMotionEvent(nanoseconds{frameTime}, *motionEvent, futureSample); + InputMessage* futureSample = nullptr; + if (!messages.empty()) { + futureSample = &messages.front(); + } + if ((motionEvent != nullptr) && (resampler != mResamplers.cend())) { + resampler->second->resampleMotionEvent(nanoseconds{requestedFrameTime}, *motionEvent, + futureSample); } + return std::make_pair(std::move(motionEvent), firstSeqForBatch); } bool InputConsumerNoResampling::consumeBatchedInputEvents( - std::optional<nsecs_t> requestedFrameTime) { + std::optional<DeviceId> deviceId, std::optional<nsecs_t> requestedFrameTime) { ensureCalledOnLooperThread(__func__); // When batching is not enabled, we want to consume all events. That's equivalent to having an - // infinite frameTime. - const nsecs_t frameTime = requestedFrameTime.value_or(std::numeric_limits<nsecs_t>::max()); + // infinite requestedFrameTime. + requestedFrameTime = requestedFrameTime.value_or(std::numeric_limits<nsecs_t>::max()); bool producedEvents = false; - for (auto& [_, messages] : mBatches) { - auto [motion, firstSeqForBatch] = createBatchedMotionEvent(frameTime, messages); + + for (auto deviceIdIter = (deviceId.has_value()) ? (mBatches.find(*deviceId)) + : (mBatches.begin()); + deviceIdIter != mBatches.cend(); ++deviceIdIter) { + std::queue<InputMessage>& messages = deviceIdIter->second; + auto [motion, firstSeqForBatch] = createBatchedMotionEvent(*requestedFrameTime, messages); if (motion != nullptr) { LOG_ALWAYS_FATAL_IF(!firstSeqForBatch.has_value()); mCallbacks.onMotionEvent(std::move(motion), *firstSeqForBatch); producedEvents = true; } else { - // This is OK, it just means that the frameTime is too old (all events that we have - // pending are in the future of the frametime). Maybe print a - // warning? If there are multiple devices active though, this might be normal and can - // just be ignored, unless none of them resulted in any consumption (in that case, this - // function would already return "false" so we could just leave it up to the caller). + // This is OK, it just means that the requestedFrameTime is too old (all events that we + // have pending are in the future of the requestedFrameTime). Maybe print a warning? If + // there are multiple devices active though, this might be normal and can just be + // ignored, unless none of them resulted in any consumption (in that case, this function + // would already return "false" so we could just leave it up to the caller). + } + + if (deviceId.has_value()) { + // We already consumed events for this device. Break here to prevent iterating over the + // other devices. + break; } } std::erase_if(mBatches, [](const auto& pair) { return pair.second.empty(); }); return producedEvents; } +bool InputConsumerNoResampling::consumeBatchedInputEvents( + std::optional<nsecs_t> requestedFrameTime) { + return consumeBatchedInputEvents(/*deviceId=*/std::nullopt, requestedFrameTime); +} + void InputConsumerNoResampling::ensureCalledOnLooperThread(const char* func) const { sp<Looper> callingThreadLooper = Looper::getForThread(); - if (callingThreadLooper != mLooper->getLooper()) { + if (callingThreadLooper != mLooper) { LOG(FATAL) << "The function " << func << " can only be called on the looper thread"; } } diff --git a/libs/input/KeyCharacterMap.cpp b/libs/input/KeyCharacterMap.cpp index 1cf5612d45..b0563abaf7 100644 --- a/libs/input/KeyCharacterMap.cpp +++ b/libs/input/KeyCharacterMap.cpp @@ -317,19 +317,8 @@ bool KeyCharacterMap::getEvents(int32_t deviceId, const char16_t* chars, size_t return true; } -void KeyCharacterMap::addKeyRemapping(int32_t fromKeyCode, int32_t toKeyCode) { - if (fromKeyCode == toKeyCode) { - mKeyRemapping.erase(fromKeyCode); -#if DEBUG_MAPPING - ALOGD("addKeyRemapping: Cleared remapping forKeyCode=%d ~ Result Successful.", fromKeyCode); -#endif - return; - } - mKeyRemapping.insert_or_assign(fromKeyCode, toKeyCode); -#if DEBUG_MAPPING - ALOGD("addKeyRemapping: fromKeyCode=%d, toKeyCode=%d ~ Result Successful.", fromKeyCode, - toKeyCode); -#endif +void KeyCharacterMap::setKeyRemapping(const std::map<int32_t, int32_t>& keyRemapping) { + mKeyRemapping = keyRemapping; } status_t KeyCharacterMap::mapKey(int32_t scanCode, int32_t usageCode, int32_t* outKeyCode) const { diff --git a/libs/input/Resampler.cpp b/libs/input/Resampler.cpp index 51fadf8ec1..328fa684f6 100644 --- a/libs/input/Resampler.cpp +++ b/libs/input/Resampler.cpp @@ -247,11 +247,6 @@ nanoseconds LegacyResampler::getResampleLatency() const { void LegacyResampler::resampleMotionEvent(nanoseconds frameTime, MotionEvent& motionEvent, const InputMessage* futureSample) { - if (mPreviousDeviceId && *mPreviousDeviceId != motionEvent.getDeviceId()) { - mLatestSamples.clear(); - } - mPreviousDeviceId = motionEvent.getDeviceId(); - const nanoseconds resampleTime = frameTime - RESAMPLE_LATENCY; updateLatestSamples(motionEvent); diff --git a/libs/input/android/os/IInputConstants.aidl b/libs/input/android/os/IInputConstants.aidl index e23fc94c5e..31592cd6e3 100644 --- a/libs/input/android/os/IInputConstants.aidl +++ b/libs/input/android/os/IInputConstants.aidl @@ -49,6 +49,12 @@ interface IInputConstants const int POLICY_FLAG_INJECTED_FROM_ACCESSIBILITY = 0x20000; /** + * The key event triggered a key gesture. Used in policy flag to notify that a key gesture was + * triggered using the event. + */ + const int POLICY_FLAG_KEY_GESTURE_TRIGGERED = 0x40000; + + /** * Common input event flag used for both motion and key events for a gesture or pointer being * canceled. */ diff --git a/libs/input/tests/Android.bp b/libs/input/tests/Android.bp index 3ec167a288..81c6175805 100644 --- a/libs/input/tests/Android.bp +++ b/libs/input/tests/Android.bp @@ -27,7 +27,6 @@ cc_test { "Resampler_test.cpp", "RingBuffer_test.cpp", "TestInputChannel.cpp", - "TestLooper.cpp", "TfLiteMotionPredictor_test.cpp", "TouchResampling_test.cpp", "TouchVideoFrame_test.cpp", diff --git a/libs/input/tests/InputConsumer_test.cpp b/libs/input/tests/InputConsumer_test.cpp index 55be453287..cbb332ed7f 100644 --- a/libs/input/tests/InputConsumer_test.cpp +++ b/libs/input/tests/InputConsumer_test.cpp @@ -16,16 +16,22 @@ #include <input/InputConsumerNoResampling.h> +#include <gtest/gtest.h> + +#include <chrono> #include <memory> #include <optional> -#include <utility> +#include <TestEventMatchers.h> #include <TestInputChannel.h> -#include <TestLooper.h> #include <android-base/logging.h> +#include <gmock/gmock.h> #include <gtest/gtest.h> #include <input/BlockingQueue.h> +#include <input/Input.h> #include <input/InputEventBuilders.h> +#include <input/Resampler.h> +#include <utils/Looper.h> #include <utils/StrongPointer.h> namespace android { @@ -34,23 +40,57 @@ namespace { using std::chrono::nanoseconds; +using ::testing::AllOf; +using ::testing::Matcher; + +struct Pointer { + int32_t id{0}; + ToolType toolType{ToolType::FINGER}; + float x{0.0f}; + float y{0.0f}; + bool isResampled{false}; + + PointerBuilder asPointerBuilder() const { + return PointerBuilder{id, toolType}.x(x).y(y).isResampled(isResampled); + } +}; } // namespace class InputConsumerTest : public testing::Test, public InputConsumerCallbacks { protected: InputConsumerTest() : mClientTestChannel{std::make_shared<TestInputChannel>("TestChannel")}, - mTestLooper{std::make_shared<TestLooper>()} { - Looper::setForThread(mTestLooper->getLooper()); - mConsumer = - std::make_unique<InputConsumerNoResampling>(mClientTestChannel, mTestLooper, *this, - std::make_unique<LegacyResampler>()); + mLooper{sp<Looper>::make(/*allowNonCallbacks=*/false)} { + Looper::setForThread(mLooper); + mConsumer = std::make_unique< + InputConsumerNoResampling>(mClientTestChannel, mLooper, *this, + []() { return std::make_unique<LegacyResampler>(); }); + } + + void invokeLooperCallback() const { + sp<LooperCallback> callback; + ASSERT_TRUE(mLooper->getFdStateDebug(mClientTestChannel->getFd(), /*ident=*/nullptr, + /*events=*/nullptr, &callback, /*data=*/nullptr)); + callback->handleEvent(mClientTestChannel->getFd(), ALOOPER_EVENT_INPUT, /*data=*/nullptr); + } + + void assertOnBatchedInputEventPendingWasCalled() { + ASSERT_GT(mOnBatchedInputEventPendingInvocationCount, 0UL) + << "onBatchedInputEventPending has not been called."; + --mOnBatchedInputEventPendingInvocationCount; + } + + void assertReceivedMotionEvent(const Matcher<MotionEvent>& matcher) { + std::unique_ptr<MotionEvent> motionEvent = mMotionEvents.pop(); + ASSERT_NE(motionEvent, nullptr); + EXPECT_THAT(*motionEvent, matcher); } - void assertOnBatchedInputEventPendingWasCalled(); + InputMessage nextPointerMessage(std::chrono::nanoseconds eventTime, DeviceId deviceId, + int32_t action, const Pointer& pointer); std::shared_ptr<TestInputChannel> mClientTestChannel; - std::shared_ptr<TestLooper> mTestLooper; + sp<Looper> mLooper; std::unique_ptr<InputConsumerNoResampling> mConsumer; BlockingQueue<std::unique_ptr<KeyEvent>> mKeyEvents; @@ -61,6 +101,7 @@ protected: BlockingQueue<std::unique_ptr<TouchModeEvent>> mTouchModeEvents; private: + uint32_t mLastSeq{0}; size_t mOnBatchedInputEventPendingInvocationCount{0}; // InputConsumerCallbacks interface @@ -96,10 +137,17 @@ private: }; }; -void InputConsumerTest::assertOnBatchedInputEventPendingWasCalled() { - ASSERT_GT(mOnBatchedInputEventPendingInvocationCount, 0UL) - << "onBatchedInputEventPending has not been called."; - --mOnBatchedInputEventPendingInvocationCount; +InputMessage InputConsumerTest::nextPointerMessage(std::chrono::nanoseconds eventTime, + DeviceId deviceId, int32_t action, + const Pointer& pointer) { + ++mLastSeq; + return InputMessageBuilder{InputMessage::Type::MOTION, mLastSeq} + .eventTime(eventTime.count()) + .deviceId(deviceId) + .source(AINPUT_SOURCE_TOUCHSCREEN) + .action(action) + .pointer(pointer.asPointerBuilder()) + .build(); } TEST_F(InputConsumerTest, MessageStreamBatchedInMotionEvent) { @@ -118,11 +166,11 @@ TEST_F(InputConsumerTest, MessageStreamBatchedInMotionEvent) { mClientTestChannel->assertNoSentMessages(); - mTestLooper->invokeCallback(mClientTestChannel->getFd(), ALOOPER_EVENT_INPUT); + invokeLooperCallback(); assertOnBatchedInputEventPendingWasCalled(); - mConsumer->consumeBatchedInputEvents(std::nullopt); + mConsumer->consumeBatchedInputEvents(/*frameTime=*/std::nullopt); std::unique_ptr<MotionEvent> downMotionEvent = mMotionEvents.pop(); ASSERT_NE(downMotionEvent, nullptr); @@ -156,7 +204,7 @@ TEST_F(InputConsumerTest, LastBatchedSampleIsLessThanResampleTime) { mClientTestChannel->assertNoSentMessages(); - mTestLooper->invokeCallback(mClientTestChannel->getFd(), ALOOPER_EVENT_INPUT); + invokeLooperCallback(); assertOnBatchedInputEventPendingWasCalled(); @@ -182,4 +230,166 @@ TEST_F(InputConsumerTest, LastBatchedSampleIsLessThanResampleTime) { mClientTestChannel->assertFinishMessage(/*seq=*/2, true); mClientTestChannel->assertFinishMessage(/*seq=*/3, true); } + +TEST_F(InputConsumerTest, BatchedEventsMultiDeviceConsumption) { + mClientTestChannel->enqueueMessage(InputMessageBuilder{InputMessage::Type::MOTION, /*seq=*/0} + .deviceId(0) + .action(AMOTION_EVENT_ACTION_DOWN) + .build()); + + invokeLooperCallback(); + assertReceivedMotionEvent(AllOf(WithDeviceId(0), WithMotionAction(AMOTION_EVENT_ACTION_DOWN))); + + mClientTestChannel->enqueueMessage(InputMessageBuilder{InputMessage::Type::MOTION, /*seq=*/1} + .deviceId(0) + .action(AMOTION_EVENT_ACTION_MOVE) + .build()); + mClientTestChannel->enqueueMessage(InputMessageBuilder{InputMessage::Type::MOTION, /*seq=*/2} + .deviceId(0) + .action(AMOTION_EVENT_ACTION_MOVE) + .build()); + mClientTestChannel->enqueueMessage(InputMessageBuilder{InputMessage::Type::MOTION, /*seq=*/3} + .deviceId(0) + .action(AMOTION_EVENT_ACTION_MOVE) + .build()); + + mClientTestChannel->enqueueMessage(InputMessageBuilder{InputMessage::Type::MOTION, /*seq=*/4} + .deviceId(1) + .action(AMOTION_EVENT_ACTION_DOWN) + .build()); + + invokeLooperCallback(); + assertReceivedMotionEvent(AllOf(WithDeviceId(1), WithMotionAction(AMOTION_EVENT_ACTION_DOWN))); + + mClientTestChannel->enqueueMessage(InputMessageBuilder{InputMessage::Type::MOTION, /*seq=*/5} + .deviceId(0) + .action(AMOTION_EVENT_ACTION_UP) + .build()); + + invokeLooperCallback(); + assertReceivedMotionEvent(AllOf(WithDeviceId(0), WithMotionAction(AMOTION_EVENT_ACTION_MOVE))); + + mClientTestChannel->assertFinishMessage(/*seq=*/0, /*handled=*/true); + mClientTestChannel->assertFinishMessage(/*seq=*/4, /*handled=*/true); + mClientTestChannel->assertFinishMessage(/*seq=*/1, /*handled=*/true); + mClientTestChannel->assertFinishMessage(/*seq=*/2, /*handled=*/true); + mClientTestChannel->assertFinishMessage(/*seq=*/3, /*handled=*/true); +} + +/** + * The test supposes a 60Hz Vsync rate and a 200Hz input rate. The InputMessages are intertwined as + * in a real use cases. The test's two devices should be resampled independently. Moreover, the + * InputMessage stream layout for the test is: + * + * DOWN(0, 0ms) + * MOVE(0, 5ms) + * MOVE(0, 10ms) + * DOWN(1, 15ms) + * + * CONSUME(16ms) + * + * MOVE(1, 20ms) + * MOVE(1, 25ms) + * MOVE(0, 30ms) + * + * CONSUME(32ms) + * + * MOVE(0, 35ms) + * UP(1, 40ms) + * UP(0, 45ms) + * + * CONSUME(48ms) + * + * The first field is device ID, and the second field is event time. + */ +TEST_F(InputConsumerTest, MultiDeviceResampling) { + mClientTestChannel->enqueueMessage(nextPointerMessage(0ms, /*deviceId=*/0, + AMOTION_EVENT_ACTION_DOWN, + Pointer{.x = 0, .y = 0})); + + mClientTestChannel->assertNoSentMessages(); + + invokeLooperCallback(); + assertReceivedMotionEvent(AllOf(WithDeviceId(0), WithMotionAction(AMOTION_EVENT_ACTION_DOWN), + WithSampleCount(1))); + + mClientTestChannel->enqueueMessage(nextPointerMessage(5ms, /*deviceId=*/0, + AMOTION_EVENT_ACTION_MOVE, + Pointer{.x = 1.0f, .y = 2.0f})); + mClientTestChannel->enqueueMessage(nextPointerMessage(10ms, /*deviceId=*/0, + AMOTION_EVENT_ACTION_MOVE, + Pointer{.x = 2.0f, .y = 4.0f})); + mClientTestChannel->enqueueMessage(nextPointerMessage(15ms, /*deviceId=*/1, + AMOTION_EVENT_ACTION_DOWN, + Pointer{.x = 10.0f, .y = 10.0f})); + + invokeLooperCallback(); + mConsumer->consumeBatchedInputEvents(16'000'000 /*ns*/); + + assertReceivedMotionEvent(AllOf(WithDeviceId(1), WithMotionAction(AMOTION_EVENT_ACTION_DOWN), + WithSampleCount(1))); + assertReceivedMotionEvent( + AllOf(WithDeviceId(0), WithMotionAction(AMOTION_EVENT_ACTION_MOVE), WithSampleCount(3), + WithSample(/*sampleIndex=*/2, + Sample{11ms, + {PointerArgs{.x = 2.2f, .y = 4.4f, .isResampled = true}}}))); + + mClientTestChannel->enqueueMessage(nextPointerMessage(20ms, /*deviceId=*/1, + AMOTION_EVENT_ACTION_MOVE, + Pointer{.x = 11.0f, .y = 12.0f})); + mClientTestChannel->enqueueMessage(nextPointerMessage(25ms, /*deviceId=*/1, + AMOTION_EVENT_ACTION_MOVE, + Pointer{.x = 12.0f, .y = 14.0f})); + mClientTestChannel->enqueueMessage(nextPointerMessage(30ms, /*deviceId=*/0, + AMOTION_EVENT_ACTION_MOVE, + Pointer{.x = 5.0f, .y = 6.0f})); + + invokeLooperCallback(); + assertOnBatchedInputEventPendingWasCalled(); + mConsumer->consumeBatchedInputEvents(32'000'000 /*ns*/); + + assertReceivedMotionEvent( + AllOf(WithDeviceId(1), WithMotionAction(AMOTION_EVENT_ACTION_MOVE), WithSampleCount(3), + WithSample(/*sampleIndex=*/2, + Sample{27ms, + {PointerArgs{.x = 12.4f, .y = 14.8f, .isResampled = true}}}))); + + mClientTestChannel->enqueueMessage(nextPointerMessage(35ms, /*deviceId=*/0, + AMOTION_EVENT_ACTION_MOVE, + Pointer{.x = 8.0f, .y = 9.0f})); + mClientTestChannel->enqueueMessage(nextPointerMessage(40ms, /*deviceId=*/1, + AMOTION_EVENT_ACTION_UP, + Pointer{.x = 12.0f, .y = 14.0f})); + mClientTestChannel->enqueueMessage(nextPointerMessage(45ms, /*deviceId=*/0, + AMOTION_EVENT_ACTION_UP, + Pointer{.x = 8.0f, .y = 9.0f})); + + invokeLooperCallback(); + mConsumer->consumeBatchedInputEvents(48'000'000 /*ns*/); + + assertReceivedMotionEvent( + AllOf(WithDeviceId(1), WithMotionAction(AMOTION_EVENT_ACTION_UP), WithSampleCount(1))); + + assertReceivedMotionEvent( + AllOf(WithDeviceId(0), WithMotionAction(AMOTION_EVENT_ACTION_MOVE), WithSampleCount(3), + WithSample(/*sampleIndex=*/2, + Sample{37'500'000ns, + {PointerArgs{.x = 9.5f, .y = 10.5f, .isResampled = true}}}))); + + assertReceivedMotionEvent( + AllOf(WithDeviceId(0), WithMotionAction(AMOTION_EVENT_ACTION_UP), WithSampleCount(1))); + + // The sequence order is based on the expected consumption. Each sequence number corresponds to + // one of the previously enqueued messages. + mClientTestChannel->assertFinishMessage(/*seq=*/1, /*handled=*/true); + mClientTestChannel->assertFinishMessage(/*seq=*/4, /*handled=*/true); + mClientTestChannel->assertFinishMessage(/*seq=*/2, /*handled=*/true); + mClientTestChannel->assertFinishMessage(/*seq=*/3, /*handled=*/true); + mClientTestChannel->assertFinishMessage(/*seq=*/5, /*handled=*/true); + mClientTestChannel->assertFinishMessage(/*seq=*/6, /*handled=*/true); + mClientTestChannel->assertFinishMessage(/*seq=*/9, /*handled=*/true); + mClientTestChannel->assertFinishMessage(/*seq=*/7, /*handled=*/true); + mClientTestChannel->assertFinishMessage(/*seq=*/8, /*handled=*/true); + mClientTestChannel->assertFinishMessage(/*seq=*/10, /*handled=*/true); +} } // namespace android diff --git a/libs/input/tests/InputPublisherAndConsumerNoResampling_test.cpp b/libs/input/tests/InputPublisherAndConsumerNoResampling_test.cpp index 467c3b46ce..1210f711de 100644 --- a/libs/input/tests/InputPublisherAndConsumerNoResampling_test.cpp +++ b/libs/input/tests/InputPublisherAndConsumerNoResampling_test.cpp @@ -364,7 +364,7 @@ private: if (!mConsumer->probablyHasInput()) { ADD_FAILURE() << "should deterministically have input because there is a batch"; } - mConsumer->consumeBatchedInputEvents(std::nullopt); + mConsumer->consumeBatchedInputEvents(/*frameTime=*/std::nullopt); }; void onFocusEvent(std::unique_ptr<FocusEvent> event, uint32_t seq) override { mFocusEvents.push(std::move(event)); diff --git a/libs/input/tests/Resampler_test.cpp b/libs/input/tests/Resampler_test.cpp index 26dee393c1..fae8518e87 100644 --- a/libs/input/tests/Resampler_test.cpp +++ b/libs/input/tests/Resampler_test.cpp @@ -87,7 +87,6 @@ InputSample::operator InputMessage() const { struct InputStream { std::vector<InputSample> samples{}; int32_t action{0}; - DeviceId deviceId{0}; /** * Converts from InputStream to MotionEvent. Enables calling LegacyResampler methods only with * the relevant data for tests. @@ -100,8 +99,8 @@ InputStream::operator MotionEvent() const { MotionEventBuilder motionEventBuilder = MotionEventBuilder(action, AINPUT_SOURCE_CLASS_POINTER) .downTime(0) - .eventTime(static_cast<std::chrono::nanoseconds>(firstSample.eventTime).count()) - .deviceId(deviceId); + .eventTime( + static_cast<std::chrono::nanoseconds>(firstSample.eventTime).count()); for (const Pointer& pointer : firstSample.pointers) { const PointerBuilder pointerBuilder = PointerBuilder(pointer.id, pointer.toolType).x(pointer.x).y(pointer.y); @@ -289,28 +288,6 @@ TEST_F(ResamplerTest, SinglePointerNotEnoughDataToResample) { assertMotionEventIsNotResampled(originalMotionEvent, motionEvent); } -TEST_F(ResamplerTest, SinglePointerDifferentDeviceIdBetweenMotionEvents) { - MotionEvent motionFromFirstDevice = - InputStream{{InputSample{4ms, {{.id = 0, .x = 1.0f, .y = 1.0f, .isResampled = false}}}, - InputSample{8ms, {{.id = 0, .x = 2.0f, .y = 2.0f, .isResampled = false}}}}, - AMOTION_EVENT_ACTION_MOVE, - .deviceId = 0}; - - mResampler->resampleMotionEvent(10ms, motionFromFirstDevice, nullptr); - - MotionEvent motionFromSecondDevice = - InputStream{{InputSample{11ms, - {{.id = 0, .x = 3.0f, .y = 3.0f, .isResampled = false}}}}, - AMOTION_EVENT_ACTION_MOVE, - .deviceId = 1}; - const MotionEvent originalMotionEvent = motionFromSecondDevice; - - mResampler->resampleMotionEvent(12ms, motionFromSecondDevice, nullptr); - // The MotionEvent should not be resampled because the second event came from a different device - // than the previous event. - assertMotionEventIsNotResampled(originalMotionEvent, motionFromSecondDevice); -} - TEST_F(ResamplerTest, SinglePointerSingleSampleInterpolation) { MotionEvent motionEvent = InputStream{{InputSample{10ms, diff --git a/libs/input/tests/TestEventMatchers.h b/libs/input/tests/TestEventMatchers.h new file mode 100644 index 0000000000..3589de599f --- /dev/null +++ b/libs/input/tests/TestEventMatchers.h @@ -0,0 +1,187 @@ +/** + * Copyright 2024 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#pragma once + +#include <chrono> +#include <ostream> +#include <vector> + +#include <android-base/logging.h> +#include <gtest/gtest.h> +#include <input/Input.h> + +namespace android { + +namespace { + +using ::testing::Matcher; + +} // namespace + +/** + * This file contains a copy of Matchers from .../inputflinger/tests/TestEventMatchers.h. Ideally, + * implementations must not be duplicated. + * TODO(b/365606513): Find a way to share TestEventMatchers.h between inputflinger and libinput. + */ + +struct PointerArgs { + float x{0.0f}; + float y{0.0f}; + bool isResampled{false}; +}; + +struct Sample { + std::chrono::nanoseconds eventTime{0}; + std::vector<PointerArgs> pointers{}; +}; + +class WithDeviceIdMatcher { +public: + using is_gtest_matcher = void; + explicit WithDeviceIdMatcher(DeviceId deviceId) : mDeviceId(deviceId) {} + + bool MatchAndExplain(const InputEvent& event, std::ostream*) const { + return mDeviceId == event.getDeviceId(); + } + + void DescribeTo(std::ostream* os) const { *os << "with device id " << mDeviceId; } + + void DescribeNegationTo(std::ostream* os) const { *os << "wrong device id"; } + +private: + const DeviceId mDeviceId; +}; + +inline WithDeviceIdMatcher WithDeviceId(int32_t deviceId) { + return WithDeviceIdMatcher(deviceId); +} + +class WithMotionActionMatcher { +public: + using is_gtest_matcher = void; + explicit WithMotionActionMatcher(int32_t action) : mAction(action) {} + + bool MatchAndExplain(const MotionEvent& event, std::ostream*) const { + bool matches = mAction == event.getAction(); + if (event.getAction() == AMOTION_EVENT_ACTION_CANCEL) { + matches &= (event.getFlags() & AMOTION_EVENT_FLAG_CANCELED) != 0; + } + return matches; + } + + void DescribeTo(std::ostream* os) const { + *os << "with motion action " << MotionEvent::actionToString(mAction); + if (mAction == AMOTION_EVENT_ACTION_CANCEL) { + *os << " and FLAG_CANCELED"; + } + } + + void DescribeNegationTo(std::ostream* os) const { *os << "wrong action"; } + +private: + const int32_t mAction; +}; + +inline WithMotionActionMatcher WithMotionAction(int32_t action) { + return WithMotionActionMatcher(action); +} + +class WithSampleCountMatcher { +public: + using is_gtest_matcher = void; + explicit WithSampleCountMatcher(size_t sampleCount) : mExpectedSampleCount{sampleCount} {} + + bool MatchAndExplain(const MotionEvent& motionEvent, std::ostream*) const { + return (motionEvent.getHistorySize() + 1) == mExpectedSampleCount; + } + + void DescribeTo(std::ostream* os) const { *os << "sample count " << mExpectedSampleCount; } + + void DescribeNegationTo(std::ostream* os) const { *os << "different sample count"; } + +private: + const size_t mExpectedSampleCount; +}; + +inline WithSampleCountMatcher WithSampleCount(size_t sampleCount) { + return WithSampleCountMatcher(sampleCount); +} + +class WithSampleMatcher { +public: + using is_gtest_matcher = void; + explicit WithSampleMatcher(size_t sampleIndex, const Sample& sample) + : mSampleIndex{sampleIndex}, mSample{sample} {} + + bool MatchAndExplain(const MotionEvent& motionEvent, std::ostream* os) const { + if (motionEvent.getHistorySize() < mSampleIndex) { + *os << "sample index out of bounds"; + return false; + } + + if (motionEvent.getHistoricalEventTime(mSampleIndex) != mSample.eventTime.count()) { + *os << "event time mismatch. sample: " + << motionEvent.getHistoricalEventTime(mSampleIndex) + << " expected: " << mSample.eventTime.count(); + return false; + } + + if (motionEvent.getPointerCount() != mSample.pointers.size()) { + *os << "pointer count mismatch. sample: " << motionEvent.getPointerCount() + << " expected: " << mSample.pointers.size(); + return false; + } + + for (size_t pointerIndex = 0; pointerIndex < motionEvent.getPointerCount(); + ++pointerIndex) { + const PointerCoords& pointerCoords = + *(motionEvent.getHistoricalRawPointerCoords(pointerIndex, mSampleIndex)); + if ((pointerCoords.getX() != mSample.pointers[pointerIndex].x) || + (pointerCoords.getY() != mSample.pointers[pointerIndex].y)) { + *os << "sample coordinates mismatch at pointer index " << pointerIndex + << ". sample: (" << pointerCoords.getX() << ", " << pointerCoords.getY() + << ") expected: (" << mSample.pointers[pointerIndex].x << ", " + << mSample.pointers[pointerIndex].y << ")"; + return false; + } + if (motionEvent.isResampled(pointerIndex, mSampleIndex) != + mSample.pointers[pointerIndex].isResampled) { + *os << "resampling flag mismatch. sample: " + << motionEvent.isResampled(pointerIndex, mSampleIndex) + << " expected: " << mSample.pointers[pointerIndex].isResampled; + return false; + } + } + return true; + } + + void DescribeTo(std::ostream* os) const { *os << "motion event sample properties match."; } + + void DescribeNegationTo(std::ostream* os) const { + *os << "motion event sample properties do not match expected properties."; + } + +private: + const size_t mSampleIndex; + const Sample mSample; +}; + +inline WithSampleMatcher WithSample(size_t sampleIndex, const Sample& sample) { + return WithSampleMatcher(sampleIndex, sample); +} + +} // namespace android diff --git a/libs/input/tests/TestInputChannel.cpp b/libs/input/tests/TestInputChannel.cpp index d5f00b699b..26a0ca2106 100644 --- a/libs/input/tests/TestInputChannel.cpp +++ b/libs/input/tests/TestInputChannel.cpp @@ -19,6 +19,11 @@ #include <TestInputChannel.h> +#include <sys/socket.h> +#include <unistd.h> + +#include <array> + #include <android-base/logging.h> #include <android-base/unique_fd.h> #include <binder/IBinder.h> @@ -27,13 +32,25 @@ namespace android { namespace { -constexpr int FAKE_FD{-1}; + +/** + * Returns a stub file descriptor by opening a socket pair and closing one of the fds. The returned + * fd can be used to construct an InputChannel. + */ +base::unique_fd generateFileDescriptor() { + std::array<int, 2> kFileDescriptors; + LOG_IF(FATAL, ::socketpair(AF_UNIX, SOCK_SEQPACKET, 0, kFileDescriptors.data()) != 0) + << "TestInputChannel. Failed to create socket pair."; + LOG_IF(FATAL, ::close(kFileDescriptors[1]) != 0) + << "TestInputChannel. Failed to close file descriptor."; + return base::unique_fd{kFileDescriptors[0]}; +} } // namespace // --- TestInputChannel --- TestInputChannel::TestInputChannel(const std::string& name) - : InputChannel{name, base::unique_fd(FAKE_FD), sp<BBinder>::make()} {} + : InputChannel{name, generateFileDescriptor(), sp<BBinder>::make()} {} void TestInputChannel::enqueueMessage(const InputMessage& message) { mReceivedMessages.push(message); diff --git a/libs/input/tests/TestLooper.cpp b/libs/input/tests/TestLooper.cpp deleted file mode 100644 index e0f01ed25d..0000000000 --- a/libs/input/tests/TestLooper.cpp +++ /dev/null @@ -1,51 +0,0 @@ -/** - * Copyright 2024 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include <TestLooper.h> - -#include <android-base/logging.h> - -namespace android { - -TestLooper::TestLooper() : mLooper(sp<Looper>::make(/*allowNonCallbacks=*/false)) {} - -int TestLooper::addFd(int fd, int ident, int events, const sp<LooperCallback>& callback, - void* data) { - mCallbacks[fd] = callback; - constexpr int SUCCESS{1}; - return SUCCESS; -} - -int TestLooper::removeFd(int fd) { - if (auto it = mCallbacks.find(fd); it != mCallbacks.cend()) { - mCallbacks.erase(fd); - constexpr int SUCCESS{1}; - return SUCCESS; - } - constexpr int FAILURE{0}; - return FAILURE; -} - -void TestLooper::invokeCallback(int fd, int events) { - auto it = mCallbacks.find(fd); - LOG_IF(FATAL, it == mCallbacks.cend()) << "Fd does not exist in mCallbacks."; - mCallbacks[fd]->handleEvent(fd, events, /*data=*/nullptr); -} - -sp<Looper> TestLooper::getLooper() const { - return mLooper; -} -} // namespace android
\ No newline at end of file diff --git a/libs/input/tests/TestLooper.h b/libs/input/tests/TestLooper.h deleted file mode 100644 index 3242bc7699..0000000000 --- a/libs/input/tests/TestLooper.h +++ /dev/null @@ -1,56 +0,0 @@ -/** - * Copyright 2024 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#pragma once - -#include <map> - -#include <input/LooperInterface.h> - -namespace android { -/** - * TestLooper provides a mechanism to directly trigger Looper's callback. - */ -class TestLooper final : public LooperInterface { -public: - TestLooper(); - - /** - * Adds a file descriptor to mCallbacks. Ident, events, and data parameters are ignored. If - * addFd is called with an existent file descriptor and a different callback, the previous - * callback is overwritten. - */ - int addFd(int fd, int ident, int events, const sp<LooperCallback>& callback, - void* data) override; - - /** - * Removes a file descriptor from mCallbacks. If fd is not in mCallbacks, returns FAILURE. - */ - int removeFd(int fd) override; - - /** - * Calls handleEvent of the file descriptor. Fd must be in mCallbacks. Otherwise, invokeCallback - * fatally logs. - */ - void invokeCallback(int fd, int events); - - sp<Looper> getLooper() const override; - -private: - std::map<int /*fd*/, sp<LooperCallback>> mCallbacks; - sp<Looper> mLooper; -}; -} // namespace android
\ No newline at end of file diff --git a/libs/renderengine/OWNERS b/libs/renderengine/OWNERS index 66e1aa1ca1..17ab29fd29 100644 --- a/libs/renderengine/OWNERS +++ b/libs/renderengine/OWNERS @@ -5,4 +5,6 @@ alecmouri@google.com djsollen@google.com jreck@google.com lpy@google.com +nscobie@google.com +sallyqi@google.com scroggo@google.com diff --git a/libs/renderengine/benchmark/RenderEngineBench.cpp b/libs/renderengine/benchmark/RenderEngineBench.cpp index a9264b3914..595573d24a 100644 --- a/libs/renderengine/benchmark/RenderEngineBench.cpp +++ b/libs/renderengine/benchmark/RenderEngineBench.cpp @@ -17,6 +17,7 @@ #include <RenderEngineBench.h> #include <android-base/file.h> #include <benchmark/benchmark.h> +#include <com_android_graphics_libgui_flags.h> #include <gui/SurfaceComposerClient.h> #include <log/log.h> #include <renderengine/ExternalTexture.h> @@ -321,5 +322,7 @@ BENCHMARK_CAPTURE(BM_homescreen_blur, kawase_dual_filter, RenderEngine::Threaded BENCHMARK_CAPTURE(BM_homescreen, SkiaGLThreaded, RenderEngine::Threaded::YES, RenderEngine::GraphicsApi::GL); +#if COM_ANDROID_GRAPHICS_LIBGUI_FLAGS_EDGE_EXTENSION_SHADER BENCHMARK_CAPTURE(BM_homescreen_edgeExtension, SkiaGLThreaded, RenderEngine::Threaded::YES, RenderEngine::GraphicsApi::GL); +#endif diff --git a/libs/vibrator/TEST_MAPPING b/libs/vibrator/TEST_MAPPING index d782b43b57..e206761efa 100644 --- a/libs/vibrator/TEST_MAPPING +++ b/libs/vibrator/TEST_MAPPING @@ -1,5 +1,5 @@ { - "postsubmit": [ + "presubmit": [ { "name": "libvibrator_test" } diff --git a/opengl/libs/EGL/egl_platform_entries.cpp b/opengl/libs/EGL/egl_platform_entries.cpp index 6e35041662..6713a5c69d 100644 --- a/opengl/libs/EGL/egl_platform_entries.cpp +++ b/opengl/libs/EGL/egl_platform_entries.cpp @@ -2141,6 +2141,10 @@ EGLBoolean eglPresentationTimeANDROIDImpl(EGLDisplay dpy, EGLSurface surface, } egl_surface_t const* const s = get_surface(surface); + if (!s->getNativeWindow()) { + setError(EGL_BAD_SURFACE, EGL_FALSE); + return EGL_FALSE; + } native_window_set_buffers_timestamp(s->getNativeWindow(), time); return EGL_TRUE; @@ -2405,7 +2409,7 @@ EGLBoolean eglGetFrameTimestampsANDROIDImpl(EGLDisplay dpy, EGLSurface surface, case 0: return EGL_TRUE; case -ENOENT: - return setError(EGL_BAD_ACCESS, (EGLBoolean)EGL_FALSE); + return setErrorQuiet(EGL_BAD_ACCESS, (EGLBoolean)EGL_FALSE); case -ENOSYS: return setError(EGL_BAD_SURFACE, (EGLBoolean)EGL_FALSE); case -EINVAL: diff --git a/services/inputflinger/dispatcher/Android.bp b/services/inputflinger/dispatcher/Android.bp index 1a0ec48525..8b2b8432c3 100644 --- a/services/inputflinger/dispatcher/Android.bp +++ b/services/inputflinger/dispatcher/Android.bp @@ -46,6 +46,7 @@ filegroup { "InputState.cpp", "InputTarget.cpp", "LatencyAggregator.cpp", + "LatencyAggregatorWithHistograms.cpp", "LatencyTracker.cpp", "Monitor.cpp", "TouchedWindow.cpp", 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/InputDispatcher.cpp b/services/inputflinger/dispatcher/InputDispatcher.cpp index 0be107de2b..3337c47248 100644 --- a/services/inputflinger/dispatcher/InputDispatcher.cpp +++ b/services/inputflinger/dispatcher/InputDispatcher.cpp @@ -155,6 +155,10 @@ constexpr std::chrono::milliseconds SLOW_INTERCEPTION_THRESHOLD = 50ms; // Number of recent events to keep for debugging purposes. constexpr size_t RECENT_QUEUE_MAX_SIZE = 10; +// Interval at which we should push the atom gathering input event latencies in +// LatencyAggregatorWithHistograms +constexpr nsecs_t LATENCY_STATISTICS_PUSH_INTERVAL = 6 * 3600 * 1000000000LL; // 6 hours + // Event log tags. See EventLogTags.logtags for reference. constexpr int LOGTAG_INPUT_INTERACTION = 62000; constexpr int LOGTAG_INPUT_FOCUS = 62001; @@ -748,7 +752,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; } @@ -875,6 +880,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}; } } @@ -944,8 +951,13 @@ InputDispatcher::InputDispatcher(InputDispatcherPolicyInterface& policy, mFocusedDisplayId(ui::LogicalDisplayId::DEFAULT), mWindowTokenWithPointerCapture(nullptr), mAwaitedApplicationDisplayId(ui::LogicalDisplayId::INVALID), - mLatencyAggregator(), - mLatencyTracker(&mLatencyAggregator) { + mInputEventTimelineProcessor( + input_flags::enable_per_device_input_latency_metrics() + ? std::move(std::unique_ptr<InputEventTimelineProcessor>( + new LatencyAggregatorWithHistograms())) + : std::move(std::unique_ptr<InputEventTimelineProcessor>( + new LatencyAggregator()))), + mLatencyTracker(*mInputEventTimelineProcessor) { mLooper = sp<Looper>::make(false); mReporter = createInputReporter(); @@ -1018,6 +1030,11 @@ void InputDispatcher::dispatchOnce() { const nsecs_t nextAnrCheck = processAnrsLocked(); nextWakeupTime = std::min(nextWakeupTime, nextAnrCheck); + if (input_flags::enable_per_device_input_latency_metrics()) { + const nsecs_t nextStatisticsPush = processLatencyStatisticsLocked(); + nextWakeupTime = std::min(nextWakeupTime, nextStatisticsPush); + } + // We are about to enter an infinitely long sleep, because we have no commands or // pending or queued events if (nextWakeupTime == LLONG_MAX) { @@ -1098,6 +1115,21 @@ nsecs_t InputDispatcher::processAnrsLocked() { return LLONG_MIN; } +/** + * Check if enough time has passed since the last latency statistics push. + * Return the time at which we should wake up next. + */ +nsecs_t InputDispatcher::processLatencyStatisticsLocked() { + const nsecs_t currentTime = now(); + // Log the atom recording latency statistics if more than 6 hours passed from the last + // push + if (currentTime - mLastStatisticPushTime >= LATENCY_STATISTICS_PUSH_INTERVAL) { + mInputEventTimelineProcessor->pushLatencyStatistics(); + mLastStatisticPushTime = currentTime; + } + return mLastStatisticPushTime + LATENCY_STATISTICS_PUSH_INTERVAL; +} + std::chrono::nanoseconds InputDispatcher::getDispatchingTimeoutLocked( const std::shared_ptr<Connection>& connection) { if (connection->monitor) { @@ -2512,7 +2544,8 @@ InputDispatcher::findTouchedWindowTargetsLocked(nsecs_t currentTime, const Motio 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. @@ -4516,6 +4549,14 @@ void InputDispatcher::notifyKey(const NotifyKeyArgs& args) { newEntry->traceTracker = mTracer->traceInboundEvent(*newEntry); } + if (input_flags::enable_per_device_input_latency_metrics()) { + if (args.id != android::os::IInputConstants::INVALID_INPUT_EVENT_ID && + IdGenerator::getSource(args.id) == IdGenerator::Source::INPUT_READER && + !mInputFilterEnabled) { + mLatencyTracker.trackNotifyKey(args); + } + } + needWake = enqueueInboundEventLocked(std::move(newEntry)); mLock.unlock(); } // release lock @@ -4648,9 +4689,7 @@ void InputDispatcher::notifyMotion(const NotifyMotionArgs& args) { if (args.id != android::os::IInputConstants::INVALID_INPUT_EVENT_ID && IdGenerator::getSource(args.id) == IdGenerator::Source::INPUT_READER && !mInputFilterEnabled) { - std::set<InputDeviceUsageSource> sources = getUsageSourcesForMotionArgs(args); - mLatencyTracker.trackListener(args.id, args.eventTime, args.readTime, args.deviceId, - sources, args.action, InputEventType::MOTION); + mLatencyTracker.trackNotifyMotion(args); } needWake = enqueueInboundEventLocked(std::move(newEntry)); @@ -5420,6 +5459,32 @@ 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 @@ -6060,7 +6125,7 @@ void InputDispatcher::dumpDispatchStateLocked(std::string& dump) const { dump += StringPrintf(INDENT2 "KeyRepeatTimeout: %" PRId64 "ms\n", ns2ms(mConfig.keyRepeatTimeout)); dump += mLatencyTracker.dump(INDENT2); - dump += mLatencyAggregator.dump(INDENT2); + dump += mInputEventTimelineProcessor->dump(INDENT2); dump += INDENT "InputTracer: "; dump += mTracer == nullptr ? "Disabled" : "Enabled"; } diff --git a/services/inputflinger/dispatcher/InputDispatcher.h b/services/inputflinger/dispatcher/InputDispatcher.h index 1904058080..d90b9de80e 100644 --- a/services/inputflinger/dispatcher/InputDispatcher.h +++ b/services/inputflinger/dispatcher/InputDispatcher.h @@ -28,6 +28,7 @@ #include "InputTarget.h" #include "InputThread.h" #include "LatencyAggregator.h" +#include "LatencyAggregatorWithHistograms.h" #include "LatencyTracker.h" #include "Monitor.h" #include "TouchState.h" @@ -326,6 +327,7 @@ private: std::chrono::nanoseconds mMonitorDispatchingTimeout GUARDED_BY(mLock); nsecs_t processAnrsLocked() REQUIRES(mLock); + nsecs_t processLatencyStatisticsLocked() REQUIRES(mLock); std::chrono::nanoseconds getDispatchingTimeoutLocked( const std::shared_ptr<Connection>& connection) REQUIRES(mLock); @@ -697,7 +699,8 @@ private: DeviceId deviceId) const REQUIRES(mLock); // Statistics gathering. - LatencyAggregator mLatencyAggregator GUARDED_BY(mLock); + nsecs_t mLastStatisticPushTime = 0; + std::unique_ptr<InputEventTimelineProcessor> mInputEventTimelineProcessor GUARDED_BY(mLock); LatencyTracker mLatencyTracker GUARDED_BY(mLock); void traceInboundQueueLengthLocked() REQUIRES(mLock); void traceOutboundQueueLength(const Connection& connection); diff --git a/services/inputflinger/dispatcher/InputEventTimeline.h b/services/inputflinger/dispatcher/InputEventTimeline.h index 951fcc8d0c..4552708adc 100644 --- a/services/inputflinger/dispatcher/InputEventTimeline.h +++ b/services/inputflinger/dispatcher/InputEventTimeline.h @@ -121,13 +121,21 @@ struct InputEventTimeline { class InputEventTimelineProcessor { protected: InputEventTimelineProcessor() {} - virtual ~InputEventTimelineProcessor() {} public: + virtual ~InputEventTimelineProcessor() {} + /** * Process the provided timeline */ virtual void processTimeline(const InputEventTimeline& timeline) = 0; + + /** + * Trigger a push for the input event latency statistics + */ + virtual void pushLatencyStatistics() = 0; + + virtual std::string dump(const char* prefix) const = 0; }; } // namespace inputdispatcher diff --git a/services/inputflinger/dispatcher/InputState.cpp b/services/inputflinger/dispatcher/InputState.cpp index 4df23c54d6..9b5a79b24d 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/LatencyAggregator.cpp b/services/inputflinger/dispatcher/LatencyAggregator.cpp index 4ddd2e9e95..d0e9d7c983 100644 --- a/services/inputflinger/dispatcher/LatencyAggregator.cpp +++ b/services/inputflinger/dispatcher/LatencyAggregator.cpp @@ -28,6 +28,8 @@ using android::base::StringPrintf; using dist_proc::aggregation::KllQuantile; using std::chrono_literals::operator""ms; +namespace { + // Convert the provided nanoseconds into hundreds of microseconds. // Use hundreds of microseconds (as opposed to microseconds) to preserve space. static inline int64_t ns2hus(nsecs_t nanos) { @@ -74,6 +76,8 @@ static std::chrono::milliseconds getSlowEventMinReportingInterval() { return std::chrono::milliseconds(std::stoi(millis)); } +} // namespace + namespace android::inputdispatcher { /** @@ -125,6 +129,9 @@ void LatencyAggregator::processTimeline(const InputEventTimeline& timeline) { processSlowEvent(timeline); } +// This version of LatencyAggregator doesn't push any atoms +void LatencyAggregator::pushLatencyStatistics() {} + void LatencyAggregator::processStatistics(const InputEventTimeline& timeline) { std::scoped_lock lock(mLock); // Before we do any processing, check that we have not yet exceeded MAX_SIZE diff --git a/services/inputflinger/dispatcher/LatencyAggregator.h b/services/inputflinger/dispatcher/LatencyAggregator.h index d6d168602a..468add1435 100644 --- a/services/inputflinger/dispatcher/LatencyAggregator.h +++ b/services/inputflinger/dispatcher/LatencyAggregator.h @@ -57,6 +57,8 @@ public: */ void processTimeline(const InputEventTimeline& timeline) override; + void pushLatencyStatistics() override; + std::string dump(const char* prefix) const; ~LatencyAggregator(); diff --git a/services/inputflinger/dispatcher/LatencyAggregatorWithHistograms.cpp b/services/inputflinger/dispatcher/LatencyAggregatorWithHistograms.cpp new file mode 100644 index 0000000000..881a96b28e --- /dev/null +++ b/services/inputflinger/dispatcher/LatencyAggregatorWithHistograms.cpp @@ -0,0 +1,345 @@ +/* + * Copyright 2024 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#define LOG_TAG "LatencyAggregatorWithHistograms" +#include "../InputDeviceMetricsSource.h" +#include "InputDispatcher.h" + +#include <inttypes.h> +#include <log/log_event_list.h> +#include <statslog.h> + +#include <android-base/logging.h> +#include <android-base/stringprintf.h> +#include <input/Input.h> +#include <log/log.h> +#include <server_configurable_flags/get_flags.h> + +using android::base::StringPrintf; +using std::chrono_literals::operator""ms; + +namespace { + +// Convert the provided nanoseconds into hundreds of microseconds. +// Use hundreds of microseconds (as opposed to microseconds) to preserve space. +static inline int64_t ns2hus(nsecs_t nanos) { + return ns2us(nanos) / 100; +} + +// Category (=namespace) name for the input settings that are applied at boot time +static const char* INPUT_NATIVE_BOOT = "input_native_boot"; +// Feature flag name for the threshold of end-to-end touch latency that would trigger +// SlowEventReported atom to be pushed +static const char* SLOW_EVENT_MIN_REPORTING_LATENCY_MILLIS = + "slow_event_min_reporting_latency_millis"; +// Feature flag name for the minimum delay before reporting a slow event after having just reported +// a slow event. This helps limit the amount of data sent to the server +static const char* SLOW_EVENT_MIN_REPORTING_INTERVAL_MILLIS = + "slow_event_min_reporting_interval_millis"; + +// If an event has end-to-end latency > 200 ms, it will get reported as a slow event. +std::chrono::milliseconds DEFAULT_SLOW_EVENT_MIN_REPORTING_LATENCY = 200ms; +// If we receive two slow events less than 1 min apart, we will only report 1 of them. +std::chrono::milliseconds DEFAULT_SLOW_EVENT_MIN_REPORTING_INTERVAL = 60000ms; + +static std::chrono::milliseconds getSlowEventMinReportingLatency() { + std::string millis = server_configurable_flags:: + GetServerConfigurableFlag(INPUT_NATIVE_BOOT, SLOW_EVENT_MIN_REPORTING_LATENCY_MILLIS, + std::to_string( + DEFAULT_SLOW_EVENT_MIN_REPORTING_LATENCY.count())); + return std::chrono::milliseconds(std::stoi(millis)); +} + +static std::chrono::milliseconds getSlowEventMinReportingInterval() { + std::string millis = server_configurable_flags:: + GetServerConfigurableFlag(INPUT_NATIVE_BOOT, SLOW_EVENT_MIN_REPORTING_INTERVAL_MILLIS, + std::to_string( + DEFAULT_SLOW_EVENT_MIN_REPORTING_INTERVAL.count())); + return std::chrono::milliseconds(std::stoi(millis)); +} + +} // namespace + +namespace android::inputdispatcher { + +int32_t LatencyStageIndexToAtomEnum(LatencyStageIndex latencyStageIndex) { + switch (latencyStageIndex) { + case LatencyStageIndex::EVENT_TO_READ: + return util::INPUT_EVENT_LATENCY_REPORTED__LATENCY_STAGE__EVENT_TO_READ; + case LatencyStageIndex::READ_TO_DELIVER: + return util::INPUT_EVENT_LATENCY_REPORTED__LATENCY_STAGE__READ_TO_DELIVER; + case LatencyStageIndex::DELIVER_TO_CONSUME: + return util::INPUT_EVENT_LATENCY_REPORTED__LATENCY_STAGE__DELIVER_TO_CONSUME; + case LatencyStageIndex::CONSUME_TO_FINISH: + return util::INPUT_EVENT_LATENCY_REPORTED__LATENCY_STAGE__CONSUME_TO_FINISH; + case LatencyStageIndex::CONSUME_TO_GPU_COMPLETE: + return util::INPUT_EVENT_LATENCY_REPORTED__LATENCY_STAGE__CONSUME_TO_GPU_COMPLETE; + case LatencyStageIndex::GPU_COMPLETE_TO_PRESENT: + return util::INPUT_EVENT_LATENCY_REPORTED__LATENCY_STAGE__GPU_COMPLETE_TO_PRESENT; + case LatencyStageIndex::END_TO_END: + return util::INPUT_EVENT_LATENCY_REPORTED__LATENCY_STAGE__END_TO_END; + default: + return util::INPUT_EVENT_LATENCY_REPORTED__LATENCY_STAGE__UNKNOWN_LATENCY_STAGE; + } +} + +int32_t InputEventTypeEnumToAtomEnum(InputEventActionType inputEventActionType) { + switch (inputEventActionType) { + case InputEventActionType::UNKNOWN_INPUT_EVENT: + return util::INPUT_EVENT_LATENCY_REPORTED__INPUT_EVENT_TYPE__UNKNOWN_INPUT_EVENT; + case InputEventActionType::MOTION_ACTION_DOWN: + return util::INPUT_EVENT_LATENCY_REPORTED__INPUT_EVENT_TYPE__MOTION_ACTION_DOWN; + case InputEventActionType::MOTION_ACTION_MOVE: + return util::INPUT_EVENT_LATENCY_REPORTED__INPUT_EVENT_TYPE__MOTION_ACTION_MOVE; + case InputEventActionType::MOTION_ACTION_UP: + return util::INPUT_EVENT_LATENCY_REPORTED__INPUT_EVENT_TYPE__MOTION_ACTION_UP; + case InputEventActionType::MOTION_ACTION_HOVER_MOVE: + return util::INPUT_EVENT_LATENCY_REPORTED__INPUT_EVENT_TYPE__MOTION_ACTION_HOVER_MOVE; + case InputEventActionType::MOTION_ACTION_SCROLL: + return util::INPUT_EVENT_LATENCY_REPORTED__INPUT_EVENT_TYPE__MOTION_ACTION_SCROLL; + case InputEventActionType::KEY: + return util::INPUT_EVENT_LATENCY_REPORTED__INPUT_EVENT_TYPE__KEY; + } +} + +void LatencyAggregatorWithHistograms::processTimeline(const InputEventTimeline& timeline) { + processStatistics(timeline); + processSlowEvent(timeline); +} + +void LatencyAggregatorWithHistograms::addSampleToHistogram( + const InputEventLatencyIdentifier& identifier, LatencyStageIndex latencyStageIndex, + nsecs_t latency) { + // Only record positive values for the statistics + if (latency > 0) { + auto it = mHistograms.find(identifier); + if (it != mHistograms.end()) { + it->second[static_cast<size_t>(latencyStageIndex)].addSample(ns2hus(latency)); + } + } +} + +void LatencyAggregatorWithHistograms::processStatistics(const InputEventTimeline& timeline) { + // Only gather data for Down, Move and Up motion events and Key events + if (!(timeline.inputEventActionType == InputEventActionType::MOTION_ACTION_DOWN || + timeline.inputEventActionType == InputEventActionType::MOTION_ACTION_MOVE || + timeline.inputEventActionType == InputEventActionType::MOTION_ACTION_UP || + timeline.inputEventActionType == InputEventActionType::KEY)) + return; + + // Don't collect data for unidentified devices. This situation can occur for the first few input + // events produced when an input device is first connected + if (timeline.vendorId == 0xFFFF && timeline.productId == 0xFFFF) return; + + InputEventLatencyIdentifier identifier = {timeline.vendorId, timeline.productId, + timeline.sources, timeline.inputEventActionType}; + // Check if there's a value in mHistograms map associated to identifier. + // If not, add an array with 7 empty histograms as an entry + if (mHistograms.count(identifier) == 0) { + if (static_cast<int32_t>(timeline.inputEventActionType) - 1 < 0) { + LOG(FATAL) << "Action index is smaller than 0. Action type: " + << ftl::enum_string(timeline.inputEventActionType); + return; + } + size_t actionIndex = + static_cast<size_t>(static_cast<int32_t>(timeline.inputEventActionType) - 1); + if (actionIndex >= NUM_INPUT_EVENT_TYPES) { + LOG(FATAL) << "Action index greater than the number of input event types. Action Type: " + << ftl::enum_string(timeline.inputEventActionType) + << "; Action Type Index: " << actionIndex; + return; + } + + std::array<Histogram, 7> histograms = + {Histogram(allBinSizes[binSizesMappings[0][actionIndex]]), + Histogram(allBinSizes[binSizesMappings[1][actionIndex]]), + Histogram(allBinSizes[binSizesMappings[2][actionIndex]]), + Histogram(allBinSizes[binSizesMappings[3][actionIndex]]), + Histogram(allBinSizes[binSizesMappings[4][actionIndex]]), + Histogram(allBinSizes[binSizesMappings[5][actionIndex]]), + Histogram(allBinSizes[binSizesMappings[6][actionIndex]])}; + mHistograms.insert({identifier, histograms}); + } + + // Process common ones first + const nsecs_t eventToRead = timeline.readTime - timeline.eventTime; + addSampleToHistogram(identifier, LatencyStageIndex::EVENT_TO_READ, eventToRead); + + // Now process per-connection ones + for (const auto& [connectionToken, connectionTimeline] : timeline.connectionTimelines) { + if (!connectionTimeline.isComplete()) { + continue; + } + const nsecs_t readToDeliver = connectionTimeline.deliveryTime - timeline.readTime; + const nsecs_t deliverToConsume = + connectionTimeline.consumeTime - connectionTimeline.deliveryTime; + const nsecs_t consumeToFinish = + connectionTimeline.finishTime - connectionTimeline.consumeTime; + const nsecs_t gpuCompletedTime = + connectionTimeline.graphicsTimeline[GraphicsTimeline::GPU_COMPLETED_TIME]; + const nsecs_t presentTime = + connectionTimeline.graphicsTimeline[GraphicsTimeline::PRESENT_TIME]; + const nsecs_t consumeToGpuComplete = gpuCompletedTime - connectionTimeline.consumeTime; + const nsecs_t gpuCompleteToPresent = presentTime - gpuCompletedTime; + const nsecs_t endToEnd = presentTime - timeline.eventTime; + + addSampleToHistogram(identifier, LatencyStageIndex::READ_TO_DELIVER, readToDeliver); + addSampleToHistogram(identifier, LatencyStageIndex::DELIVER_TO_CONSUME, deliverToConsume); + addSampleToHistogram(identifier, LatencyStageIndex::CONSUME_TO_FINISH, consumeToFinish); + addSampleToHistogram(identifier, LatencyStageIndex::CONSUME_TO_GPU_COMPLETE, + consumeToGpuComplete); + addSampleToHistogram(identifier, LatencyStageIndex::GPU_COMPLETE_TO_PRESENT, + gpuCompleteToPresent); + addSampleToHistogram(identifier, LatencyStageIndex::END_TO_END, endToEnd); + } +} + +void LatencyAggregatorWithHistograms::pushLatencyStatistics() { + for (auto& [id, histograms] : mHistograms) { + auto [vendorId, productId, sources, action] = id; + for (size_t latencyStageIndex = static_cast<size_t>(LatencyStageIndex::EVENT_TO_READ); + latencyStageIndex < static_cast<size_t>(LatencyStageIndex::SIZE); + ++latencyStageIndex) { + // Convert sources set to vector for atom logging: + std::vector<int32_t> sourcesVector = {}; + for (auto& elem : sources) { + sourcesVector.push_back(static_cast<int32_t>(elem)); + } + + // convert histogram bin counts array to vector for atom logging: + std::array arr = histograms[latencyStageIndex].getBinCounts(); + std::vector<int32_t> binCountsVector(arr.begin(), arr.end()); + + if (static_cast<int32_t>(action) - 1 < 0) { + ALOGW("Action index is smaller than 0. Action type: %s", + ftl::enum_string(action).c_str()); + continue; + } + size_t actionIndex = static_cast<size_t>(static_cast<int32_t>(action) - 1); + if (actionIndex >= NUM_INPUT_EVENT_TYPES) { + ALOGW("Action index greater than the number of input event types. Action Type: %s; " + "Action Type Index: %zu", + ftl::enum_string(action).c_str(), actionIndex); + continue; + } + + stats_write(android::util::INPUT_EVENT_LATENCY_REPORTED, vendorId, productId, + sourcesVector, InputEventTypeEnumToAtomEnum(action), + LatencyStageIndexToAtomEnum( + static_cast<LatencyStageIndex>(latencyStageIndex)), + histogramVersions[latencyStageIndex][actionIndex], binCountsVector); + } + } + mHistograms.clear(); +} + +// TODO (b/270049345): For now, we just copied the code from LatencyAggregator to populate the old +// atom, but eventually we should migrate this to use the new SlowEventReported atom +void LatencyAggregatorWithHistograms::processSlowEvent(const InputEventTimeline& timeline) { + static const std::chrono::duration sSlowEventThreshold = getSlowEventMinReportingLatency(); + static const std::chrono::duration sSlowEventReportingInterval = + getSlowEventMinReportingInterval(); + for (const auto& [token, connectionTimeline] : timeline.connectionTimelines) { + if (!connectionTimeline.isComplete()) { + continue; + } + mNumEventsSinceLastSlowEventReport++; + const nsecs_t presentTime = + connectionTimeline.graphicsTimeline[GraphicsTimeline::PRESENT_TIME]; + const std::chrono::nanoseconds endToEndLatency = + std::chrono::nanoseconds(presentTime - timeline.eventTime); + if (endToEndLatency < sSlowEventThreshold) { + continue; + } + // This is a slow event. Before we report it, check if we are reporting too often + const std::chrono::duration elapsedSinceLastReport = + std::chrono::nanoseconds(timeline.eventTime - mLastSlowEventTime); + if (elapsedSinceLastReport < sSlowEventReportingInterval) { + mNumSkippedSlowEvents++; + continue; + } + + const nsecs_t eventToRead = timeline.readTime - timeline.eventTime; + const nsecs_t readToDeliver = connectionTimeline.deliveryTime - timeline.readTime; + const nsecs_t deliverToConsume = + connectionTimeline.consumeTime - connectionTimeline.deliveryTime; + const nsecs_t consumeToFinish = + connectionTimeline.finishTime - connectionTimeline.consumeTime; + const nsecs_t gpuCompletedTime = + connectionTimeline.graphicsTimeline[GraphicsTimeline::GPU_COMPLETED_TIME]; + const nsecs_t consumeToGpuComplete = gpuCompletedTime - connectionTimeline.consumeTime; + const nsecs_t gpuCompleteToPresent = presentTime - gpuCompletedTime; + + android::util::stats_write(android::util::SLOW_INPUT_EVENT_REPORTED, + timeline.inputEventActionType == + InputEventActionType::MOTION_ACTION_DOWN, + static_cast<int32_t>(ns2us(eventToRead)), + static_cast<int32_t>(ns2us(readToDeliver)), + static_cast<int32_t>(ns2us(deliverToConsume)), + static_cast<int32_t>(ns2us(consumeToFinish)), + static_cast<int32_t>(ns2us(consumeToGpuComplete)), + static_cast<int32_t>(ns2us(gpuCompleteToPresent)), + static_cast<int32_t>(ns2us(endToEndLatency.count())), + static_cast<int32_t>(mNumEventsSinceLastSlowEventReport), + static_cast<int32_t>(mNumSkippedSlowEvents)); + mNumEventsSinceLastSlowEventReport = 0; + mNumSkippedSlowEvents = 0; + mLastSlowEventTime = timeline.readTime; + } +} + +std::string LatencyAggregatorWithHistograms::dump(const char* prefix) const { + std::string statisticsStr = StringPrintf("%s Histograms:\n", prefix); + for (const auto& [id, histograms] : mHistograms) { + auto [vendorId, productId, sources, action] = id; + + std::string identifierStr = + StringPrintf("%s Identifier: vendor %d, product %d, sources: {", prefix, vendorId, + productId); + bool firstSource = true; + for (const auto& source : sources) { + if (!firstSource) { + identifierStr += ", "; + } + identifierStr += StringPrintf("%d", static_cast<int32_t>(source)); + firstSource = false; + } + identifierStr += StringPrintf("}, action: %d\n", static_cast<int32_t>(action)); + + std::string histogramsStr; + for (size_t stageIndex = 0; stageIndex < static_cast<size_t>(LatencyStageIndex::SIZE); + stageIndex++) { + const auto& histogram = histograms[stageIndex]; + const std::array<int, NUM_BINS>& binCounts = histogram.getBinCounts(); + + histogramsStr += StringPrintf("%s %zu: ", prefix, stageIndex); + histogramsStr += StringPrintf("%d", binCounts[0]); + for (size_t bin = 1; bin < NUM_BINS; bin++) { + histogramsStr += StringPrintf(", %d", binCounts[bin]); + } + histogramsStr += StringPrintf("\n"); + } + statisticsStr += identifierStr + histogramsStr; + } + + return StringPrintf("%sLatencyAggregatorWithHistograms:\n", prefix) + statisticsStr + + StringPrintf("%s mLastSlowEventTime=%" PRId64 "\n", prefix, mLastSlowEventTime) + + StringPrintf("%s mNumEventsSinceLastSlowEventReport = %zu\n", prefix, + mNumEventsSinceLastSlowEventReport) + + StringPrintf("%s mNumSkippedSlowEvents = %zu\n", prefix, mNumSkippedSlowEvents); +} + +} // namespace android::inputdispatcher diff --git a/services/inputflinger/dispatcher/LatencyAggregatorWithHistograms.h b/services/inputflinger/dispatcher/LatencyAggregatorWithHistograms.h new file mode 100644 index 0000000000..2ceb0e7cb7 --- /dev/null +++ b/services/inputflinger/dispatcher/LatencyAggregatorWithHistograms.h @@ -0,0 +1,164 @@ +/* + * Copyright 2024 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#pragma once + +#include <android-base/thread_annotations.h> +#include <utils/Timers.h> + +#include "InputEventTimeline.h" + +namespace android::inputdispatcher { + +static constexpr size_t NUM_BINS = 20; +static constexpr size_t NUM_INPUT_EVENT_TYPES = 6; + +enum class LatencyStageIndex : size_t { + EVENT_TO_READ = 0, + READ_TO_DELIVER = 1, + DELIVER_TO_CONSUME = 2, + CONSUME_TO_FINISH = 3, + CONSUME_TO_GPU_COMPLETE = 4, + GPU_COMPLETE_TO_PRESENT = 5, + END_TO_END = 6, + SIZE = 7, // must be last +}; + +// Let's create a full timeline here: +// eventTime +// readTime +// <---- after this point, the data becomes per-connection +// deliveryTime // time at which the event was sent to the receiver +// consumeTime // time at which the receiver read the event +// finishTime // time at which the dispatcher reads the response from the receiver that the event +// was processed +// GraphicsTimeline::GPU_COMPLETED_TIME +// GraphicsTimeline::PRESENT_TIME + +/** + * Keep histograms with latencies of the provided events + */ +class LatencyAggregatorWithHistograms final : public InputEventTimelineProcessor { +public: + /** + * Record a complete event timeline + */ + void processTimeline(const InputEventTimeline& timeline) override; + + void pushLatencyStatistics() override; + + std::string dump(const char* prefix) const override; + +private: + // ---------- Slow event handling ---------- + void processSlowEvent(const InputEventTimeline& timeline); + nsecs_t mLastSlowEventTime = 0; + // How many slow events have been skipped due to rate limiting + size_t mNumSkippedSlowEvents = 0; + // How many events have been received since the last time we reported a slow event + size_t mNumEventsSinceLastSlowEventReport = 0; + + // ---------- Statistics handling ---------- + /** + * Data structure to gather time samples into NUM_BINS buckets + */ + class Histogram { + public: + Histogram(const std::array<int, NUM_BINS - 1>& binSizes) : mBinSizes(binSizes) { + mBinCounts.fill(0); + } + + // Increments binCounts of the appropriate bin when adding a new sample + void addSample(int64_t sample) { + size_t binIndex = getSampleBinIndex(sample); + mBinCounts[binIndex]++; + } + + const std::array<int32_t, NUM_BINS>& getBinCounts() const { return mBinCounts; } + + private: + // reference to an array that represents the range of values each bin holds. + // in bin i+1 live samples such that *mBinSizes[i] <= sample < *mBinSizes[i+1] + const std::array<int, NUM_BINS - 1>& mBinSizes; + std::array<int32_t, NUM_BINS> + mBinCounts; // the number of samples that currently live in each bin + + size_t getSampleBinIndex(int64_t sample) { + auto it = std::upper_bound(mBinSizes.begin(), mBinSizes.end(), sample); + return std::distance(mBinSizes.begin(), it); + } + }; + + void processStatistics(const InputEventTimeline& timeline); + + // Identifier for the an input event. If two input events have the same identifiers we + // want to use the same histograms to count the latency samples + using InputEventLatencyIdentifier = + std::tuple<uint16_t /*vendorId*/, uint16_t /*productId*/, + const std::set<InputDeviceUsageSource> /*sources*/, + InputEventActionType /*inputEventActionType*/>; + + // Maps an input event identifier to an array of 7 histograms, one for each latency + // stage. It is cleared after an atom push + std::map<InputEventLatencyIdentifier, std::array<Histogram, 7>> mHistograms; + + void addSampleToHistogram(const InputEventLatencyIdentifier& identifier, + LatencyStageIndex latencyStageIndex, nsecs_t time); + + // Stores all possible arrays of bin sizes. The order in the vector does not matter, as long + // as binSizesMappings points to the right index + static constexpr std::array<std::array<int, NUM_BINS - 1>, 6> allBinSizes = { + {{10, 15, 20, 25, 30, 35, 40, 45, 50, 55, 60, 65, 70, 75, 80, 85, 90, 95, 100}, + {1, 2, 3, 4, 5, 6, 8, 10, 12, 14, 16, 18, 20, 22, 24, 26, 28, 30, 32}, + {15, 30, 45, 60, 75, 90, 105, 120, 135, 150, 165, 180, 195, 210, 225, 240, 255, 270, + 285}, + {40, 80, 120, 160, 200, 240, 280, 320, 360, 400, 440, 480, 520, 560, 600, 640, 680, + 720, 760}, + {20, 40, 60, 80, 100, 120, 140, 160, 180, 200, 220, 240, 260, 280, 300, 320, 340, 360, + 380}, + {200, 300, 400, 500, 600, 700, 800, 900, 1000, 1100, 1200, 1300, 1400, 1500, 1600, + 1700, 1800, 1900, 2000}}}; + + // Stores indexes in allBinSizes to use with each {LatencyStage, InputEventType} pair. + // Bin sizes for a certain latencyStage and inputEventType are at: + // *(allBinSizes[binSizesMappings[latencyStageIndex][inputEventTypeIndex]]) + // inputEventTypeIndex is the int value of InputEventActionType enum decreased by 1 since we + // don't want to record latencies for unknown events. + // e.g. MOTION_ACTION_DOWN is 0, MOTION_ACTION_MOVE is 1... + static constexpr std::array<std::array<int8_t, NUM_INPUT_EVENT_TYPES>, + static_cast<size_t>(LatencyStageIndex::SIZE)> + binSizesMappings = {{{0, 0, 0, 0, 0, 0}, + {1, 1, 1, 1, 1, 1}, + {1, 1, 1, 1, 1, 1}, + {2, 2, 2, 2, 2, 2}, + {3, 3, 3, 3, 3, 3}, + {4, 4, 4, 4, 4, 4}, + {5, 5, 5, 5, 5, 5}}}; + + // Similar to binSizesMappings, but holds the index of the array of bin ranges to use on the + // server. The index gets pushed with the atom within the histogram_version field. + static constexpr std::array<std::array<int8_t, NUM_INPUT_EVENT_TYPES>, + static_cast<size_t>(LatencyStageIndex::SIZE)> + histogramVersions = {{{0, 0, 0, 0, 0, 0}, + {1, 1, 1, 1, 1, 1}, + {1, 1, 1, 1, 1, 1}, + {2, 2, 2, 2, 2, 2}, + {3, 3, 3, 3, 3, 3}, + {4, 4, 4, 4, 4, 4}, + {5, 5, 5, 5, 5, 5}}}; +}; + +} // namespace android::inputdispatcher diff --git a/services/inputflinger/dispatcher/LatencyTracker.cpp b/services/inputflinger/dispatcher/LatencyTracker.cpp index 69024b326a..0852026823 100644 --- a/services/inputflinger/dispatcher/LatencyTracker.cpp +++ b/services/inputflinger/dispatcher/LatencyTracker.cpp @@ -62,9 +62,27 @@ static void eraseByValue(std::multimap<K, V>& map, const V& value) { } } -LatencyTracker::LatencyTracker(InputEventTimelineProcessor* processor) - : mTimelineProcessor(processor) { - LOG_ALWAYS_FATAL_IF(processor == nullptr); +LatencyTracker::LatencyTracker(InputEventTimelineProcessor& processor) + : mTimelineProcessor(&processor) {} + +void LatencyTracker::trackNotifyMotion(const NotifyMotionArgs& args) { + std::set<InputDeviceUsageSource> sources = getUsageSourcesForMotionArgs(args); + trackListener(args.id, args.eventTime, args.readTime, args.deviceId, sources, args.action, + InputEventType::MOTION); +} + +void LatencyTracker::trackNotifyKey(const NotifyKeyArgs& args) { + int32_t keyboardType = AINPUT_KEYBOARD_TYPE_NONE; + for (auto& inputDevice : mInputDevices) { + if (args.deviceId == inputDevice.getId()) { + keyboardType = inputDevice.getKeyboardType(); + break; + } + } + std::set<InputDeviceUsageSource> sources = + std::set{getUsageSourceForKeyArgs(keyboardType, args)}; + trackListener(args.id, args.eventTime, args.readTime, args.deviceId, sources, args.action, + InputEventType::KEY); } void LatencyTracker::trackListener(int32_t inputEventId, nsecs_t eventTime, nsecs_t readTime, diff --git a/services/inputflinger/dispatcher/LatencyTracker.h b/services/inputflinger/dispatcher/LatencyTracker.h index b4053ba1b3..eb58222ce6 100644 --- a/services/inputflinger/dispatcher/LatencyTracker.h +++ b/services/inputflinger/dispatcher/LatencyTracker.h @@ -42,7 +42,7 @@ public: * Create a LatencyTracker. * param reportingFunction: the function that will be called in order to report full latency. */ - LatencyTracker(InputEventTimelineProcessor* processor); + LatencyTracker(InputEventTimelineProcessor& processor); /** * Start keeping track of an event identified by inputEventId. This must be called first. * If duplicate events are encountered (events that have the same eventId), none of them will be @@ -59,6 +59,13 @@ public: nsecs_t deliveryTime, nsecs_t consumeTime, nsecs_t finishTime); void trackGraphicsLatency(int32_t inputEventId, const sp<IBinder>& connectionToken, std::array<nsecs_t, GraphicsTimeline::SIZE> timeline); + /** + * trackNotifyMotion and trackNotifyKeys are intermediates between InputDispatcher and + * trackListener. They compute the InputDeviceUsageSource set and call trackListener with + * the relevant parameters for latency computation. + */ + void trackNotifyMotion(const NotifyMotionArgs& args); + void trackNotifyKey(const NotifyKeyArgs& args); std::string dump(const char* prefix) const; void setInputDevices(const std::vector<InputDeviceInfo>& inputDevices); 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 5a70dd5e2c..451d91704c 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); 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/include/InputReaderBase.h b/services/inputflinger/include/InputReaderBase.h index 69874c90f3..2f6c6d74e7 100644 --- a/services/inputflinger/include/InputReaderBase.h +++ b/services/inputflinger/include/InputReaderBase.h @@ -93,6 +93,9 @@ struct InputReaderConfiguration { // The touchpad settings changed. TOUCHPAD_SETTINGS = 1u << 13, + // The key remapping has changed. + KEY_REMAPPING = 1u << 14, + // All devices must be reopened. MUST_REOPEN = 1u << 31, }; @@ -246,6 +249,9 @@ struct InputReaderConfiguration { // True if a pointer icon should be shown for direct stylus pointers. bool stylusPointerIconEnabled; + // Keycodes to be remapped. + std::map<int32_t /* fromKeyCode */, int32_t /* toKeyCode */> keyRemapping; + InputReaderConfiguration() : virtualKeyQuietTime(0), defaultPointerDisplayId(ui::LogicalDisplayId::DEFAULT), @@ -333,9 +339,6 @@ public: virtual int32_t getKeyCodeState(int32_t deviceId, uint32_t sourceMask, int32_t keyCode) = 0; virtual int32_t getSwitchState(int32_t deviceId, uint32_t sourceMask, int32_t sw) = 0; - virtual void addKeyRemapping(int32_t deviceId, int32_t fromKeyCode, - int32_t toKeyCode) const = 0; - virtual int32_t getKeyCodeForKeyLocation(int32_t deviceId, int32_t locationKeyCode) const = 0; /* Toggle Caps Lock */ diff --git a/services/inputflinger/reader/EventHub.cpp b/services/inputflinger/reader/EventHub.cpp index e11adb8c76..0865eed4a2 100644 --- a/services/inputflinger/reader/EventHub.cpp +++ b/services/inputflinger/reader/EventHub.cpp @@ -1177,7 +1177,8 @@ bool EventHub::markSupportedKeyCodes(int32_t deviceId, const std::vector<int32_t return false; } -void EventHub::addKeyRemapping(int32_t deviceId, int32_t fromKeyCode, int32_t toKeyCode) const { +void EventHub::setKeyRemapping(int32_t deviceId, + const std::map<int32_t, int32_t>& keyRemapping) const { std::scoped_lock _l(mLock); Device* device = getDeviceLocked(deviceId); if (device == nullptr) { @@ -1185,7 +1186,7 @@ void EventHub::addKeyRemapping(int32_t deviceId, int32_t fromKeyCode, int32_t to } const std::shared_ptr<KeyCharacterMap> kcm = device->getKeyCharacterMap(); if (kcm) { - kcm->addKeyRemapping(fromKeyCode, toKeyCode); + kcm->setKeyRemapping(keyRemapping); } } diff --git a/services/inputflinger/reader/InputDevice.cpp b/services/inputflinger/reader/InputDevice.cpp index 70f024ea96..6185f1ab9e 100644 --- a/services/inputflinger/reader/InputDevice.cpp +++ b/services/inputflinger/reader/InputDevice.cpp @@ -365,6 +365,18 @@ std::list<NotifyArgs> InputDevice::configureInternal(nsecs_t when, // so update the enabled state when there is a change in display info. out += updateEnableState(when, readerConfig, forceEnable); } + + if (!changes.any() || changes.test(InputReaderConfiguration::Change::KEY_REMAPPING)) { + const bool isFullKeyboard = + (mSources & AINPUT_SOURCE_KEYBOARD) == AINPUT_SOURCE_KEYBOARD && + mKeyboardType == KeyboardType::ALPHABETIC; + if (isFullKeyboard) { + for_each_subdevice([&readerConfig](auto& context) { + context.setKeyRemapping(readerConfig.keyRemapping); + }); + bumpGeneration(); + } + } } return out; } @@ -689,12 +701,6 @@ void InputDevice::updateMetaState(int32_t keyCode) { }); } -void InputDevice::addKeyRemapping(int32_t fromKeyCode, int32_t toKeyCode) { - for_each_subdevice([fromKeyCode, toKeyCode](auto& context) { - context.addKeyRemapping(fromKeyCode, toKeyCode); - }); -} - void InputDevice::bumpGeneration() { mGeneration = mContext->bumpGeneration(); } diff --git a/services/inputflinger/reader/InputReader.cpp b/services/inputflinger/reader/InputReader.cpp index a6f5df7593..8b664d5c4e 100644 --- a/services/inputflinger/reader/InputReader.cpp +++ b/services/inputflinger/reader/InputReader.cpp @@ -626,15 +626,6 @@ bool InputReader::markSupportedKeyCodesLocked(int32_t deviceId, uint32_t sourceM return result; } -void InputReader::addKeyRemapping(int32_t deviceId, int32_t fromKeyCode, int32_t toKeyCode) const { - std::scoped_lock _l(mLock); - - InputDevice* device = findInputDeviceLocked(deviceId); - if (device != nullptr) { - device->addKeyRemapping(fromKeyCode, toKeyCode); - } -} - int32_t InputReader::getKeyCodeForKeyLocation(int32_t deviceId, int32_t locationKeyCode) const { std::scoped_lock _l(mLock); diff --git a/services/inputflinger/reader/include/EventHub.h b/services/inputflinger/reader/include/EventHub.h index 657126a825..edc30379b2 100644 --- a/services/inputflinger/reader/include/EventHub.h +++ b/services/inputflinger/reader/include/EventHub.h @@ -281,8 +281,8 @@ public: virtual bool hasMscEvent(int32_t deviceId, int mscEvent) const = 0; - virtual void addKeyRemapping(int32_t deviceId, int32_t fromKeyCode, - int32_t toKeyCode) const = 0; + virtual void setKeyRemapping(int32_t deviceId, + const std::map<int32_t, int32_t>& keyRemapping) const = 0; virtual status_t mapKey(int32_t deviceId, int32_t scanCode, int32_t usageCode, int32_t metaState, int32_t* outKeycode, int32_t* outMetaState, @@ -513,8 +513,8 @@ public: bool hasMscEvent(int32_t deviceId, int mscEvent) const override final; - void addKeyRemapping(int32_t deviceId, int32_t fromKeyCode, - int32_t toKeyCode) const override final; + void setKeyRemapping(int32_t deviceId, + const std::map<int32_t, int32_t>& keyRemapping) const override final; status_t mapKey(int32_t deviceId, int32_t scanCode, int32_t usageCode, int32_t metaState, int32_t* outKeycode, int32_t* outMetaState, diff --git a/services/inputflinger/reader/include/InputDevice.h b/services/inputflinger/reader/include/InputDevice.h index 021978dee7..62cc4da5ec 100644 --- a/services/inputflinger/reader/include/InputDevice.h +++ b/services/inputflinger/reader/include/InputDevice.h @@ -124,8 +124,6 @@ public: int32_t getMetaState(); void updateMetaState(int32_t keyCode); - void addKeyRemapping(int32_t fromKeyCode, int32_t toKeyCode); - void setKeyboardType(KeyboardType keyboardType); void bumpGeneration(); @@ -329,8 +327,8 @@ public: inline bool hasMscEvent(int mscEvent) const { return mEventHub->hasMscEvent(mId, mscEvent); } - inline void addKeyRemapping(int32_t fromKeyCode, int32_t toKeyCode) const { - mEventHub->addKeyRemapping(mId, fromKeyCode, toKeyCode); + inline void setKeyRemapping(const std::map<int32_t, int32_t>& keyRemapping) const { + mEventHub->setKeyRemapping(mId, keyRemapping); } inline status_t mapKey(int32_t scanCode, int32_t usageCode, int32_t metaState, diff --git a/services/inputflinger/reader/include/InputReader.h b/services/inputflinger/reader/include/InputReader.h index 2cc0a00496..100387195a 100644 --- a/services/inputflinger/reader/include/InputReader.h +++ b/services/inputflinger/reader/include/InputReader.h @@ -65,8 +65,6 @@ public: int32_t getKeyCodeState(int32_t deviceId, uint32_t sourceMask, int32_t keyCode) override; int32_t getSwitchState(int32_t deviceId, uint32_t sourceMask, int32_t sw) override; - void addKeyRemapping(int32_t deviceId, int32_t fromKeyCode, int32_t toKeyCode) const override; - int32_t getKeyCodeForKeyLocation(int32_t deviceId, int32_t locationKeyCode) const override; void toggleCapsLockState(int32_t deviceId) override; diff --git a/services/inputflinger/tests/FakeEventHub.cpp b/services/inputflinger/tests/FakeEventHub.cpp index 31fbf209a3..943de6e3cf 100644 --- a/services/inputflinger/tests/FakeEventHub.cpp +++ b/services/inputflinger/tests/FakeEventHub.cpp @@ -151,9 +151,10 @@ void FakeEventHub::addKeyCodeMapping(int32_t deviceId, int32_t fromKeyCode, int3 getDevice(deviceId)->keyCodeMapping.insert_or_assign(fromKeyCode, toKeyCode); } -void FakeEventHub::addKeyRemapping(int32_t deviceId, int32_t fromKeyCode, int32_t toKeyCode) const { +void FakeEventHub::setKeyRemapping(int32_t deviceId, + const std::map<int32_t, int32_t>& keyRemapping) const { Device* device = getDevice(deviceId); - device->keyRemapping.insert_or_assign(fromKeyCode, toKeyCode); + device->keyRemapping = keyRemapping; } void FakeEventHub::addLed(int32_t deviceId, int32_t led, bool initialState) { diff --git a/services/inputflinger/tests/FakeEventHub.h b/services/inputflinger/tests/FakeEventHub.h index 3d8dddd532..2dfbb2388a 100644 --- a/services/inputflinger/tests/FakeEventHub.h +++ b/services/inputflinger/tests/FakeEventHub.h @@ -55,7 +55,7 @@ class FakeEventHub : public EventHubInterface { KeyedVector<int32_t, int32_t> absoluteAxisValue; KeyedVector<int32_t, KeyInfo> keysByScanCode; KeyedVector<int32_t, KeyInfo> keysByUsageCode; - std::unordered_map<int32_t, int32_t> keyRemapping; + std::map<int32_t, int32_t> keyRemapping; KeyedVector<int32_t, bool> leds; // fake mapping which would normally come from keyCharacterMap std::unordered_map<int32_t, int32_t> keyCodeMapping; @@ -129,7 +129,7 @@ public: void addKey(int32_t deviceId, int32_t scanCode, int32_t usageCode, int32_t keyCode, uint32_t flags); void addKeyCodeMapping(int32_t deviceId, int32_t fromKeyCode, int32_t toKeyCode); - void addKeyRemapping(int32_t deviceId, int32_t fromKeyCode, int32_t toKeyCode) const; + void setKeyRemapping(int32_t deviceId, const std::map<int32_t, int32_t>& keyRemapping) const; void addVirtualKeyDefinition(int32_t deviceId, const VirtualKeyDefinition& definition); void addSensorAxis(int32_t deviceId, int32_t absCode, InputDeviceSensorType sensorType, diff --git a/services/inputflinger/tests/InputDispatcher_test.cpp b/services/inputflinger/tests/InputDispatcher_test.cpp index f066b031f3..c5702e92d9 100644 --- a/services/inputflinger/tests/InputDispatcher_test.cpp +++ b/services/inputflinger/tests/InputDispatcher_test.cpp @@ -1913,6 +1913,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; /** diff --git a/services/inputflinger/tests/InputReader_test.cpp b/services/inputflinger/tests/InputReader_test.cpp index 4a9e89319d..17c37d5a41 100644 --- a/services/inputflinger/tests/InputReader_test.cpp +++ b/services/inputflinger/tests/InputReader_test.cpp @@ -3330,11 +3330,11 @@ TEST_F(KeyboardInputMapperTest, Process_SimpleKeyPress) { TEST_F(KeyboardInputMapperTest, Process_KeyRemapping) { mFakeEventHub->addKey(EVENTHUB_ID, KEY_A, 0, AKEYCODE_A, 0); mFakeEventHub->addKey(EVENTHUB_ID, KEY_B, 0, AKEYCODE_B, 0); - mFakeEventHub->addKeyRemapping(EVENTHUB_ID, AKEYCODE_A, AKEYCODE_B); KeyboardInputMapper& mapper = constructAndAddMapper<KeyboardInputMapper>(AINPUT_SOURCE_KEYBOARD); + mFakeEventHub->setKeyRemapping(EVENTHUB_ID, {{AKEYCODE_A, AKEYCODE_B}}); // Key down by scan code. process(mapper, ARBITRARY_TIME, READ_TIME, EV_KEY, KEY_A, 1); NotifyKeyArgs args; diff --git a/services/inputflinger/tests/InterfaceMocks.h b/services/inputflinger/tests/InterfaceMocks.h index 5a3d79da5e..f41b39ac8e 100644 --- a/services/inputflinger/tests/InterfaceMocks.h +++ b/services/inputflinger/tests/InterfaceMocks.h @@ -97,7 +97,8 @@ public: MOCK_METHOD(bool, hasRelativeAxis, (int32_t deviceId, int axis), (const)); MOCK_METHOD(bool, hasInputProperty, (int32_t deviceId, int property), (const)); MOCK_METHOD(bool, hasMscEvent, (int32_t deviceId, int mscEvent), (const)); - MOCK_METHOD(void, addKeyRemapping, (int32_t deviceId, int fromKeyCode, int toKeyCode), (const)); + MOCK_METHOD(void, setKeyRemapping, + (int32_t deviceId, (const std::map<int32_t, int32_t>& keyRemapping)), (const)); MOCK_METHOD(status_t, mapKey, (int32_t deviceId, int scanCode, int usageCode, int32_t metaState, int32_t* outKeycode, int32_t* outMetaState, uint32_t* outFlags), @@ -247,8 +248,6 @@ public: MOCK_METHOD(int32_t, getMetaState, (), ()); MOCK_METHOD(void, updateMetaState, (int32_t keyCode), ()); - MOCK_METHOD(void, addKeyRemapping, (int32_t fromKeyCode, int32_t toKeyCode), ()); - MOCK_METHOD(void, setKeyboardType, (KeyboardType keyboardType), ()); MOCK_METHOD(void, bumpGeneration, (), ()); diff --git a/services/inputflinger/tests/LatencyTracker_test.cpp b/services/inputflinger/tests/LatencyTracker_test.cpp index 0f92833e64..3f14c2312b 100644 --- a/services/inputflinger/tests/LatencyTracker_test.cpp +++ b/services/inputflinger/tests/LatencyTracker_test.cpp @@ -87,7 +87,7 @@ protected: connection1 = sp<BBinder>::make(); connection2 = sp<BBinder>::make(); - mTracker = std::make_unique<LatencyTracker>(this); + mTracker = std::make_unique<LatencyTracker>(*this); setDefaultInputDeviceInfo(*mTracker); } void TearDown() override {} @@ -106,6 +106,8 @@ private: void processTimeline(const InputEventTimeline& timeline) override { mReceivedTimelines.push_back(timeline); } + void pushLatencyStatistics() override {} + std::string dump(const char* prefix) const { return ""; }; std::deque<InputEventTimeline> mReceivedTimelines; }; diff --git a/services/inputflinger/tests/fuzzers/InputReaderFuzzer.cpp b/services/inputflinger/tests/fuzzers/InputReaderFuzzer.cpp index 3e4a19becd..5442a65f2f 100644 --- a/services/inputflinger/tests/fuzzers/InputReaderFuzzer.cpp +++ b/services/inputflinger/tests/fuzzers/InputReaderFuzzer.cpp @@ -155,10 +155,6 @@ public: return reader->getLightPlayerId(deviceId, lightId); } - void addKeyRemapping(int32_t deviceId, int32_t fromKeyCode, int32_t toKeyCode) const { - reader->addKeyRemapping(deviceId, fromKeyCode, toKeyCode); - } - int32_t getKeyCodeForKeyLocation(int32_t deviceId, int32_t locationKeyCode) const { return reader->getKeyCodeForKeyLocation(deviceId, locationKeyCode); } diff --git a/services/inputflinger/tests/fuzzers/LatencyTrackerFuzzer.cpp b/services/inputflinger/tests/fuzzers/LatencyTrackerFuzzer.cpp index 695eb3c5dd..908fa40f05 100644 --- a/services/inputflinger/tests/fuzzers/LatencyTrackerFuzzer.cpp +++ b/services/inputflinger/tests/fuzzers/LatencyTrackerFuzzer.cpp @@ -38,6 +38,10 @@ public: connectionTimeline.isComplete(); } }; + + void pushLatencyStatistics() override {} + + std::string dump(const char* prefix) const { return ""; }; }; static sp<IBinder> getConnectionToken(FuzzedDataProvider& fdp, @@ -53,7 +57,7 @@ extern "C" int LLVMFuzzerTestOneInput(uint8_t* data, size_t size) { FuzzedDataProvider fdp(data, size); EmptyProcessor emptyProcessor; - LatencyTracker tracker(&emptyProcessor); + LatencyTracker tracker(emptyProcessor); // Make some pre-defined tokens to ensure that some timelines are complete. std::array<sp<IBinder> /*token*/, 10> predefinedTokens; diff --git a/services/inputflinger/tests/fuzzers/MapperHelpers.h b/services/inputflinger/tests/fuzzers/MapperHelpers.h index 7e362f4e35..fa8270a3d9 100644 --- a/services/inputflinger/tests/fuzzers/MapperHelpers.h +++ b/services/inputflinger/tests/fuzzers/MapperHelpers.h @@ -202,7 +202,8 @@ public: int32_t getSwitchState(int32_t deviceId, int32_t sw) const override { return mFdp->ConsumeIntegral<int32_t>(); } - void addKeyRemapping(int32_t deviceId, int32_t fromKeyCode, int32_t toKeyCode) const override {} + void setKeyRemapping(int32_t deviceId, + const std::map<int32_t, int32_t>& keyRemapping) const override {} int32_t getKeyCodeForKeyLocation(int32_t deviceId, int32_t locationKeyCode) const override { return mFdp->ConsumeIntegral<int32_t>(); } diff --git a/services/surfaceflinger/FrontEnd/LayerSnapshotBuilder.cpp b/services/surfaceflinger/FrontEnd/LayerSnapshotBuilder.cpp index ac15b92175..ee605b7a3e 100644 --- a/services/surfaceflinger/FrontEnd/LayerSnapshotBuilder.cpp +++ b/services/surfaceflinger/FrontEnd/LayerSnapshotBuilder.cpp @@ -279,24 +279,6 @@ void updateVisibility(LayerSnapshot& snapshot, bool visible) { snapshot.getDebugString().c_str()); } -bool needsInputInfo(const LayerSnapshot& snapshot, const RequestedLayerState& requested) { - if (requested.potentialCursor) { - return false; - } - - if (snapshot.inputInfo.token != nullptr) { - return true; - } - - if (snapshot.hasBufferOrSidebandStream()) { - return true; - } - - return requested.windowInfoHandle && - requested.windowInfoHandle->getInfo()->inputConfig.test( - gui::WindowInfo::InputConfig::NO_INPUT_CHANNEL); -} - void updateMetadata(LayerSnapshot& snapshot, const RequestedLayerState& requested, const LayerSnapshotBuilder::Args& args) { snapshot.metadata.clear(); @@ -1162,7 +1144,7 @@ void LayerSnapshotBuilder::updateInput(LayerSnapshot& snapshot, } updateVisibility(snapshot, snapshot.isVisible); - if (!needsInputInfo(snapshot, requested)) { + if (!requested.needsInputInfo()) { return; } @@ -1172,7 +1154,7 @@ void LayerSnapshotBuilder::updateInput(LayerSnapshot& snapshot, bool noValidDisplay = !displayInfoOpt.has_value(); auto displayInfo = displayInfoOpt.value_or(sDefaultInfo); - if (!requested.windowInfoHandle) { + if (!requested.hasInputInfo()) { snapshot.inputInfo.inputConfig = InputConfig::NO_INPUT_CHANNEL; } fillInputFrameInfo(snapshot.inputInfo, displayInfo.transform, snapshot); diff --git a/services/surfaceflinger/FrontEnd/RequestedLayerState.cpp b/services/surfaceflinger/FrontEnd/RequestedLayerState.cpp index 17d2610d7a..5734ccf38f 100644 --- a/services/surfaceflinger/FrontEnd/RequestedLayerState.cpp +++ b/services/surfaceflinger/FrontEnd/RequestedLayerState.cpp @@ -62,6 +62,8 @@ RequestedLayerState::RequestedLayerState(const LayerCreationArgs& args) metadata.merge(args.metadata); changes |= RequestedLayerState::Changes::Metadata; handleAlive = true; + // TODO: b/305254099 remove once we don't pass invisible windows to input + windowInfoHandle = nullptr; if (parentId != UNASSIGNED_LAYER_ID) { canBeRoot = false; } @@ -553,6 +555,24 @@ bool RequestedLayerState::hasInputInfo() const { windowInfo->inputConfig.test(gui::WindowInfo::InputConfig::NO_INPUT_CHANNEL); } +bool RequestedLayerState::needsInputInfo() const { + if (potentialCursor) { + return false; + } + + if ((sidebandStream != nullptr) || (externalTexture != nullptr)) { + return true; + } + + if (!windowInfoHandle) { + return false; + } + + const auto windowInfo = windowInfoHandle->getInfo(); + return windowInfo->token != nullptr || + windowInfo->inputConfig.test(gui::WindowInfo::InputConfig::NO_INPUT_CHANNEL); +} + bool RequestedLayerState::hasBlur() const { return backgroundBlurRadius > 0 || blurRegions.size() > 0; } diff --git a/services/surfaceflinger/FrontEnd/RequestedLayerState.h b/services/surfaceflinger/FrontEnd/RequestedLayerState.h index 48b9640486..1d96dff336 100644 --- a/services/surfaceflinger/FrontEnd/RequestedLayerState.h +++ b/services/surfaceflinger/FrontEnd/RequestedLayerState.h @@ -87,6 +87,7 @@ struct RequestedLayerState : layer_state_t { aidl::android::hardware::graphics::composer3::Composition getCompositionType() const; bool hasValidRelativeParent() const; bool hasInputInfo() const; + bool needsInputInfo() const; bool hasBlur() const; bool hasFrameUpdate() const; bool hasReadyFrame() const; diff --git a/services/surfaceflinger/Layer.cpp b/services/surfaceflinger/Layer.cpp index c17ea3b579..dcb0812b67 100644 --- a/services/surfaceflinger/Layer.cpp +++ b/services/surfaceflinger/Layer.cpp @@ -355,26 +355,6 @@ aidl::android::hardware::graphics::composer3::Composition Layer::getCompositionT // transaction // ---------------------------------------------------------------------------- -uint32_t Layer::doTransaction(uint32_t flags) { - SFTRACE_CALL(); - - const State& s(getDrawingState()); - - if (s.sequence != mLastCommittedTxSequence) { - // invalidate and recompute the visible regions if needed - mLastCommittedTxSequence = s.sequence; - flags |= eVisibleRegion; - } - - if (!mPotentialCursor && (flags & Layer::eVisibleRegion)) { - mFlinger->mUpdateInputInfo = true; - } - - commitTransaction(); - - return flags; -} - void Layer::commitTransaction() { // Set the present state for all bufferlessSurfaceFramesTX to Presented. The // bufferSurfaceFrameTX will be presented in latchBuffer. @@ -389,12 +369,6 @@ void Layer::commitTransaction() { mDrawingState.bufferlessSurfaceFramesTX.clear(); } -uint32_t Layer::clearTransactionFlags(uint32_t mask) { - const auto flags = mTransactionFlags & mask; - mTransactionFlags &= ~mask; - return flags; -} - void Layer::setTransactionFlags(uint32_t mask) { mTransactionFlags |= mask; } @@ -769,10 +743,6 @@ void Layer::prepareReleaseCallbacks(ftl::Future<FenceResult> futureFenceResult, // Older fences for the same layer stack can be dropped when a new fence arrives. // An assumption here is that RenderEngine performs work sequentially, so an // incoming fence will not fire before an existing fence. - SFTRACE_NAME( - ftl::Concat("Adding additional fence for: ", ftl::truncated<20>(mName.c_str()), - ", Replacing?: ", mAdditionalPreviousReleaseFences.contains(layerStack)) - .c_str()); mAdditionalPreviousReleaseFences.emplace_or_replace(layerStack, std::move(futureFenceResult)); } @@ -871,16 +841,6 @@ bool Layer::setTransformToDisplayInverse(bool transformToDisplayInverse) { return true; } -bool Layer::setBufferCrop(const Rect& bufferCrop) { - if (mDrawingState.bufferCrop == bufferCrop) return false; - - mDrawingState.sequence++; - mDrawingState.bufferCrop = bufferCrop; - - setTransactionFlags(eTransactionNeeded); - return true; -} - void Layer::releasePreviousBuffer() { mReleasePreviousBuffer = true; if (!mBufferInfo.mBuffer || @@ -1495,10 +1455,6 @@ void Layer::onCompositionPresented(const DisplayDevice* display, mBufferInfo.mFrameLatencyNeeded = false; } -bool Layer::willReleaseBufferOnLatch() const { - return !mDrawingState.buffer && mBufferInfo.mBuffer; -} - bool Layer::latchBufferImpl(bool& recomputeVisibleRegions, nsecs_t latchTime, bool bgColorOnly) { SFTRACE_FORMAT_INSTANT("latchBuffer %s - %" PRIu64, getDebugName(), getDrawingState().frameNumber); @@ -1559,34 +1515,10 @@ bool Layer::latchBufferImpl(bool& recomputeVisibleRegions, nsecs_t latchTime, bo return true; } -bool Layer::isProtected() const { - return (mBufferInfo.mBuffer != nullptr) && - (mBufferInfo.mBuffer->getUsage() & GRALLOC_USAGE_PROTECTED); -} - bool Layer::getTransformToDisplayInverse() const { return mBufferInfo.mTransformToDisplayInverse; } -Rect Layer::getBufferCrop() const { - // this is the crop rectangle that applies to the buffer - // itself (as opposed to the window) - if (!mBufferInfo.mCrop.isEmpty()) { - // if the buffer crop is defined, we use that - return mBufferInfo.mCrop; - } else if (mBufferInfo.mBuffer != nullptr) { - // otherwise we use the whole buffer - return mBufferInfo.mBuffer->getBounds(); - } else { - // if we don't have a buffer yet, we use an empty/invalid crop - return Rect(); - } -} - -uint32_t Layer::getBufferTransform() const { - return mBufferInfo.mTransform; -} - ui::Dataspace Layer::translateDataspace(ui::Dataspace dataspace) { ui::Dataspace updatedDataspace = dataspace; // translate legacy dataspaces to modern dataspaces diff --git a/services/surfaceflinger/Layer.h b/services/surfaceflinger/Layer.h index 9caa20cfb8..9bc557e917 100644 --- a/services/surfaceflinger/Layer.h +++ b/services/surfaceflinger/Layer.h @@ -88,12 +88,6 @@ public: // Windows that are not in focus, but voted for a specific mode ID. static constexpr int32_t PRIORITY_NOT_FOCUSED_WITH_MODE = 2; - enum { // flags for doTransaction() - eDontUpdateGeometryState = 0x00000001, - eVisibleRegion = 0x00000002, - eInputInfoChanged = 0x00000004 - }; - using FrameRate = scheduler::LayerInfo::FrameRate; using FrameRateCompatibility = scheduler::FrameRateCompatibility; using FrameRateSelectionStrategy = scheduler::LayerInfo::FrameRateSelectionStrategy; @@ -171,9 +165,6 @@ public: static bool isLayerFocusedBasedOnPriority(int32_t priority); static void miniDumpHeader(std::string& result); - // Provide unique string for each class type in the Layer hierarchy - const char* getType() const { return "Layer"; } - // This second set of geometry attributes are controlled by // setGeometryAppliesWithResize, and their default mode is to be // immediate. If setGeometryAppliesWithResize is specified @@ -200,7 +191,6 @@ public: bool willPresent); sp<LayerFE> getCompositionEngineLayerFE(const frontend::LayerHierarchy::TraversalPath&); - sp<LayerFE> getOrCreateCompositionEngineLayerFE(const frontend::LayerHierarchy::TraversalPath&); // If we have received a new buffer this frame, we will pass its surface // damage down to hardware composer. Otherwise, we must send a region with @@ -208,18 +198,7 @@ public: Region getVisibleRegion(const DisplayDevice*) const; void updateLastLatchTime(nsecs_t latchtime); - /* - * isProtected - true if the layer may contain protected contents in the - * GRALLOC_USAGE_PROTECTED sense. - */ - bool isProtected() const; - /* - * usesSourceCrop - true if content should use a source crop - */ - bool usesSourceCrop() const { return hasBufferOrSidebandStream(); } - Rect getCrop(const Layer::State& s) const { return s.crop; } - bool needsFiltering(const DisplayDevice*) const; // from graphics API static ui::Dataspace translateDataspace(ui::Dataspace dataspace); @@ -242,23 +221,6 @@ public: bool latchBufferImpl(bool& /*recomputeVisibleRegions*/, nsecs_t /*latchTime*/, bool bgColorOnly); - /* - * Returns true if the currently presented buffer will be released when this layer state - * is latched. This will return false if there is no buffer currently presented. - */ - bool willReleaseBufferOnLatch() const; - - /* - * returns the rectangle that crops the content of the layer and scales it - * to the layer's size. - */ - Rect getBufferCrop() const; - - /* - * Returns the transform applied to the buffer. - */ - uint32_t getBufferTransform() const; - sp<GraphicBuffer> getBuffer() const; /** * Returns active buffer size in the correct orientation. Buffer size is determined by undoing @@ -313,8 +275,6 @@ public: const char* getDebugName() const; - uint32_t getTransactionFlags() const { return mTransactionFlags; } - static bool computeTrustedPresentationState(const FloatRect& bounds, const FloatRect& sourceBounds, const Region& coveredRegion, @@ -332,9 +292,6 @@ public: // Sets the masked bits. void setTransactionFlags(uint32_t mask); - // Clears and returns the masked bits. - uint32_t clearTransactionFlags(uint32_t mask); - int32_t getSequence() const { return sequence; } // For tracing. @@ -348,14 +305,6 @@ public: void writeCompositionStateToProto(perfetto::protos::LayerProto* layerProto, ui::LayerStack layerStack); - gui::WindowInfo::Type getWindowType() const { return mWindowType; } - - /* - * doTransaction - process the transaction. This is a good place to figure - * out which attributes of the surface have changed. - */ - uint32_t doTransaction(uint32_t transactionFlags); - inline const State& getDrawingState() const { return mDrawingState; } inline State& getDrawingState() { return mDrawingState; } @@ -366,13 +315,6 @@ public: void getFrameStats(FrameStats* outStats) const; void onDisconnect(); - ui::Transform getTransform() const; - - half4 getColor() const; - int32_t getBackgroundBlurRadius() const; - bool drawShadows() const { return mEffectiveShadowRadius > 0.f; }; - - bool isHandleAlive() const { return mHandleAlive; } bool onHandleDestroyed() { return mHandleAlive = false; } /** @@ -383,7 +325,6 @@ public: */ Rect getCroppedBufferSize(const Layer::State& s) const; - void setFrameTimelineInfoForBuffer(const FrameTimelineInfo& /*info*/) {} void setFrameTimelineVsyncForBufferTransaction(const FrameTimelineInfo& info, nsecs_t postTime, gui::GameMode gameMode); void setFrameTimelineVsyncForBufferlessTransaction(const FrameTimelineInfo& info, @@ -412,14 +353,9 @@ public: // this to be called once. sp<IBinder> getHandle(); const std::string& getName() const { return mName; } - void setInputInfo(const gui::WindowInfo& info); virtual uid_t getOwnerUid() const { return mOwnerUid; } - pid_t getOwnerPid() { return mOwnerPid; } - - int32_t getOwnerAppId() { return mOwnerAppId; } - // Used to check if mUsedVsyncIdForRefreshRateSelection should be expired when it stop updating. nsecs_t mMaxTimeForUseVsyncId = 0; // True when DrawState.useVsyncIdForRefreshRateSelection previously set to true during updating @@ -431,9 +367,6 @@ public: // the same. const int32_t sequence; - bool mPendingHWCDestroy{false}; - - bool setBufferCrop(const Rect& /* bufferCrop */); // See mPendingBufferTransactions void decrementPendingBufferCount(); std::atomic<int32_t>* getPendingBufferCounter() { return &mPendingBufferTransactions; } @@ -513,10 +446,6 @@ protected: int64_t mEnteredTrustedPresentationStateTime = -1; uint32_t mTransactionFlags{0}; - // Updated in doTransaction, used to track the last sequence number we - // committed. Currently this is really only used for updating visible - // regions. - int32_t mLastCommittedTxSequence = -1; // Timestamp history for UIAutomation. Thread safe. FrameTracker mFrameTracker; @@ -632,7 +561,7 @@ private: // You can understand the trace this way: // - If the integer increases, a buffer arrived at the server. // - If the integer decreases in latchBuffer, that buffer was latched - // - If the integer decreases in setBuffer or doTransaction, a buffer was dropped + // - If the integer decreases in setBuffer, a buffer was dropped std::atomic<int32_t> mPendingBufferTransactions{0}; // Contains requested position and matrix updates. This will be applied if the client does diff --git a/services/surfaceflinger/OWNERS b/services/surfaceflinger/OWNERS index ffc1dd7979..fa0ecee6b5 100644 --- a/services/surfaceflinger/OWNERS +++ b/services/surfaceflinger/OWNERS @@ -5,6 +5,8 @@ alecmouri@google.com 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 @@ rnlee@google.com 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 0000000000..9f40e27a10 --- /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/SurfaceFlinger.cpp b/services/surfaceflinger/SurfaceFlinger.cpp index 65a0ed3065..06560d06ba 100644 --- a/services/surfaceflinger/SurfaceFlinger.cpp +++ b/services/surfaceflinger/SurfaceFlinger.cpp @@ -946,16 +946,20 @@ void SurfaceFlinger::init() FTL_FAKE_GUARD(kMainThreadContext) { })); })); - mLayerTracing.setTakeLayersSnapshotProtoFunction([&](uint32_t traceFlags) { - auto snapshot = perfetto::protos::LayersSnapshotProto{}; - mScheduler - ->schedule([&]() FTL_FAKE_GUARD(mStateLock) FTL_FAKE_GUARD(kMainThreadContext) { - snapshot = takeLayersSnapshotProto(traceFlags, TimePoint::now(), - mLastCommittedVsyncId, true); - }) - .wait(); - return snapshot; - }); + mLayerTracing.setTakeLayersSnapshotProtoFunction( + [&](uint32_t traceFlags, + const LayerTracing::OnLayersSnapshotCallback& onLayersSnapshot) { + // Do not wait the future to avoid deadlocks + // between main and Perfetto threads (b/313130597) + static_cast<void>(mScheduler->schedule( + [&, onLayersSnapshot]() FTL_FAKE_GUARD(mStateLock) + FTL_FAKE_GUARD(kMainThreadContext) { + auto snapshot = + takeLayersSnapshotProto(traceFlags, TimePoint::now(), + mLastCommittedVsyncId, true); + onLayersSnapshot(std::move(snapshot)); + })); + }); // Commit secondary display(s). processDisplayChangesLocked(); diff --git a/services/surfaceflinger/Tracing/LayerDataSource.cpp b/services/surfaceflinger/Tracing/LayerDataSource.cpp index ed1e2ec89c..cc0063cd2a 100644 --- a/services/surfaceflinger/Tracing/LayerDataSource.cpp +++ b/services/surfaceflinger/Tracing/LayerDataSource.cpp @@ -82,10 +82,13 @@ void LayerDataSource::OnFlush(const LayerDataSource::FlushArgs& args) { } } -void LayerDataSource::OnStop(const LayerDataSource::StopArgs&) { +void LayerDataSource::OnStop(const LayerDataSource::StopArgs& args) { ALOGD("Received OnStop event (mode = 0x%02x, flags = 0x%02x)", mMode, mFlags); if (auto* p = mLayerTracing.load()) { - p->onStop(mMode); + // In dump mode we need to defer the stop (through HandleStopAsynchronously()) till + // the layers snapshot has been captured and written to perfetto. We must avoid writing + // to perfetto within the OnStop callback to prevent deadlocks (b/313130597). + p->onStop(mMode, mFlags, args.HandleStopAsynchronously()); } } diff --git a/services/surfaceflinger/Tracing/LayerTracing.cpp b/services/surfaceflinger/Tracing/LayerTracing.cpp index d40b888504..d78f9bbbae 100644 --- a/services/surfaceflinger/Tracing/LayerTracing.cpp +++ b/services/surfaceflinger/Tracing/LayerTracing.cpp @@ -32,7 +32,7 @@ namespace android { LayerTracing::LayerTracing() { - mTakeLayersSnapshotProto = [](uint32_t) { return perfetto::protos::LayersSnapshotProto{}; }; + mTakeLayersSnapshotProto = [](uint32_t, const OnLayersSnapshotCallback&) {}; LayerDataSource::Initialize(*this); } @@ -45,7 +45,7 @@ LayerTracing::~LayerTracing() { } void LayerTracing::setTakeLayersSnapshotProtoFunction( - const std::function<perfetto::protos::LayersSnapshotProto(uint32_t)>& callback) { + const std::function<void(uint32_t, const OnLayersSnapshotCallback&)>& callback) { mTakeLayersSnapshotProto = callback; } @@ -62,7 +62,10 @@ void LayerTracing::onStart(Mode mode, uint32_t flags) { // It might take a while before a layers change occurs and a "spontaneous" snapshot is // taken. Let's manually take a snapshot, so that the trace's first entry will contain // the current layers state. - addProtoSnapshotToOstream(mTakeLayersSnapshotProto(flags), Mode::MODE_ACTIVE); + auto onLayersSnapshot = [this](perfetto::protos::LayersSnapshotProto&& snapshot) { + addProtoSnapshotToOstream(std::move(snapshot), Mode::MODE_ACTIVE); + }; + mTakeLayersSnapshotProto(flags, onLayersSnapshot); ALOGD("Started active tracing (traced initial snapshot)"); break; } @@ -89,9 +92,7 @@ void LayerTracing::onStart(Mode mode, uint32_t flags) { break; } case Mode::MODE_DUMP: { - auto snapshot = mTakeLayersSnapshotProto(flags); - addProtoSnapshotToOstream(std::move(snapshot), Mode::MODE_DUMP); - ALOGD("Started dump tracing (dumped single snapshot)"); + ALOGD("Started dump tracing"); break; } default: { @@ -125,10 +126,27 @@ void LayerTracing::onFlush(Mode mode, uint32_t flags, bool isBugreport) { ALOGD("Flushed generated tracing"); } -void LayerTracing::onStop(Mode mode) { - if (mode == Mode::MODE_ACTIVE) { - mIsActiveTracingStarted.store(false); - ALOGD("Stopped active tracing"); +void LayerTracing::onStop(Mode mode, uint32_t flags, std::function<void()>&& deferredStopDone) { + switch (mode) { + case Mode::MODE_ACTIVE: { + mIsActiveTracingStarted.store(false); + deferredStopDone(); + ALOGD("Stopped active tracing"); + break; + } + case Mode::MODE_DUMP: { + auto onLayersSnapshot = [this, deferredStopDone = std::move(deferredStopDone)]( + perfetto::protos::LayersSnapshotProto&& snapshot) { + addProtoSnapshotToOstream(std::move(snapshot), Mode::MODE_DUMP); + deferredStopDone(); + ALOGD("Stopped dump tracing (written single snapshot)"); + }; + mTakeLayersSnapshotProto(flags, onLayersSnapshot); + break; + } + default: { + deferredStopDone(); + } } } diff --git a/services/surfaceflinger/Tracing/LayerTracing.h b/services/surfaceflinger/Tracing/LayerTracing.h index 2895ba7026..e99fe4c7f1 100644 --- a/services/surfaceflinger/Tracing/LayerTracing.h +++ b/services/surfaceflinger/Tracing/LayerTracing.h @@ -87,6 +87,7 @@ EOF class LayerTracing { public: using Mode = perfetto::protos::pbzero::SurfaceFlingerLayersConfig::Mode; + using OnLayersSnapshotCallback = std::function<void(perfetto::protos::LayersSnapshotProto&&)>; enum Flag : uint32_t { TRACE_INPUT = 1 << 1, @@ -102,7 +103,7 @@ public: LayerTracing(std::ostream&); ~LayerTracing(); void setTakeLayersSnapshotProtoFunction( - const std::function<perfetto::protos::LayersSnapshotProto(uint32_t)>&); + const std::function<void(uint32_t, const OnLayersSnapshotCallback&)>&); void setTransactionTracing(TransactionTracing&); // Start event from perfetto data source @@ -110,7 +111,7 @@ public: // Flush event from perfetto data source void onFlush(Mode mode, uint32_t flags, bool isBugreport); // Stop event from perfetto data source - void onStop(Mode mode); + void onStop(Mode mode, uint32_t flags, std::function<void()>&& deferredStopDone); void addProtoSnapshotToOstream(perfetto::protos::LayersSnapshotProto&& snapshot, Mode mode); bool isActiveTracingStarted() const; @@ -123,7 +124,7 @@ private: void writeSnapshotToPerfetto(const perfetto::protos::LayersSnapshotProto& snapshot, Mode mode); bool checkAndUpdateLastVsyncIdWrittenToPerfetto(Mode mode, std::int64_t vsyncId); - std::function<perfetto::protos::LayersSnapshotProto(uint32_t)> mTakeLayersSnapshotProto; + std::function<void(uint32_t, const OnLayersSnapshotCallback&)> mTakeLayersSnapshotProto; TransactionTracing* mTransactionTracing; std::atomic<bool> mIsActiveTracingStarted{false}; diff --git a/services/surfaceflinger/TransactionCallbackInvoker.cpp b/services/surfaceflinger/TransactionCallbackInvoker.cpp index 2b20648e42..c6856aea75 100644 --- a/services/surfaceflinger/TransactionCallbackInvoker.cpp +++ b/services/surfaceflinger/TransactionCallbackInvoker.cpp @@ -30,7 +30,6 @@ #include <binder/IInterface.h> #include <common/FlagManager.h> #include <common/trace.h> -#include <ftl/concat.h> #include <utils/RefBase.h> namespace android { @@ -130,9 +129,6 @@ status_t TransactionCallbackInvoker::addCallbackHandle(const sp<CallbackHandle>& if (FlagManager::getInstance().ce_fence_promise()) { for (auto& future : handle->previousReleaseFences) { - SFTRACE_NAME(ftl::Concat("Merging fence for layer: ", - ftl::truncated<20>(handle->name.c_str())) - .c_str()); mergeFence(handle->name.c_str(), future.get().value_or(Fence::NO_FENCE), prevFence); } } else { diff --git a/services/surfaceflinger/tests/unittests/LayerLifecycleManagerTest.cpp b/services/surfaceflinger/tests/unittests/LayerLifecycleManagerTest.cpp index b4efe0fe14..c7cc21ce07 100644 --- a/services/surfaceflinger/tests/unittests/LayerLifecycleManagerTest.cpp +++ b/services/surfaceflinger/tests/unittests/LayerLifecycleManagerTest.cpp @@ -619,4 +619,14 @@ TEST_F(LayerLifecycleManagerTest, isSimpleBufferUpdate) { } } +TEST_F(LayerLifecycleManagerTest, testInputInfoOfRequestedLayerState) { + // By default the layer has no buffer, so it doesn't need an input info + EXPECT_FALSE(getRequestedLayerState(mLifecycleManager, 111)->needsInputInfo()); + + setBuffer(111); + mLifecycleManager.commitChanges(); + + EXPECT_TRUE(getRequestedLayerState(mLifecycleManager, 111)->needsInputInfo()); +} + } // namespace android::surfaceflinger::frontend diff --git a/services/surfaceflinger/tests/unittests/LayerSnapshotTest.cpp b/services/surfaceflinger/tests/unittests/LayerSnapshotTest.cpp index 90207232b0..75d2fa3c7f 100644 --- a/services/surfaceflinger/tests/unittests/LayerSnapshotTest.cpp +++ b/services/surfaceflinger/tests/unittests/LayerSnapshotTest.cpp @@ -1762,6 +1762,7 @@ TEST_F(LayerSnapshotTest, hideLayerWithNanMatrix) { UPDATE_AND_VERIFY(mSnapshotBuilder, {2}); EXPECT_TRUE(getSnapshot(1)->isHiddenByPolicy()); } + TEST_F(LayerSnapshotTest, edgeExtensionPropagatesInHierarchy) { if (!com::android::graphics::libgui::flags::edge_extension_shader()) { GTEST_SKIP() << "Skipping test because edge_extension_shader is off"; @@ -1920,4 +1921,18 @@ TEST_F(LayerSnapshotTest, multipleEdgeExtensionIncreaseBoundSizeWithinCrop) { EXPECT_GE(getSnapshot({.id = 1221})->transformedBounds.top, 0); } +TEST_F(LayerSnapshotTest, shouldUpdateInputWhenNoInputInfo) { + // By default the layer has no buffer, so we don't expect it to have an input info + EXPECT_FALSE(getSnapshot(111)->hasInputInfo()); + + setBuffer(111); + + UPDATE_AND_VERIFY(mSnapshotBuilder, STARTING_ZORDER); + + EXPECT_TRUE(getSnapshot(111)->hasInputInfo()); + EXPECT_TRUE(getSnapshot(111)->inputInfo.inputConfig.test( + gui::WindowInfo::InputConfig::NO_INPUT_CHANNEL)); + EXPECT_FALSE(getSnapshot(2)->hasInputInfo()); +} + } // namespace android::surfaceflinger::frontend diff --git a/services/surfaceflinger/tests/unittests/TestableScheduler.h b/services/surfaceflinger/tests/unittests/TestableScheduler.h index d92f891148..9de3346fb0 100644 --- a/services/surfaceflinger/tests/unittests/TestableScheduler.h +++ b/services/surfaceflinger/tests/unittests/TestableScheduler.h @@ -131,7 +131,9 @@ public: using Scheduler::resyncAllToHardwareVsync; auto& mutableLayerHistory() { return mLayerHistory; } - auto& mutableAttachedChoreographers() { return mAttachedChoreographers; } + auto& mutableAttachedChoreographers() NO_THREAD_SAFETY_ANALYSIS { + return mAttachedChoreographers; + } size_t layerHistorySize() NO_THREAD_SAFETY_ANALYSIS { return mLayerHistory.mActiveLayerInfos.size() + mLayerHistory.mInactiveLayerInfos.size(); diff --git a/services/surfaceflinger/tests/unittests/TestableSurfaceFlinger.h b/services/surfaceflinger/tests/unittests/TestableSurfaceFlinger.h index 347a396e74..c043b880ec 100644 --- a/services/surfaceflinger/tests/unittests/TestableSurfaceFlinger.h +++ b/services/surfaceflinger/tests/unittests/TestableSurfaceFlinger.h @@ -490,8 +490,10 @@ public: 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; } @@ -659,8 +661,10 @@ public: * post-conditions. */ - const auto& displays() const { return mFlinger->mDisplays; } - const auto& physicalDisplays() const { return mFlinger->mPhysicalDisplays; } + const auto& displays() const NO_THREAD_SAFETY_ANALYSIS { return mFlinger->mDisplays; } + const auto& physicalDisplays() const NO_THREAD_SAFETY_ANALYSIS { + return mFlinger->mPhysicalDisplays; + } const auto& currentState() const { return mFlinger->mCurrentState; } const auto& drawingState() const { return mFlinger->mDrawingState; } const auto& transactionFlags() const { return mFlinger->mTransactionFlags; } @@ -673,13 +677,17 @@ public: auto& mutableDisplayModeController() { return mFlinger->mDisplayModeController; } auto& mutableCurrentState() { return mFlinger->mCurrentState; } auto& mutableDisplayColorSetting() { return mFlinger->mDisplayColorSetting; } - auto& mutableDisplays() { return mFlinger->mDisplays; } - auto& mutablePhysicalDisplays() { return mFlinger->mPhysicalDisplays; } + auto& mutableDisplays() NO_THREAD_SAFETY_ANALYSIS { return mFlinger->mDisplays; } + auto& mutablePhysicalDisplays() NO_THREAD_SAFETY_ANALYSIS { + return mFlinger->mPhysicalDisplays; + } auto& mutableDrawingState() { return mFlinger->mDrawingState; } auto& mutableGeometryDirty() { return mFlinger->mGeometryDirty; } auto& mutableVisibleRegionsDirty() { return mFlinger->mVisibleRegionsDirty; } auto& mutableMainThreadId() { return mFlinger->mMainThreadId; } - auto& mutablePendingHotplugEvents() { return mFlinger->mPendingHotplugEvents; } + auto& mutablePendingHotplugEvents() NO_THREAD_SAFETY_ANALYSIS { + return mFlinger->mPendingHotplugEvents; + } auto& mutableTransactionFlags() { return mFlinger->mTransactionFlags; } auto& mutableDebugDisableHWC() { return mFlinger->mDebugDisableHWC; } auto& mutableMaxRenderTargetSize() { return mFlinger->mMaxRenderTargetSize; } @@ -687,7 +695,7 @@ public: auto& mutableHwcDisplayData() { return getHwComposer().mDisplayData; } auto& mutableHwcPhysicalDisplayIdMap() { return getHwComposer().mPhysicalDisplayIdMap; } auto& mutablePrimaryHwcDisplayId() { return getHwComposer().mPrimaryHwcDisplayId; } - auto& mutableActiveDisplayId() { return mFlinger->mActiveDisplayId; } + auto& mutableActiveDisplayId() NO_THREAD_SAFETY_ANALYSIS { return mFlinger->mActiveDisplayId; } auto& mutablePreviouslyComposedLayers() { return mFlinger->mPreviouslyComposedLayers; } auto& mutableActiveDisplayRotationFlags() { @@ -695,7 +703,9 @@ public: } auto& mutableMinAcquiredBuffers() { return SurfaceFlinger::minAcquiredBuffers; } - auto& mutableLayerSnapshotBuilder() { return mFlinger->mLayerSnapshotBuilder; }; + auto& mutableLayerSnapshotBuilder() NO_THREAD_SAFETY_ANALYSIS { + return mFlinger->mLayerSnapshotBuilder; + } auto fromHandle(const sp<IBinder>& handle) { return LayerHandle::getLayer(handle); } diff --git a/services/vibratorservice/TEST_MAPPING b/services/vibratorservice/TEST_MAPPING index af486736d8..1eb3a581b7 100644 --- a/services/vibratorservice/TEST_MAPPING +++ b/services/vibratorservice/TEST_MAPPING @@ -4,11 +4,6 @@ "name": "libvibratorservice_test" } ], - "postsubmit": [ - { - "name": "libvibratorservice_test" - } - ], "imports": [ { "path": "cts/tests/vibrator" |