Merge "Fix fs-verity API for secondary users" into main
diff --git a/cmds/idlcli/vibrator.h b/cmds/idlcli/vibrator.h
index dfbb886..e100eac 100644
--- a/cmds/idlcli/vibrator.h
+++ b/cmds/idlcli/vibrator.h
@@ -74,7 +74,7 @@
}
template <typename I>
-using shared_ptr = std::result_of_t<decltype(getService<I>)&(std::string)>;
+using shared_ptr = std::invoke_result_t<decltype(getService<I>)&, std::string>;
template <typename I>
class HalWrapper {
diff --git a/cmds/lshal/Timeout.h b/cmds/lshal/Timeout.h
index 805e8dc..d97ba89 100644
--- a/cmds/lshal/Timeout.h
+++ b/cmds/lshal/Timeout.h
@@ -16,83 +16,44 @@
#pragma once
-#include <condition_variable>
#include <chrono>
-#include <functional>
-#include <mutex>
-#include <thread>
+#include <future>
#include <hidl/Status.h>
+#include <utils/Errors.h>
namespace android {
namespace lshal {
-class BackgroundTaskState {
-public:
- explicit BackgroundTaskState(std::function<void(void)> &&func)
- : mFunc(std::forward<decltype(func)>(func)) {}
- void notify() {
- std::unique_lock<std::mutex> lock(mMutex);
- mFinished = true;
- lock.unlock();
- mCondVar.notify_all();
- }
- template<class C, class D>
- bool wait(std::chrono::time_point<C, D> end) {
- std::unique_lock<std::mutex> lock(mMutex);
- mCondVar.wait_until(lock, end, [this](){ return this->mFinished; });
- return mFinished;
- }
- void operator()() {
- mFunc();
- }
-private:
- std::mutex mMutex;
- std::condition_variable mCondVar;
- bool mFinished = false;
- std::function<void(void)> mFunc;
-};
-
-void *callAndNotify(void *data) {
- BackgroundTaskState &state = *static_cast<BackgroundTaskState *>(data);
- state();
- state.notify();
- return nullptr;
-}
-
-template<class R, class P>
-bool timeout(std::chrono::duration<R, P> delay, std::function<void(void)> &&func) {
- auto now = std::chrono::system_clock::now();
- BackgroundTaskState state{std::forward<decltype(func)>(func)};
- pthread_t thread;
- if (pthread_create(&thread, nullptr, callAndNotify, &state)) {
- std::cerr << "FATAL: could not create background thread." << std::endl;
- return false;
- }
- bool success = state.wait(now + delay);
- if (!success) {
- pthread_kill(thread, SIGINT);
- }
- pthread_join(thread, nullptr);
- return success;
-}
-
+// Call function on interfaceObject and wait for result until the given timeout has reached.
+// Callback functions pass to timeoutIPC() may be executed after the this function
+// has returned, especially if deadline has been reached. Hence, care must be taken when passing
+// data between the background thread and the main thread. See b/311143089.
template<class R, class P, class Function, class I, class... Args>
-typename std::result_of<Function(I *, Args...)>::type
+typename std::invoke_result<Function, I *, Args...>::type
timeoutIPC(std::chrono::duration<R, P> wait, const sp<I> &interfaceObject, Function &&func,
Args &&... args) {
using ::android::hardware::Status;
- typename std::result_of<Function(I *, Args...)>::type ret{Status::ok()};
- auto boundFunc = std::bind(std::forward<Function>(func),
- interfaceObject.get(), std::forward<Args>(args)...);
- bool success = timeout(wait, [&ret, &boundFunc] {
- ret = std::move(boundFunc());
- });
- if (!success) {
+
+ // Execute on a background thread but do not defer execution.
+ auto future =
+ std::async(std::launch::async, func, interfaceObject, std::forward<Args>(args)...);
+ auto status = future.wait_for(wait);
+ if (status == std::future_status::ready) {
+ return future.get();
+ }
+
+ // This future belongs to a background thread that we no longer care about.
+ // Putting this in the global list avoids std::future::~future() that may wait for the
+ // result to come back.
+ // This leaks memory, but lshal is a debugging tool, so this is fine.
+ static std::vector<decltype(future)> gDeadPool{};
+ gDeadPool.emplace_back(std::move(future));
+
+ if (status == std::future_status::timeout) {
return Status::fromStatusT(TIMED_OUT);
}
- return ret;
+ return Status::fromExceptionCode(Status::Exception::EX_ILLEGAL_STATE, "Illegal future_status");
}
-
-} // namespace lshal
-} // namespace android
+} // namespace lshal
+} // namespace android
diff --git a/cmds/lshal/main.cpp b/cmds/lshal/main.cpp
index 366c938..bd5fa32 100644
--- a/cmds/lshal/main.cpp
+++ b/cmds/lshal/main.cpp
@@ -18,5 +18,6 @@
int main(int argc, char **argv) {
using namespace ::android::lshal;
- return Lshal{}.main(Arg{argc, argv});
+ // Use _exit() to force terminate background threads in Timeout.h
+ _exit(Lshal{}.main(Arg{argc, argv}));
}
diff --git a/cmds/lshal/test.cpp b/cmds/lshal/test.cpp
index cba7c4b..c24f827 100644
--- a/cmds/lshal/test.cpp
+++ b/cmds/lshal/test.cpp
@@ -14,6 +14,10 @@
* limitations under the License.
*/
+#include <chrono>
+#include <future>
+#include <mutex>
+#include "android/hidl/base/1.0/IBase.h"
#define LOG_TAG "Lshal"
#include <android-base/logging.h>
@@ -36,6 +40,8 @@
using namespace testing;
+using std::chrono_literals::operator""ms;
+
using ::android::hidl::base::V1_0::DebugInfo;
using ::android::hidl::base::V1_0::IBase;
using ::android::hidl::manager::V1_0::IServiceManager;
@@ -934,12 +940,9 @@
return hardware::Void();
}));
EXPECT_CALL(*serviceManager, get(_, _))
- .WillRepeatedly(
- Invoke([&](const hidl_string&, const hidl_string& instance) -> sp<IBase> {
- int id = getIdFromInstanceName(instance);
- if (id > inheritanceLevel) return nullptr;
- return sp<IBase>(service);
- }));
+ .WillRepeatedly(Invoke([&](const hidl_string&, const hidl_string&) -> sp<IBase> {
+ return sp<IBase>(service);
+ }));
const std::string expected = "[fake description 0]\n"
"Interface\n"
@@ -957,6 +960,110 @@
EXPECT_EQ("", err.str());
}
+// In SlowService, everything goes slooooooow. Each IPC call will wait for
+// the specified time before calling the callback function or returning.
+class SlowService : public IBase {
+public:
+ explicit SlowService(std::chrono::milliseconds wait) : mWait(wait) {}
+ android::hardware::Return<void> interfaceDescriptor(interfaceDescriptor_cb cb) override {
+ std::this_thread::sleep_for(mWait);
+ cb(getInterfaceName(1));
+ storeHistory("interfaceDescriptor");
+ return hardware::Void();
+ }
+ android::hardware::Return<void> interfaceChain(interfaceChain_cb cb) override {
+ std::this_thread::sleep_for(mWait);
+ std::vector<hidl_string> ret;
+ ret.push_back(getInterfaceName(1));
+ ret.push_back(IBase::descriptor);
+ cb(ret);
+ storeHistory("interfaceChain");
+ return hardware::Void();
+ }
+ android::hardware::Return<void> getHashChain(getHashChain_cb cb) override {
+ std::this_thread::sleep_for(mWait);
+ std::vector<hidl_hash> ret;
+ ret.push_back(getHashFromId(0));
+ ret.push_back(getHashFromId(0xff));
+ cb(ret);
+ storeHistory("getHashChain");
+ return hardware::Void();
+ }
+ android::hardware::Return<void> debug(const hidl_handle&,
+ const hidl_vec<hidl_string>&) override {
+ std::this_thread::sleep_for(mWait);
+ storeHistory("debug");
+ return Void();
+ }
+
+ template <class R, class P, class Pred>
+ bool waitForHistory(std::chrono::duration<R, P> wait, Pred predicate) {
+ std::unique_lock<std::mutex> lock(mLock);
+ return mCv.wait_for(lock, wait, [&]() { return predicate(mCallHistory); });
+ }
+
+private:
+ void storeHistory(std::string hist) {
+ {
+ std::lock_guard<std::mutex> lock(mLock);
+ mCallHistory.emplace_back(std::move(hist));
+ }
+ mCv.notify_all();
+ }
+
+ const std::chrono::milliseconds mWait;
+ std::mutex mLock;
+ std::condition_variable mCv;
+ // List of functions that have finished being called on this interface.
+ std::vector<std::string> mCallHistory;
+};
+
+class TimeoutTest : public ListTest {
+public:
+ void setMockServiceManager(sp<IBase> service) {
+ EXPECT_CALL(*serviceManager, list(_))
+ .WillRepeatedly(Invoke([&](IServiceManager::list_cb cb) {
+ std::vector<hidl_string> ret;
+ ret.push_back(getInterfaceName(1) + "/default");
+ cb(ret);
+ return hardware::Void();
+ }));
+ EXPECT_CALL(*serviceManager, get(_, _))
+ .WillRepeatedly(Invoke([&](const hidl_string&, const hidl_string&) -> sp<IBase> {
+ return service;
+ }));
+ }
+};
+
+TEST_F(TimeoutTest, BackgroundThreadIsKept) {
+ auto lshalIpcTimeout = 100ms;
+ auto serviceIpcTimeout = 200ms;
+ lshal->setWaitTimeForTest(lshalIpcTimeout, lshalIpcTimeout);
+ sp<SlowService> service = new SlowService(serviceIpcTimeout);
+ setMockServiceManager(service);
+
+ optind = 1; // mimic Lshal::parseArg()
+ EXPECT_NE(0u, mockList->main(createArg({"lshal", "--types=b", "-i", "--neat"})));
+ EXPECT_THAT(err.str(), HasSubstr("Skipping \"a.h.foo1@1.0::IFoo/default\""));
+ EXPECT_TRUE(service->waitForHistory(serviceIpcTimeout * 5, [](const auto& hist) {
+ return hist.size() == 1 && hist[0] == "interfaceChain";
+ })) << "The background thread should continue after the main thread moves on, but it is killed";
+}
+
+TEST_F(TimeoutTest, BackgroundThreadDoesNotBlockMainThread) {
+ auto lshalIpcTimeout = 100ms;
+ auto serviceIpcTimeout = 2000ms;
+ auto start = std::chrono::system_clock::now();
+ lshal->setWaitTimeForTest(lshalIpcTimeout, lshalIpcTimeout);
+ sp<SlowService> service = new SlowService(serviceIpcTimeout);
+ setMockServiceManager(service);
+
+ optind = 1; // mimic Lshal::parseArg()
+ EXPECT_NE(0u, mockList->main(createArg({"lshal", "--types=b", "-i", "--neat"})));
+ EXPECT_LE(std::chrono::system_clock::now(), start + 5 * lshalIpcTimeout)
+ << "The main thread should not be blocked by the background task";
+}
+
class ListVintfTest : public ListTest {
public:
virtual void SetUp() override {
@@ -1079,5 +1186,6 @@
int main(int argc, char **argv) {
::testing::InitGoogleMock(&argc, argv);
- return RUN_ALL_TESTS();
+ // Use _exit() to force terminate background threads in Timeout.h
+ _exit(RUN_ALL_TESTS());
}
diff --git a/include/android/performance_hint.h b/include/android/performance_hint.h
index 9d2c791..30200c7 100644
--- a/include/android/performance_hint.h
+++ b/include/android/performance_hint.h
@@ -123,7 +123,8 @@
*
* @return APerformanceHintManager instance on success, nullptr on failure.
*/
-APerformanceHintManager* _Nullable APerformanceHint_getManager() __INTRODUCED_IN(__ANDROID_API_T__);
+APerformanceHintManager* _Nullable APerformanceHint_getManager()
+ __INTRODUCED_IN(__ANDROID_API_T__);
/**
* Creates a session for the given set of threads and sets their initial target work
@@ -260,7 +261,8 @@
*
* @param aWorkDuration The {@link AWorkDuration} created by calling {@link AWorkDuration_create()}
*/
-void AWorkDuration_release(AWorkDuration* _Nonnull WorkDuration) __INTRODUCED_IN(__ANDROID_API_V__);
+void AWorkDuration_release(AWorkDuration* _Nonnull aWorkDuration)
+ __INTRODUCED_IN(__ANDROID_API_V__);
/**
* Sets the work period start timestamp in nanoseconds.
diff --git a/include/android/thermal.h b/include/android/thermal.h
index 0b57e93..fa168cd 100644
--- a/include/android/thermal.h
+++ b/include/android/thermal.h
@@ -111,7 +111,7 @@
* It's passed the updated thermal status as parameter, as well as the
* pointer provided by the client that registered a callback.
*/
-typedef void (*AThermal_StatusCallback)(void* data, AThermalStatus status);
+typedef void (*AThermal_StatusCallback)(void* _Nullable data, AThermalStatus status);
/**
* Acquire an instance of the thermal manager. This must be freed using
@@ -121,7 +121,7 @@
*
* @return manager instance on success, nullptr on failure.
*/
-AThermalManager* AThermal_acquireManager() __INTRODUCED_IN(30);
+AThermalManager* _Nonnull AThermal_acquireManager() __INTRODUCED_IN(30);
/**
* Release the thermal manager pointer acquired via
@@ -131,7 +131,7 @@
*
* @param manager The manager to be released.
*/
-void AThermal_releaseManager(AThermalManager *manager) __INTRODUCED_IN(30);
+void AThermal_releaseManager(AThermalManager* _Nonnull manager) __INTRODUCED_IN(30);
/**
* Gets the current thermal status.
@@ -143,7 +143,8 @@
*
* @return current thermal status, ATHERMAL_STATUS_ERROR on failure.
*/
-AThermalStatus AThermal_getCurrentThermalStatus(AThermalManager *manager) __INTRODUCED_IN(30);
+AThermalStatus
+AThermal_getCurrentThermalStatus(AThermalManager* _Nonnull manager) __INTRODUCED_IN(30);
/**
* Register the thermal status listener for thermal status change.
@@ -160,8 +161,9 @@
* EPERM if the required permission is not held.
* EPIPE if communication with the system service has failed.
*/
-int AThermal_registerThermalStatusListener(AThermalManager *manager,
- AThermal_StatusCallback callback, void *data) __INTRODUCED_IN(30);
+int AThermal_registerThermalStatusListener(AThermalManager* _Nonnull manager,
+ AThermal_StatusCallback _Nullable callback,
+ void* _Nullable data) __INTRODUCED_IN(30);
/**
* Unregister the thermal status listener previously resgistered.
@@ -178,8 +180,9 @@
* EPERM if the required permission is not held.
* EPIPE if communication with the system service has failed.
*/
-int AThermal_unregisterThermalStatusListener(AThermalManager *manager,
- AThermal_StatusCallback callback, void *data) __INTRODUCED_IN(30);
+int AThermal_unregisterThermalStatusListener(AThermalManager* _Nonnull manager,
+ AThermal_StatusCallback _Nullable callback,
+ void* _Nullable data) __INTRODUCED_IN(30);
/**
* Provides an estimate of how much thermal headroom the device currently has before
@@ -219,8 +222,8 @@
* as described above. Returns NaN if the device does not support this functionality or
* if this function is called significantly faster than once per second.
*/
-float AThermal_getThermalHeadroom(AThermalManager *manager,
- int forecastSeconds) __INTRODUCED_IN(31);
+float AThermal_getThermalHeadroom(AThermalManager* _Nonnull manager,
+ int forecastSeconds) __INTRODUCED_IN(31);
/**
* This struct defines an instance of headroom threshold value and its status.
@@ -282,9 +285,10 @@
* EPIPE if communication with the system service has failed.
* ENOSYS if the feature is disabled by the current system.
*/
-int AThermal_getThermalHeadroomThresholds(AThermalManager* manager,
- const AThermalHeadroomThreshold ** outThresholds,
- size_t* size) __INTRODUCED_IN(35);
+int AThermal_getThermalHeadroomThresholds(AThermalManager* _Nonnull manager,
+ const AThermalHeadroomThreshold* _Nonnull
+ * _Nullable outThresholds,
+ size_t* _Nonnull size) __INTRODUCED_IN(35);
#ifdef __cplusplus
}
diff --git a/include/input/VirtualInputDevice.h b/include/input/VirtualInputDevice.h
index 21a2877..222dac8 100644
--- a/include/input/VirtualInputDevice.h
+++ b/include/input/VirtualInputDevice.h
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2023 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.
@@ -64,6 +64,8 @@
class VirtualMouse : public VirtualInputDevice {
public:
+ // Expose to share with VirtualStylus.
+ static const std::map<int, UinputAction> BUTTON_ACTION_MAPPING;
VirtualMouse(android::base::unique_fd fd);
virtual ~VirtualMouse() override;
bool writeButtonEvent(int32_t androidButtonCode, int32_t androidAction,
@@ -74,12 +76,13 @@
std::chrono::nanoseconds eventTime);
private:
- static const std::map<int, UinputAction> BUTTON_ACTION_MAPPING;
static const std::map<int, int> BUTTON_CODE_MAPPING;
};
class VirtualTouchscreen : public VirtualInputDevice {
public:
+ // Expose to share with VirtualStylus.
+ static const std::map<int, UinputAction> TOUCH_ACTION_MAPPING;
VirtualTouchscreen(android::base::unique_fd fd);
virtual ~VirtualTouchscreen() override;
// TODO(b/259554911): changing float parameters to int32_t.
@@ -88,9 +91,7 @@
std::chrono::nanoseconds eventTime);
private:
- static const std::map<int, UinputAction> TOUCH_ACTION_MAPPING;
static const std::map<int, int> TOOL_TYPE_MAPPING;
-
/* The set of active touch pointers on this device.
* We only allow pointer id to go up to MAX_POINTERS because the maximum slots of virtual
* touchscreen is set up with MAX_POINTERS. Note that in other cases Android allows pointer id
@@ -101,4 +102,24 @@
bool handleTouchDown(int32_t pointerId, std::chrono::nanoseconds eventTime);
bool handleTouchUp(int32_t pointerId, std::chrono::nanoseconds eventTime);
};
+
+class VirtualStylus : public VirtualInputDevice {
+public:
+ VirtualStylus(android::base::unique_fd fd);
+ ~VirtualStylus() override;
+ bool writeMotionEvent(int32_t toolType, int32_t action, int32_t locationX, int32_t locationY,
+ int32_t pressure, int32_t tiltX, int32_t tiltY,
+ std::chrono::nanoseconds eventTime);
+ bool writeButtonEvent(int32_t androidButtonCode, int32_t androidAction,
+ std::chrono::nanoseconds eventTime);
+
+private:
+ static const std::map<int, int> TOOL_TYPE_MAPPING;
+ static const std::map<int, int> BUTTON_CODE_MAPPING;
+ // True if the stylus is touching or hovering on the screen.
+ bool mIsStylusDown;
+ bool handleStylusDown(uint16_t tool, std::chrono::nanoseconds eventTime);
+ bool handleStylusUp(uint16_t tool, std::chrono::nanoseconds eventTime);
+};
+
} // namespace android
diff --git a/include/private/performance_hint_private.h b/include/private/performance_hint_private.h
index d50c5f8..d8f9db4 100644
--- a/include/private/performance_hint_private.h
+++ b/include/private/performance_hint_private.h
@@ -53,6 +53,26 @@
* CPU resources to what was used previously, and must wake up if inactive.
*/
CPU_LOAD_RESUME = 3,
+
+ /**
+ * This hint indicates an increase in GPU workload intensity. It means that
+ * this hint session needs extra GPU resources to meet the target duration.
+ * This hint must be sent before reporting the actual duration to the session.
+ */
+ GPU_LOAD_UP = 5,
+
+ /**
+ * This hint indicates a decrease in GPU workload intensity. It means that
+ * this hint session can reduce GPU resources and still meet the target duration.
+ */
+ GPU_LOAD_DOWN = 6,
+
+ /*
+ * This hint indicates an upcoming GPU workload that is completely changed and
+ * unknown. It means that the hint session should reset GPU resources to a known
+ * baseline to prepare for an arbitrary load, and must wake up if inactive.
+ */
+ GPU_LOAD_RESET = 7,
};
/**
diff --git a/libs/binder/UtilsHost.h b/libs/binder/UtilsHost.h
index b582f17..d6fe9fa 100644
--- a/libs/binder/UtilsHost.h
+++ b/libs/binder/UtilsHost.h
@@ -16,6 +16,7 @@
#pragma once
+#include <functional>
#include <optional>
#include <ostream>
#include <string>
diff --git a/libs/binder/ndk/include_ndk/android/persistable_bundle.h b/libs/binder/ndk/include_ndk/android/persistable_bundle.h
index eff8104..98c0cb2 100644
--- a/libs/binder/ndk/include_ndk/android/persistable_bundle.h
+++ b/libs/binder/ndk/include_ndk/android/persistable_bundle.h
@@ -13,7 +13,12 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
+
+#pragma once
+
#include <android/binder_parcel.h>
+#include <stdbool.h>
+#include <stdint.h>
#include <sys/cdefs.h>
#include <sys/types.h>
@@ -31,14 +36,30 @@
struct APersistableBundle;
typedef struct APersistableBundle APersistableBundle;
+enum {
+ /**
+ * This can be returned from functions that need to distinguish between an empty
+ * value and a non-existent key.
+ */
+ APERSISTABLEBUNDLE_KEY_NOT_FOUND = -1,
+
+ /**
+ * This can be returned from functions that take a APersistableBundle_stringAllocator.
+ * This means the allocator has failed and returned a nullptr.
+ */
+ APERSISTABLEBUNDLE_ALLOCATOR_FAILED = -2,
+};
+
/**
* This is a user supplied allocator that allocates a buffer for the
- * APersistableBundle APIs to fill in with a string.
+ * APersistableBundle APIs to fill in with a UTF-8 string.
+ * The caller that supplies this function is responsible for freeing the
+ * returned data.
*
* \param the required size in bytes for the allocated buffer
- * \param void* _Nullable context if needed by the callback
+ * \param context pointer if needed by the callback
*
- * \return allocated buffer of sizeBytes. Null if allocation failed.
+ * \return allocated buffer of sizeBytes for a UTF-8 string. Null if allocation failed.
*/
typedef char* _Nullable (*_Nonnull APersistableBundle_stringAllocator)(int32_t sizeBytes,
void* _Nullable context);
@@ -54,10 +75,12 @@
/**
* Create a new APersistableBundle based off an existing APersistableBundle.
+ * This is a deep copy, so the new APersistableBundle has its own values from
+ * copying the original underlying PersistableBundle.
*
* Available since API level __ANDROID_API_V__.
*
- * \param bundle to duplicate
+ * \param pBundle to duplicate
*
* \return Pointer to a new APersistableBundle
*/
@@ -68,11 +91,11 @@
* Delete an APersistableBundle. This must always be called when finished using
* the object.
*
- * \param bundle to delete
+ * \param pBundle to delete. No-op if null.
*
* Available since API level __ANDROID_API_V__.
*/
-void APersistableBundle_delete(APersistableBundle* _Nonnull pBundle)
+void APersistableBundle_delete(APersistableBundle* _Nullable pBundle)
__INTRODUCED_IN(__ANDROID_API_V__);
/**
@@ -80,8 +103,8 @@
*
* Available since API level __ANDROID_API_V__.
*
- * \param lhs bundle to compare agains the other param
- * \param rhs bundle to compare agains the other param
+ * \param lhs bundle to compare against the other param
+ * \param rhs bundle to compare against the other param
*
* \return true when equal, false when not
*/
@@ -134,11 +157,11 @@
*
* Available since API level __ANDROID_API_V__.
*
- * \param bundle to get the size of (number of mappings)
+ * \param pBundle to get the size of (number of mappings)
*
* \return number of mappings in the object
*/
-int32_t APersistableBundle_size(APersistableBundle* _Nonnull pBundle)
+int32_t APersistableBundle_size(const APersistableBundle* _Nonnull pBundle)
__INTRODUCED_IN(__ANDROID_API_V__);
/**
@@ -146,8 +169,8 @@
*
* Available since API level __ANDROID_API_V__.
*
- * \param bundle to operate on
- * \param key for the mapping to erase
+ * \param pBundle to operate on
+ * \param key for the mapping in UTF-8 to erase
*
* \return number of entries erased. Either 0 or 1.
*/
@@ -158,8 +181,8 @@
* Put a boolean associated with the provided key.
* New values with the same key will overwrite existing values.
*
- * \param bundle to operate on
- * \param key for the mapping
+ * \param pBundle to operate on
+ * \param key for the mapping in UTF-8
* \param value to put for the mapping
*
* Available since API level __ANDROID_API_V__.
@@ -171,9 +194,9 @@
* Put an int32_t associated with the provided key.
* New values with the same key will overwrite existing values.
*
- * \param bundle to operate on
- * \param key for the mapping
- * \param value to put for the mapping
+ * \param pBundle to operate on
+ * \param key for the mapping in UTF-8
+ * \param val value to put for the mapping
*
* Available since API level __ANDROID_API_V__.
*/
@@ -184,9 +207,9 @@
* Put an int64_t associated with the provided key.
* New values with the same key will overwrite existing values.
*
- * \param bundle to operate on
- * \param key for the mapping
- * \param value to put for the mapping
+ * \param pBundle to operate on
+ * \param key for the mapping in UTF-8
+ * \param val value to put for the mapping
*
* Available since API level __ANDROID_API_V__.
*/
@@ -197,9 +220,9 @@
* Put a double associated with the provided key.
* New values with the same key will overwrite existing values.
*
- * \param bundle to operate on
- * \param key for the mapping
- * \param value to put for the mapping
+ * \param pBundle to operate on
+ * \param key for the mapping in UTF-8
+ * \param val value to put for the mapping
*
* Available since API level __ANDROID_API_V__.
*/
@@ -209,10 +232,11 @@
/**
* Put a string associated with the provided key.
* New values with the same key will overwrite existing values.
+ * The value is copied.
*
- * \param bundle to operate on
- * \param key for the mapping
- * \param value to put for the mapping
+ * \param pBundle to operate on
+ * \param key for the mapping in UTF-8
+ * \param vec vector to put for the mapping
*
* Available since API level __ANDROID_API_V__.
*/
@@ -222,11 +246,12 @@
/**
* Put a boolean vector associated with the provided key.
* New values with the same key will overwrite existing values.
+ * The values are copied.
*
- * \param bundle to operate on
- * \param key for the mapping
- * \param value to put for the mapping
- * \param size in number of elements in the vector
+ * \param pBundle to operate on
+ * \param key for the mapping in UTF-8
+ * \param vec vector to put for the mapping
+ * \param num number of elements in the vector
*
* Available since API level __ANDROID_API_V__.
*/
@@ -237,11 +262,12 @@
/**
* Put an int32_t vector associated with the provided key.
* New values with the same key will overwrite existing values.
+ * The values are copied.
*
- * \param bundle to operate on
- * \param key for the mapping
- * \param value to put for the mapping
- * \param size in number of elements in the vector
+ * \param pBundle to operate on
+ * \param key for the mapping in UTF-8
+ * \param vec vector to put for the mapping
+ * \param num number of elements in the vector
*
* Available since API level __ANDROID_API_V__.
*/
@@ -252,11 +278,12 @@
/**
* Put an int64_t vector associated with the provided key.
* New values with the same key will overwrite existing values.
+ * The values are copied.
*
- * \param bundle to operate on
- * \param key for the mapping
- * \param value to put for the mapping
- * \param size in number of elements in the vector
+ * \param pBundle to operate on
+ * \param key for the mapping in UTF-8
+ * \param vec vector to put for the mapping
+ * \param num number of elements in the vector
*
* Available since API level __ANDROID_API_V__.
*/
@@ -267,11 +294,12 @@
/**
* Put a double vector associated with the provided key.
* New values with the same key will overwrite existing values.
+ * The values are copied.
*
- * \param bundle to operate on
- * \param key for the mapping
- * \param value to put for the mapping
- * \param size in number of elements in the vector
+ * \param pBundle to operate on
+ * \param key for the mapping in UTF-8
+ * \param vec vector to put for the mapping
+ * \param num number of elements in the vector
*
* Available since API level __ANDROID_API_V__.
*/
@@ -282,11 +310,12 @@
/**
* Put a string vector associated with the provided key.
* New values with the same key will overwrite existing values.
+ * The values are copied.
*
- * \param bundle to operate on
- * \param key for the mapping
- * \param value to put for the mapping
- * \param size in number of elements in the vector
+ * \param pBundle to operate on
+ * \param key for the mapping in UTF-8
+ * \param vec vector to put for the mapping
+ * \param num number of elements in the vector
*
* Available since API level __ANDROID_API_V__.
*/
@@ -298,10 +327,11 @@
/**
* Put an APersistableBundle associated with the provided key.
* New values with the same key will overwrite existing values.
+ * The value is deep-copied.
*
- * \param bundle to operate on
- * \param key for the mapping
- * \param value to put for the mapping
+ * \param pBundle to operate on
+ * \param key for the mapping in UTF-8
+ * \param val value to put for the mapping
*
* Available since API level __ANDROID_API_V__.
*/
@@ -315,9 +345,9 @@
*
* Available since API level __ANDROID_API_V__.
*
- * \param bundle to operate on
- * \param key for the mapping
- * \param nonnull pointer to write the value to
+ * \param pBundle to operate on
+ * \param key for the mapping in UTF-8
+ * \param val pointer to write the value to
*
* \return true if a value exists for the provided key
*/
@@ -330,9 +360,9 @@
*
* Available since API level __ANDROID_API_V__.
*
- * \param bundle to operate on
- * \param key for the mapping
- * \param nonnull pointer to write the value to
+ * \param pBundle to operate on
+ * \param key for the mapping in UTF-8
+ * \param val pointer to write the value to
*
* \return true if a value exists for the provided key
*/
@@ -344,9 +374,9 @@
*
* Available since API level __ANDROID_API_V__.
*
- * \param bundle to operate on
- * \param key for the mapping
- * \param nonnull pointer to write the value to
+ * \param pBundle to operate on
+ * \param key for the mapping in UTF-8
+ * \param val pointer to write the value to
*
* \return true if a value exists for the provided key
*/
@@ -359,9 +389,9 @@
*
* Available since API level __ANDROID_API_V__.
*
- * \param bundle to operate on
- * \param key for the mapping
- * \param nonnull pointer to write the value to
+ * \param pBundle to operate on
+ * \param key for the mapping in UTF-8
+ * \param val pointer to write the value to
*
* \return true if a value exists for the provided key
*/
@@ -371,17 +401,19 @@
/**
* Get a string associated with the provided key.
+ * The caller is responsible for freeing the returned data.
*
* Available since API level __ANDROID_API_V__.
*
- * \param bundle to operate on
- * \param key for the mapping
- * \param nonnull pointer to write the value to
- * \param function pointer to the string dup allocator
+ * \param pBundle to operate on
+ * \param key for the mapping in UTF-8
+ * \param val pointer to write the value to in UTF-8
+ * \param stringAllocator function pointer to the string allocator
+ * \param context pointer that will be passed to the stringAllocator
*
- * \return size of string associated with the provided key on success
- * 0 if no string exists for the provided key
- * -1 if the provided allocator fails and returns false
+ * \return size of string in bytes associated with the provided key on success
+ * APERSISTABLEBUNDLE_KEY_NOT_FOUND if the key was not found
+ * APERSISTABLEBUNDLE_ALLOCATOR_FAILED if the provided allocator fails
*/
int32_t APersistableBundle_getString(const APersistableBundle* _Nonnull pBundle,
const char* _Nonnull key, char* _Nullable* _Nonnull val,
@@ -393,7 +425,7 @@
* provided pre-allocated buffer from the user.
*
* This function returns the size in bytes of stored vector.
- * The supplied buffer will be filled in based on the smaller of the suplied
+ * The supplied buffer will be filled in based on the smaller of the supplied
* bufferSizeBytes or the actual size of the stored data.
* If the buffer is null or if the supplied bufferSizeBytes is smaller than the
* actual stored data, then not all of the stored data will be returned.
@@ -401,13 +433,14 @@
* Users can call this function with null buffer and 0 bufferSizeBytes to get
* the required size of the buffer to use on a subsequent call.
*
- * \param bundle to operate on
- * \param key for the mapping
- * \param nonnull pointer to a pre-allocated buffer to write the values to
- * \param size of the pre-allocated buffer
+ * \param pBundle to operate on
+ * \param key for the mapping in UTF-8
+ * \param buffer pointer to a pre-allocated buffer to write the values to
+ * \param bufferSizeBytes size of the pre-allocated buffer
*
* \return size of the stored vector in bytes. This is the required size of the
* pre-allocated user supplied buffer if all of the stored contents are desired.
+ * APERSISTABLEBUNDLE_KEY_NOT_FOUND if the key was not found
*/
int32_t APersistableBundle_getBooleanVector(const APersistableBundle* _Nonnull pBundle,
const char* _Nonnull key, bool* _Nullable buffer,
@@ -419,7 +452,7 @@
* provided pre-allocated buffer from the user.
*
* This function returns the size in bytes of stored vector.
- * The supplied buffer will be filled in based on the smaller of the suplied
+ * The supplied buffer will be filled in based on the smaller of the supplied
* bufferSizeBytes or the actual size of the stored data.
* If the buffer is null or if the supplied bufferSizeBytes is smaller than the
* actual stored data, then not all of the stored data will be returned.
@@ -427,13 +460,14 @@
* Users can call this function with null buffer and 0 bufferSizeBytes to get
* the required size of the buffer to use on a subsequent call.
*
- * \param bundle to operate on
- * \param key for the mapping
- * \param nonnull pointer to a pre-allocated buffer to write the values to
- * \param size of the pre-allocated buffer
+ * \param pBundle to operate on
+ * \param key for the mapping in UTF-8
+ * \param buffer pointer to a pre-allocated buffer to write the values to
+ * \param bufferSizeBytes size of the pre-allocated buffer
*
* \return size of the stored vector in bytes. This is the required size of the
* pre-allocated user supplied buffer if all of the stored contents are desired.
+ * APERSISTABLEBUNDLE_KEY_NOT_FOUND if the key was not found
*/
int32_t APersistableBundle_getIntVector(const APersistableBundle* _Nonnull pBundle,
const char* _Nonnull key, int32_t* _Nullable buffer,
@@ -444,7 +478,7 @@
* provided pre-allocated buffer from the user.
*
* This function returns the size in bytes of stored vector.
- * The supplied buffer will be filled in based on the smaller of the suplied
+ * The supplied buffer will be filled in based on the smaller of the supplied
* bufferSizeBytes or the actual size of the stored data.
* If the buffer is null or if the supplied bufferSizeBytes is smaller than the
* actual stored data, then not all of the stored data will be returned.
@@ -452,13 +486,14 @@
* Users can call this function with null buffer and 0 bufferSizeBytes to get
* the required size of the buffer to use on a subsequent call.
*
- * \param bundle to operate on
- * \param key for the mapping
- * \param nonnull pointer to a pre-allocated buffer to write the values to
- * \param size of the pre-allocated buffer
+ * \param pBundle to operate on
+ * \param key for the mapping in UTF-8
+ * \param buffer pointer to a pre-allocated buffer to write the values to
+ * \param bufferSizeBytes size of the pre-allocated buffer
*
* \return size of the stored vector in bytes. This is the required size of the
* pre-allocated user supplied buffer if all of the stored contents are desired.
+ * APERSISTABLEBUNDLE_KEY_NOT_FOUND if the key was not found
*/
int32_t APersistableBundle_getLongVector(const APersistableBundle* _Nonnull pBundle,
const char* _Nonnull key, int64_t* _Nullable buffer,
@@ -470,7 +505,7 @@
* provided pre-allocated buffer from the user.
*
* This function returns the size in bytes of stored vector.
- * The supplied buffer will be filled in based on the smaller of the suplied
+ * The supplied buffer will be filled in based on the smaller of the supplied
* bufferSizeBytes or the actual size of the stored data.
* If the buffer is null or if the supplied bufferSizeBytes is smaller than the
* actual stored data, then not all of the stored data will be returned.
@@ -478,13 +513,14 @@
* Users can call this function with null buffer and 0 bufferSizeBytes to get
* the required size of the buffer to use on a subsequent call.
*
- * \param bundle to operate on
- * \param key for the mapping
- * \param nonnull pointer to a pre-allocated buffer to write the values to
- * \param size of the pre-allocated buffer
+ * \param pBundle to operate on
+ * \param key for the mapping in UTF-8
+ * \param buffer pointer to a pre-allocated buffer to write the values to
+ * \param bufferSizeBytes size of the pre-allocated buffer
*
* \return size of the stored vector in bytes. This is the required size of the
* pre-allocated user supplied buffer if all of the stored contents are desired.
+ * APERSISTABLEBUNDLE_KEY_NOT_FOUND if the key was not found
*/
int32_t APersistableBundle_getDoubleVector(const APersistableBundle* _Nonnull pBundle,
const char* _Nonnull key, double* _Nullable buffer,
@@ -496,9 +532,10 @@
* provided pre-allocated buffer from the user. The user must provide an
* APersistableBundle_stringAllocator for the individual strings to be
* allocated.
+ * The caller is responsible for freeing the returned data.
*
* This function returns the size in bytes of stored vector.
- * The supplied buffer will be filled in based on the smaller of the suplied
+ * The supplied buffer will be filled in based on the smaller of the supplied
* bufferSizeBytes or the actual size of the stored data.
* If the buffer is null or if the supplied bufferSizeBytes is smaller than the
* actual stored data, then not all of the stored data will be returned.
@@ -506,17 +543,18 @@
* Users can call this function with null buffer and 0 bufferSizeBytes to get
* the required size of the buffer to use on a subsequent call.
*
- * \param bundle to operate on
- * \param key for the mapping
- * \param nonnull pointer to a pre-allocated buffer to write the values to
- * \param size of the pre-allocated buffer
- * \param function pointer to the string dup allocator
+ * \param pBundle to operate on
+ * \param key for the mapping in UTF-8
+ * \param buffer pointer to a pre-allocated buffer to write the string pointers to
+ * \param bufferSizeBytes size of the pre-allocated buffer
+ * \param stringAllocator function pointer to the string allocator
+ * \param context pointer that will be passed to the stringAllocator
*
* \return size of the stored vector in bytes. This is the required size of the
* pre-allocated user supplied buffer if all of the stored contents are desired.
* 0 if no string vector exists for the provided key
- * -1 if the user supplied APersistableBundle_stringAllocator returns
- * false
+ * APERSISTABLEBUNDLE_KEY_NOT_FOUND if the key was not found
+ * APERSISTABLEBUNDLE_ALLOCATOR_FAILED if the provided allocator fails
*/
int32_t APersistableBundle_getStringVector(const APersistableBundle* _Nonnull pBundle,
const char* _Nonnull key,
@@ -531,9 +569,9 @@
*
* Available since API level __ANDROID_API_V__.
*
- * \param bundle to operate on
- * \param key for the mapping
- * \param nonnull pointer to an APersistableBundle pointer to write to point to
+ * \param pBundle to operate on
+ * \param key for the mapping in UTF-8
+ * \param val pointer to an APersistableBundle pointer to write to point to
* a new copy of the stored APersistableBundle. The caller takes ownership of
* the new APersistableBundle and must be deleted with
* APersistableBundle_delete.
@@ -550,9 +588,10 @@
* provided pre-allocated buffer from the user. The user must provide an
* APersistableBundle_stringAllocator for the individual strings to be
* allocated.
+ * The caller is responsible for freeing the returned data.
*
* This function returns the size in bytes required to fit the fill list of keys.
- * The supplied buffer will be filled in based on the smaller of the suplied
+ * The supplied buffer will be filled in based on the smaller of the supplied
* bufferSizeBytes or the actual size of the stored data.
* If the buffer is null or if the supplied bufferSizeBytes is smaller than the
* actual stored data, then not all of the stored data will be returned.
@@ -560,16 +599,15 @@
* Users can call this function with null buffer and 0 bufferSizeBytes to get
* the required size of the buffer to use on a subsequent call.
*
- * \param bundle to operate on
- * \param nonnull pointer to a pre-allocated buffer to write the values to
- * \param size of the pre-allocated buffer
- * \param function pointer to the string dup allocator
+ * \param pBundle to operate on
+ * \param outKeys pointer to a pre-allocated buffer to write the UTF-8 keys to
+ * \param bufferSizeBytes size of the pre-allocated buffer
+ * \param stringAllocator function pointer to the string allocator
+ * \param context pointer that will be passed to the stringAllocator
*
* \return size of the buffer of keys in bytes. This is the required size of the
* pre-allocated user supplied buffer if all of the stored contents are desired.
- * 0 if no string vector exists for the provided key
- * -1 if the user supplied APersistableBundle_stringAllocator returns
- * false
+ * APERSISTABLEBUNDLE_ALLOCATOR_FAILED if the provided allocator fails
*/
int32_t APersistableBundle_getBooleanKeys(const APersistableBundle* _Nonnull pBundle,
char* _Nullable* _Nullable outKeys,
@@ -583,9 +621,10 @@
* provided pre-allocated buffer from the user. The user must provide an
* APersistableBundle_stringAllocator for the individual strings to be
* allocated.
+ * The caller is responsible for freeing the returned data.
*
* This function returns the size in bytes required to fit the fill list of keys.
- * The supplied buffer will be filled in based on the smaller of the suplied
+ * The supplied buffer will be filled in based on the smaller of the supplied
* bufferSizeBytes or the actual size of the stored data.
* If the buffer is null or if the supplied bufferSizeBytes is smaller than the
* actual stored data, then not all of the stored data will be returned.
@@ -593,16 +632,15 @@
* Users can call this function with null buffer and 0 bufferSizeBytes to get
* the required size of the buffer to use on a subsequent call.
*
- * \param bundle to operate on
- * \param nonnull pointer to a pre-allocated buffer to write the values to
- * \param size of the pre-allocated buffer
- * \param function pointer to the string dup allocator
+ * \param pBundle to operate on
+ * \param outKeys pointer to a pre-allocated buffer to write the UTF-8 keys to
+ * \param bufferSizeBytes size of the pre-allocated buffer
+ * \param stringAllocator function pointer to the string allocator
+ * \param context pointer that will be passed to the stringAllocator
*
* \return size of the buffer of keys in bytes. This is the required size of the
* pre-allocated user supplied buffer if all of the stored contents are desired.
- * 0 if no string vector exists for the provided key
- * -1 if the user supplied APersistableBundle_stringAllocator returns
- * false
+ * APERSISTABLEBUNDLE_ALLOCATOR_FAILED if the provided allocator fails
*/
int32_t APersistableBundle_getIntKeys(const APersistableBundle* _Nonnull pBundle,
char* _Nullable* _Nullable outKeys, int32_t bufferSizeBytes,
@@ -614,9 +652,10 @@
* provided pre-allocated buffer from the user. The user must provide an
* APersistableBundle_stringAllocator for the individual strings to be
* allocated.
+ * The caller is responsible for freeing the returned data.
*
* This function returns the size in bytes required to fit the fill list of keys.
- * The supplied buffer will be filled in based on the smaller of the suplied
+ * The supplied buffer will be filled in based on the smaller of the supplied
* bufferSizeBytes or the actual size of the stored data.
* If the buffer is null or if the supplied bufferSizeBytes is smaller than the
* actual stored data, then not all of the stored data will be returned.
@@ -624,16 +663,15 @@
* Users can call this function with null buffer and 0 bufferSizeBytes to get
* the required size of the buffer to use on a subsequent call.
*
- * \param bundle to operate on
- * \param nonnull pointer to a pre-allocated buffer to write the values to
- * \param size of the pre-allocated buffer
- * \param function pointer to the string dup allocator
+ * \param pBundle to operate on
+ * \param outKeys pointer to a pre-allocated buffer to write the UTF-8 keys to
+ * \param bufferSizeBytes size of the pre-allocated buffer
+ * \param stringAllocator function pointer to the string allocator
+ * \param context pointer that will be passed to the stringAllocator
*
* \return size of the buffer of keys in bytes. This is the required size of the
* pre-allocated user supplied buffer if all of the stored contents are desired.
- * 0 if no string vector exists for the provided key
- * -1 if the user supplied APersistableBundle_stringAllocator returns
- * false
+ * APERSISTABLEBUNDLE_ALLOCATOR_FAILED if the provided allocator fails
*/
int32_t APersistableBundle_getLongKeys(const APersistableBundle* _Nonnull pBundle,
char* _Nullable* _Nullable outKeys, int32_t bufferSizeBytes,
@@ -645,9 +683,10 @@
* provided pre-allocated buffer from the user. The user must provide an
* APersistableBundle_stringAllocator for the individual strings to be
* allocated.
+ * The caller is responsible for freeing the returned data.
*
* This function returns the size in bytes required to fit the fill list of keys.
- * The supplied buffer will be filled in based on the smaller of the suplied
+ * The supplied buffer will be filled in based on the smaller of the supplied
* bufferSizeBytes or the actual size of the stored data.
* If the buffer is null or if the supplied bufferSizeBytes is smaller than the
* actual stored data, then not all of the stored data will be returned.
@@ -655,16 +694,15 @@
* Users can call this function with null buffer and 0 bufferSizeBytes to get
* the required size of the buffer to use on a subsequent call.
*
- * \param bundle to operate on
- * \param nonnull pointer to a pre-allocated buffer to write the values to
- * \param size of the pre-allocated buffer
- * \param function pointer to the string dup allocator
+ * \param pBundle to operate on
+ * \param outKeys pointer to a pre-allocated buffer to write the UTF-8 keys to
+ * \param bufferSizeBytes size of the pre-allocated buffer
+ * \param stringAllocator function pointer to the string allocator
+ * \param context pointer that will be passed to the stringAllocator
*
* \return size of the buffer of keys in bytes. This is the required size of the
* pre-allocated user supplied buffer if all of the stored contents are desired.
- * 0 if no string vector exists for the provided key
- * -1 if the user supplied APersistableBundle_stringAllocator returns
- * false
+ * APERSISTABLEBUNDLE_ALLOCATOR_FAILED if the provided allocator fails
*/
int32_t APersistableBundle_getDoubleKeys(const APersistableBundle* _Nonnull pBundle,
char* _Nullable* _Nullable outKeys,
@@ -678,9 +716,10 @@
* provided pre-allocated buffer from the user. The user must provide an
* APersistableBundle_stringAllocator for the individual strings to be
* allocated.
+ * The caller is responsible for freeing the returned data.
*
* This function returns the size in bytes required to fit the fill list of keys.
- * The supplied buffer will be filled in based on the smaller of the suplied
+ * The supplied buffer will be filled in based on the smaller of the supplied
* bufferSizeBytes or the actual size of the stored data.
* If the buffer is null or if the supplied bufferSizeBytes is smaller than the
* actual stored data, then not all of the stored data will be returned.
@@ -688,16 +727,15 @@
* Users can call this function with null buffer and 0 bufferSizeBytes to get
* the required size of the buffer to use on a subsequent call.
*
- * \param bundle to operate on
- * \param nonnull pointer to a pre-allocated buffer to write the values to
- * \param size of the pre-allocated buffer
- * \param function pointer to the string dup allocator
+ * \param pBundle to operate on
+ * \param outKeys pointer to a pre-allocated buffer to write the UTF-8 keys to
+ * \param bufferSizeBytes size of the pre-allocated buffer
+ * \param stringAllocator function pointer to the string allocator
+ * \param context pointer that will be passed to the stringAllocator
*
* \return size of the buffer of keys in bytes. This is the required size of the
* pre-allocated user supplied buffer if all of the stored contents are desired.
- * 0 if no string vector exists for the provided key
- * -1 if the user supplied APersistableBundle_stringAllocator returns
- * false
+ * APERSISTABLEBUNDLE_ALLOCATOR_FAILED if the provided allocator fails
*/
int32_t APersistableBundle_getStringKeys(const APersistableBundle* _Nonnull pBundle,
char* _Nullable* _Nullable outKeys,
@@ -711,9 +749,10 @@
* provided pre-allocated buffer from the user. The user must provide an
* APersistableBundle_stringAllocator for the individual strings to be
* allocated.
+ * The caller is responsible for freeing the returned data.
*
* This function returns the size in bytes required to fit the fill list of keys.
- * The supplied buffer will be filled in based on the smaller of the suplied
+ * The supplied buffer will be filled in based on the smaller of the supplied
* bufferSizeBytes or the actual size of the stored data.
* If the buffer is null or if the supplied bufferSizeBytes is smaller than the
* actual stored data, then not all of the stored data will be returned.
@@ -721,16 +760,15 @@
* Users can call this function with null buffer and 0 bufferSizeBytes to get
* the required size of the buffer to use on a subsequent call.
*
- * \param bundle to operate on
- * \param nonnull pointer to a pre-allocated buffer to write the values to
- * \param size of the pre-allocated buffer
- * \param function pointer to the string dup allocator
+ * \param pBundle to operate on
+ * \param outKeys pointer to a pre-allocated buffer to write the UTF-8 keys to
+ * \param bufferSizeBytes size of the pre-allocated buffer
+ * \param stringAllocator function pointer to the string allocator
+ * \param context pointer that will be passed to the stringAllocator
*
* \return size of the buffer of keys in bytes. This is the required size of the
* pre-allocated user supplied buffer if all of the stored contents are desired.
- * 0 if no string vector exists for the provided key
- * -1 if the user supplied APersistableBundle_stringAllocator returns
- * false
+ * APERSISTABLEBUNDLE_ALLOCATOR_FAILED if the provided allocator fails
*/
int32_t APersistableBundle_getBooleanVectorKeys(const APersistableBundle* _Nonnull pBundle,
char* _Nullable* _Nullable outKeys,
@@ -744,9 +782,10 @@
* provided pre-allocated buffer from the user. The user must provide an
* APersistableBundle_stringAllocator for the individual strings to be
* allocated.
+ * The caller is responsible for freeing the returned data.
*
* This function returns the size in bytes required to fit the fill list of keys.
- * The supplied buffer will be filled in based on the smaller of the suplied
+ * The supplied buffer will be filled in based on the smaller of the supplied
* bufferSizeBytes or the actual size of the stored data.
* If the buffer is null or if the supplied bufferSizeBytes is smaller than the
* actual stored data, then not all of the stored data will be returned.
@@ -754,16 +793,15 @@
* Users can call this function with null buffer and 0 bufferSizeBytes to get
* the required size of the buffer to use on a subsequent call.
*
- * \param bundle to operate on
- * \param nonnull pointer to a pre-allocated buffer to write the values to
- * \param size of the pre-allocated buffer
- * \param function pointer to the string dup allocator
+ * \param pBundle to operate on
+ * \param outKeys pointer to a pre-allocated buffer to write the UTF-8 keys to
+ * \param bufferSizeBytes size of the pre-allocated buffer
+ * \param stringAllocator function pointer to the string allocator
+ * \param context pointer that will be passed to the stringAllocator
*
* \return size of the buffer of keys in bytes. This is the required size of the
* pre-allocated user supplied buffer if all of the stored contents are desired.
- * 0 if no string vector exists for the provided key
- * -1 if the user supplied APersistableBundle_stringAllocator returns
- * false
+ * APERSISTABLEBUNDLE_ALLOCATOR_FAILED if the provided allocator fails
*/
int32_t APersistableBundle_getIntVectorKeys(const APersistableBundle* _Nonnull pBundle,
char* _Nullable* _Nullable outKeys,
@@ -777,9 +815,10 @@
* provided pre-allocated buffer from the user. The user must provide an
* APersistableBundle_stringAllocator for the individual strings to be
* allocated.
+ * The caller is responsible for freeing the returned data.
*
* This function returns the size in bytes required to fit the fill list of keys.
- * The supplied buffer will be filled in based on the smaller of the suplied
+ * The supplied buffer will be filled in based on the smaller of the supplied
* bufferSizeBytes or the actual size of the stored data.
* If the buffer is null or if the supplied bufferSizeBytes is smaller than the
* actual stored data, then not all of the stored data will be returned.
@@ -787,16 +826,15 @@
* Users can call this function with null buffer and 0 bufferSizeBytes to get
* the required size of the buffer to use on a subsequent call.
*
- * \param bundle to operate on
- * \param nonnull pointer to a pre-allocated buffer to write the values to
- * \param size of the pre-allocated buffer
- * \param function pointer to the string dup allocator
+ * \param pBundle to operate on
+ * \param outKeys pointer to a pre-allocated buffer to write the UTF-8 keys to
+ * \param bufferSizeBytes size of the pre-allocated buffer
+ * \param stringAllocator function pointer to the string allocator
+ * \param context pointer that will be passed to the stringAllocator
*
* \return size of the buffer of keys in bytes. This is the required size of the
* pre-allocated user supplied buffer if all of the stored contents are desired.
- * 0 if no string vector exists for the provided key
- * -1 if the user supplied APersistableBundle_stringAllocator returns
- * false
+ * APERSISTABLEBUNDLE_ALLOCATOR_FAILED if the provided allocator fails
*/
int32_t APersistableBundle_getLongVectorKeys(const APersistableBundle* _Nonnull pBundle,
char* _Nullable* _Nullable outKeys,
@@ -810,9 +848,10 @@
* provided pre-allocated buffer from the user. The user must provide an
* APersistableBundle_stringAllocator for the individual strings to be
* allocated.
+ * The caller is responsible for freeing the returned data.
*
* This function returns the size in bytes required to fit the fill list of keys.
- * The supplied buffer will be filled in based on the smaller of the suplied
+ * The supplied buffer will be filled in based on the smaller of the supplied
* bufferSizeBytes or the actual size of the stored data.
* If the buffer is null or if the supplied bufferSizeBytes is smaller than the
* actual stored data, then not all of the stored data will be returned.
@@ -820,16 +859,14 @@
* Users can call this function with null buffer and 0 bufferSizeBytes to get
* the required size of the buffer to use on a subsequent call.
*
- * \param bundle to operate on
- * \param nonnull pointer to a pre-allocated buffer to write the values to
- * \param size of the pre-allocated buffer
- * \param function pointer to the string dup allocator
+ * \param pBundle to operate on
+ * \param outKeys pointer to a pre-allocated buffer to write the UTF-8 keys to
+ * \param bufferSizeBytes size of the pre-allocated buffer
+ * \param stringAllocator function pointer to the string allocator
+ * \param context pointer that will be passed to the stringAllocator
*
* \return size of the buffer of keys in bytes. This is the required size of the
* pre-allocated user supplied buffer if all of the stored contents are desired.
- * 0 if no string vector exists for the provided key
- * -1 if the user supplied APersistableBundle_stringAllocator returns
- * false
*/
int32_t APersistableBundle_getDoubleVectorKeys(const APersistableBundle* _Nonnull pBundle,
char* _Nullable* _Nullable outKeys,
@@ -843,9 +880,10 @@
* provided pre-allocated buffer from the user. The user must provide an
* APersistableBundle_stringAllocator for the individual strings to be
* allocated.
+ * The caller is responsible for freeing the returned data.
*
* This function returns the size in bytes required to fit the fill list of keys.
- * The supplied buffer will be filled in based on the smaller of the suplied
+ * The supplied buffer will be filled in based on the smaller of the supplied
* bufferSizeBytes or the actual size of the stored data.
* If the buffer is null or if the supplied bufferSizeBytes is smaller than the
* actual stored data, then not all of the stored data will be returned.
@@ -853,15 +891,14 @@
* Users can call this function with null buffer and 0 bufferSizeBytes to get
* the required size of the buffer to use on a subsequent call.
*
- * \param bundle to operate on
- * \param nonnull pointer to a pre-allocated buffer to write the values to
- * \param size of the pre-allocated buffer
- * \param function pointer to the string dup allocator
+ * \param pBundle to operate on
+ * \param outKeys pointer to a pre-allocated buffer to write the UTF-8 keys to
+ * \param bufferSizeBytes size of the pre-allocated buffer
+ * \param stringAllocator function pointer to the string allocator
+ * \param context pointer that will be passed to the stringAllocator
*
* \return size of the buffer of keys in bytes. This is the required size of the
* pre-allocated user supplied buffer if all of the stored contents are desired.
- * 0 if no string vector exists for the provided key
- * -1 if the user supplied APersistableBundle_stringAllocator returns
* false
*/
int32_t APersistableBundle_getStringVectorKeys(const APersistableBundle* _Nonnull pBundle,
@@ -876,9 +913,10 @@
* provided pre-allocated buffer from the user. The user must provide an
* APersistableBundle_stringAllocator for the individual strings to be
* allocated.
+ * The caller is responsible for freeing the returned data in bytes.
*
* This function returns the size in bytes required to fit the fill list of keys.
- * The supplied buffer will be filled in based on the smaller of the suplied
+ * The supplied buffer will be filled in based on the smaller of the supplied
* bufferSizeBytes or the actual size of the stored data.
* If the buffer is null or if the supplied bufferSizeBytes is smaller than the
* actual stored data, then not all of the stored data will be returned.
@@ -886,16 +924,15 @@
* Users can call this function with null buffer and 0 bufferSizeBytes to get
* the required size of the buffer to use on a subsequent call.
*
- * \param bundle to operate on
- * \param nonnull pointer to a pre-allocated buffer to write the values to
- * \param size of the pre-allocated buffer
- * \param function pointer to the string dup allocator
+ * \param pBundle to operate on
+ * \param outKeys pointer to a pre-allocated buffer to write the UTF-8 keys to
+ * \param bufferSizeBytes size of the pre-allocated buffer
+ * \param stringAllocator function pointer to the string allocator
+ * \param context pointer that will be passed to the stringAllocator
*
* \return size of the buffer of keys in bytes. This is the required size of the
* pre-allocated user supplied buffer if all of the stored contents are desired.
- * 0 if no string vector exists for the provided key
- * -1 if the user supplied APersistableBundle_stringAllocator returns
- * false
+ * APERSISTABLEBUNDLE_ALLOCATOR_FAILED if the provided allocator fails
*/
int32_t APersistableBundle_getPersistableBundleKeys(
const APersistableBundle* _Nonnull pBundle, char* _Nullable* _Nullable outKeys,
diff --git a/libs/binder/ndk/persistable_bundle.cpp b/libs/binder/ndk/persistable_bundle.cpp
index 404611c..9b6877d 100644
--- a/libs/binder/ndk/persistable_bundle.cpp
+++ b/libs/binder/ndk/persistable_bundle.cpp
@@ -76,7 +76,7 @@
return pBundle->mPBundle.writeToParcel(AParcel_viewPlatformParcel(parcel));
}
-int32_t APersistableBundle_size(APersistableBundle* pBundle) {
+int32_t APersistableBundle_size(const APersistableBundle* pBundle) {
size_t size = pBundle->mPBundle.size();
LOG_ALWAYS_FATAL_IF(size > INT32_MAX,
"The APersistableBundle has gotten too large! There will be an overflow in "
@@ -167,40 +167,42 @@
void* context) {
android::String16 outVal;
bool ret = pBundle->mPBundle.getString(android::String16(key), &outVal);
- if (ret) {
- android::String8 tmp8(outVal);
- *val = stringAllocator(tmp8.bytes() + 1, context);
- if (*val) {
- strncpy(*val, tmp8.c_str(), tmp8.bytes() + 1);
- return tmp8.bytes();
- } else {
- return -1;
- }
+ if (!ret) return APERSISTABLEBUNDLE_KEY_NOT_FOUND;
+ android::String8 tmp8(outVal);
+ *val = stringAllocator(tmp8.bytes() + 1, context);
+ if (*val) {
+ strncpy(*val, tmp8.c_str(), tmp8.bytes() + 1);
+ return tmp8.bytes();
+ } else {
+ return APERSISTABLEBUNDLE_ALLOCATOR_FAILED;
}
- return 0;
}
int32_t APersistableBundle_getBooleanVector(const APersistableBundle* pBundle, const char* key,
bool* buffer, int32_t bufferSizeBytes) {
std::vector<bool> newVec;
- pBundle->mPBundle.getBooleanVector(android::String16(key), &newVec);
+ bool ret = pBundle->mPBundle.getBooleanVector(android::String16(key), &newVec);
+ if (!ret) return APERSISTABLEBUNDLE_KEY_NOT_FOUND;
return getVecInternal<bool>(newVec, buffer, bufferSizeBytes);
}
int32_t APersistableBundle_getIntVector(const APersistableBundle* pBundle, const char* key,
int32_t* buffer, int32_t bufferSizeBytes) {
std::vector<int32_t> newVec;
- pBundle->mPBundle.getIntVector(android::String16(key), &newVec);
+ bool ret = pBundle->mPBundle.getIntVector(android::String16(key), &newVec);
+ if (!ret) return APERSISTABLEBUNDLE_KEY_NOT_FOUND;
return getVecInternal<int32_t>(newVec, buffer, bufferSizeBytes);
}
int32_t APersistableBundle_getLongVector(const APersistableBundle* pBundle, const char* key,
int64_t* buffer, int32_t bufferSizeBytes) {
std::vector<int64_t> newVec;
- pBundle->mPBundle.getLongVector(android::String16(key), &newVec);
+ bool ret = pBundle->mPBundle.getLongVector(android::String16(key), &newVec);
+ if (!ret) return APERSISTABLEBUNDLE_KEY_NOT_FOUND;
return getVecInternal<int64_t>(newVec, buffer, bufferSizeBytes);
}
int32_t APersistableBundle_getDoubleVector(const APersistableBundle* pBundle, const char* key,
double* buffer, int32_t bufferSizeBytes) {
std::vector<double> newVec;
- pBundle->mPBundle.getDoubleVector(android::String16(key), &newVec);
+ bool ret = pBundle->mPBundle.getDoubleVector(android::String16(key), &newVec);
+ if (!ret) return APERSISTABLEBUNDLE_KEY_NOT_FOUND;
return getVecInternal<double>(newVec, buffer, bufferSizeBytes);
}
int32_t APersistableBundle_getStringVector(const APersistableBundle* pBundle, const char* key,
@@ -208,7 +210,8 @@
APersistableBundle_stringAllocator stringAllocator,
void* context) {
std::vector<android::String16> newVec;
- pBundle->mPBundle.getStringVector(android::String16(key), &newVec);
+ bool ret = pBundle->mPBundle.getStringVector(android::String16(key), &newVec);
+ if (!ret) return APERSISTABLEBUNDLE_KEY_NOT_FOUND;
return getStringsInternal<std::vector<android::String16>>(newVec, vec, bufferSizeBytes,
stringAllocator, context);
}
diff --git a/libs/binder/ndk/persistable_bundle_internal.h b/libs/binder/ndk/persistable_bundle_internal.h
index 279c66f..bee10fd 100644
--- a/libs/binder/ndk/persistable_bundle_internal.h
+++ b/libs/binder/ndk/persistable_bundle_internal.h
@@ -61,7 +61,7 @@
int32_t numAvailable = bufferSizeBytes / sizeof(char*);
int32_t numFill = numAvailable < num ? numAvailable : num;
if (!stringAllocator) {
- return -1;
+ return APERSISTABLEBUNDLE_ALLOCATOR_FAILED;
}
if (numFill > 0 && buffer) {
@@ -70,7 +70,7 @@
android::String8 tmp8 = android::String8(val);
buffer[i] = stringAllocator(tmp8.bytes() + 1, context);
if (buffer[i] == nullptr) {
- return -1;
+ return APERSISTABLEBUNDLE_ALLOCATOR_FAILED;
}
strncpy(buffer[i], tmp8.c_str(), tmp8.bytes() + 1);
i++;
diff --git a/libs/binder/tests/binderThroughputTest.cpp b/libs/binder/tests/binderThroughputTest.cpp
index d7f6318..10912c7 100644
--- a/libs/binder/tests/binderThroughputTest.cpp
+++ b/libs/binder/tests/binderThroughputTest.cpp
@@ -220,29 +220,34 @@
workers.push_back(serviceMgr->waitForService(generateServiceName(i)));
}
- // Run the benchmark if client
+ p.signal();
+ p.wait();
+
ProcResults results(iterations);
-
chrono::time_point<chrono::high_resolution_clock> start, end;
- for (int i = 0; (!cs_pair || num >= server_count) && i < iterations; i++) {
- Parcel data, reply;
- int target = cs_pair ? num % server_count : rand() % workers.size();
- int sz = payload_size;
- while (sz >= sizeof(uint32_t)) {
- data.writeInt32(0);
- sz -= sizeof(uint32_t);
- }
- start = chrono::high_resolution_clock::now();
- status_t ret = workers[target]->transact(BINDER_NOP, data, &reply);
- end = chrono::high_resolution_clock::now();
+ // Skip the benchmark if server of a cs_pair.
+ if (!(cs_pair && num < server_count)) {
+ for (int i = 0; i < iterations; i++) {
+ Parcel data, reply;
+ int target = cs_pair ? num % server_count : rand() % workers.size();
+ int sz = payload_size;
- uint64_t cur_time = uint64_t(chrono::duration_cast<chrono::nanoseconds>(end - start).count());
- results.add_time(cur_time);
+ while (sz >= sizeof(uint32_t)) {
+ data.writeInt32(0);
+ sz -= sizeof(uint32_t);
+ }
+ start = chrono::high_resolution_clock::now();
+ status_t ret = workers[target]->transact(BINDER_NOP, data, &reply);
+ end = chrono::high_resolution_clock::now();
- if (ret != NO_ERROR) {
- cout << "thread " << num << " failed " << ret << "i : " << i << endl;
- exit(EXIT_FAILURE);
+ uint64_t cur_time = uint64_t(chrono::duration_cast<chrono::nanoseconds>(end - start).count());
+ results.add_time(cur_time);
+
+ if (ret != NO_ERROR) {
+ cout << "thread " << num << " failed " << ret << "i : " << i << endl;
+ exit(EXIT_FAILURE);
+ }
}
}
@@ -300,8 +305,15 @@
pipes.push_back(make_worker(i, iterations, workers, payload_size, cs_pair));
}
wait_all(pipes);
+ // All workers have now been spawned and added themselves to service
+ // manager. Signal each worker to obtain a handle to the server workers from
+ // servicemanager.
+ signal_all(pipes);
+ // Wait for each worker to finish obtaining a handle to all server workers
+ // from servicemanager.
+ wait_all(pipes);
- // Run the workers and wait for completion.
+ // Run the benchmark and wait for completion.
chrono::time_point<chrono::high_resolution_clock> start, end;
cout << "waiting for workers to complete" << endl;
start = chrono::high_resolution_clock::now();
diff --git a/libs/binder/tests/format.h b/libs/binder/tests/format.h
index b5440a4..c588de7 100644
--- a/libs/binder/tests/format.h
+++ b/libs/binder/tests/format.h
@@ -18,7 +18,7 @@
// ETA for this blocker is 2023-10-27~2023-11-10.
// Also, remember to remove fmtlib's format.cc from trusty makefiles.
-#if __has_include(<format>)
+#if __has_include(<format>) && !defined(_LIBCPP_HAS_NO_INCOMPLETE_FORMAT)
#include <format>
#else
#include <fmt/format.h>
diff --git a/libs/gui/WindowInfo.cpp b/libs/gui/WindowInfo.cpp
index ba1d196..95b2641 100644
--- a/libs/gui/WindowInfo.cpp
+++ b/libs/gui/WindowInfo.cpp
@@ -258,8 +258,7 @@
mInfo = handle->mInfo;
}
-std::ostream& operator<<(std::ostream& out, const WindowInfoHandle& window) {
- const WindowInfo& info = *window.getInfo();
+std::ostream& operator<<(std::ostream& out, const WindowInfo& info) {
std::string transform;
info.transform.dump(transform, "transform", " ");
out << "name=" << info.name << ", id=" << info.id << ", displayId=" << info.displayId
@@ -277,4 +276,10 @@
return out;
}
+std::ostream& operator<<(std::ostream& out, const WindowInfoHandle& window) {
+ const WindowInfo& info = *window.getInfo();
+ out << info;
+ return out;
+}
+
} // namespace android::gui
diff --git a/libs/gui/include/gui/WindowInfo.h b/libs/gui/include/gui/WindowInfo.h
index b72b71a..e72fd59 100644
--- a/libs/gui/include/gui/WindowInfo.h
+++ b/libs/gui/include/gui/WindowInfo.h
@@ -269,6 +269,8 @@
status_t readFromParcel(const android::Parcel* parcel) override;
};
+std::ostream& operator<<(std::ostream& out, const WindowInfo& window);
+
/*
* Handle for a window that can receive input.
*
diff --git a/libs/input/Android.bp b/libs/input/Android.bp
index dd8dc8d..b74c3b2 100644
--- a/libs/input/Android.bp
+++ b/libs/input/Android.bp
@@ -220,7 +220,7 @@
"libPlatformProperties",
"libtinyxml2",
"libutils",
- "libvintf",
+ "libz", // needed by libkernelconfigs
"server_configurable_flags",
],
@@ -239,6 +239,7 @@
"libgui_window_info_static",
"libui-types",
"libtflite_static",
+ "libkernelconfigs",
],
whole_static_libs: [
diff --git a/libs/input/KeyLayoutMap.cpp b/libs/input/KeyLayoutMap.cpp
index 3c1ae3e..ab8c341 100644
--- a/libs/input/KeyLayoutMap.cpp
+++ b/libs/input/KeyLayoutMap.cpp
@@ -27,8 +27,7 @@
#include <utils/Timers.h>
#include <utils/Tokenizer.h>
#if defined(__ANDROID__)
-#include <vintf/RuntimeInfo.h>
-#include <vintf/VintfObject.h>
+#include <vintf/KernelConfigs.h>
#endif
#include <cstdlib>
@@ -98,12 +97,10 @@
bool kernelConfigsArePresent(const std::set<std::string>& configs) {
#if defined(__ANDROID__)
- std::shared_ptr<const android::vintf::RuntimeInfo> runtimeInfo =
- android::vintf::VintfObject::GetInstance()->getRuntimeInfo(
- vintf::RuntimeInfo::FetchFlag::CONFIG_GZ);
- LOG_ALWAYS_FATAL_IF(runtimeInfo == nullptr, "Kernel configs could not be fetched");
+ std::map<std::string, std::string> kernelConfigs;
+ const status_t result = android::kernelconfigs::LoadKernelConfigs(&kernelConfigs);
+ LOG_ALWAYS_FATAL_IF(result != OK, "Kernel configs could not be fetched");
- const std::map<std::string, std::string>& kernelConfigs = runtimeInfo->kernelConfigs();
for (const std::string& requiredConfig : configs) {
const auto configIt = kernelConfigs.find(requiredConfig);
if (configIt == kernelConfigs.end()) {
diff --git a/libs/input/VirtualInputDevice.cpp b/libs/input/VirtualInputDevice.cpp
index db7031a..eea06f1 100644
--- a/libs/input/VirtualInputDevice.cpp
+++ b/libs/input/VirtualInputDevice.cpp
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2023 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.
@@ -39,7 +39,9 @@
}
namespace android {
+
VirtualInputDevice::VirtualInputDevice(unique_fd fd) : mFd(std::move(fd)) {}
+
VirtualInputDevice::~VirtualInputDevice() {
ioctl(mFd, UI_DEV_DESTROY);
}
@@ -56,7 +58,7 @@
return TEMP_FAILURE_RETRY(write(mFd, &ev, sizeof(struct input_event))) == sizeof(ev);
}
-/** Utility method to write keyboard key events or mouse button events. */
+/** Utility method to write keyboard key events or mouse/stylus button events. */
bool VirtualInputDevice::writeEvKeyEvent(int32_t androidCode, int32_t androidAction,
const std::map<int, int>& evKeyCodeMapping,
const std::map<int, UinputAction>& actionMapping,
@@ -68,13 +70,17 @@
}
auto actionIterator = actionMapping.find(androidAction);
if (actionIterator == actionMapping.end()) {
+ ALOGE("Unsupported native action for android action %d", androidAction);
return false;
}
- if (!writeInputEvent(EV_KEY, static_cast<uint16_t>(evKeyCodeIterator->second),
- static_cast<int32_t>(actionIterator->second), eventTime)) {
+ int32_t action = static_cast<int32_t>(actionIterator->second);
+ uint16_t evKeyCode = static_cast<uint16_t>(evKeyCodeIterator->second);
+ if (!writeInputEvent(EV_KEY, evKeyCode, action, eventTime)) {
+ ALOGE("Failed to write native action %d and EV keycode %u.", action, evKeyCode);
return false;
}
if (!writeInputEvent(EV_SYN, SYN_REPORT, 0, eventTime)) {
+ ALOGE("Failed to write SYN_REPORT for EV_KEY event.");
return false;
}
return true;
@@ -85,6 +91,7 @@
{AKEY_EVENT_ACTION_DOWN, UinputAction::PRESS},
{AKEY_EVENT_ACTION_UP, UinputAction::RELEASE},
};
+
// Keycode mapping from https://source.android.com/devices/input/keyboard-devices
const std::map<int, int> VirtualKeyboard::KEY_CODE_MAPPING = {
{AKEYCODE_0, KEY_0},
@@ -195,7 +202,9 @@
{AKEYCODE_NUMPAD_COMMA, KEY_KPCOMMA},
{AKEYCODE_LANGUAGE_SWITCH, KEY_LANGUAGE},
};
+
VirtualKeyboard::VirtualKeyboard(unique_fd fd) : VirtualInputDevice(std::move(fd)) {}
+
VirtualKeyboard::~VirtualKeyboard() {}
bool VirtualKeyboard::writeKeyEvent(int32_t androidKeyCode, int32_t androidAction,
@@ -275,6 +284,7 @@
{AMOTION_EVENT_ACTION_MOVE, UinputAction::MOVE},
{AMOTION_EVENT_ACTION_CANCEL, UinputAction::CANCEL},
};
+
// Tool type mapping from https://source.android.com/devices/input/touch-devices
const std::map<int, int> VirtualTouchscreen::TOOL_TYPE_MAPPING = {
{AMOTION_EVENT_TOOL_TYPE_FINGER, MT_TOOL_FINGER},
@@ -393,4 +403,110 @@
return true;
}
+// --- VirtualStylus ---
+const std::map<int, int> VirtualStylus::TOOL_TYPE_MAPPING = {
+ {AMOTION_EVENT_TOOL_TYPE_STYLUS, BTN_TOOL_PEN},
+ {AMOTION_EVENT_TOOL_TYPE_ERASER, BTN_TOOL_RUBBER},
+};
+
+// Button code mapping from https://source.android.com/devices/input/touch-devices
+const std::map<int, int> VirtualStylus::BUTTON_CODE_MAPPING = {
+ {AMOTION_EVENT_BUTTON_STYLUS_PRIMARY, BTN_STYLUS},
+ {AMOTION_EVENT_BUTTON_STYLUS_SECONDARY, BTN_STYLUS2},
+};
+
+VirtualStylus::VirtualStylus(unique_fd fd)
+ : VirtualInputDevice(std::move(fd)), mIsStylusDown(false) {}
+
+VirtualStylus::~VirtualStylus() {}
+
+bool VirtualStylus::writeMotionEvent(int32_t toolType, int32_t action, int32_t locationX,
+ int32_t locationY, int32_t pressure, int32_t tiltX,
+ int32_t tiltY, std::chrono::nanoseconds eventTime) {
+ auto actionIterator = VirtualTouchscreen::TOUCH_ACTION_MAPPING.find(action);
+ if (actionIterator == VirtualTouchscreen::TOUCH_ACTION_MAPPING.end()) {
+ ALOGE("Unsupported action passed for stylus: %d.", action);
+ return false;
+ }
+ UinputAction uinputAction = actionIterator->second;
+ auto toolTypeIterator = TOOL_TYPE_MAPPING.find(toolType);
+ if (toolTypeIterator == TOOL_TYPE_MAPPING.end()) {
+ ALOGE("Unsupported tool type passed for stylus: %d.", toolType);
+ return false;
+ }
+ uint16_t tool = static_cast<uint16_t>(toolTypeIterator->second);
+ if (uinputAction == UinputAction::PRESS && !handleStylusDown(tool, eventTime)) {
+ return false;
+ }
+ if (!mIsStylusDown) {
+ ALOGE("Action UP or MOVE received with no prior action DOWN for stylus %d.", mFd.get());
+ return false;
+ }
+ if (uinputAction == UinputAction::RELEASE && !handleStylusUp(tool, eventTime)) {
+ return false;
+ }
+ if (!writeInputEvent(EV_ABS, ABS_X, locationX, eventTime)) {
+ ALOGE("Unsupported x-axis location passed for stylus: %d.", locationX);
+ return false;
+ }
+ if (!writeInputEvent(EV_ABS, ABS_Y, locationY, eventTime)) {
+ ALOGE("Unsupported y-axis location passed for stylus: %d.", locationY);
+ return false;
+ }
+ if (!writeInputEvent(EV_ABS, ABS_TILT_X, tiltX, eventTime)) {
+ ALOGE("Unsupported x-axis tilt passed for stylus: %d.", tiltX);
+ return false;
+ }
+ if (!writeInputEvent(EV_ABS, ABS_TILT_Y, tiltY, eventTime)) {
+ ALOGE("Unsupported y-axis tilt passed for stylus: %d.", tiltY);
+ return false;
+ }
+ if (!writeInputEvent(EV_ABS, ABS_PRESSURE, pressure, eventTime)) {
+ ALOGE("Unsupported pressure passed for stylus: %d.", pressure);
+ return false;
+ }
+ if (!writeInputEvent(EV_SYN, SYN_REPORT, 0, eventTime)) {
+ ALOGE("Failed to write SYN_REPORT for stylus motion event.");
+ return false;
+ }
+ return true;
+}
+
+bool VirtualStylus::writeButtonEvent(int32_t androidButtonCode, int32_t androidAction,
+ std::chrono::nanoseconds eventTime) {
+ return writeEvKeyEvent(androidButtonCode, androidAction, BUTTON_CODE_MAPPING,
+ VirtualMouse::BUTTON_ACTION_MAPPING, eventTime);
+}
+
+bool VirtualStylus::handleStylusDown(uint16_t tool, std::chrono::nanoseconds eventTime) {
+ if (mIsStylusDown) {
+ ALOGE("Repetitive action DOWN event received for a stylus that is already down.");
+ return false;
+ }
+ if (!writeInputEvent(EV_KEY, tool, static_cast<int32_t>(UinputAction::PRESS), eventTime)) {
+ ALOGE("Failed to write EV_KEY for stylus tool type: %u.", tool);
+ return false;
+ }
+ if (!writeInputEvent(EV_KEY, BTN_TOUCH, static_cast<int32_t>(UinputAction::PRESS), eventTime)) {
+ ALOGE("Failed to write BTN_TOUCH for stylus press.");
+ return false;
+ }
+ mIsStylusDown = true;
+ return true;
+}
+
+bool VirtualStylus::handleStylusUp(uint16_t tool, std::chrono::nanoseconds eventTime) {
+ if (!writeInputEvent(EV_KEY, tool, static_cast<int32_t>(UinputAction::RELEASE), eventTime)) {
+ ALOGE("Failed to write EV_KEY for stylus tool type: %u.", tool);
+ return false;
+ }
+ if (!writeInputEvent(EV_KEY, BTN_TOUCH, static_cast<int32_t>(UinputAction::RELEASE),
+ eventTime)) {
+ ALOGE("Failed to write BTN_TOUCH for stylus release.");
+ return false;
+ }
+ mIsStylusDown = false;
+ return true;
+}
+
} // namespace android
diff --git a/libs/input/tests/Android.bp b/libs/input/tests/Android.bp
index 138898f..13cfb49 100644
--- a/libs/input/tests/Android.bp
+++ b/libs/input/tests/Android.bp
@@ -36,8 +36,10 @@
"libgmock",
"libgui_window_info_static",
"libinput",
+ "libkernelconfigs",
"libtflite_static",
"libui-types",
+ "libz", // needed by libkernelconfigs
],
cflags: [
"-Wall",
@@ -61,7 +63,6 @@
"libPlatformProperties",
"libtinyxml2",
"libutils",
- "libvintf",
"server_configurable_flags",
],
data: [
diff --git a/libs/renderengine/skia/AutoBackendTexture.cpp b/libs/renderengine/skia/AutoBackendTexture.cpp
index 92fe4c0..ee95e59 100644
--- a/libs/renderengine/skia/AutoBackendTexture.cpp
+++ b/libs/renderengine/skia/AutoBackendTexture.cpp
@@ -77,7 +77,7 @@
backendFormat,
isOutputBuffer);
} else {
- LOG_ALWAYS_FATAL("Unexpected backend %d", backend);
+ LOG_ALWAYS_FATAL("Unexpected backend %u", static_cast<unsigned>(backend));
}
mColorType = GrAHardwareBufferUtils::GetSkColorTypeFromBufferFormat(desc.format);
@@ -145,8 +145,8 @@
"\n\tGrBackendTexture: (%i x %i) hasMipmaps: %i isProtected: %i "
"texType: %i\n\t\tVkImageInfo: success: %i fFormat: %i "
"fSampleCount: %u fLevelCount: %u colorType %i",
- msg, tex.isValid(), dataspace, tex.width(), tex.height(),
- tex.hasMipmaps(), tex.isProtected(),
+ msg, tex.isValid(), static_cast<int32_t>(dataspace), tex.width(),
+ tex.height(), tex.hasMipmaps(), tex.isProtected(),
static_cast<int>(tex.textureType()), retrievedImageInfo,
imageInfo.fFormat, imageInfo.fSampleCount, imageInfo.fLevelCount,
colorType);
diff --git a/libs/sensor/SensorManager.cpp b/libs/sensor/SensorManager.cpp
index f8ee3fc..b82a79f 100644
--- a/libs/sensor/SensorManager.cpp
+++ b/libs/sensor/SensorManager.cpp
@@ -88,49 +88,51 @@
SensorManager* sensorManager;
auto iterator = sPackageInstances.find(packageName);
+ const uid_t uid = IPCThreadState::self()->getCallingUid();
+ const int deviceId = getDeviceIdForUid(uid);
+
+ // Return the cached instance if the device association of the package has not changed.
if (iterator != sPackageInstances.end()) {
sensorManager = iterator->second;
- } else {
- String16 opPackageName = packageName;
- const uid_t uid = IPCThreadState::self()->getCallingUid();
-
- // It is possible that the calling code has no access to the package name.
- // In this case we will get the packages for the calling UID and pick the
- // first one for attributing the app op. This will work correctly for
- // runtime permissions as for legacy apps we will toggle the app op for
- // all packages in the UID. The caveat is that the operation may be attributed
- // to the wrong package and stats based on app ops may be slightly off.
- if (opPackageName.size() <= 0) {
- sp<IBinder> binder = defaultServiceManager()->getService(String16("permission"));
- if (binder != nullptr) {
- Vector<String16> packages;
- interface_cast<IPermissionController>(binder)->getPackagesForUid(uid, packages);
- if (!packages.isEmpty()) {
- opPackageName = packages[0];
- } else {
- ALOGE("No packages for calling UID");
- }
- } else {
- ALOGE("Cannot get permission service");
- }
+ if (sensorManager->mDeviceId == deviceId) {
+ return *sensorManager;
}
-
- // Check if the calling UID is observed on a virtual device. If so, provide that device's
- // sensors by default instead of the default device's sensors.
- const int deviceId = getDeviceIdForUid(uid);
- sensorManager = new SensorManager(opPackageName, deviceId);
-
- // If we had no package name, we looked it up from the UID and the sensor
- // manager instance we created should also be mapped to the empty package
- // name, to avoid looking up the packages for a UID and get the same result.
- if (packageName.size() <= 0) {
- sPackageInstances.insert(std::make_pair(String16(), sensorManager));
- }
-
- // Stash the per package sensor manager.
- sPackageInstances.insert(std::make_pair(opPackageName, sensorManager));
}
+ // It is possible that the calling code has no access to the package name.
+ // In this case we will get the packages for the calling UID and pick the
+ // first one for attributing the app op. This will work correctly for
+ // runtime permissions as for legacy apps we will toggle the app op for
+ // all packages in the UID. The caveat is that the operation may be attributed
+ // to the wrong package and stats based on app ops may be slightly off.
+ String16 opPackageName = packageName;
+ if (opPackageName.size() <= 0) {
+ sp<IBinder> binder = defaultServiceManager()->getService(String16("permission"));
+ if (binder != nullptr) {
+ Vector<String16> packages;
+ interface_cast<IPermissionController>(binder)->getPackagesForUid(uid, packages);
+ if (!packages.isEmpty()) {
+ opPackageName = packages[0];
+ } else {
+ ALOGE("No packages for calling UID");
+ }
+ } else {
+ ALOGE("Cannot get permission service");
+ }
+ }
+
+ sensorManager = new SensorManager(opPackageName, deviceId);
+
+ // If we had no package name, we looked it up from the UID and the sensor
+ // manager instance we created should also be mapped to the empty package
+ // name, to avoid looking up the packages for a UID and get the same result.
+ if (packageName.size() <= 0) {
+ sPackageInstances.insert(std::make_pair(String16(), sensorManager));
+ }
+
+ // Stash the per package sensor manager.
+ sPackageInstances.insert(std::make_pair(opPackageName, sensorManager));
+
return *sensorManager;
}
diff --git a/services/inputflinger/Android.bp b/services/inputflinger/Android.bp
index 69f42bc..a7955cf 100644
--- a/services/inputflinger/Android.bp
+++ b/services/inputflinger/Android.bp
@@ -104,6 +104,8 @@
"libprotobuf-cpp-lite",
"libstatslog",
"libutils",
+ "libstatspull",
+ "libstatssocket",
"server_configurable_flags",
],
static_libs: [
@@ -123,14 +125,6 @@
android: {
shared_libs: [
"libgui",
- "libstatspull",
- "libstatssocket",
- ],
- },
- host: {
- static_libs: [
- "libstatspull",
- "libstatssocket",
],
},
},
diff --git a/services/inputflinger/dispatcher/Android.bp b/services/inputflinger/dispatcher/Android.bp
index 8b57730..c7bacee 100644
--- a/services/inputflinger/dispatcher/Android.bp
+++ b/services/inputflinger/dispatcher/Android.bp
@@ -65,6 +65,8 @@
"libprotobuf-cpp-lite",
"libstatslog",
"libutils",
+ "libstatspull",
+ "libstatssocket",
"server_configurable_flags",
],
static_libs: [
@@ -75,14 +77,6 @@
android: {
shared_libs: [
"libgui",
- "libstatspull",
- "libstatssocket",
- ],
- },
- host: {
- static_libs: [
- "libstatspull",
- "libstatssocket",
],
},
},
diff --git a/services/inputflinger/dispatcher/InputDispatcher.cpp b/services/inputflinger/dispatcher/InputDispatcher.cpp
index 162a7bd..e998d91 100644
--- a/services/inputflinger/dispatcher/InputDispatcher.cpp
+++ b/services/inputflinger/dispatcher/InputDispatcher.cpp
@@ -763,6 +763,21 @@
return state.hasActiveStylus();
}
+Result<void> validateWindowInfosUpdate(const gui::WindowInfosUpdate& update) {
+ struct HashFunction {
+ size_t operator()(const WindowInfo& info) const { return info.id; }
+ };
+
+ std::unordered_set<WindowInfo, HashFunction> windowSet;
+ for (const WindowInfo& info : update.windowInfos) {
+ const auto [_, inserted] = windowSet.insert(info);
+ if (!inserted) {
+ return Error() << "Duplicate entry for " << info;
+ }
+ }
+ return {};
+}
+
} // namespace
// --- InputDispatcher ---
@@ -3841,8 +3856,7 @@
connection->getInputChannelName().c_str(), seq, toString(handled));
}
- if (connection->status == Connection::Status::BROKEN ||
- connection->status == Connection::Status::ZOMBIE) {
+ if (connection->status != Connection::Status::NORMAL) {
return;
}
@@ -4003,7 +4017,7 @@
void InputDispatcher::synthesizeCancelationEventsForConnectionLocked(
const std::shared_ptr<Connection>& connection, const CancelationOptions& options) {
- if (connection->status == Connection::Status::BROKEN) {
+ if (connection->status != Connection::Status::NORMAL) {
return;
}
@@ -4115,7 +4129,7 @@
void InputDispatcher::synthesizePointerDownEventsForConnectionLocked(
const nsecs_t downTime, const std::shared_ptr<Connection>& connection,
ftl::Flags<InputTarget::Flags> targetFlags) {
- if (connection->status == Connection::Status::BROKEN) {
+ if (connection->status != Connection::Status::NORMAL) {
return;
}
@@ -6762,6 +6776,15 @@
}
void InputDispatcher::onWindowInfosChanged(const gui::WindowInfosUpdate& update) {
+ if (auto result = validateWindowInfosUpdate(update); !result.ok()) {
+ {
+ // acquire lock
+ std::scoped_lock _l(mLock);
+ logDispatchStateLocked();
+ }
+ LOG_ALWAYS_FATAL("Incorrect WindowInfosUpdate provided: %s",
+ result.error().message().c_str());
+ };
// The listener sends the windows as a flattened array. Separate the windows by display for
// more convenient parsing.
std::unordered_map<int32_t, std::vector<sp<WindowInfoHandle>>> handlesPerDisplay;
diff --git a/services/inputflinger/reader/Android.bp b/services/inputflinger/reader/Android.bp
index e1806a0..f954370 100644
--- a/services/inputflinger/reader/Android.bp
+++ b/services/inputflinger/reader/Android.bp
@@ -86,6 +86,7 @@
"liblog",
"libPlatformProperties",
"libstatslog",
+ "libstatspull",
"libutils",
],
static_libs: [
@@ -99,15 +100,9 @@
"libinputreader_headers",
],
target: {
- android: {
- shared_libs: [
- "libstatspull",
- ],
- },
host: {
static_libs: [
"libbinder",
- "libstatspull",
],
},
},
diff --git a/services/inputflinger/reader/EventHub.cpp b/services/inputflinger/reader/EventHub.cpp
index f7bbc51..3ca691e 100644
--- a/services/inputflinger/reader/EventHub.cpp
+++ b/services/inputflinger/reader/EventHub.cpp
@@ -810,14 +810,20 @@
case EV_SYN: {
switch (event.code) {
case SYN_REPORT:
- currentFrameDropped = false;
+ if (currentFrameDropped) {
+ // To recover after a SYN_DROPPED, we need to query the state of the device
+ // to synchronize our device state with the kernel's to account for the
+ // dropped events on receiving the next SYN_REPORT.
+ // Note we don't drop the SYN_REPORT at this point but it is used by the
+ // InputDevice to reset and repopulate mapper state
+ readDeviceState();
+ currentFrameDropped = false;
+ }
break;
case SYN_DROPPED:
// When we receive SYN_DROPPED, all events in the current frame should be
- // dropped. We query the state of the device to synchronize our device state
- // with the kernel's to account for the dropped events.
+ // dropped up to and including next SYN_REPORT
currentFrameDropped = true;
- readDeviceState();
break;
default:
break;
@@ -1141,6 +1147,22 @@
return OK;
}
+base::Result<std::vector<int32_t>> EventHub::getMtSlotValues(int32_t deviceId, int32_t axis,
+ size_t slotCount) const {
+ std::scoped_lock _l(mLock);
+ const Device* device = getDeviceLocked(deviceId);
+ if (device == nullptr || !device->hasValidFd() || !device->absBitmask.test(axis)) {
+ return base::ResultError("device problem or axis not supported", NAME_NOT_FOUND);
+ }
+ std::vector<int32_t> outValues(slotCount + 1);
+ outValues[0] = axis;
+ const size_t bufferSize = outValues.size() * sizeof(int32_t);
+ if (ioctl(device->fd, EVIOCGMTSLOTS(bufferSize), outValues.data()) != OK) {
+ return base::ErrnoError();
+ }
+ return std::move(outValues);
+}
+
bool EventHub::markSupportedKeyCodes(int32_t deviceId, const std::vector<int32_t>& keyCodes,
uint8_t* outFlags) const {
std::scoped_lock _l(mLock);
diff --git a/services/inputflinger/reader/InputDevice.cpp b/services/inputflinger/reader/InputDevice.cpp
index fb32f96..a41064b 100644
--- a/services/inputflinger/reader/InputDevice.cpp
+++ b/services/inputflinger/reader/InputDevice.cpp
@@ -350,6 +350,7 @@
if (mDropUntilNextSync) {
if (rawEvent->type == EV_SYN && rawEvent->code == SYN_REPORT) {
+ out += reset(rawEvent->when);
mDropUntilNextSync = false;
ALOGD_IF(debugRawEvents(), "Recovered from input event buffer overrun.");
} else {
@@ -359,7 +360,6 @@
} else if (rawEvent->type == EV_SYN && rawEvent->code == SYN_DROPPED) {
ALOGI("Detected input event buffer overrun for device %s.", getName().c_str());
mDropUntilNextSync = true;
- out += reset(rawEvent->when);
} else {
for_each_mapper_in_subdevice(rawEvent->deviceId, [&](InputMapper& mapper) {
out += mapper.process(rawEvent);
diff --git a/services/inputflinger/reader/include/EventHub.h b/services/inputflinger/reader/include/EventHub.h
index 0bcab42..a7e0675 100644
--- a/services/inputflinger/reader/include/EventHub.h
+++ b/services/inputflinger/reader/include/EventHub.h
@@ -336,6 +336,10 @@
virtual int32_t getSwitchState(int32_t deviceId, int32_t sw) const = 0;
virtual status_t getAbsoluteAxisValue(int32_t deviceId, int32_t axis,
int32_t* outValue) const = 0;
+ /* Query Multi-Touch slot values for an axis. Returns error or an 1 indexed array of size
+ * (slotCount + 1). The value at the 0 index is set to queried axis. */
+ virtual base::Result<std::vector<int32_t>> getMtSlotValues(int32_t deviceId, int32_t axis,
+ size_t slotCount) const = 0;
virtual int32_t getKeyCodeForKeyLocation(int32_t deviceId, int32_t locationKeyCode) const = 0;
/*
@@ -552,6 +556,8 @@
int32_t locationKeyCode) const override final;
status_t getAbsoluteAxisValue(int32_t deviceId, int32_t axis,
int32_t* outValue) const override final;
+ base::Result<std::vector<int32_t>> getMtSlotValues(int32_t deviceId, int32_t axis,
+ size_t slotCount) const override final;
bool markSupportedKeyCodes(int32_t deviceId, const std::vector<int32_t>& keyCodes,
uint8_t* outFlags) const override final;
diff --git a/services/inputflinger/reader/include/InputDevice.h b/services/inputflinger/reader/include/InputDevice.h
index cbd719c..ba7234b 100644
--- a/services/inputflinger/reader/include/InputDevice.h
+++ b/services/inputflinger/reader/include/InputDevice.h
@@ -372,6 +372,10 @@
inline status_t getAbsoluteAxisValue(int32_t code, int32_t* outValue) const {
return mEventHub->getAbsoluteAxisValue(mId, code, outValue);
}
+ inline base::Result<std::vector<int32_t>> getMtSlotValues(int32_t axis,
+ size_t slotCount) const {
+ return mEventHub->getMtSlotValues(mId, axis, slotCount);
+ }
inline bool markSupportedKeyCodes(const std::vector<int32_t>& keyCodes,
uint8_t* outFlags) const {
return mEventHub->markSupportedKeyCodes(mId, keyCodes, outFlags);
diff --git a/services/inputflinger/reader/mapper/KeyboardInputMapper.cpp b/services/inputflinger/reader/mapper/KeyboardInputMapper.cpp
index f068cc8..9e7e956 100644
--- a/services/inputflinger/reader/mapper/KeyboardInputMapper.cpp
+++ b/services/inputflinger/reader/mapper/KeyboardInputMapper.cpp
@@ -61,6 +61,36 @@
scanCode >= BTN_WHEEL;
}
+static bool isMediaKey(int32_t keyCode) {
+ switch (keyCode) {
+ case AKEYCODE_MEDIA_PLAY:
+ case AKEYCODE_MEDIA_PAUSE:
+ case AKEYCODE_MEDIA_PLAY_PAUSE:
+ case AKEYCODE_MUTE:
+ case AKEYCODE_HEADSETHOOK:
+ case AKEYCODE_MEDIA_STOP:
+ case AKEYCODE_MEDIA_NEXT:
+ case AKEYCODE_MEDIA_PREVIOUS:
+ case AKEYCODE_MEDIA_REWIND:
+ case AKEYCODE_MEDIA_RECORD:
+ case AKEYCODE_MEDIA_FAST_FORWARD:
+ case AKEYCODE_MEDIA_SKIP_FORWARD:
+ case AKEYCODE_MEDIA_SKIP_BACKWARD:
+ case AKEYCODE_MEDIA_STEP_FORWARD:
+ case AKEYCODE_MEDIA_STEP_BACKWARD:
+ case AKEYCODE_MEDIA_AUDIO_TRACK:
+ case AKEYCODE_VOLUME_UP:
+ case AKEYCODE_VOLUME_DOWN:
+ case AKEYCODE_VOLUME_MUTE:
+ case AKEYCODE_TV_AUDIO_DESCRIPTION:
+ case AKEYCODE_TV_AUDIO_DESCRIPTION_MIX_UP:
+ case AKEYCODE_TV_AUDIO_DESCRIPTION_MIX_DOWN:
+ return true;
+ default:
+ return false;
+ }
+}
+
// --- KeyboardInputMapper ---
KeyboardInputMapper::KeyboardInputMapper(InputDeviceContext& deviceContext,
@@ -301,7 +331,8 @@
// For internal keyboards and devices for which the default wake behavior is explicitly
// prevented (e.g. TV remotes), the key layout file should specify the policy flags for each
// wake key individually.
- if (down && getDeviceContext().isExternal() && !mParameters.doNotWakeByDefault) {
+ if (down && getDeviceContext().isExternal() && !mParameters.doNotWakeByDefault &&
+ !(mKeyboardType != AINPUT_KEYBOARD_TYPE_ALPHABETIC && isMediaKey(keyCode))) {
policyFlags |= POLICY_FLAG_WAKE;
}
diff --git a/services/inputflinger/reader/mapper/MultiTouchInputMapper.cpp b/services/inputflinger/reader/mapper/MultiTouchInputMapper.cpp
index 2dd05f5..0c58dab 100644
--- a/services/inputflinger/reader/mapper/MultiTouchInputMapper.cpp
+++ b/services/inputflinger/reader/mapper/MultiTouchInputMapper.cpp
@@ -35,12 +35,8 @@
MultiTouchInputMapper::~MultiTouchInputMapper() {}
std::list<NotifyArgs> MultiTouchInputMapper::reset(nsecs_t when) {
- // The evdev multi-touch protocol does not allow userspace applications to query the initial or
- // current state of the pointers at any time. This means if we clear our accumulated state when
- // resetting the input mapper, there's no way to rebuild the full initial state of the pointers.
- // We can only wait for updates to all the pointers and axes. Rather than clearing the state and
- // rebuilding the state from scratch, we work around this kernel API limitation by never
- // fully clearing any state specific to the multi-touch protocol.
+ mPointerIdBits.clear();
+ mMultiTouchMotionAccumulator.reset(mDeviceContext);
return TouchInputMapper::reset(when);
}
diff --git a/services/inputflinger/reader/mapper/accumulator/MultiTouchMotionAccumulator.cpp b/services/inputflinger/reader/mapper/accumulator/MultiTouchMotionAccumulator.cpp
index b0fc903..b3f1700 100644
--- a/services/inputflinger/reader/mapper/accumulator/MultiTouchMotionAccumulator.cpp
+++ b/services/inputflinger/reader/mapper/accumulator/MultiTouchMotionAccumulator.cpp
@@ -30,24 +30,12 @@
size_t slotCount, bool usingSlotsProtocol) {
mUsingSlotsProtocol = usingSlotsProtocol;
mSlots = std::vector<Slot>(slotCount);
+ populateCurrentSlot(deviceContext);
+}
- mCurrentSlot = -1;
- if (mUsingSlotsProtocol) {
- // Query the driver for the current slot index and use it as the initial slot before we
- // start reading events from the device. It is possible that the current slot index will
- // not be the same as it was when the first event was written into the evdev buffer, which
- // means the input mapper could start out of sync with the initial state of the events in
- // the evdev buffer. In the extremely unlikely case that this happens, the data from two
- // slots will be confused until the next ABS_MT_SLOT event is received. This can cause the
- // touch point to "jump", but at least there will be no stuck touches.
- int32_t initialSlot;
- if (const auto status = deviceContext.getAbsoluteAxisValue(ABS_MT_SLOT, &initialSlot);
- status == OK) {
- mCurrentSlot = initialSlot;
- } else {
- ALOGD("Could not retrieve current multi-touch slot index. status=%d", status);
- }
- }
+void MultiTouchMotionAccumulator::reset(const InputDeviceContext& deviceContext) {
+ resetSlots();
+ syncSlots(deviceContext);
}
void MultiTouchMotionAccumulator::resetSlots() {
@@ -84,54 +72,10 @@
if (!mUsingSlotsProtocol) {
slot.mInUse = true;
}
-
- switch (rawEvent->code) {
- case ABS_MT_POSITION_X:
- slot.mAbsMtPositionX = rawEvent->value;
- warnIfNotInUse(*rawEvent, slot);
- break;
- case ABS_MT_POSITION_Y:
- slot.mAbsMtPositionY = rawEvent->value;
- warnIfNotInUse(*rawEvent, slot);
- break;
- case ABS_MT_TOUCH_MAJOR:
- slot.mAbsMtTouchMajor = rawEvent->value;
- break;
- case ABS_MT_TOUCH_MINOR:
- slot.mAbsMtTouchMinor = rawEvent->value;
- slot.mHaveAbsMtTouchMinor = true;
- break;
- case ABS_MT_WIDTH_MAJOR:
- slot.mAbsMtWidthMajor = rawEvent->value;
- break;
- case ABS_MT_WIDTH_MINOR:
- slot.mAbsMtWidthMinor = rawEvent->value;
- slot.mHaveAbsMtWidthMinor = true;
- break;
- case ABS_MT_ORIENTATION:
- slot.mAbsMtOrientation = rawEvent->value;
- break;
- case ABS_MT_TRACKING_ID:
- if (mUsingSlotsProtocol && rawEvent->value < 0) {
- // The slot is no longer in use but it retains its previous contents,
- // which may be reused for subsequent touches.
- slot.mInUse = false;
- } else {
- slot.mInUse = true;
- slot.mAbsMtTrackingId = rawEvent->value;
- }
- break;
- case ABS_MT_PRESSURE:
- slot.mAbsMtPressure = rawEvent->value;
- break;
- case ABS_MT_DISTANCE:
- slot.mAbsMtDistance = rawEvent->value;
- break;
- case ABS_MT_TOOL_TYPE:
- slot.mAbsMtToolType = rawEvent->value;
- slot.mHaveAbsMtToolType = true;
- break;
+ if (rawEvent->code == ABS_MT_POSITION_X || rawEvent->code == ABS_MT_POSITION_Y) {
+ warnIfNotInUse(*rawEvent, slot);
}
+ slot.populateAxisValue(rawEvent->code, rawEvent->value);
}
} else if (rawEvent->type == EV_SYN && rawEvent->code == SYN_MT_REPORT) {
// MultiTouch Sync: The driver has returned all data for *one* of the pointers.
@@ -139,6 +83,36 @@
}
}
+void MultiTouchMotionAccumulator::syncSlots(const InputDeviceContext& deviceContext) {
+ if (!mUsingSlotsProtocol) {
+ return;
+ }
+ constexpr std::array<int32_t, 11> axisCodes = {ABS_MT_POSITION_X, ABS_MT_POSITION_Y,
+ ABS_MT_TOUCH_MAJOR, ABS_MT_TOUCH_MINOR,
+ ABS_MT_WIDTH_MAJOR, ABS_MT_WIDTH_MINOR,
+ ABS_MT_ORIENTATION, ABS_MT_TRACKING_ID,
+ ABS_MT_PRESSURE, ABS_MT_DISTANCE,
+ ABS_MT_TOOL_TYPE};
+ const size_t numSlots = mSlots.size();
+ for (int32_t axisCode : axisCodes) {
+ if (!deviceContext.hasAbsoluteAxis(axisCode)) {
+ continue;
+ }
+ const auto result = deviceContext.getMtSlotValues(axisCode, numSlots);
+ if (result.ok()) {
+ const std::vector<int32_t>& mtSlotValues = result.value();
+ for (size_t i = 1; i <= numSlots; ++i) {
+ // The returned slot values are in a 1-indexed vector of size numSlots + 1.
+ mSlots[i - 1].populateAxisValue(axisCode, mtSlotValues[i]);
+ }
+ } else {
+ ALOGE("Could not retrieve multi-touch slot value for axis=%d error=%s status=%d",
+ axisCode, result.error().message().c_str(), result.error().code().value());
+ }
+ }
+ populateCurrentSlot(deviceContext);
+}
+
void MultiTouchMotionAccumulator::finishSync() {
if (!mUsingSlotsProtocol) {
resetSlots();
@@ -160,6 +134,21 @@
[](const Slot& slot) { return slot.mInUse; });
}
+void MultiTouchMotionAccumulator::populateCurrentSlot(
+ const android::InputDeviceContext& deviceContext) {
+ if (!mUsingSlotsProtocol) {
+ return;
+ }
+ int32_t initialSlot;
+ if (const auto status = deviceContext.getAbsoluteAxisValue(ABS_MT_SLOT, &initialSlot);
+ status == OK) {
+ mCurrentSlot = initialSlot;
+ } else {
+ ALOGE("Could not retrieve current multi-touch slot index. status=%s",
+ statusToString(status).c_str());
+ }
+}
+
// --- MultiTouchMotionAccumulator::Slot ---
ToolType MultiTouchMotionAccumulator::Slot::getToolType() const {
@@ -176,4 +165,52 @@
return ToolType::UNKNOWN;
}
+void MultiTouchMotionAccumulator::Slot::populateAxisValue(int32_t axisCode, int32_t value) {
+ switch (axisCode) {
+ case ABS_MT_POSITION_X:
+ mAbsMtPositionX = value;
+ break;
+ case ABS_MT_POSITION_Y:
+ mAbsMtPositionY = value;
+ break;
+ case ABS_MT_TOUCH_MAJOR:
+ mAbsMtTouchMajor = value;
+ break;
+ case ABS_MT_TOUCH_MINOR:
+ mAbsMtTouchMinor = value;
+ mHaveAbsMtTouchMinor = true;
+ break;
+ case ABS_MT_WIDTH_MAJOR:
+ mAbsMtWidthMajor = value;
+ break;
+ case ABS_MT_WIDTH_MINOR:
+ mAbsMtWidthMinor = value;
+ mHaveAbsMtWidthMinor = true;
+ break;
+ case ABS_MT_ORIENTATION:
+ mAbsMtOrientation = value;
+ break;
+ case ABS_MT_TRACKING_ID:
+ if (value < 0) {
+ // The slot is no longer in use but it retains its previous contents,
+ // which may be reused for subsequent touches.
+ mInUse = false;
+ } else {
+ mInUse = true;
+ mAbsMtTrackingId = value;
+ }
+ break;
+ case ABS_MT_PRESSURE:
+ mAbsMtPressure = value;
+ break;
+ case ABS_MT_DISTANCE:
+ mAbsMtDistance = value;
+ break;
+ case ABS_MT_TOOL_TYPE:
+ mAbsMtToolType = value;
+ mHaveAbsMtToolType = true;
+ break;
+ }
+}
+
} // namespace android
diff --git a/services/inputflinger/reader/mapper/accumulator/MultiTouchMotionAccumulator.h b/services/inputflinger/reader/mapper/accumulator/MultiTouchMotionAccumulator.h
index 0e3e2bb..a0f2147 100644
--- a/services/inputflinger/reader/mapper/accumulator/MultiTouchMotionAccumulator.h
+++ b/services/inputflinger/reader/mapper/accumulator/MultiTouchMotionAccumulator.h
@@ -68,12 +68,14 @@
int32_t mAbsMtToolType = 0;
void clear() { *this = Slot(); }
+ void populateAxisValue(int32_t axisCode, int32_t value);
};
MultiTouchMotionAccumulator();
void configure(const InputDeviceContext& deviceContext, size_t slotCount,
bool usingSlotsProtocol);
+ void reset(const InputDeviceContext& deviceContext);
void process(const RawEvent* rawEvent);
void finishSync();
@@ -85,12 +87,14 @@
}
private:
- int32_t mCurrentSlot;
+ int32_t mCurrentSlot{-1};
std::vector<Slot> mSlots;
bool mUsingSlotsProtocol;
void resetSlots();
+ void syncSlots(const InputDeviceContext& deviceContext);
void warnIfNotInUse(const RawEvent& event, const Slot& slot);
+ void populateCurrentSlot(const android::InputDeviceContext& deviceContext);
};
} // namespace android
diff --git a/services/inputflinger/tests/Android.bp b/services/inputflinger/tests/Android.bp
index db31ded..55aa226 100644
--- a/services/inputflinger/tests/Android.bp
+++ b/services/inputflinger/tests/Android.bp
@@ -68,6 +68,7 @@
"TimerProvider_test.cpp",
"TestInputListener.cpp",
"TouchpadInputMapper_test.cpp",
+ "MultiTouchInputMapper_test.cpp",
"KeyboardInputMapper_test.cpp",
"UinputDevice.cpp",
"UnwantedInteractionBlocker_test.cpp",
diff --git a/services/inputflinger/tests/FakeEventHub.cpp b/services/inputflinger/tests/FakeEventHub.cpp
index 212fceb..daa000f 100644
--- a/services/inputflinger/tests/FakeEventHub.cpp
+++ b/services/inputflinger/tests/FakeEventHub.cpp
@@ -431,6 +431,38 @@
return -1;
}
+void FakeEventHub::setMtSlotValues(int32_t deviceId, int32_t axis,
+ const std::vector<int32_t>& values) {
+ Device* device = getDevice(deviceId);
+ if (!device) {
+ FAIL() << "Missing device";
+ }
+ device->mtSlotValues[axis] = values;
+}
+
+base::Result<std::vector<int32_t>> FakeEventHub::getMtSlotValues(int32_t deviceId, int32_t axis,
+ size_t slotCount) const {
+ Device* device = getDevice(deviceId);
+ if (!device) {
+ ADD_FAILURE() << "Missing device";
+ return base::ResultError("Missing device", UNKNOWN_ERROR);
+ }
+ const auto& mtSlotValuesIterator = device->mtSlotValues.find(axis);
+ if (mtSlotValuesIterator == device->mtSlotValues.end()) {
+ return base::ResultError("axis not supported", NAME_NOT_FOUND);
+ }
+ const auto& mtSlotValues = mtSlotValuesIterator->second;
+ if (mtSlotValues.size() != slotCount) {
+ ADD_FAILURE() << "MtSlot values specified for " << mtSlotValues.size()
+ << " slots but expected for " << slotCount << " Slots";
+ return base::ResultError("Slot count mismatch", NAME_NOT_FOUND);
+ }
+ std::vector<int32_t> outValues(slotCount + 1);
+ outValues[0] = axis;
+ std::copy(mtSlotValues.begin(), mtSlotValues.end(), outValues.begin() + 1);
+ return std::move(outValues);
+}
+
int32_t FakeEventHub::getKeyCodeForKeyLocation(int32_t deviceId, int32_t locationKeyCode) const {
Device* device = getDevice(deviceId);
if (!device) {
diff --git a/services/inputflinger/tests/FakeEventHub.h b/services/inputflinger/tests/FakeEventHub.h
index 8e06940..f07b344 100644
--- a/services/inputflinger/tests/FakeEventHub.h
+++ b/services/inputflinger/tests/FakeEventHub.h
@@ -65,6 +65,7 @@
bool enabled;
std::optional<RawLayoutInfo> layoutInfo;
std::string sysfsRootPath;
+ std::unordered_map<int32_t, std::vector<int32_t>> mtSlotValues;
status_t enable() {
enabled = true;
@@ -154,6 +155,11 @@
int32_t value);
void assertQueueIsEmpty();
void setSysfsRootPath(int32_t deviceId, std::string sysfsRootPath) const;
+ // Populate fake slot values to be returned by the getter, size of the values should be equal to
+ // the slot count
+ void setMtSlotValues(int32_t deviceId, int32_t axis, const std::vector<int32_t>& values);
+ base::Result<std::vector<int32_t>> getMtSlotValues(int32_t deviceId, int32_t axis,
+ size_t slotCount) const override;
private:
Device* getDevice(int32_t deviceId) const;
diff --git a/services/inputflinger/tests/InputDispatcher_test.cpp b/services/inputflinger/tests/InputDispatcher_test.cpp
index 8be1b3b..48d7e55 100644
--- a/services/inputflinger/tests/InputDispatcher_test.cpp
+++ b/services/inputflinger/tests/InputDispatcher_test.cpp
@@ -1680,6 +1680,24 @@
window->consumeMotionDown(ADISPLAY_ID_DEFAULT);
}
+using InputDispatcherDeathTest = InputDispatcherTest;
+
+/**
+ * When 'onWindowInfosChanged' arguments contain a duplicate entry for the same window, dispatcher
+ * should crash.
+ */
+TEST_F(InputDispatcherDeathTest, DuplicateWindowInfosAbortDispatcher) {
+ testing::GTEST_FLAG(death_test_style) = "threadsafe";
+ ScopedSilentDeath _silentDeath;
+
+ std::shared_ptr<FakeApplicationHandle> application = std::make_shared<FakeApplicationHandle>();
+ sp<FakeWindowHandle> window = sp<FakeWindowHandle>::make(application, mDispatcher,
+ "Fake Window", ADISPLAY_ID_DEFAULT);
+ ASSERT_DEATH(mDispatcher->onWindowInfosChanged(
+ {{*window->getInfo(), *window->getInfo()}, {}, 0, 0}),
+ "Incorrect WindowInfosUpdate provided");
+}
+
TEST_F(InputDispatcherTest, WhenDisplayNotSpecified_InjectMotionToDefaultDisplay) {
std::shared_ptr<FakeApplicationHandle> application = std::make_shared<FakeApplicationHandle>();
sp<FakeWindowHandle> window = sp<FakeWindowHandle>::make(application, mDispatcher,
@@ -6974,6 +6992,39 @@
mWindow->assertNoEvents();
}
+TEST_F(InputDispatcherFallbackKeyTest, WindowRemovedDuringPolicyCall) {
+ setFallback(AKEYCODE_B);
+ mDispatcher->notifyKey(
+ KeyArgsBuilder(ACTION_DOWN, AINPUT_SOURCE_KEYBOARD).keyCode(AKEYCODE_A).build());
+
+ // Do not handle this key event.
+ consumeKey(/*handled=*/false,
+ AllOf(WithKeyAction(ACTION_DOWN), WithKeyCode(AKEYCODE_A), WithFlags(0)));
+ ASSERT_NO_FATAL_FAILURE(mFakePolicy->assertUnhandledKeyReported(AKEYCODE_A));
+ consumeKey(/*handled=*/true,
+ AllOf(WithKeyAction(ACTION_DOWN), WithKeyCode(AKEYCODE_B),
+ WithFlags(AKEY_EVENT_FLAG_FALLBACK)));
+
+ mFakePolicy->setUnhandledKeyHandler([&](const KeyEvent& event) {
+ // When the unhandled key is reported to the policy next, remove the input channel.
+ mDispatcher->removeInputChannel(mWindow->getToken());
+ return KeyEventBuilder(event).keyCode(AKEYCODE_B).build();
+ });
+ // Release the original key, and let the app now handle the previously unhandled key.
+ // This should result in the previously generated fallback key to be cancelled.
+ // Since the policy was notified of the unhandled DOWN event earlier, it will also be notified
+ // of the UP event for consistency. The Dispatcher calls into the policy from its own thread
+ // without holding the lock, because it need to synchronously fetch the fallback key. While in
+ // the policy call, we will now remove the input channel. Once the policy call returns, the
+ // Dispatcher will no longer have a channel to send cancellation events to. Ensure this does
+ // not cause any crashes.
+ mDispatcher->notifyKey(
+ KeyArgsBuilder(ACTION_UP, AINPUT_SOURCE_KEYBOARD).keyCode(AKEYCODE_A).build());
+ consumeKey(/*handled=*/true,
+ AllOf(WithKeyAction(ACTION_UP), WithKeyCode(AKEYCODE_A), WithFlags(0)));
+ ASSERT_NO_FATAL_FAILURE(mFakePolicy->assertUnhandledKeyReported(AKEYCODE_A));
+}
+
class InputDispatcherKeyRepeatTest : public InputDispatcherTest {
protected:
static constexpr std::chrono::nanoseconds KEY_REPEAT_TIMEOUT = 40ms;
diff --git a/services/inputflinger/tests/InputReader_test.cpp b/services/inputflinger/tests/InputReader_test.cpp
index 86e57bc..460a7b1 100644
--- a/services/inputflinger/tests/InputReader_test.cpp
+++ b/services/inputflinger/tests/InputReader_test.cpp
@@ -2919,13 +2919,11 @@
mapper.assertProcessWasCalled();
// Simulate a kernel buffer overflow, which generates a SYN_DROPPED event.
- // This should reset the mapper.
event.type = EV_SYN;
event.code = SYN_DROPPED;
event.value = 0;
unused = mDevice->process(&event, /*count=*/1);
mapper.assertProcessWasNotCalled();
- mapper.assertResetWasCalled();
// All events until the next SYN_REPORT should be dropped.
event.type = EV_KEY;
@@ -2935,11 +2933,13 @@
mapper.assertProcessWasNotCalled();
// We get the SYN_REPORT event now, which is not forwarded to mappers.
+ // This should reset the mapper.
event.type = EV_SYN;
event.code = SYN_REPORT;
event.value = 0;
unused = mDevice->process(&event, /*count=*/1);
mapper.assertProcessWasNotCalled();
+ mapper.assertResetWasCalled();
// The mapper receives events normally now.
event.type = EV_KEY;
@@ -4100,7 +4100,7 @@
void SetUp() override { InputMapperTest::SetUp(DEVICE_CLASSES | InputDeviceClass::EXTERNAL); }
};
-TEST_F(KeyboardInputMapperTest_ExternalDevice, WakeBehavior) {
+TEST_F(KeyboardInputMapperTest_ExternalDevice, WakeBehavior_AlphabeticKeyboard) {
// For external devices, keys will trigger wake on key down. Media keys should also trigger
// wake if triggered from external devices.
@@ -4139,6 +4139,36 @@
ASSERT_EQ(POLICY_FLAG_WAKE, args.policyFlags);
}
+TEST_F(KeyboardInputMapperTest_ExternalDevice, WakeBehavior_NoneAlphabeticKeyboard) {
+ // For external devices, keys will trigger wake on key down. Media keys should not trigger
+ // wake if triggered from external non-alphaebtic keyboard (e.g. headsets).
+
+ mFakeEventHub->addKey(EVENTHUB_ID, KEY_PLAY, 0, AKEYCODE_MEDIA_PLAY, 0);
+ mFakeEventHub->addKey(EVENTHUB_ID, KEY_PLAYPAUSE, 0, AKEYCODE_MEDIA_PLAY_PAUSE,
+ POLICY_FLAG_WAKE);
+
+ KeyboardInputMapper& mapper =
+ constructAndAddMapper<KeyboardInputMapper>(AINPUT_SOURCE_KEYBOARD,
+ AINPUT_KEYBOARD_TYPE_NON_ALPHABETIC);
+
+ process(mapper, ARBITRARY_TIME, READ_TIME, EV_KEY, KEY_PLAY, 1);
+ NotifyKeyArgs args;
+ ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyKeyWasCalled(&args));
+ ASSERT_EQ(uint32_t(0), args.policyFlags);
+
+ process(mapper, ARBITRARY_TIME + 1, READ_TIME, EV_KEY, KEY_PLAY, 0);
+ ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyKeyWasCalled(&args));
+ ASSERT_EQ(uint32_t(0), args.policyFlags);
+
+ process(mapper, ARBITRARY_TIME, READ_TIME, EV_KEY, KEY_PLAYPAUSE, 1);
+ ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyKeyWasCalled(&args));
+ ASSERT_EQ(POLICY_FLAG_WAKE, args.policyFlags);
+
+ process(mapper, ARBITRARY_TIME + 1, READ_TIME, EV_KEY, KEY_PLAYPAUSE, 0);
+ ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyKeyWasCalled(&args));
+ ASSERT_EQ(POLICY_FLAG_WAKE, args.policyFlags);
+}
+
TEST_F(KeyboardInputMapperTest_ExternalDevice, DoNotWakeByDefaultBehavior) {
// Tv Remote key's wake behavior is prescribed by the keylayout file.
@@ -9498,15 +9528,16 @@
ASSERT_EQ(uint32_t(1), motionArgs.getPointerCount());
}
-TEST_F(MultiTouchInputMapperTest, Reset_PreservesLastTouchState) {
+TEST_F(MultiTouchInputMapperTest, Reset_RepopulatesMultiTouchState) {
addConfigurationProperty("touch.deviceType", "touchScreen");
prepareDisplay(ui::ROTATION_0);
prepareAxes(POSITION | ID | SLOT | PRESSURE);
MultiTouchInputMapper& mapper = constructAndAddMapper<MultiTouchInputMapper>();
// First finger down.
+ constexpr int32_t x1 = 100, y1 = 200, x2 = 300, y2 = 400;
processId(mapper, FIRST_TRACKING_ID);
- processPosition(mapper, 100, 200);
+ processPosition(mapper, x1, y1);
processPressure(mapper, RAW_PRESSURE_MAX);
processSync(mapper);
ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(
@@ -9515,14 +9546,32 @@
// Second finger down.
processSlot(mapper, SECOND_SLOT);
processId(mapper, SECOND_TRACKING_ID);
- processPosition(mapper, 300, 400);
+ processPosition(mapper, x2, y2);
processPressure(mapper, RAW_PRESSURE_MAX);
processSync(mapper);
ASSERT_NO_FATAL_FAILURE(
mFakeListener->assertNotifyMotionWasCalled(WithMotionAction(ACTION_POINTER_1_DOWN)));
+ // Set MT Slot state to be repopulated for the required slots
+ std::vector<int32_t> mtSlotValues(RAW_SLOT_MAX + 1, -1);
+ mtSlotValues[0] = FIRST_TRACKING_ID;
+ mtSlotValues[1] = SECOND_TRACKING_ID;
+ mFakeEventHub->setMtSlotValues(EVENTHUB_ID, ABS_MT_TRACKING_ID, mtSlotValues);
+
+ mtSlotValues[0] = x1;
+ mtSlotValues[1] = x2;
+ mFakeEventHub->setMtSlotValues(EVENTHUB_ID, ABS_MT_POSITION_X, mtSlotValues);
+
+ mtSlotValues[0] = y1;
+ mtSlotValues[1] = y2;
+ mFakeEventHub->setMtSlotValues(EVENTHUB_ID, ABS_MT_POSITION_Y, mtSlotValues);
+
+ mtSlotValues[0] = RAW_PRESSURE_MAX;
+ mtSlotValues[1] = RAW_PRESSURE_MAX;
+ mFakeEventHub->setMtSlotValues(EVENTHUB_ID, ABS_MT_PRESSURE, mtSlotValues);
+
// Reset the mapper. When the mapper is reset, we expect the current multi-touch state to be
- // preserved. Resetting should cancel the ongoing gesture.
+ // repopulated. Resetting should cancel the ongoing gesture.
resetMapper(mapper, ARBITRARY_TIME);
ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(
WithMotionAction(AMOTION_EVENT_ACTION_CANCEL)));
diff --git a/services/inputflinger/tests/InterfaceMocks.h b/services/inputflinger/tests/InterfaceMocks.h
index 9de80af..db89168 100644
--- a/services/inputflinger/tests/InterfaceMocks.h
+++ b/services/inputflinger/tests/InterfaceMocks.h
@@ -132,6 +132,9 @@
MOCK_METHOD(status_t, getAbsoluteAxisValue, (int32_t deviceId, int32_t axis, int32_t* outValue),
(const, override));
+ MOCK_METHOD(base::Result<std::vector<int32_t>>, getMtSlotValues,
+ (int32_t deviceId, int32_t axis, size_t slotCount), (const, override));
+
MOCK_METHOD(int32_t, getKeyCodeForKeyLocation, (int32_t deviceId, int32_t locationKeyCode),
(const, override));
MOCK_METHOD(bool, markSupportedKeyCodes,
diff --git a/services/inputflinger/tests/MultiTouchInputMapper_test.cpp b/services/inputflinger/tests/MultiTouchInputMapper_test.cpp
new file mode 100644
index 0000000..d726385
--- /dev/null
+++ b/services/inputflinger/tests/MultiTouchInputMapper_test.cpp
@@ -0,0 +1,276 @@
+/*
+ * 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 "MultiTouchInputMapper.h"
+
+#include <android-base/logging.h>
+#include <gtest/gtest.h>
+#include <list>
+#include <optional>
+
+#include "InputMapperTest.h"
+#include "InterfaceMocks.h"
+#include "TestEventMatchers.h"
+
+#define TAG "MultiTouchpadInputMapperUnit_test"
+
+namespace android {
+
+using testing::_;
+using testing::IsEmpty;
+using testing::Return;
+using testing::SetArgPointee;
+using testing::VariantWith;
+
+static constexpr int32_t DISPLAY_ID = 0;
+static constexpr int32_t DISPLAY_WIDTH = 480;
+static constexpr int32_t DISPLAY_HEIGHT = 800;
+static constexpr std::optional<uint8_t> NO_PORT = std::nullopt; // no physical port is specified
+static constexpr int32_t SLOT_COUNT = 5;
+
+static constexpr int32_t ACTION_POINTER_0_UP =
+ AMOTION_EVENT_ACTION_POINTER_UP | (0 << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT);
+static constexpr int32_t ACTION_POINTER_1_DOWN =
+ AMOTION_EVENT_ACTION_POINTER_DOWN | (1 << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT);
+
+/**
+ * Unit tests for MultiTouchInputMapper.
+ */
+class MultiTouchInputMapperUnitTest : public InputMapperUnitTest {
+protected:
+ void SetUp() override {
+ InputMapperUnitTest::SetUp();
+
+ // Present scan codes
+ expectScanCodes(/*present=*/true,
+ {BTN_TOUCH, BTN_TOOL_FINGER, BTN_TOOL_DOUBLETAP, BTN_TOOL_TRIPLETAP,
+ BTN_TOOL_QUADTAP, BTN_TOOL_QUINTTAP});
+
+ // Missing scan codes that the mapper checks for.
+ expectScanCodes(/*present=*/false,
+ {BTN_TOOL_PEN, BTN_TOOL_RUBBER, BTN_TOOL_BRUSH, BTN_TOOL_PENCIL,
+ BTN_TOOL_AIRBRUSH});
+
+ // Current scan code state - all keys are UP by default
+ setScanCodeState(KeyState::UP, {BTN_LEFT, BTN_RIGHT, BTN_MIDDLE,
+ BTN_BACK, BTN_SIDE, BTN_FORWARD,
+ BTN_EXTRA, BTN_TASK, BTN_TOUCH,
+ BTN_STYLUS, BTN_STYLUS2, BTN_0,
+ BTN_TOOL_FINGER, BTN_TOOL_PEN, BTN_TOOL_RUBBER,
+ BTN_TOOL_BRUSH, BTN_TOOL_PENCIL, BTN_TOOL_AIRBRUSH,
+ BTN_TOOL_MOUSE, BTN_TOOL_LENS, BTN_TOOL_DOUBLETAP,
+ BTN_TOOL_TRIPLETAP, BTN_TOOL_QUADTAP, BTN_TOOL_QUINTTAP});
+
+ setKeyCodeState(KeyState::UP,
+ {AKEYCODE_STYLUS_BUTTON_PRIMARY, AKEYCODE_STYLUS_BUTTON_SECONDARY});
+
+ // Input properties - only INPUT_PROP_DIRECT for touchscreen
+ EXPECT_CALL(mMockEventHub, hasInputProperty(EVENTHUB_ID, _)).WillRepeatedly(Return(false));
+ EXPECT_CALL(mMockEventHub, hasInputProperty(EVENTHUB_ID, INPUT_PROP_DIRECT))
+ .WillRepeatedly(Return(true));
+
+ // Axes that the device has
+ setupAxis(ABS_MT_SLOT, /*valid=*/true, /*min=*/0, /*max=*/SLOT_COUNT - 1, /*resolution=*/0);
+ setupAxis(ABS_MT_TRACKING_ID, /*valid=*/true, /*min*/ 0, /*max=*/255, /*resolution=*/0);
+ setupAxis(ABS_MT_POSITION_X, /*valid=*/true, /*min=*/0, /*max=*/2000, /*resolution=*/24);
+ setupAxis(ABS_MT_POSITION_Y, /*valid=*/true, /*min=*/0, /*max=*/1000, /*resolution=*/24);
+
+ // Axes that the device does not have
+ setupAxis(ABS_MT_PRESSURE, /*valid=*/false, /*min*/ 0, /*max=*/255, /*resolution=*/0);
+ setupAxis(ABS_MT_ORIENTATION, /*valid=*/false, /*min=*/0, /*max=*/0, /*resolution=*/0);
+ setupAxis(ABS_MT_DISTANCE, /*valid=*/false, /*min=*/0, /*max=*/0, /*resolution=*/0);
+ setupAxis(ABS_MT_TOUCH_MAJOR, /*valid=*/false, /*min=*/0, /*max=*/0, /*resolution=*/0);
+ setupAxis(ABS_MT_TOUCH_MINOR, /*valid=*/false, /*min=*/0, /*max=*/0, /*resolution=*/0);
+ setupAxis(ABS_MT_WIDTH_MAJOR, /*valid=*/false, /*min=*/0, /*max=*/0, /*resolution=*/0);
+ setupAxis(ABS_MT_WIDTH_MINOR, /*valid=*/false, /*min=*/0, /*max=*/0, /*resolution=*/0);
+ setupAxis(ABS_MT_TOOL_TYPE, /*valid=*/false, /*min=*/0, /*max=*/0, /*resolution=*/0);
+
+ // reset current slot at the beginning
+ EXPECT_CALL(mMockEventHub, getAbsoluteAxisValue(EVENTHUB_ID, ABS_MT_SLOT, _))
+ .WillRepeatedly([](int32_t, int32_t, int32_t* outValue) {
+ *outValue = 0;
+ return OK;
+ });
+
+ // mark all slots not in use
+ mockSlotValues({});
+
+ mFakePolicy->setDefaultPointerDisplayId(DISPLAY_ID);
+ mFakePolicy->addDisplayViewport(DISPLAY_ID, DISPLAY_WIDTH, DISPLAY_HEIGHT, ui::ROTATION_0,
+ /*isActive=*/true, "local:0", NO_PORT,
+ ViewportType::INTERNAL);
+ createDevice();
+ mMapper = createInputMapper<MultiTouchInputMapper>(*mDeviceContext,
+ mFakePolicy->getReaderConfiguration());
+ }
+
+ // Mocks position and tracking Ids for the provided slots. Remaining slots will be marked
+ // unused.
+ void mockSlotValues(
+ const std::unordered_map<int32_t /*slotIndex*/,
+ std::pair<Point /*position*/, int32_t /*trackingId*/>>&
+ slotValues) {
+ EXPECT_CALL(mMockEventHub, getMtSlotValues(EVENTHUB_ID, _, SLOT_COUNT))
+ .WillRepeatedly([=](int32_t, int32_t axis,
+ size_t slotCount) -> base::Result<std::vector<int32_t>> {
+ // tracking Id for the unused slots must set to be < 0
+ std::vector<int32_t> outMtSlotValues(slotCount + 1, -1);
+ outMtSlotValues[0] = axis;
+ switch (axis) {
+ case ABS_MT_POSITION_X:
+ for (const auto& [slotIndex, valuePair] : slotValues) {
+ outMtSlotValues[slotIndex] = valuePair.first.x;
+ }
+ return outMtSlotValues;
+ case ABS_MT_POSITION_Y:
+ for (const auto& [slotIndex, valuePair] : slotValues) {
+ outMtSlotValues[slotIndex] = valuePair.first.y;
+ }
+ return outMtSlotValues;
+ case ABS_MT_TRACKING_ID:
+ for (const auto& [slotIndex, valuePair] : slotValues) {
+ outMtSlotValues[slotIndex] = valuePair.second;
+ }
+ return outMtSlotValues;
+ default:
+ return base::ResultError("Axis not supported", NAME_NOT_FOUND);
+ }
+ });
+ }
+
+ std::list<NotifyArgs> processPosition(int32_t x, int32_t y) {
+ std::list<NotifyArgs> args;
+ args += process(EV_ABS, ABS_MT_POSITION_X, x);
+ args += process(EV_ABS, ABS_MT_POSITION_Y, y);
+ return args;
+ }
+
+ std::list<NotifyArgs> processId(int32_t id) { return process(EV_ABS, ABS_MT_TRACKING_ID, id); }
+
+ std::list<NotifyArgs> processKey(int32_t code, int32_t value) {
+ return process(EV_KEY, code, value);
+ }
+
+ std::list<NotifyArgs> processSlot(int32_t slot) { return process(EV_ABS, ABS_MT_SLOT, slot); }
+
+ std::list<NotifyArgs> processSync() { return process(EV_SYN, SYN_REPORT, 0); }
+};
+
+// This test simulates a multi-finger gesture with unexpected reset in between. This might happen
+// due to buffer overflow and device with report a SYN_DROPPED. In this case we expect mapper to be
+// reset, MT slot state to be re-populated and the gesture should be cancelled and restarted.
+TEST_F(MultiTouchInputMapperUnitTest, MultiFingerGestureWithUnexpectedReset) {
+ std::list<NotifyArgs> args;
+
+ // Two fingers down at once.
+ constexpr int32_t FIRST_TRACKING_ID = 1, SECOND_TRACKING_ID = 2;
+ int32_t x1 = 100, y1 = 125, x2 = 200, y2 = 225;
+ processKey(BTN_TOUCH, 1);
+ args += processPosition(x1, y1);
+ args += processId(FIRST_TRACKING_ID);
+ args += processSlot(1);
+ args += processPosition(x2, y2);
+ args += processId(SECOND_TRACKING_ID);
+ ASSERT_THAT(args, IsEmpty());
+
+ args = processSync();
+ ASSERT_THAT(args,
+ ElementsAre(VariantWith<NotifyMotionArgs>(
+ WithMotionAction(AMOTION_EVENT_ACTION_DOWN)),
+ VariantWith<NotifyMotionArgs>(
+ WithMotionAction(ACTION_POINTER_1_DOWN))));
+
+ // Move.
+ x1 += 10;
+ y1 += 15;
+ x2 += 5;
+ y2 -= 10;
+ args = processSlot(0);
+ args += processPosition(x1, y1);
+ args += processSlot(1);
+ args += processPosition(x2, y2);
+ ASSERT_THAT(args, IsEmpty());
+
+ args = processSync();
+ ASSERT_THAT(args,
+ ElementsAre(VariantWith<NotifyMotionArgs>(
+ WithMotionAction(AMOTION_EVENT_ACTION_MOVE))));
+ const auto pointerCoordsBeforeReset = std::get<NotifyMotionArgs>(args.back()).pointerCoords;
+
+ // On buffer overflow mapper will be reset and MT slots data will be repopulated
+ EXPECT_CALL(mMockEventHub, getAbsoluteAxisValue(EVENTHUB_ID, ABS_MT_SLOT, _))
+ .WillRepeatedly([=](int32_t, int32_t, int32_t* outValue) {
+ *outValue = 1;
+ return OK;
+ });
+
+ mockSlotValues(
+ {{1, {Point{x1, y1}, FIRST_TRACKING_ID}}, {2, {Point{x2, y2}, SECOND_TRACKING_ID}}});
+
+ setScanCodeState(KeyState::DOWN, {BTN_TOUCH});
+
+ args = mMapper->reset(systemTime(SYSTEM_TIME_MONOTONIC));
+ ASSERT_THAT(args,
+ ElementsAre(VariantWith<NotifyMotionArgs>(
+ WithMotionAction(AMOTION_EVENT_ACTION_CANCEL))));
+
+ // SYN_REPORT should restart the gesture again
+ args = processSync();
+ ASSERT_THAT(args,
+ ElementsAre(VariantWith<NotifyMotionArgs>(
+ WithMotionAction(AMOTION_EVENT_ACTION_DOWN)),
+ VariantWith<NotifyMotionArgs>(
+ WithMotionAction(ACTION_POINTER_1_DOWN))));
+ ASSERT_EQ(std::get<NotifyMotionArgs>(args.back()).pointerCoords, pointerCoordsBeforeReset);
+
+ // Move.
+ x1 += 10;
+ y1 += 15;
+ x2 += 5;
+ y2 -= 10;
+ args = processSlot(0);
+ args += processPosition(x1, y1);
+ args += processSlot(1);
+ args += processPosition(x2, y2);
+ ASSERT_THAT(args, IsEmpty());
+
+ args = processSync();
+ ASSERT_THAT(args,
+ ElementsAre(VariantWith<NotifyMotionArgs>(
+ WithMotionAction(AMOTION_EVENT_ACTION_MOVE))));
+
+ // First finger up.
+ args = processSlot(0);
+ args += processId(-1);
+ ASSERT_THAT(args, IsEmpty());
+
+ args = processSync();
+ ASSERT_THAT(args,
+ ElementsAre(VariantWith<NotifyMotionArgs>(WithMotionAction(ACTION_POINTER_0_UP))));
+
+ // Second finger up.
+ processKey(BTN_TOUCH, 0);
+ args = processSlot(1);
+ args += processId(-1);
+ ASSERT_THAT(args, IsEmpty());
+
+ args = processSync();
+ ASSERT_THAT(args,
+ ElementsAre(
+ VariantWith<NotifyMotionArgs>(WithMotionAction(AMOTION_EVENT_ACTION_UP))));
+}
+
+} // namespace android
diff --git a/services/inputflinger/tests/TouchpadInputMapper_test.cpp b/services/inputflinger/tests/TouchpadInputMapper_test.cpp
index 4be1e8c..fbafbad 100644
--- a/services/inputflinger/tests/TouchpadInputMapper_test.cpp
+++ b/services/inputflinger/tests/TouchpadInputMapper_test.cpp
@@ -103,12 +103,19 @@
setupAxis(ABS_MT_TOUCH_MINOR, /*valid=*/false, /*min=*/0, /*max=*/0, /*resolution=*/0);
setupAxis(ABS_MT_WIDTH_MAJOR, /*valid=*/false, /*min=*/0, /*max=*/0, /*resolution=*/0);
setupAxis(ABS_MT_WIDTH_MINOR, /*valid=*/false, /*min=*/0, /*max=*/0, /*resolution=*/0);
+ setupAxis(ABS_MT_TRACKING_ID, /*valid=*/false, /*min=*/0, /*max=*/0, /*resolution=*/0);
+ setupAxis(ABS_MT_DISTANCE, /*valid=*/false, /*min=*/0, /*max=*/0, /*resolution=*/0);
+ setupAxis(ABS_MT_TOOL_TYPE, /*valid=*/false, /*min=*/0, /*max=*/0, /*resolution=*/0);
EXPECT_CALL(mMockEventHub, getAbsoluteAxisValue(EVENTHUB_ID, ABS_MT_SLOT, testing::_))
.WillRepeatedly([](int32_t eventHubId, int32_t, int32_t* outValue) {
*outValue = 0;
return OK;
});
+ EXPECT_CALL(mMockEventHub, getMtSlotValues(EVENTHUB_ID, testing::_, testing::_))
+ .WillRepeatedly([]() -> base::Result<std::vector<int32_t>> {
+ return base::ResultError("Axis not supported", NAME_NOT_FOUND);
+ });
createDevice();
mMapper = createInputMapper<TouchpadInputMapper>(*mDeviceContext, mReaderConfiguration);
}
diff --git a/services/inputflinger/tests/fuzzers/MapperHelpers.h b/services/inputflinger/tests/fuzzers/MapperHelpers.h
index 81c570d..7898126 100644
--- a/services/inputflinger/tests/fuzzers/MapperHelpers.h
+++ b/services/inputflinger/tests/fuzzers/MapperHelpers.h
@@ -201,6 +201,18 @@
int32_t* outValue) const override {
return mFdp->ConsumeIntegral<status_t>();
}
+ base::Result<std::vector<int32_t>> getMtSlotValues(int32_t deviceId, int32_t axis,
+ size_t slotCount) const override {
+ if (mFdp->ConsumeBool()) {
+ std::vector<int32_t> outValues(slotCount + 1);
+ for (size_t i = 0; i < outValues.size(); i++) {
+ outValues.push_back(mFdp->ConsumeIntegral<int32_t>());
+ }
+ return std::move(outValues);
+ } else {
+ return base::ResultError("Fuzzer", UNKNOWN_ERROR);
+ }
+ }
bool markSupportedKeyCodes(int32_t deviceId, const std::vector<int32_t>& keyCodes,
uint8_t* outFlags) const override {
return mFdp->ConsumeBool();
diff --git a/services/surfaceflinger/CompositionEngine/tests/OutputTest.cpp b/services/surfaceflinger/CompositionEngine/tests/OutputTest.cpp
index 30ba9e4..a7296f3 100644
--- a/services/surfaceflinger/CompositionEngine/tests/OutputTest.cpp
+++ b/services/surfaceflinger/CompositionEngine/tests/OutputTest.cpp
@@ -3370,7 +3370,6 @@
static const mat4 kDefaultColorTransformMat;
static const Region kDebugRegion;
- static const compositionengine::CompositionRefreshArgs kDefaultRefreshArgs;
static const HdrCapabilities kHdrCapabilities;
StrictMock<mock::CompositionEngine> mCompositionEngine;
@@ -3393,7 +3392,6 @@
const Rect OutputComposeSurfacesTest::kDefaultOutputViewport{1005, 1006, 1007, 1008};
const Rect OutputComposeSurfacesTest::kDefaultOutputDestinationClip{1013, 1014, 1015, 1016};
const mat4 OutputComposeSurfacesTest::kDefaultColorTransformMat{mat4() * 0.5f};
-const compositionengine::CompositionRefreshArgs OutputComposeSurfacesTest::kDefaultRefreshArgs;
const Region OutputComposeSurfacesTest::kDebugRegion{Rect{100, 101, 102, 103}};
const HdrCapabilities OutputComposeSurfacesTest::
diff --git a/services/surfaceflinger/FrontEnd/LayerSnapshotBuilder.cpp b/services/surfaceflinger/FrontEnd/LayerSnapshotBuilder.cpp
index ad5e42b..bb32afa 100644
--- a/services/surfaceflinger/FrontEnd/LayerSnapshotBuilder.cpp
+++ b/services/surfaceflinger/FrontEnd/LayerSnapshotBuilder.cpp
@@ -1217,8 +1217,8 @@
Rect inputBoundsInDisplaySpace =
getInputBoundsInDisplaySpace(*cropLayerSnapshot, inputBounds,
displayInfo.transform);
- snapshot->inputInfo.touchableRegion = snapshot->inputInfo.touchableRegion.intersect(
- displayInfo.transform.transform(inputBoundsInDisplaySpace));
+ snapshot->inputInfo.touchableRegion =
+ snapshot->inputInfo.touchableRegion.intersect(inputBoundsInDisplaySpace);
}
// If the layer is a clone, we need to crop the input region to cloned root to prevent
diff --git a/services/surfaceflinger/tests/unittests/FrameTracerTest.cpp b/services/surfaceflinger/tests/unittests/FrameTracerTest.cpp
index 2c71a2e..99062f0 100644
--- a/services/surfaceflinger/tests/unittests/FrameTracerTest.cpp
+++ b/services/surfaceflinger/tests/unittests/FrameTracerTest.cpp
@@ -179,23 +179,35 @@
tracingSession->StopBlocking();
auto packets = readGraphicsFramePacketsBlocking(tracingSession.get());
- EXPECT_EQ(packets.size(), 1);
+ ASSERT_EQ(packets.size(), 2);
- const auto& packet = packets[0];
- ASSERT_TRUE(packet.has_timestamp());
- EXPECT_EQ(packet.timestamp(), timestamp);
- ASSERT_TRUE(packet.has_graphics_frame_event());
- const auto& frame_event = packet.graphics_frame_event();
- ASSERT_TRUE(frame_event.has_buffer_event());
- const auto& buffer_event = frame_event.buffer_event();
- ASSERT_TRUE(buffer_event.has_buffer_id());
- EXPECT_EQ(buffer_event.buffer_id(), bufferID);
- ASSERT_TRUE(buffer_event.has_frame_number());
- EXPECT_EQ(buffer_event.frame_number(), frameNumber);
- ASSERT_TRUE(buffer_event.has_type());
- EXPECT_EQ(buffer_event.type(), perfetto::protos::GraphicsFrameEvent_BufferEventType(type));
- ASSERT_TRUE(buffer_event.has_duration_ns());
- EXPECT_EQ(buffer_event.duration_ns(), duration);
+ const auto& packet1 = packets[0];
+ ASSERT_TRUE(packet1.has_timestamp());
+ EXPECT_EQ(packet1.timestamp(), timestamp);
+ ASSERT_TRUE(packet1.has_graphics_frame_event());
+ const auto& frame_event1 = packet1.graphics_frame_event();
+ ASSERT_TRUE(frame_event1.has_buffer_event());
+ const auto& buffer_event1 = frame_event1.buffer_event();
+ ASSERT_TRUE(buffer_event1.has_buffer_id());
+ EXPECT_EQ(buffer_event1.buffer_id(), bufferID);
+ ASSERT_TRUE(buffer_event1.has_frame_number());
+ EXPECT_EQ(buffer_event1.frame_number(), frameNumber);
+ ASSERT_TRUE(buffer_event1.has_type());
+ EXPECT_EQ(buffer_event1.type(), perfetto::protos::GraphicsFrameEvent_BufferEventType(type));
+ ASSERT_TRUE(buffer_event1.has_duration_ns());
+ EXPECT_EQ(buffer_event1.duration_ns(), duration);
+
+ const auto& packet2 = packets[1];
+ ASSERT_TRUE(packet2.has_timestamp());
+ EXPECT_EQ(packet2.timestamp(), 0);
+ ASSERT_TRUE(packet2.has_graphics_frame_event());
+ const auto& frame_event2 = packet2.graphics_frame_event();
+ ASSERT_TRUE(frame_event2.has_buffer_event());
+ const auto& buffer_event2 = frame_event2.buffer_event();
+ ASSERT_TRUE(buffer_event2.has_type());
+ EXPECT_EQ(buffer_event2.type(),
+ perfetto::protos::GraphicsFrameEvent_BufferEventType(
+ FrameTracer::FrameEvent::UNSPECIFIED));
}
}
@@ -219,7 +231,18 @@
tracingSession->StopBlocking();
auto packets = readGraphicsFramePacketsBlocking(tracingSession.get());
- EXPECT_EQ(packets.size(), 0);
+ ASSERT_EQ(packets.size(), 1);
+ const auto& packet = packets[0];
+ ASSERT_TRUE(packet.has_timestamp());
+ EXPECT_EQ(packet.timestamp(), 0);
+ ASSERT_TRUE(packet.has_graphics_frame_event());
+ const auto& frame_event = packet.graphics_frame_event();
+ ASSERT_TRUE(frame_event.has_buffer_event());
+ const auto& buffer_event = frame_event.buffer_event();
+ ASSERT_TRUE(buffer_event.has_type());
+ EXPECT_EQ(buffer_event.type(),
+ perfetto::protos::GraphicsFrameEvent_BufferEventType(
+ FrameTracer::FrameEvent::UNSPECIFIED));
}
{
@@ -235,22 +258,56 @@
tracingSession->StopBlocking();
auto packets = readGraphicsFramePacketsBlocking(tracingSession.get());
- EXPECT_EQ(packets.size(), 2); // Two packets because of the extra trace made above.
+ ASSERT_EQ(packets.size(), 3);
- const auto& packet = packets[1];
- ASSERT_TRUE(packet.has_timestamp());
- EXPECT_EQ(packet.timestamp(), timestamp);
- ASSERT_TRUE(packet.has_graphics_frame_event());
- const auto& frame_event = packet.graphics_frame_event();
- ASSERT_TRUE(frame_event.has_buffer_event());
- const auto& buffer_event = frame_event.buffer_event();
- ASSERT_TRUE(buffer_event.has_buffer_id());
- EXPECT_EQ(buffer_event.buffer_id(), bufferID);
- ASSERT_TRUE(buffer_event.has_frame_number());
- EXPECT_EQ(buffer_event.frame_number(), frameNumber);
- ASSERT_TRUE(buffer_event.has_type());
- EXPECT_EQ(buffer_event.type(), perfetto::protos::GraphicsFrameEvent_BufferEventType(type));
- EXPECT_FALSE(buffer_event.has_duration_ns());
+ const auto& packet1 = packets[0];
+ ASSERT_TRUE(packet1.has_timestamp());
+ EXPECT_EQ(packet1.timestamp(), timestamp);
+ ASSERT_TRUE(packet1.has_graphics_frame_event());
+ const auto& frame_event1 = packet1.graphics_frame_event();
+ ASSERT_TRUE(frame_event1.has_buffer_event());
+ const auto& buffer_event1 = frame_event1.buffer_event();
+ ASSERT_TRUE(buffer_event1.has_buffer_id());
+ EXPECT_EQ(buffer_event1.buffer_id(), bufferID);
+ ASSERT_TRUE(buffer_event1.has_frame_number());
+ EXPECT_EQ(buffer_event1.frame_number(), frameNumber);
+ ASSERT_TRUE(buffer_event1.has_type());
+ EXPECT_EQ(buffer_event1.type(),
+ perfetto::protos::GraphicsFrameEvent::BufferEventType(type));
+ EXPECT_FALSE(buffer_event1.has_duration_ns());
+
+ const auto& packet2 = packets[1];
+ ASSERT_TRUE(packet2.has_timestamp());
+ EXPECT_EQ(packet2.timestamp(), timestamp);
+ ASSERT_TRUE(packet2.has_graphics_frame_event());
+ const auto& frame_event2 = packet2.graphics_frame_event();
+ ASSERT_TRUE(frame_event2.has_buffer_event());
+ const auto& buffer_event2 = frame_event2.buffer_event();
+ ASSERT_TRUE(buffer_event2.has_buffer_id());
+ EXPECT_EQ(buffer_event2.buffer_id(), bufferID);
+ ASSERT_TRUE(buffer_event2.has_frame_number());
+ EXPECT_EQ(buffer_event2.frame_number(), frameNumber);
+ ASSERT_TRUE(buffer_event2.has_type());
+ EXPECT_EQ(buffer_event2.type(),
+ perfetto::protos::GraphicsFrameEvent::BufferEventType(type));
+ EXPECT_FALSE(buffer_event2.has_duration_ns());
+
+ const auto& packet3 = packets[2];
+ ASSERT_TRUE(packet3.has_timestamp());
+ EXPECT_EQ(packet3.timestamp(), 0);
+ ASSERT_TRUE(packet3.has_graphics_frame_event());
+ const auto& frame_event3 = packet3.graphics_frame_event();
+ ASSERT_TRUE(frame_event3.has_buffer_event());
+ const auto& buffer_event3 = frame_event3.buffer_event();
+ ASSERT_TRUE(buffer_event3.has_buffer_id());
+ EXPECT_EQ(buffer_event3.buffer_id(), bufferID);
+ ASSERT_TRUE(buffer_event3.has_frame_number());
+ EXPECT_EQ(buffer_event3.frame_number(), 0);
+ ASSERT_TRUE(buffer_event3.has_type());
+ EXPECT_EQ(buffer_event3.type(),
+ perfetto::protos::GraphicsFrameEvent::BufferEventType(
+ FrameTracer::FrameEvent::UNSPECIFIED));
+ EXPECT_FALSE(buffer_event3.has_duration_ns());
}
}
@@ -285,7 +342,7 @@
tracingSession->StopBlocking();
auto packets = readGraphicsFramePacketsBlocking(tracingSession.get());
- EXPECT_EQ(packets.size(), 2);
+ ASSERT_EQ(packets.size(), 3);
const auto& packet1 = packets[0];
ASSERT_TRUE(packet1.has_timestamp());
@@ -293,6 +350,8 @@
ASSERT_TRUE(packet1.has_graphics_frame_event());
ASSERT_TRUE(packet1.graphics_frame_event().has_buffer_event());
ASSERT_FALSE(packet1.graphics_frame_event().buffer_event().has_duration_ns());
+ EXPECT_EQ(packet1.graphics_frame_event().buffer_event().type(),
+ perfetto::protos::GraphicsFrameEvent::BufferEventType(type));
const auto& packet2 = packets[1];
ASSERT_TRUE(packet2.has_timestamp());
@@ -300,6 +359,17 @@
ASSERT_TRUE(packet2.has_graphics_frame_event());
ASSERT_TRUE(packet2.graphics_frame_event().has_buffer_event());
ASSERT_FALSE(packet2.graphics_frame_event().buffer_event().has_duration_ns());
+ EXPECT_EQ(packet2.graphics_frame_event().buffer_event().type(),
+ perfetto::protos::GraphicsFrameEvent::BufferEventType(type));
+
+ const auto& packet3 = packets[2];
+ ASSERT_TRUE(packet3.has_timestamp());
+ EXPECT_EQ(packet3.timestamp(), 0);
+ ASSERT_TRUE(packet3.has_graphics_frame_event());
+ ASSERT_TRUE(packet3.graphics_frame_event().has_buffer_event());
+ EXPECT_EQ(packet3.graphics_frame_event().buffer_event().type(),
+ perfetto::protos::GraphicsFrameEvent::BufferEventType(
+ FrameTracer::FrameEvent::UNSPECIFIED));
}
TEST_F(FrameTracerTest, traceFenceOlderThanDeadline_ShouldBeIgnored) {
@@ -322,7 +392,15 @@
tracingSession->StopBlocking();
auto packets = readGraphicsFramePacketsBlocking(tracingSession.get());
- EXPECT_EQ(packets.size(), 0);
+ ASSERT_EQ(packets.size(), 1);
+ const auto& packet = packets[0];
+ ASSERT_TRUE(packet.has_timestamp());
+ EXPECT_EQ(packet.timestamp(), 0);
+ ASSERT_TRUE(packet.has_graphics_frame_event());
+ ASSERT_TRUE(packet.graphics_frame_event().has_buffer_event());
+ EXPECT_EQ(packet.graphics_frame_event().buffer_event().type(),
+ perfetto::protos::GraphicsFrameEvent::BufferEventType(
+ FrameTracer::FrameEvent::UNSPECIFIED));
}
TEST_F(FrameTracerTest, traceFenceWithValidStartTime_ShouldHaveCorrectDuration) {
@@ -357,7 +435,7 @@
tracingSession->StopBlocking();
auto packets = readGraphicsFramePacketsBlocking(tracingSession.get());
- EXPECT_EQ(packets.size(), 2);
+ ASSERT_EQ(packets.size(), 3);
const auto& packet1 = packets[0];
ASSERT_TRUE(packet1.has_timestamp());
@@ -367,6 +445,7 @@
ASSERT_TRUE(packet1.graphics_frame_event().buffer_event().has_duration_ns());
const auto& buffer_event1 = packet1.graphics_frame_event().buffer_event();
EXPECT_EQ(buffer_event1.duration_ns(), duration);
+ EXPECT_EQ(buffer_event1.type(), perfetto::protos::GraphicsFrameEvent::BufferEventType(type));
const auto& packet2 = packets[1];
ASSERT_TRUE(packet2.has_timestamp());
@@ -376,6 +455,17 @@
ASSERT_TRUE(packet2.graphics_frame_event().buffer_event().has_duration_ns());
const auto& buffer_event2 = packet2.graphics_frame_event().buffer_event();
EXPECT_EQ(buffer_event2.duration_ns(), duration);
+ EXPECT_EQ(buffer_event2.type(), perfetto::protos::GraphicsFrameEvent::BufferEventType(type));
+
+ const auto& packet3 = packets[2];
+ ASSERT_TRUE(packet3.has_timestamp());
+ EXPECT_EQ(packet3.timestamp(), 0);
+ ASSERT_TRUE(packet3.has_graphics_frame_event());
+ ASSERT_TRUE(packet3.graphics_frame_event().has_buffer_event());
+ const auto& buffer_event3 = packet3.graphics_frame_event().buffer_event();
+ EXPECT_EQ(buffer_event3.type(),
+ perfetto::protos::GraphicsFrameEvent::BufferEventType(
+ FrameTracer::FrameEvent::UNSPECIFIED));
}
} // namespace
diff --git a/services/surfaceflinger/tests/unittests/LayerSnapshotTest.cpp b/services/surfaceflinger/tests/unittests/LayerSnapshotTest.cpp
index 55b20b3..6394d63 100644
--- a/services/surfaceflinger/tests/unittests/LayerSnapshotTest.cpp
+++ b/services/surfaceflinger/tests/unittests/LayerSnapshotTest.cpp
@@ -353,6 +353,23 @@
EXPECT_EQ(getSnapshot({.id = 111})->inputInfo.touchableRegion.bounds(), modifiedTouchCrop);
}
+TEST_F(LayerSnapshotTest, CanCropTouchableRegionWithDisplayTransform) {
+ DisplayInfo displayInfo;
+ displayInfo.transform = ui::Transform(ui::Transform::RotationFlags::ROT_90, 1000, 1000);
+ mFrontEndDisplayInfos.emplace_or_replace(ui::LayerStack::fromValue(1), displayInfo);
+
+ Rect touchCrop{300, 300, 400, 500};
+ createRootLayer(3);
+ setCrop(3, touchCrop);
+ setLayerStack(3, 1);
+ Region touch{Rect{0, 0, 1000, 1000}};
+ setTouchableRegionCrop(3, touch, /*touchCropId=*/3, /*replaceTouchableRegionWithCrop=*/false);
+
+ UPDATE_AND_VERIFY(mSnapshotBuilder, {1, 11, 111, 12, 121, 122, 1221, 13, 2, 3});
+ Rect rotatedCrop = {500, 300, 700, 400};
+ EXPECT_EQ(getSnapshot({.id = 3})->inputInfo.touchableRegion.bounds(), rotatedCrop);
+}
+
TEST_F(LayerSnapshotTest, blurUpdatesWhenAlphaChanges) {
int blurRadius = 42;
setBackgroundBlurRadius(1221, static_cast<uint32_t>(blurRadius));
diff --git a/vulkan/libvulkan/driver.cpp b/vulkan/libvulkan/driver.cpp
index 0e45d2d..81fd118 100644
--- a/vulkan/libvulkan/driver.cpp
+++ b/vulkan/libvulkan/driver.cpp
@@ -340,8 +340,9 @@
ALOGD("Unload builtin Vulkan driver.");
// Close the opened device
- ALOG_ASSERT(!hal_.dev_->common.close(hal_.dev_->common),
- "hw_device_t::close() failed.");
+ int err = hal_.dev_->common.close(
+ const_cast<struct hw_device_t*>(&hal_.dev_->common));
+ ALOG_ASSERT(!err, "hw_device_t::close() failed.");
// Close the opened shared library in the hw_module_t
android_unload_sphal_library(hal_.dev_->common.module->dso);